From 8d0723c2c36c7200d317fe1285ab86d24068c342 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 16 Feb 2018 21:37:47 +0100 Subject: [PATCH 001/828] ImFontAtlas: Added IsBuilt() helper. --- imgui.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/imgui.h b/imgui.h index 7629e06c874f..f075d72d3f5c 100644 --- a/imgui.h +++ b/imgui.h @@ -1627,7 +1627,7 @@ enum ImFontAtlasFlags_ // 2. Call GetTexDataAsAlpha8() or GetTexDataAsRGBA32() to build and retrieve pixels data. // 3. Upload the pixels data into a texture within your graphics system. // 4. Call SetTexID(my_tex_id); and pass the pointer/identifier to your texture. This value will be passed back to you during rendering to identify the texture. -// IMPORTANT: If you pass a 'glyph_ranges' array to AddFont*** functions, you need to make sure that your array persist up until the ImFont is build (when calling GetTextData*** or Build()). We only copy the pointer, not the data. +// IMPORTANT: If you pass a 'glyph_ranges' array to AddFont*** functions, you need to make sure that your array persist up until the ImFont is build (when calling GetTexData*** or Build()). We only copy the pointer, not the data. struct ImFontAtlas { IMGUI_API ImFontAtlas(); @@ -1648,6 +1648,7 @@ struct ImFontAtlas // RGBA32 format is provided for convenience and compatibility, but note that unless you use CustomRect to draw color data, the RGB pixels emitted from Fonts will all be white (~75% of waste). // Pitch = Width * BytesPerPixels IMGUI_API bool Build(); // Build pixels data. This is called automatically for you by the GetTexData*** functions. + IMGUI_API bool IsBuilt() { return Fonts.Size > 0 && (TexPixelsAlpha8 != NULL || TexPixelsRGBA32 != NULL); } IMGUI_API void GetTexDataAsAlpha8(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel = NULL); // 1 byte per-pixel IMGUI_API void GetTexDataAsRGBA32(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel = NULL); // 4 bytes-per-pixel void SetTexID(ImTextureID id) { TexID = id; } From b0a8734c92120b53e4f477d57a7ce20dd5c033bb Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 16 Feb 2018 21:38:38 +0100 Subject: [PATCH 002/828] Examples: GLFW+GL3: Split imgui_impl_glfw_gl3 into imgui_impl_glfw and imgui_impl_gl3 (wip) --- examples/opengl3_example/imgui_impl_glfw.cpp | 221 ++++++++++++++ ...mgui_impl_glfw_gl3.h => imgui_impl_glfw.h} | 19 +- ...pl_glfw_gl3.cpp => imgui_impl_opengl3.cpp} | 279 +++--------------- examples/opengl3_example/imgui_impl_opengl3.h | 11 + examples/opengl3_example/main.cpp | 14 +- .../opengl3_example/opengl3_example.vcxproj | 6 +- .../opengl3_example.vcxproj.filters | 18 +- 7 files changed, 304 insertions(+), 264 deletions(-) create mode 100644 examples/opengl3_example/imgui_impl_glfw.cpp rename examples/opengl3_example/{imgui_impl_glfw_gl3.h => imgui_impl_glfw.h} (57%) rename examples/opengl3_example/{imgui_impl_glfw_gl3.cpp => imgui_impl_opengl3.cpp} (50%) create mode 100644 examples/opengl3_example/imgui_impl_opengl3.h diff --git a/examples/opengl3_example/imgui_impl_glfw.cpp b/examples/opengl3_example/imgui_impl_glfw.cpp new file mode 100644 index 000000000000..7b341a87dd2b --- /dev/null +++ b/examples/opengl3_example/imgui_impl_glfw.cpp @@ -0,0 +1,221 @@ +// ImGui GLFW binding with OpenGL3 + shaders +// (GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) +// (GL3W is a helper library to access OpenGL functions since there is no standard header to access modern OpenGL functions easily. Alternatives are GLEW, Glad, etc.) + +// Implemented features: +// [X] User texture binding. Cast 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. +// [X] Gamepad navigation mapping. Enable with 'io.NavFlags |= ImGuiNavFlags_EnableGamepad'. + +// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. +// If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). +// If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp. +// https://github.com/ocornut/imgui + +// CHANGELOG +// (minor and older changes stripped away, please see git history for details) +// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplGlfwGL3_RenderDrawData() in the .h file so you can call it yourself. +// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. +// 2018-02-06: Inputs: Added mapping for ImGuiKey_Space. +// 2018-01-25: Inputs: Added gamepad support if ImGuiNavFlags_EnableGamepad is set. +// 2018-01-25: Inputs: Honoring the io.WantMoveMouse by repositioning the mouse by using navigation and ImGuiNavFlags_MoveMouse is set. +// 2018-01-20: Inputs: Added Horizontal Mouse Wheel support. +// 2018-01-18: Inputs: Added mapping for ImGuiKey_Insert. +// 2018-01-07: OpenGL: Changed GLSL shader version from 330 to 150. (Also changed GL context from 3.3 to 3.2 in example's main.cpp) +// 2017-09-01: OpenGL: Save and restore current bound sampler. Save and restore current polygon mode. +// 2017-08-25: Inputs: MousePos set to -FLT_MAX,-FLT_MAX when mouse is unavailable/missing (instead of -1,-1). +// 2017-05-01: OpenGL: Fixed save and restore of current blend function state. +// 2016-10-15: Misc: Added a void* user_data parameter to Clipboard function handlers. +// 2016-09-05: OpenGL: Fixed save and restore of current scissor rectangle. +// 2016-04-30: OpenGL: Fixed save and restore of current GL_ACTIVE_TEXTURE. + +#include "imgui.h" +#include "imgui_impl_glfw.h" + +// GL3W/GLFW +#include +#ifdef _WIN32 +#undef APIENTRY +#define GLFW_EXPOSE_NATIVE_WIN32 +#include +#endif + +// Data +static GLFWwindow* g_Window = NULL; +static double g_Time = 0.0f; +static bool g_MouseJustPressed[3] = { false, false, false }; + +static const char* ImGui_ImplGlfwGL3_GetClipboardText(void* user_data) +{ + return glfwGetClipboardString((GLFWwindow*)user_data); +} + +static void ImGui_ImplGlfwGL3_SetClipboardText(void* user_data, const char* text) +{ + glfwSetClipboardString((GLFWwindow*)user_data, text); +} + +void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow*, int button, int action, int /*mods*/) +{ + if (action == GLFW_PRESS && button >= 0 && button < 3) + g_MouseJustPressed[button] = true; +} + +void ImGui_ImplGlfw_ScrollCallback(GLFWwindow*, double xoffset, double yoffset) +{ + ImGuiIO& io = ImGui::GetIO(); + io.MouseWheelH += (float)xoffset; + io.MouseWheel += (float)yoffset; +} + +void ImGui_ImplGlfw_KeyCallback(GLFWwindow*, int key, int, int action, int mods) +{ + ImGuiIO& io = ImGui::GetIO(); + if (action == GLFW_PRESS) + io.KeysDown[key] = true; + if (action == GLFW_RELEASE) + io.KeysDown[key] = false; + + (void)mods; // Modifiers are not reliable across systems + io.KeyCtrl = io.KeysDown[GLFW_KEY_LEFT_CONTROL] || io.KeysDown[GLFW_KEY_RIGHT_CONTROL]; + io.KeyShift = io.KeysDown[GLFW_KEY_LEFT_SHIFT] || io.KeysDown[GLFW_KEY_RIGHT_SHIFT]; + io.KeyAlt = io.KeysDown[GLFW_KEY_LEFT_ALT] || io.KeysDown[GLFW_KEY_RIGHT_ALT]; + io.KeySuper = io.KeysDown[GLFW_KEY_LEFT_SUPER] || io.KeysDown[GLFW_KEY_RIGHT_SUPER]; +} + +void ImGui_ImplGlfw_CharCallback(GLFWwindow*, unsigned int c) +{ + ImGuiIO& io = ImGui::GetIO(); + if (c > 0 && c < 0x10000) + io.AddInputCharacter((unsigned short)c); +} + +bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks) +{ + g_Window = window; + + ImGuiIO& io = ImGui::GetIO(); + io.KeyMap[ImGuiKey_Tab] = GLFW_KEY_TAB; // Keyboard mapping. ImGui will use those indices to peek into the io.KeyDown[] array. + io.KeyMap[ImGuiKey_LeftArrow] = GLFW_KEY_LEFT; + io.KeyMap[ImGuiKey_RightArrow] = GLFW_KEY_RIGHT; + io.KeyMap[ImGuiKey_UpArrow] = GLFW_KEY_UP; + io.KeyMap[ImGuiKey_DownArrow] = GLFW_KEY_DOWN; + io.KeyMap[ImGuiKey_PageUp] = GLFW_KEY_PAGE_UP; + io.KeyMap[ImGuiKey_PageDown] = GLFW_KEY_PAGE_DOWN; + io.KeyMap[ImGuiKey_Home] = GLFW_KEY_HOME; + io.KeyMap[ImGuiKey_End] = GLFW_KEY_END; + io.KeyMap[ImGuiKey_Insert] = GLFW_KEY_INSERT; + io.KeyMap[ImGuiKey_Delete] = GLFW_KEY_DELETE; + io.KeyMap[ImGuiKey_Backspace] = GLFW_KEY_BACKSPACE; + io.KeyMap[ImGuiKey_Space] = GLFW_KEY_SPACE; + io.KeyMap[ImGuiKey_Enter] = GLFW_KEY_ENTER; + io.KeyMap[ImGuiKey_Escape] = GLFW_KEY_ESCAPE; + io.KeyMap[ImGuiKey_A] = GLFW_KEY_A; + io.KeyMap[ImGuiKey_C] = GLFW_KEY_C; + io.KeyMap[ImGuiKey_V] = GLFW_KEY_V; + io.KeyMap[ImGuiKey_X] = GLFW_KEY_X; + io.KeyMap[ImGuiKey_Y] = GLFW_KEY_Y; + io.KeyMap[ImGuiKey_Z] = GLFW_KEY_Z; + + io.SetClipboardTextFn = ImGui_ImplGlfwGL3_SetClipboardText; + io.GetClipboardTextFn = ImGui_ImplGlfwGL3_GetClipboardText; + io.ClipboardUserData = g_Window; +#ifdef _WIN32 + io.ImeWindowHandle = glfwGetWin32Window(g_Window); +#endif + + if (install_callbacks) + { + glfwSetMouseButtonCallback(window, ImGui_ImplGlfw_MouseButtonCallback); + glfwSetScrollCallback(window, ImGui_ImplGlfw_ScrollCallback); + glfwSetKeyCallback(window, ImGui_ImplGlfw_KeyCallback); + glfwSetCharCallback(window, ImGui_ImplGlfw_CharCallback); + } + + return true; +} + +void ImGui_ImplGlfw_Shutdown() +{ +} + +void ImGui_ImplGlfw_NewFrame() +{ + ImGuiIO& io = ImGui::GetIO(); + IM_ASSERT(io.Fonts->IsBuilt()); // Font atlas needs to be built, call renderer _NewFrame() function e.g. ImGui_ImplOpenGL3_NewFrame() + + // Setup display size + int w, h; + int display_w, display_h; + glfwGetWindowSize(g_Window, &w, &h); + glfwGetFramebufferSize(g_Window, &display_w, &display_h); + io.DisplaySize = ImVec2((float)w, (float)h); + io.DisplayFramebufferScale = ImVec2(w > 0 ? ((float)display_w / w) : 0, h > 0 ? ((float)display_h / h) : 0); + + // Setup time step + double current_time = glfwGetTime(); + io.DeltaTime = g_Time > 0.0 ? (float)(current_time - g_Time) : (float)(1.0f/60.0f); + g_Time = current_time; + + // Setup inputs + // (we already got mouse wheel, keyboard keys & characters from glfw callbacks polled in glfwPollEvents()) + if (glfwGetWindowAttrib(g_Window, GLFW_FOCUSED)) + { + if (io.WantMoveMouse) + { + glfwSetCursorPos(g_Window, (double)io.MousePos.x, (double)io.MousePos.y); // Set mouse position if requested by io.WantMoveMouse flag (used when io.NavMovesTrue is enabled by user and using directional navigation) + } + else + { + double mouse_x, mouse_y; + glfwGetCursorPos(g_Window, &mouse_x, &mouse_y); + io.MousePos = ImVec2((float)mouse_x, (float)mouse_y); + } + } + else + { + io.MousePos = ImVec2(-FLT_MAX,-FLT_MAX); + } + + for (int i = 0; i < 3; i++) + { + // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame. + io.MouseDown[i] = g_MouseJustPressed[i] || glfwGetMouseButton(g_Window, i) != 0; + g_MouseJustPressed[i] = false; + } + + // Hide OS mouse cursor if ImGui is drawing it + glfwSetInputMode(g_Window, GLFW_CURSOR, io.MouseDrawCursor ? GLFW_CURSOR_HIDDEN : GLFW_CURSOR_NORMAL); + + // Gamepad navigation mapping [BETA] + memset(io.NavInputs, 0, sizeof(io.NavInputs)); + if (io.NavFlags & ImGuiNavFlags_EnableGamepad) + { + // Update gamepad inputs + #define MAP_BUTTON(NAV_NO, BUTTON_NO) { if (buttons_count > BUTTON_NO && buttons[BUTTON_NO] == GLFW_PRESS) io.NavInputs[NAV_NO] = 1.0f; } + #define MAP_ANALOG(NAV_NO, AXIS_NO, V0, V1) { float v = (axes_count > AXIS_NO) ? axes[AXIS_NO] : V0; v = (v - V0) / (V1 - V0); if (v > 1.0f) v = 1.0f; if (io.NavInputs[NAV_NO] < v) io.NavInputs[NAV_NO] = v; } + int axes_count = 0, buttons_count = 0; + const float* axes = glfwGetJoystickAxes(GLFW_JOYSTICK_1, &axes_count); + const unsigned char* buttons = glfwGetJoystickButtons(GLFW_JOYSTICK_1, &buttons_count); + MAP_BUTTON(ImGuiNavInput_Activate, 0); // Cross / A + MAP_BUTTON(ImGuiNavInput_Cancel, 1); // Circle / B + MAP_BUTTON(ImGuiNavInput_Menu, 2); // Square / X + MAP_BUTTON(ImGuiNavInput_Input, 3); // Triangle / Y + MAP_BUTTON(ImGuiNavInput_DpadLeft, 13); // D-Pad Left + MAP_BUTTON(ImGuiNavInput_DpadRight, 11); // D-Pad Right + MAP_BUTTON(ImGuiNavInput_DpadUp, 10); // D-Pad Up + MAP_BUTTON(ImGuiNavInput_DpadDown, 12); // D-Pad Down + MAP_BUTTON(ImGuiNavInput_FocusPrev, 4); // L1 / LB + MAP_BUTTON(ImGuiNavInput_FocusNext, 5); // R1 / RB + MAP_BUTTON(ImGuiNavInput_TweakSlow, 4); // L1 / LB + MAP_BUTTON(ImGuiNavInput_TweakFast, 5); // R1 / RB + MAP_ANALOG(ImGuiNavInput_LStickLeft, 0, -0.3f, -0.9f); + MAP_ANALOG(ImGuiNavInput_LStickRight,0, +0.3f, +0.9f); + MAP_ANALOG(ImGuiNavInput_LStickUp, 1, +0.3f, +0.9f); + MAP_ANALOG(ImGuiNavInput_LStickDown, 1, -0.3f, -0.9f); + #undef MAP_BUTTON + #undef MAP_ANALOG + } + + // Start the frame. This call will update the io.WantCaptureMouse, io.WantCaptureKeyboard flag that you can use to dispatch inputs (or not) to your application. + ImGui::NewFrame(); +} diff --git a/examples/opengl3_example/imgui_impl_glfw_gl3.h b/examples/opengl3_example/imgui_impl_glfw.h similarity index 57% rename from examples/opengl3_example/imgui_impl_glfw_gl3.h rename to examples/opengl3_example/imgui_impl_glfw.h index 0e039d8d0b78..d1b42eed05d6 100644 --- a/examples/opengl3_example/imgui_impl_glfw_gl3.h +++ b/examples/opengl3_example/imgui_impl_glfw.h @@ -13,19 +13,14 @@ struct GLFWwindow; -IMGUI_API bool ImGui_ImplGlfwGL3_Init(GLFWwindow* window, bool install_callbacks); -IMGUI_API void ImGui_ImplGlfwGL3_Shutdown(); -IMGUI_API void ImGui_ImplGlfwGL3_NewFrame(); -IMGUI_API void ImGui_ImplGlfwGL3_RenderDrawData(ImDrawData* draw_data); - -// Use if you want to reset your rendering device without losing ImGui state. -IMGUI_API void ImGui_ImplGlfwGL3_InvalidateDeviceObjects(); -IMGUI_API bool ImGui_ImplGlfwGL3_CreateDeviceObjects(); +IMGUI_API bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks); +IMGUI_API void ImGui_ImplGlfw_Shutdown(); +IMGUI_API void ImGui_ImplGlfw_NewFrame(); // GLFW callbacks (installed by default if you enable 'install_callbacks' during initialization) // Provided here if you want to chain callbacks. // You can also handle inputs yourself and use those as a reference. -IMGUI_API void ImGui_ImplGlfwGL3_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods); -IMGUI_API void ImGui_ImplGlfwGL3_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset); -IMGUI_API void ImGui_ImplGlfwGL3_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods); -IMGUI_API void ImGui_ImplGlfwGL3_CharCallback(GLFWwindow* window, unsigned int c); +IMGUI_API void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods); +IMGUI_API void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset); +IMGUI_API void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods); +IMGUI_API void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c); diff --git a/examples/opengl3_example/imgui_impl_glfw_gl3.cpp b/examples/opengl3_example/imgui_impl_opengl3.cpp similarity index 50% rename from examples/opengl3_example/imgui_impl_glfw_gl3.cpp rename to examples/opengl3_example/imgui_impl_opengl3.cpp index 24097686238e..8a89f1e79a7f 100644 --- a/examples/opengl3_example/imgui_impl_glfw_gl3.cpp +++ b/examples/opengl3_example/imgui_impl_opengl3.cpp @@ -1,60 +1,33 @@ -// ImGui GLFW binding with OpenGL3 + shaders -// (GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) -// (GL3W is a helper library to access OpenGL functions since there is no standard header to access modern OpenGL functions easily. Alternatives are GLEW, Glad, etc.) - -// Implemented features: -// [X] User texture binding. Cast 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. -// [X] Gamepad navigation mapping. Enable with 'io.NavFlags |= ImGuiNavFlags_EnableGamepad'. - -// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. -// If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). -// If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp. -// https://github.com/ocornut/imgui - -// CHANGELOG -// (minor and older changes stripped away, please see git history for details) -// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplGlfwGL3_RenderDrawData() in the .h file so you can call it yourself. -// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. -// 2018-02-06: Inputs: Added mapping for ImGuiKey_Space. -// 2018-01-25: Inputs: Added gamepad support if ImGuiNavFlags_EnableGamepad is set. -// 2018-01-25: Inputs: Honoring the io.WantMoveMouse by repositioning the mouse by using navigation and ImGuiNavFlags_MoveMouse is set. -// 2018-01-20: Inputs: Added Horizontal Mouse Wheel support. -// 2018-01-18: Inputs: Added mapping for ImGuiKey_Insert. -// 2018-01-07: OpenGL: Changed GLSL shader version from 330 to 150. (Also changed GL context from 3.3 to 3.2 in example's main.cpp) -// 2017-09-01: OpenGL: Save and restore current bound sampler. Save and restore current polygon mode. -// 2017-08-25: Inputs: MousePos set to -FLT_MAX,-FLT_MAX when mouse is unavailable/missing (instead of -1,-1). -// 2017-05-01: OpenGL: Fixed save and restore of current blend function state. -// 2016-10-15: Misc: Added a void* user_data parameter to Clipboard function handlers. -// 2016-09-05: OpenGL: Fixed save and restore of current scissor rectangle. -// 2016-04-30: OpenGL: Fixed save and restore of current GL_ACTIVE_TEXTURE. - #include "imgui.h" -#include "imgui_impl_glfw_gl3.h" - -// GL3W/GLFW -#include // This example is using gl3w to access OpenGL functions (because it is small). You may use glew/glad/glLoadGen/etc. whatever already works for you. -#include -#ifdef _WIN32 -#undef APIENTRY -#define GLFW_EXPOSE_NATIVE_WIN32 -#define GLFW_EXPOSE_NATIVE_WGL -#include -#endif - -// Data -static GLFWwindow* g_Window = NULL; -static double g_Time = 0.0f; -static bool g_MouseJustPressed[3] = { false, false, false }; +#include "imgui_impl_opengl3.h" +#include // This example is using gl3w to access OpenGL functions (because it is small). You may use glew/glad/glLoadGen/etc. whatever already works for you. + static GLuint g_FontTexture = 0; static int g_ShaderHandle = 0, g_VertHandle = 0, g_FragHandle = 0; static int g_AttribLocationTex = 0, g_AttribLocationProjMtx = 0; static int g_AttribLocationPosition = 0, g_AttribLocationUV = 0, g_AttribLocationColor = 0; static unsigned int g_VboHandle = 0, g_VaoHandle = 0, g_ElementsHandle = 0; +bool ImGui_ImplOpenGL3_Init() +{ + return true; +} + +void ImGui_ImplOpenGL3_Shutdown() +{ + ImGui_ImplOpenGL3_DestroyDeviceObjects(); +} + +void ImGui_ImplOpenGL3_NewFrame() +{ + if (!g_FontTexture) + ImGui_ImplOpenGL3_CreateDeviceObjects(); +} + // OpenGL3 Render function. // (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop) // Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly, in order to be able to run within any OpenGL engine that doesn't do so. -void ImGui_ImplGlfwGL3_RenderDrawData(ImDrawData* draw_data) +void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) { // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) ImGuiIO& io = ImGui::GetIO(); @@ -100,10 +73,10 @@ void ImGui_ImplGlfwGL3_RenderDrawData(ImDrawData* draw_data) glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height); const float ortho_projection[4][4] = { - { 2.0f/io.DisplaySize.x, 0.0f, 0.0f, 0.0f }, - { 0.0f, 2.0f/-io.DisplaySize.y, 0.0f, 0.0f }, + { 2.0f / io.DisplaySize.x, 0.0f, 0.0f, 0.0f }, + { 0.0f, 2.0f / -io.DisplaySize.y, 0.0f, 0.0f }, { 0.0f, 0.0f, -1.0f, 0.0f }, - {-1.0f, 1.0f, 0.0f, 1.0f }, + { -1.0f, 1.0f, 0.0f, 1.0f }, }; glUseProgram(g_ShaderHandle); glUniform1i(g_AttribLocationTex, 0); @@ -158,52 +131,7 @@ void ImGui_ImplGlfwGL3_RenderDrawData(ImDrawData* draw_data) glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]); } -static const char* ImGui_ImplGlfwGL3_GetClipboardText(void* user_data) -{ - return glfwGetClipboardString((GLFWwindow*)user_data); -} - -static void ImGui_ImplGlfwGL3_SetClipboardText(void* user_data, const char* text) -{ - glfwSetClipboardString((GLFWwindow*)user_data, text); -} - -void ImGui_ImplGlfwGL3_MouseButtonCallback(GLFWwindow*, int button, int action, int /*mods*/) -{ - if (action == GLFW_PRESS && button >= 0 && button < 3) - g_MouseJustPressed[button] = true; -} - -void ImGui_ImplGlfwGL3_ScrollCallback(GLFWwindow*, double xoffset, double yoffset) -{ - ImGuiIO& io = ImGui::GetIO(); - io.MouseWheelH += (float)xoffset; - io.MouseWheel += (float)yoffset; -} - -void ImGui_ImplGlfwGL3_KeyCallback(GLFWwindow*, int key, int, int action, int mods) -{ - ImGuiIO& io = ImGui::GetIO(); - if (action == GLFW_PRESS) - io.KeysDown[key] = true; - if (action == GLFW_RELEASE) - io.KeysDown[key] = false; - - (void)mods; // Modifiers are not reliable across systems - io.KeyCtrl = io.KeysDown[GLFW_KEY_LEFT_CONTROL] || io.KeysDown[GLFW_KEY_RIGHT_CONTROL]; - io.KeyShift = io.KeysDown[GLFW_KEY_LEFT_SHIFT] || io.KeysDown[GLFW_KEY_RIGHT_SHIFT]; - io.KeyAlt = io.KeysDown[GLFW_KEY_LEFT_ALT] || io.KeysDown[GLFW_KEY_RIGHT_ALT]; - io.KeySuper = io.KeysDown[GLFW_KEY_LEFT_SUPER] || io.KeysDown[GLFW_KEY_RIGHT_SUPER]; -} - -void ImGui_ImplGlfwGL3_CharCallback(GLFWwindow*, unsigned int c) -{ - ImGuiIO& io = ImGui::GetIO(); - if (c > 0 && c < 0x10000) - io.AddInputCharacter((unsigned short)c); -} - -bool ImGui_ImplGlfwGL3_CreateFontsTexture() +bool ImGui_ImplOpenGL3_CreateFontsTexture() { // Build texture atlas ImGuiIO& io = ImGui::GetIO(); @@ -230,7 +158,18 @@ bool ImGui_ImplGlfwGL3_CreateFontsTexture() return true; } -bool ImGui_ImplGlfwGL3_CreateDeviceObjects() +void ImGui_ImplOpenGL3_DestroyFontsTexture() +{ + if (g_FontTexture) + { + ImGuiIO& io = ImGui::GetIO(); + glDeleteTextures(1, &g_FontTexture); + io.Fonts->TexID = 0; + g_FontTexture = 0; + } +} + +bool ImGui_ImplOpenGL3_CreateDeviceObjects() { // Backup GL state GLint last_texture, last_array_buffer, last_vertex_array; @@ -238,6 +177,7 @@ bool ImGui_ImplGlfwGL3_CreateDeviceObjects() glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer); glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array); + // Create shaders const GLchar *vertex_shader = "#version 150\n" "uniform mat4 ProjMtx;\n" @@ -295,7 +235,7 @@ bool ImGui_ImplGlfwGL3_CreateDeviceObjects() glVertexAttribPointer(g_AttribLocationUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, uv)); glVertexAttribPointer(g_AttribLocationColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, col)); - ImGui_ImplGlfwGL3_CreateFontsTexture(); + ImGui_ImplOpenGL3_CreateFontsTexture(); // Restore modified GL state glBindTexture(GL_TEXTURE_2D, last_texture); @@ -305,7 +245,7 @@ bool ImGui_ImplGlfwGL3_CreateDeviceObjects() return true; } -void ImGui_ImplGlfwGL3_InvalidateDeviceObjects() +void ImGui_ImplOpenGL3_DestroyDeviceObjects() { if (g_VaoHandle) glDeleteVertexArrays(1, &g_VaoHandle); if (g_VboHandle) glDeleteBuffers(1, &g_VboHandle); @@ -323,144 +263,5 @@ void ImGui_ImplGlfwGL3_InvalidateDeviceObjects() if (g_ShaderHandle) glDeleteProgram(g_ShaderHandle); g_ShaderHandle = 0; - if (g_FontTexture) - { - glDeleteTextures(1, &g_FontTexture); - ImGui::GetIO().Fonts->TexID = 0; - g_FontTexture = 0; - } -} - -bool ImGui_ImplGlfwGL3_Init(GLFWwindow* window, bool install_callbacks) -{ - g_Window = window; - - ImGuiIO& io = ImGui::GetIO(); - io.KeyMap[ImGuiKey_Tab] = GLFW_KEY_TAB; // Keyboard mapping. ImGui will use those indices to peek into the io.KeyDown[] array. - io.KeyMap[ImGuiKey_LeftArrow] = GLFW_KEY_LEFT; - io.KeyMap[ImGuiKey_RightArrow] = GLFW_KEY_RIGHT; - io.KeyMap[ImGuiKey_UpArrow] = GLFW_KEY_UP; - io.KeyMap[ImGuiKey_DownArrow] = GLFW_KEY_DOWN; - io.KeyMap[ImGuiKey_PageUp] = GLFW_KEY_PAGE_UP; - io.KeyMap[ImGuiKey_PageDown] = GLFW_KEY_PAGE_DOWN; - io.KeyMap[ImGuiKey_Home] = GLFW_KEY_HOME; - io.KeyMap[ImGuiKey_End] = GLFW_KEY_END; - io.KeyMap[ImGuiKey_Insert] = GLFW_KEY_INSERT; - io.KeyMap[ImGuiKey_Delete] = GLFW_KEY_DELETE; - io.KeyMap[ImGuiKey_Backspace] = GLFW_KEY_BACKSPACE; - io.KeyMap[ImGuiKey_Space] = GLFW_KEY_SPACE; - io.KeyMap[ImGuiKey_Enter] = GLFW_KEY_ENTER; - io.KeyMap[ImGuiKey_Escape] = GLFW_KEY_ESCAPE; - io.KeyMap[ImGuiKey_A] = GLFW_KEY_A; - io.KeyMap[ImGuiKey_C] = GLFW_KEY_C; - io.KeyMap[ImGuiKey_V] = GLFW_KEY_V; - io.KeyMap[ImGuiKey_X] = GLFW_KEY_X; - io.KeyMap[ImGuiKey_Y] = GLFW_KEY_Y; - io.KeyMap[ImGuiKey_Z] = GLFW_KEY_Z; - - io.SetClipboardTextFn = ImGui_ImplGlfwGL3_SetClipboardText; - io.GetClipboardTextFn = ImGui_ImplGlfwGL3_GetClipboardText; - io.ClipboardUserData = g_Window; -#ifdef _WIN32 - io.ImeWindowHandle = glfwGetWin32Window(g_Window); -#endif - - if (install_callbacks) - { - glfwSetMouseButtonCallback(window, ImGui_ImplGlfwGL3_MouseButtonCallback); - glfwSetScrollCallback(window, ImGui_ImplGlfwGL3_ScrollCallback); - glfwSetKeyCallback(window, ImGui_ImplGlfwGL3_KeyCallback); - glfwSetCharCallback(window, ImGui_ImplGlfwGL3_CharCallback); - } - - return true; -} - -void ImGui_ImplGlfwGL3_Shutdown() -{ - ImGui_ImplGlfwGL3_InvalidateDeviceObjects(); -} - -void ImGui_ImplGlfwGL3_NewFrame() -{ - if (!g_FontTexture) - ImGui_ImplGlfwGL3_CreateDeviceObjects(); - - ImGuiIO& io = ImGui::GetIO(); - - // Setup display size (every frame to accommodate for window resizing) - int w, h; - int display_w, display_h; - glfwGetWindowSize(g_Window, &w, &h); - glfwGetFramebufferSize(g_Window, &display_w, &display_h); - io.DisplaySize = ImVec2((float)w, (float)h); - io.DisplayFramebufferScale = ImVec2(w > 0 ? ((float)display_w / w) : 0, h > 0 ? ((float)display_h / h) : 0); - - // Setup time step - double current_time = glfwGetTime(); - io.DeltaTime = g_Time > 0.0 ? (float)(current_time - g_Time) : (float)(1.0f/60.0f); - g_Time = current_time; - - // Setup inputs - // (we already got mouse wheel, keyboard keys & characters from glfw callbacks polled in glfwPollEvents()) - if (glfwGetWindowAttrib(g_Window, GLFW_FOCUSED)) - { - if (io.WantMoveMouse) - { - glfwSetCursorPos(g_Window, (double)io.MousePos.x, (double)io.MousePos.y); // Set mouse position if requested by io.WantMoveMouse flag (used when io.NavMovesTrue is enabled by user and using directional navigation) - } - else - { - double mouse_x, mouse_y; - glfwGetCursorPos(g_Window, &mouse_x, &mouse_y); - io.MousePos = ImVec2((float)mouse_x, (float)mouse_y); - } - } - else - { - io.MousePos = ImVec2(-FLT_MAX,-FLT_MAX); - } - - for (int i = 0; i < 3; i++) - { - // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame. - io.MouseDown[i] = g_MouseJustPressed[i] || glfwGetMouseButton(g_Window, i) != 0; - g_MouseJustPressed[i] = false; - } - - // Hide OS mouse cursor if ImGui is drawing it - glfwSetInputMode(g_Window, GLFW_CURSOR, io.MouseDrawCursor ? GLFW_CURSOR_HIDDEN : GLFW_CURSOR_NORMAL); - - // Gamepad navigation mapping [BETA] - memset(io.NavInputs, 0, sizeof(io.NavInputs)); - if (io.NavFlags & ImGuiNavFlags_EnableGamepad) - { - // Update gamepad inputs - #define MAP_BUTTON(NAV_NO, BUTTON_NO) { if (buttons_count > BUTTON_NO && buttons[BUTTON_NO] == GLFW_PRESS) io.NavInputs[NAV_NO] = 1.0f; } - #define MAP_ANALOG(NAV_NO, AXIS_NO, V0, V1) { float v = (axes_count > AXIS_NO) ? axes[AXIS_NO] : V0; v = (v - V0) / (V1 - V0); if (v > 1.0f) v = 1.0f; if (io.NavInputs[NAV_NO] < v) io.NavInputs[NAV_NO] = v; } - int axes_count = 0, buttons_count = 0; - const float* axes = glfwGetJoystickAxes(GLFW_JOYSTICK_1, &axes_count); - const unsigned char* buttons = glfwGetJoystickButtons(GLFW_JOYSTICK_1, &buttons_count); - MAP_BUTTON(ImGuiNavInput_Activate, 0); // Cross / A - MAP_BUTTON(ImGuiNavInput_Cancel, 1); // Circle / B - MAP_BUTTON(ImGuiNavInput_Menu, 2); // Square / X - MAP_BUTTON(ImGuiNavInput_Input, 3); // Triangle / Y - MAP_BUTTON(ImGuiNavInput_DpadLeft, 13); // D-Pad Left - MAP_BUTTON(ImGuiNavInput_DpadRight, 11); // D-Pad Right - MAP_BUTTON(ImGuiNavInput_DpadUp, 10); // D-Pad Up - MAP_BUTTON(ImGuiNavInput_DpadDown, 12); // D-Pad Down - MAP_BUTTON(ImGuiNavInput_FocusPrev, 4); // L1 / LB - MAP_BUTTON(ImGuiNavInput_FocusNext, 5); // R1 / RB - MAP_BUTTON(ImGuiNavInput_TweakSlow, 4); // L1 / LB - MAP_BUTTON(ImGuiNavInput_TweakFast, 5); // R1 / RB - MAP_ANALOG(ImGuiNavInput_LStickLeft, 0, -0.3f, -0.9f); - MAP_ANALOG(ImGuiNavInput_LStickRight,0, +0.3f, +0.9f); - MAP_ANALOG(ImGuiNavInput_LStickUp, 1, +0.3f, +0.9f); - MAP_ANALOG(ImGuiNavInput_LStickDown, 1, -0.3f, -0.9f); - #undef MAP_BUTTON - #undef MAP_ANALOG - } - - // Start the frame. This call will update the io.WantCaptureMouse, io.WantCaptureKeyboard flag that you can use to dispatch inputs (or not) to your application. - ImGui::NewFrame(); + ImGui_ImplOpenGL3_DestroyFontsTexture(); } diff --git a/examples/opengl3_example/imgui_impl_opengl3.h b/examples/opengl3_example/imgui_impl_opengl3.h new file mode 100644 index 000000000000..eaf8cf8a3caf --- /dev/null +++ b/examples/opengl3_example/imgui_impl_opengl3.h @@ -0,0 +1,11 @@ + +IMGUI_API bool ImGui_ImplOpenGL3_Init(); +IMGUI_API void ImGui_ImplOpenGL3_Shutdown(); +IMGUI_API void ImGui_ImplOpenGL3_NewFrame(); +IMGUI_API void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data); + +// Called by Init/NewFrame/Shutdown +IMGUI_API bool ImGui_ImplOpenGL3_CreateFontsTexture(); +IMGUI_API void ImGui_ImplOpenGL3_DestroyFontsTexture(); +IMGUI_API bool ImGui_ImplOpenGL3_CreateDeviceObjects(); +IMGUI_API void ImGui_ImplOpenGL3_DestroyDeviceObjects(); diff --git a/examples/opengl3_example/main.cpp b/examples/opengl3_example/main.cpp index a5f59e8aaf22..a57b96a2d595 100644 --- a/examples/opengl3_example/main.cpp +++ b/examples/opengl3_example/main.cpp @@ -4,7 +4,8 @@ // (GL3W is a helper library to access OpenGL functions since there is no standard header to access modern OpenGL functions easily. Alternatives are GLEW, Glad, etc.) #include "imgui.h" -#include "imgui_impl_glfw_gl3.h" +#include "imgui_impl_glfw.h" +#include "imgui_impl_opengl3.h" #include #include // This example is using gl3w to access OpenGL functions (because it is small). You may use glew/glad/glLoadGen/etc. whatever already works for you. #include @@ -34,7 +35,8 @@ int main(int, char**) // Setup ImGui binding ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - ImGui_ImplGlfwGL3_Init(window, true); + ImGui_ImplGlfw_Init(window, true); + ImGui_ImplOpenGL3_Init(); //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; // Enable Keyboard Controls //io.NavFlags |= ImGuiNavFlags_EnableGamepad; // Enable Gamepad Controls @@ -69,7 +71,8 @@ int main(int, char**) // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. glfwPollEvents(); - ImGui_ImplGlfwGL3_NewFrame(); + ImGui_ImplOpenGL3_NewFrame(); + ImGui_ImplGlfw_NewFrame(); // 1. Show a simple window. // Tip: if we don't call ImGui::Begin()/ImGui::End() the widgets automatically appears in a window called "Debug". @@ -115,12 +118,13 @@ int main(int, char**) glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w); glClear(GL_COLOR_BUFFER_BIT); ImGui::Render(); - ImGui_ImplGlfwGL3_RenderDrawData(ImGui::GetDrawData()); + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); glfwSwapBuffers(window); } // Cleanup - ImGui_ImplGlfwGL3_Shutdown(); + ImGui_ImplOpenGL3_Shutdown(); + ImGui_ImplGlfw_Shutdown(); ImGui::DestroyContext(); glfwTerminate(); diff --git a/examples/opengl3_example/opengl3_example.vcxproj b/examples/opengl3_example/opengl3_example.vcxproj index e3a9c82047b1..3ade8ec8a54f 100644 --- a/examples/opengl3_example/opengl3_example.vcxproj +++ b/examples/opengl3_example/opengl3_example.vcxproj @@ -154,7 +154,8 @@ - + + @@ -163,7 +164,8 @@ - + + diff --git a/examples/opengl3_example/opengl3_example.vcxproj.filters b/examples/opengl3_example/opengl3_example.vcxproj.filters index 0170ab469332..f45ad98338e6 100644 --- a/examples/opengl3_example/opengl3_example.vcxproj.filters +++ b/examples/opengl3_example/opengl3_example.vcxproj.filters @@ -19,9 +19,6 @@ imgui - - sources - gl3w @@ -31,6 +28,12 @@ imgui + + sources + + + sources + @@ -39,9 +42,6 @@ imgui - - sources - gl3w @@ -51,6 +51,12 @@ imgui + + sources + + + sources + From c8a996951190b9628ff9f5625c781dbea6861d8c Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 16 Feb 2018 22:01:48 +0100 Subject: [PATCH 003/828] Examples: GLFW+GL2: Moved glfw, gl2 files to root folder, split imgui_impl_glfw_gl2 into _opengl2 and reused imgui_impl_glfw --- .../{opengl3_example => }/imgui_impl_glfw.cpp | 2 +- .../{opengl3_example => }/imgui_impl_glfw.h | 0 examples/imgui_impl_opengl2.cpp | 174 ++++++++++ examples/imgui_impl_opengl2.h | 11 + .../imgui_impl_opengl3.cpp | 8 +- .../imgui_impl_opengl3.h | 0 .../opengl2_example/imgui_impl_glfw_gl2.cpp | 321 ------------------ .../opengl2_example/imgui_impl_glfw_gl2.h | 32 -- examples/opengl2_example/main.cpp | 14 +- .../opengl2_example/opengl2_example.vcxproj | 6 +- .../opengl2_example.vcxproj.filters | 18 +- examples/opengl3_example/main.cpp | 4 +- .../opengl3_example/opengl3_example.vcxproj | 8 +- .../opengl3_example.vcxproj.filters | 8 +- 14 files changed, 226 insertions(+), 380 deletions(-) rename examples/{opengl3_example => }/imgui_impl_glfw.cpp (99%) rename examples/{opengl3_example => }/imgui_impl_glfw.h (100%) create mode 100644 examples/imgui_impl_opengl2.cpp create mode 100644 examples/imgui_impl_opengl2.h rename examples/{opengl3_example => }/imgui_impl_opengl3.cpp (98%) rename examples/{opengl3_example => }/imgui_impl_opengl3.h (100%) delete mode 100644 examples/opengl2_example/imgui_impl_glfw_gl2.cpp delete mode 100644 examples/opengl2_example/imgui_impl_glfw_gl2.h diff --git a/examples/opengl3_example/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp similarity index 99% rename from examples/opengl3_example/imgui_impl_glfw.cpp rename to examples/imgui_impl_glfw.cpp index 7b341a87dd2b..ab423ff3f4e9 100644 --- a/examples/opengl3_example/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -36,7 +36,7 @@ #ifdef _WIN32 #undef APIENTRY #define GLFW_EXPOSE_NATIVE_WIN32 -#include +#include // for glfwGetWin32Window #endif // Data diff --git a/examples/opengl3_example/imgui_impl_glfw.h b/examples/imgui_impl_glfw.h similarity index 100% rename from examples/opengl3_example/imgui_impl_glfw.h rename to examples/imgui_impl_glfw.h diff --git a/examples/imgui_impl_opengl2.cpp b/examples/imgui_impl_opengl2.cpp new file mode 100644 index 000000000000..06c770affe44 --- /dev/null +++ b/examples/imgui_impl_opengl2.cpp @@ -0,0 +1,174 @@ +// **DO NOT USE THIS CODE IF YOUR CODE/ENGINE IS USING MODERN OPENGL (SHADERS, VBO, VAO, etc.)** +// **Prefer using the code in the opengl3_example/ folder** +// This code is mostly provided as a reference to learn how ImGui integration works, because it is shorter to read. +// If your code is using GL3+ context or any semi modern OpenGL calls, using this is likely to make everything more +// complicated, will require your code to reset every single OpenGL attributes to their initial state, and might +// confuse your GPU driver. +// The GL2 code is unable to reset attributes or even call e.g. "glUseProgram(0)" because they don't exist in that API. + +#include "imgui.h" +#include "imgui_impl_opengl2.h" + +// Include OpenGL header (without an OpenGL loader) requires a bit of fiddling +#if defined(_WIN32) && !defined(APIENTRY) +#define APIENTRY __stdcall // It is customary to use APIENTRY for OpenGL function pointer declarations on all platforms. Additionally, the Windows OpenGL header needs APIENTRY. +#endif +#if defined(_WIN32) && !defined(WINGDIAPI) +#define WINGDIAPI __declspec(dllimport) // Some Windows OpenGL headers need this +#endif +#if defined(__APPLE__) +#include +#else +#include +#endif + +// Data +static GLuint g_FontTexture = 0; + +// Functions +bool ImGui_ImplOpenGL2_Init() +{ + return true; +} + +void ImGui_ImplOpenGL2_Shutdown() +{ + ImGui_ImplOpenGL2_DestroyDeviceObjects(); +} + +void ImGui_ImplOpenGL2_NewFrame() +{ + if (!g_FontTexture) + ImGui_ImplOpenGL2_CreateDeviceObjects(); +} + +// OpenGL2 Render function. +// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop) +// Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly, in order to be able to run within any OpenGL engine that doesn't do so. +void ImGui_ImplOpenGL2_RenderDrawData(ImDrawData* draw_data) +{ + // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) + ImGuiIO& io = ImGui::GetIO(); + int fb_width = (int)(io.DisplaySize.x * io.DisplayFramebufferScale.x); + int fb_height = (int)(io.DisplaySize.y * io.DisplayFramebufferScale.y); + if (fb_width == 0 || fb_height == 0) + return; + draw_data->ScaleClipRects(io.DisplayFramebufferScale); + + // We are using the OpenGL fixed pipeline to make the example code simpler to read! + // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, vertex/texcoord/color pointers, polygon fill. + GLint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); + GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode); + GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport); + GLint last_scissor_box[4]; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box); + glPushAttrib(GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT | GL_TRANSFORM_BIT); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glEnable(GL_SCISSOR_TEST); + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + glEnable(GL_TEXTURE_2D); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + //glUseProgram(0); // You may want this if using this code in an OpenGL 3+ context where shaders may be bound + + // Setup viewport, orthographic projection matrix + glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height); + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glOrtho(0.0f, io.DisplaySize.x, io.DisplaySize.y, 0.0f, -1.0f, +1.0f); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + // Render command lists + for (int n = 0; n < draw_data->CmdListsCount; n++) + { + const ImDrawList* cmd_list = draw_data->CmdLists[n]; + const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data; + const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data; + glVertexPointer(2, GL_FLOAT, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, pos))); + glTexCoordPointer(2, GL_FLOAT, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, uv))); + glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, col))); + + for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) + { + const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; + if (pcmd->UserCallback) + { + pcmd->UserCallback(cmd_list, pcmd); + } + else + { + glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId); + glScissor((int)pcmd->ClipRect.x, (int)(fb_height - pcmd->ClipRect.w), (int)(pcmd->ClipRect.z - pcmd->ClipRect.x), (int)(pcmd->ClipRect.w - pcmd->ClipRect.y)); + glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer); + } + idx_buffer += pcmd->ElemCount; + } + } + + // Restore modified state + glDisableClientState(GL_COLOR_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + glBindTexture(GL_TEXTURE_2D, (GLuint)last_texture); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glPopAttrib(); + glPolygonMode(GL_FRONT, last_polygon_mode[0]); glPolygonMode(GL_BACK, last_polygon_mode[1]); + glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]); + glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]); +} + +bool ImGui_ImplOpenGL2_CreateFontsTexture() +{ + // Build texture atlas + ImGuiIO& io = ImGui::GetIO(); + unsigned char* pixels; + int width, height; + io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bits (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory. + + // Upload texture to graphics system + GLint last_texture; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); + glGenTextures(1, &g_FontTexture); + glBindTexture(GL_TEXTURE_2D, g_FontTexture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + + // Store our identifier + io.Fonts->TexID = (void *)(intptr_t)g_FontTexture; + + // Restore state + glBindTexture(GL_TEXTURE_2D, last_texture); + + return true; +} + +void ImGui_ImplOpenGL2_DestroyFontsTexture() +{ + if (g_FontTexture) + { + ImGuiIO& io = ImGui::GetIO(); + glDeleteTextures(1, &g_FontTexture); + io.Fonts->TexID = 0; + g_FontTexture = 0; + } +} + +bool ImGui_ImplOpenGL2_CreateDeviceObjects() +{ + return ImGui_ImplOpenGL2_CreateFontsTexture(); +} + +void ImGui_ImplOpenGL2_DestroyDeviceObjects() +{ + ImGui_ImplOpenGL2_DestroyFontsTexture(); +} diff --git a/examples/imgui_impl_opengl2.h b/examples/imgui_impl_opengl2.h new file mode 100644 index 000000000000..c1ef2f5e70d3 --- /dev/null +++ b/examples/imgui_impl_opengl2.h @@ -0,0 +1,11 @@ + +IMGUI_API bool ImGui_ImplOpenGL2_Init(); +IMGUI_API void ImGui_ImplOpenGL2_Shutdown(); +IMGUI_API void ImGui_ImplOpenGL2_NewFrame(); +IMGUI_API void ImGui_ImplOpenGL2_RenderDrawData(ImDrawData* draw_data); + +// Called by Init/NewFrame/Shutdown +IMGUI_API bool ImGui_ImplOpenGL2_CreateFontsTexture(); +IMGUI_API void ImGui_ImplOpenGL2_DestroyFontsTexture(); +IMGUI_API bool ImGui_ImplOpenGL2_CreateDeviceObjects(); +IMGUI_API void ImGui_ImplOpenGL2_DestroyDeviceObjects(); diff --git a/examples/opengl3_example/imgui_impl_opengl3.cpp b/examples/imgui_impl_opengl3.cpp similarity index 98% rename from examples/opengl3_example/imgui_impl_opengl3.cpp rename to examples/imgui_impl_opengl3.cpp index 8a89f1e79a7f..241dc3826e7a 100644 --- a/examples/opengl3_example/imgui_impl_opengl3.cpp +++ b/examples/imgui_impl_opengl3.cpp @@ -2,12 +2,14 @@ #include "imgui_impl_opengl3.h" #include // This example is using gl3w to access OpenGL functions (because it is small). You may use glew/glad/glLoadGen/etc. whatever already works for you. +// Data static GLuint g_FontTexture = 0; static int g_ShaderHandle = 0, g_VertHandle = 0, g_FragHandle = 0; static int g_AttribLocationTex = 0, g_AttribLocationProjMtx = 0; static int g_AttribLocationPosition = 0, g_AttribLocationUV = 0, g_AttribLocationColor = 0; static unsigned int g_VboHandle = 0, g_VaoHandle = 0, g_ElementsHandle = 0; +// Functions bool ImGui_ImplOpenGL3_Init() { return true; @@ -73,10 +75,10 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height); const float ortho_projection[4][4] = { - { 2.0f / io.DisplaySize.x, 0.0f, 0.0f, 0.0f }, - { 0.0f, 2.0f / -io.DisplaySize.y, 0.0f, 0.0f }, + { 2.0f/io.DisplaySize.x, 0.0f, 0.0f, 0.0f }, + { 0.0f, 2.0f/-io.DisplaySize.y, 0.0f, 0.0f }, { 0.0f, 0.0f, -1.0f, 0.0f }, - { -1.0f, 1.0f, 0.0f, 1.0f }, + { -1.0f, 1.0f, 0.0f, 1.0f }, }; glUseProgram(g_ShaderHandle); glUniform1i(g_AttribLocationTex, 0); diff --git a/examples/opengl3_example/imgui_impl_opengl3.h b/examples/imgui_impl_opengl3.h similarity index 100% rename from examples/opengl3_example/imgui_impl_opengl3.h rename to examples/imgui_impl_opengl3.h diff --git a/examples/opengl2_example/imgui_impl_glfw_gl2.cpp b/examples/opengl2_example/imgui_impl_glfw_gl2.cpp deleted file mode 100644 index d4a4357048b0..000000000000 --- a/examples/opengl2_example/imgui_impl_glfw_gl2.cpp +++ /dev/null @@ -1,321 +0,0 @@ -// ImGui GLFW binding with OpenGL (legacy, fixed pipeline) -// (GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) - -// Implemented features: -// [X] User texture binding. Cast 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. - -// **DO NOT USE THIS CODE IF YOUR CODE/ENGINE IS USING MODERN OPENGL (SHADERS, VBO, VAO, etc.)** -// **Prefer using the code in the opengl3_example/ folder** -// This code is mostly provided as a reference to learn how ImGui integration works, because it is shorter to read. -// If your code is using GL3+ context or any semi modern OpenGL calls, using this is likely to make everything more -// complicated, will require your code to reset every single OpenGL attributes to their initial state, and might -// confuse your GPU driver. -// The GL2 code is unable to reset attributes or even call e.g. "glUseProgram(0)" because they don't exist in that API. - -// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. -// If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). -// If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp. -// https://github.com/ocornut/imgui - -// CHANGELOG -// (minor and older changes stripped away, please see git history for details) -// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplGlfwGL2_RenderDrawData() in the .h file so you can call it yourself. -// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. -// 2018-02-06: Inputs: Added mapping for ImGuiKey_Space. -// 2018-01-25: Inputs: Honoring the io.WantMoveMouse by repositioning the mouse by using navigation and ImGuiNavFlags_MoveMouse is set. -// 2018-01-20: Inputs: Added Horizontal Mouse Wheel support. -// 2018-01-18: Inputs: Added mapping for ImGuiKey_Insert. -// 2018-01-09: Misc: Renamed imgui_impl_glfw.* to imgui_impl_glfw_gl2.*. -// 2017-09-01: OpenGL: Save and restore current polygon mode. -// 2017-08-25: Inputs: MousePos set to -FLT_MAX,-FLT_MAX when mouse is unavailable/missing (instead of -1,-1). -// 2016-10-15: Misc: Added a void* user_data parameter to Clipboard function handlers. -// 2016-09-10: OpenGL: Uploading font texture as RGBA32 to increase compatibility with users shaders (not ideal). -// 2016-09-05: OpenGL: Fixed save and restore of current scissor rectangle. - -#include "imgui.h" -#include "imgui_impl_glfw_gl2.h" - -// GLFW -#include -#ifdef _WIN32 -#undef APIENTRY -#define GLFW_EXPOSE_NATIVE_WIN32 -#define GLFW_EXPOSE_NATIVE_WGL -#include -#endif - -// Data -static GLFWwindow* g_Window = NULL; -static double g_Time = 0.0f; -static bool g_MouseJustPressed[3] = { false, false, false }; -static GLuint g_FontTexture = 0; - -// OpenGL2 Render function. -// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop) -// Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly, in order to be able to run within any OpenGL engine that doesn't do so. -void ImGui_ImplGlfwGL2_RenderDrawData(ImDrawData* draw_data) -{ - // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) - ImGuiIO& io = ImGui::GetIO(); - int fb_width = (int)(io.DisplaySize.x * io.DisplayFramebufferScale.x); - int fb_height = (int)(io.DisplaySize.y * io.DisplayFramebufferScale.y); - if (fb_width == 0 || fb_height == 0) - return; - draw_data->ScaleClipRects(io.DisplayFramebufferScale); - - // We are using the OpenGL fixed pipeline to make the example code simpler to read! - // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, vertex/texcoord/color pointers, polygon fill. - GLint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); - GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode); - GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport); - GLint last_scissor_box[4]; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box); - glPushAttrib(GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT | GL_TRANSFORM_BIT); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glDisable(GL_CULL_FACE); - glDisable(GL_DEPTH_TEST); - glEnable(GL_SCISSOR_TEST); - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glEnableClientState(GL_COLOR_ARRAY); - glEnable(GL_TEXTURE_2D); - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - //glUseProgram(0); // You may want this if using this code in an OpenGL 3+ context where shaders may be bound - - // Setup viewport, orthographic projection matrix - glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height); - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - glOrtho(0.0f, io.DisplaySize.x, io.DisplaySize.y, 0.0f, -1.0f, +1.0f); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - - // Render command lists - for (int n = 0; n < draw_data->CmdListsCount; n++) - { - const ImDrawList* cmd_list = draw_data->CmdLists[n]; - const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data; - const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data; - glVertexPointer(2, GL_FLOAT, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, pos))); - glTexCoordPointer(2, GL_FLOAT, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, uv))); - glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, col))); - - for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) - { - const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; - if (pcmd->UserCallback) - { - pcmd->UserCallback(cmd_list, pcmd); - } - else - { - glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId); - glScissor((int)pcmd->ClipRect.x, (int)(fb_height - pcmd->ClipRect.w), (int)(pcmd->ClipRect.z - pcmd->ClipRect.x), (int)(pcmd->ClipRect.w - pcmd->ClipRect.y)); - glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer); - } - idx_buffer += pcmd->ElemCount; - } - } - - // Restore modified state - glDisableClientState(GL_COLOR_ARRAY); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - glDisableClientState(GL_VERTEX_ARRAY); - glBindTexture(GL_TEXTURE_2D, (GLuint)last_texture); - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glPopAttrib(); - glPolygonMode(GL_FRONT, last_polygon_mode[0]); glPolygonMode(GL_BACK, last_polygon_mode[1]); - glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]); - glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]); -} - -static const char* ImGui_ImplGlfwGL2_GetClipboardText(void* user_data) -{ - return glfwGetClipboardString((GLFWwindow*)user_data); -} - -static void ImGui_ImplGlfwGL2_SetClipboardText(void* user_data, const char* text) -{ - glfwSetClipboardString((GLFWwindow*)user_data, text); -} - -void ImGui_ImplGlfwGL2_MouseButtonCallback(GLFWwindow*, int button, int action, int /*mods*/) -{ - if (action == GLFW_PRESS && button >= 0 && button < 3) - g_MouseJustPressed[button] = true; -} - -void ImGui_ImplGlfwGL2_ScrollCallback(GLFWwindow*, double xoffset, double yoffset) -{ - ImGuiIO& io = ImGui::GetIO(); - io.MouseWheelH += (float)xoffset; - io.MouseWheel += (float)yoffset; -} - -void ImGui_ImplGlfwGL2_KeyCallback(GLFWwindow*, int key, int, int action, int mods) -{ - ImGuiIO& io = ImGui::GetIO(); - if (action == GLFW_PRESS) - io.KeysDown[key] = true; - if (action == GLFW_RELEASE) - io.KeysDown[key] = false; - - (void)mods; // Modifiers are not reliable across systems - io.KeyCtrl = io.KeysDown[GLFW_KEY_LEFT_CONTROL] || io.KeysDown[GLFW_KEY_RIGHT_CONTROL]; - io.KeyShift = io.KeysDown[GLFW_KEY_LEFT_SHIFT] || io.KeysDown[GLFW_KEY_RIGHT_SHIFT]; - io.KeyAlt = io.KeysDown[GLFW_KEY_LEFT_ALT] || io.KeysDown[GLFW_KEY_RIGHT_ALT]; - io.KeySuper = io.KeysDown[GLFW_KEY_LEFT_SUPER] || io.KeysDown[GLFW_KEY_RIGHT_SUPER]; -} - -void ImGui_ImplGlfwGL2_CharCallback(GLFWwindow*, unsigned int c) -{ - ImGuiIO& io = ImGui::GetIO(); - if (c > 0 && c < 0x10000) - io.AddInputCharacter((unsigned short)c); -} - -bool ImGui_ImplGlfwGL2_CreateDeviceObjects() -{ - // Build texture atlas - ImGuiIO& io = ImGui::GetIO(); - unsigned char* pixels; - int width, height; - io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bits (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory. - - // Upload texture to graphics system - GLint last_texture; - glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); - glGenTextures(1, &g_FontTexture); - glBindTexture(GL_TEXTURE_2D, g_FontTexture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); - - // Store our identifier - io.Fonts->TexID = (void *)(intptr_t)g_FontTexture; - - // Restore state - glBindTexture(GL_TEXTURE_2D, last_texture); - - return true; -} - -void ImGui_ImplGlfwGL2_InvalidateDeviceObjects() -{ - if (g_FontTexture) - { - glDeleteTextures(1, &g_FontTexture); - ImGui::GetIO().Fonts->TexID = 0; - g_FontTexture = 0; - } -} - -bool ImGui_ImplGlfwGL2_Init(GLFWwindow* window, bool install_callbacks) -{ - g_Window = window; - - ImGuiIO& io = ImGui::GetIO(); - io.KeyMap[ImGuiKey_Tab] = GLFW_KEY_TAB; // Keyboard mapping. ImGui will use those indices to peek into the io.KeyDown[] array. - io.KeyMap[ImGuiKey_LeftArrow] = GLFW_KEY_LEFT; - io.KeyMap[ImGuiKey_RightArrow] = GLFW_KEY_RIGHT; - io.KeyMap[ImGuiKey_UpArrow] = GLFW_KEY_UP; - io.KeyMap[ImGuiKey_DownArrow] = GLFW_KEY_DOWN; - io.KeyMap[ImGuiKey_PageUp] = GLFW_KEY_PAGE_UP; - io.KeyMap[ImGuiKey_PageDown] = GLFW_KEY_PAGE_DOWN; - io.KeyMap[ImGuiKey_Home] = GLFW_KEY_HOME; - io.KeyMap[ImGuiKey_End] = GLFW_KEY_END; - io.KeyMap[ImGuiKey_Insert] = GLFW_KEY_INSERT; - io.KeyMap[ImGuiKey_Delete] = GLFW_KEY_DELETE; - io.KeyMap[ImGuiKey_Backspace] = GLFW_KEY_BACKSPACE; - io.KeyMap[ImGuiKey_Space] = GLFW_KEY_SPACE; - io.KeyMap[ImGuiKey_Enter] = GLFW_KEY_ENTER; - io.KeyMap[ImGuiKey_Escape] = GLFW_KEY_ESCAPE; - io.KeyMap[ImGuiKey_A] = GLFW_KEY_A; - io.KeyMap[ImGuiKey_C] = GLFW_KEY_C; - io.KeyMap[ImGuiKey_V] = GLFW_KEY_V; - io.KeyMap[ImGuiKey_X] = GLFW_KEY_X; - io.KeyMap[ImGuiKey_Y] = GLFW_KEY_Y; - io.KeyMap[ImGuiKey_Z] = GLFW_KEY_Z; - - io.SetClipboardTextFn = ImGui_ImplGlfwGL2_SetClipboardText; - io.GetClipboardTextFn = ImGui_ImplGlfwGL2_GetClipboardText; - io.ClipboardUserData = g_Window; -#ifdef _WIN32 - io.ImeWindowHandle = glfwGetWin32Window(g_Window); -#endif - - if (install_callbacks) - { - glfwSetMouseButtonCallback(window, ImGui_ImplGlfwGL2_MouseButtonCallback); - glfwSetScrollCallback(window, ImGui_ImplGlfwGL2_ScrollCallback); - glfwSetKeyCallback(window, ImGui_ImplGlfwGL2_KeyCallback); - glfwSetCharCallback(window, ImGui_ImplGlfwGL2_CharCallback); - } - - return true; -} - -void ImGui_ImplGlfwGL2_Shutdown() -{ - ImGui_ImplGlfwGL2_InvalidateDeviceObjects(); -} - -void ImGui_ImplGlfwGL2_NewFrame() -{ - if (!g_FontTexture) - ImGui_ImplGlfwGL2_CreateDeviceObjects(); - - ImGuiIO& io = ImGui::GetIO(); - - // Setup display size (every frame to accommodate for window resizing) - int w, h; - int display_w, display_h; - glfwGetWindowSize(g_Window, &w, &h); - glfwGetFramebufferSize(g_Window, &display_w, &display_h); - io.DisplaySize = ImVec2((float)w, (float)h); - io.DisplayFramebufferScale = ImVec2(w > 0 ? ((float)display_w / w) : 0, h > 0 ? ((float)display_h / h) : 0); - - // Setup time step - double current_time = glfwGetTime(); - io.DeltaTime = g_Time > 0.0 ? (float)(current_time - g_Time) : (float)(1.0f/60.0f); - g_Time = current_time; - - // Setup inputs - // (we already got mouse wheel, keyboard keys & characters from glfw callbacks polled in glfwPollEvents()) - if (glfwGetWindowAttrib(g_Window, GLFW_FOCUSED)) - { - if (io.WantMoveMouse) - { - glfwSetCursorPos(g_Window, (double)io.MousePos.x, (double)io.MousePos.y); // Set mouse position if requested by io.WantMoveMouse flag (used when io.NavMovesTrue is enabled by user and using directional navigation) - } - else - { - double mouse_x, mouse_y; - glfwGetCursorPos(g_Window, &mouse_x, &mouse_y); - io.MousePos = ImVec2((float)mouse_x, (float)mouse_y); - } - } - else - { - io.MousePos = ImVec2(-FLT_MAX,-FLT_MAX); - } - - for (int i = 0; i < 3; i++) - { - // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame. - io.MouseDown[i] = g_MouseJustPressed[i] || glfwGetMouseButton(g_Window, i) != 0; - g_MouseJustPressed[i] = false; - } - - // Hide OS mouse cursor if ImGui is drawing it - glfwSetInputMode(g_Window, GLFW_CURSOR, io.MouseDrawCursor ? GLFW_CURSOR_HIDDEN : GLFW_CURSOR_NORMAL); - - // Start the frame. This call will update the io.WantCaptureMouse, io.WantCaptureKeyboard flag that you can use to dispatch inputs (or not) to your application. - ImGui::NewFrame(); -} diff --git a/examples/opengl2_example/imgui_impl_glfw_gl2.h b/examples/opengl2_example/imgui_impl_glfw_gl2.h deleted file mode 100644 index 268b0e498450..000000000000 --- a/examples/opengl2_example/imgui_impl_glfw_gl2.h +++ /dev/null @@ -1,32 +0,0 @@ -// ImGui GLFW binding with OpenGL (legacy, fixed pipeline) -// (GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) - -// Implemented features: -// [X] User texture binding. Cast 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. - -// **DO NOT USE THIS CODE IF YOUR CODE/ENGINE IS USING MODERN OPENGL (SHADERS, VBO, VAO, etc.)** -// **Prefer using the code in the opengl3_example/ folder** -// See imgui_impl_glfw.cpp for details. - -// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. -// If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). -// If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp. -// https://github.com/ocornut/imgui - -struct GLFWwindow; - -IMGUI_API bool ImGui_ImplGlfwGL2_Init(GLFWwindow* window, bool install_callbacks); -IMGUI_API void ImGui_ImplGlfwGL2_Shutdown(); -IMGUI_API void ImGui_ImplGlfwGL2_NewFrame(); -IMGUI_API void ImGui_ImplGlfwGL2_RenderDrawData(ImDrawData* draw_data); - -// Use if you want to reset your rendering device without losing ImGui state. -IMGUI_API void ImGui_ImplGlfwGL2_InvalidateDeviceObjects(); -IMGUI_API bool ImGui_ImplGlfwGL2_CreateDeviceObjects(); - -// GLFW callbacks (registered by default to GLFW if you enable 'install_callbacks' during initialization) -// Provided here if you want to chain callbacks yourself. You may also handle inputs yourself and use those as a reference. -IMGUI_API void ImGui_ImplGlfwGL2_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods); -IMGUI_API void ImGui_ImplGlfwGL2_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset); -IMGUI_API void ImGui_ImplGlfwGL2_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods); -IMGUI_API void ImGui_ImplGlfwGL2_CharCallback(GLFWwindow* window, unsigned int c); diff --git a/examples/opengl2_example/main.cpp b/examples/opengl2_example/main.cpp index caae8c2b66c9..6eefca8fffa6 100644 --- a/examples/opengl2_example/main.cpp +++ b/examples/opengl2_example/main.cpp @@ -7,7 +7,8 @@ // See imgui_impl_glfw.cpp for details. #include "imgui.h" -#include "imgui_impl_glfw_gl2.h" +#include "../imgui_impl_glfw.h" +#include "../imgui_impl_opengl2.h" #include #include @@ -29,7 +30,8 @@ int main(int, char**) // Setup ImGui binding ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - ImGui_ImplGlfwGL2_Init(window, true); + ImGui_ImplGlfw_Init(window, true); + ImGui_ImplOpenGL2_Init(); //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; // Enable Keyboard Controls // Setup style @@ -63,7 +65,8 @@ int main(int, char**) // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. glfwPollEvents(); - ImGui_ImplGlfwGL2_NewFrame(); + ImGui_ImplOpenGL2_NewFrame(); + ImGui_ImplGlfw_NewFrame(); // 1. Show a simple window. // Tip: if we don't call ImGui::Begin()/ImGui::End() the widgets automatically appears in a window called "Debug". @@ -110,12 +113,13 @@ int main(int, char**) glClear(GL_COLOR_BUFFER_BIT); //glUseProgram(0); // You may want this if using this code in an OpenGL 3+ context where shaders may be bound, but prefer using the GL3+ code. ImGui::Render(); - ImGui_ImplGlfwGL2_RenderDrawData(ImGui::GetDrawData()); + ImGui_ImplOpenGL2_RenderDrawData(ImGui::GetDrawData()); glfwSwapBuffers(window); } // Cleanup - ImGui_ImplGlfwGL2_Shutdown(); + ImGui_ImplOpenGL2_Shutdown(); + ImGui_ImplGlfw_Shutdown(); ImGui::DestroyContext(); glfwTerminate(); diff --git a/examples/opengl2_example/opengl2_example.vcxproj b/examples/opengl2_example/opengl2_example.vcxproj index 3756748d32ce..501d73c00ad3 100644 --- a/examples/opengl2_example/opengl2_example.vcxproj +++ b/examples/opengl2_example/opengl2_example.vcxproj @@ -153,14 +153,16 @@ - + + - + + diff --git a/examples/opengl2_example/opengl2_example.vcxproj.filters b/examples/opengl2_example/opengl2_example.vcxproj.filters index 0c85d9fa380d..acf77fa4f06e 100644 --- a/examples/opengl2_example/opengl2_example.vcxproj.filters +++ b/examples/opengl2_example/opengl2_example.vcxproj.filters @@ -16,15 +16,18 @@ imgui - - sources - imgui imgui + + sources + + + sources + @@ -33,12 +36,15 @@ imgui - - sources - imgui + + sources + + + sources + diff --git a/examples/opengl3_example/main.cpp b/examples/opengl3_example/main.cpp index a57b96a2d595..3a49f8ee5074 100644 --- a/examples/opengl3_example/main.cpp +++ b/examples/opengl3_example/main.cpp @@ -4,8 +4,8 @@ // (GL3W is a helper library to access OpenGL functions since there is no standard header to access modern OpenGL functions easily. Alternatives are GLEW, Glad, etc.) #include "imgui.h" -#include "imgui_impl_glfw.h" -#include "imgui_impl_opengl3.h" +#include "../imgui_impl_glfw.h" +#include "../imgui_impl_opengl3.h" #include #include // This example is using gl3w to access OpenGL functions (because it is small). You may use glew/glad/glLoadGen/etc. whatever already works for you. #include diff --git a/examples/opengl3_example/opengl3_example.vcxproj b/examples/opengl3_example/opengl3_example.vcxproj index 3ade8ec8a54f..e0ff1ba3f7a8 100644 --- a/examples/opengl3_example/opengl3_example.vcxproj +++ b/examples/opengl3_example/opengl3_example.vcxproj @@ -153,19 +153,19 @@ + + - - + + - - diff --git a/examples/opengl3_example/opengl3_example.vcxproj.filters b/examples/opengl3_example/opengl3_example.vcxproj.filters index f45ad98338e6..bffa93731721 100644 --- a/examples/opengl3_example/opengl3_example.vcxproj.filters +++ b/examples/opengl3_example/opengl3_example.vcxproj.filters @@ -28,10 +28,10 @@ imgui - + sources - + sources @@ -51,10 +51,10 @@ imgui - + sources - + sources From 42c32bf00c17c230d0f264739fa37675ff1788ec Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 16 Feb 2018 22:22:10 +0100 Subject: [PATCH 004/828] Examples: OpenGL2, OpenGL3: Added glPixelStorei() calls borrowed from SDL examples. --- examples/imgui_impl_opengl2.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/imgui_impl_opengl2.cpp b/examples/imgui_impl_opengl2.cpp index 06c770affe44..8810b094d5a5 100644 --- a/examples/imgui_impl_opengl2.cpp +++ b/examples/imgui_impl_opengl2.cpp @@ -141,6 +141,7 @@ bool ImGui_ImplOpenGL2_CreateFontsTexture() glBindTexture(GL_TEXTURE_2D, g_FontTexture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); // Store our identifier From ef521d1e0bb7d671e04a6e171fc268308daf44ff Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 16 Feb 2018 22:22:47 +0100 Subject: [PATCH 005/828] Examples: SDL: Extracted into imgui_impl_sdl.*, reused imgui_impl_opengl* files. --- ...i_impl_sdl_gl2.cpp => imgui_impl_sdl2.cpp} | 189 ++------ ...imgui_impl_sdl_gl3.h => imgui_impl_sdl2.h} | 13 +- .../sdl_opengl2_example/imgui_impl_sdl_gl2.h | 27 -- examples/sdl_opengl2_example/main.cpp | 16 +- .../sdl_opengl2_example.vcxproj | 6 +- .../sdl_opengl2_example.vcxproj.filters | 10 +- .../imgui_impl_sdl_gl3.cpp | 457 ------------------ examples/sdl_opengl3_example/main.cpp | 16 +- .../sdl_opengl3_example.vcxproj | 6 +- .../sdl_opengl3_example.vcxproj.filters | 18 +- 10 files changed, 82 insertions(+), 676 deletions(-) rename examples/{sdl_opengl2_example/imgui_impl_sdl_gl2.cpp => imgui_impl_sdl2.cpp} (52%) rename examples/{sdl_opengl3_example/imgui_impl_sdl_gl3.h => imgui_impl_sdl2.h} (63%) delete mode 100644 examples/sdl_opengl2_example/imgui_impl_sdl_gl2.h delete mode 100644 examples/sdl_opengl3_example/imgui_impl_sdl_gl3.cpp diff --git a/examples/sdl_opengl2_example/imgui_impl_sdl_gl2.cpp b/examples/imgui_impl_sdl2.cpp similarity index 52% rename from examples/sdl_opengl2_example/imgui_impl_sdl_gl2.cpp rename to examples/imgui_impl_sdl2.cpp index 769ebb9fd531..6b39c3f8da0b 100644 --- a/examples/sdl_opengl2_example/imgui_impl_sdl_gl2.cpp +++ b/examples/imgui_impl_sdl2.cpp @@ -1,17 +1,10 @@ -// ImGui SDL2 binding with OpenGL (legacy, fixed pipeline) +// ImGui SDL2 binding with OpenGL3 // (SDL is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) +// (GL3W is a helper library to access OpenGL functions since there is no standard header to access modern OpenGL functions easily. Alternatives are GLEW, Glad, etc.) // Implemented features: // [X] User texture binding. Cast 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. -// **DO NOT USE THIS CODE IF YOUR CODE/ENGINE IS USING MODERN OPENGL (SHADERS, VBO, VAO, etc.)** -// **Prefer using the code in the sdl_opengl3_example/ folder** -// This code is mostly provided as a reference to learn how ImGui integration works, because it is shorter to read. -// If your code is using GL3+ context or any semi modern OpenGL calls, using this is likely to make everything more -// complicated, will require your code to reset every single OpenGL attributes to their initial state, and might -// confuse your GPU driver. -// The GL2 code is unable to reset attributes or even call e.g. "glUseProgram(0)" because they don't exist in that API. - // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). // If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp. @@ -20,7 +13,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) // 2018-02-16: Inputs: Added support for mouse cursors, honoring ImGui::GetMouseCursor() value. -// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplSdlGL2_RenderDrawData() in the .h file so you can call it yourself. +// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplSdlGL3_RenderDrawData() in the .h file so you can call it yourself. // 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. // 2018-02-06: Inputs: Added mapping for ImGuiKey_Space. // 2018-02-05: Misc: Using SDL_GetPerformanceCounter() instead of SDL_GetTicks() to be able to handle very high framerate (1000+ FPS). @@ -28,114 +21,33 @@ // 2018-01-20: Inputs: Added Horizontal Mouse Wheel support. // 2018-01-19: Inputs: When available (SDL 2.0.4+) using SDL_CaptureMouse() to retrieve coordinates outside of client area when dragging. Otherwise (SDL 2.0.3 and before) testing for SDL_WINDOW_INPUT_FOCUS instead of SDL_WINDOW_MOUSE_FOCUS. // 2018-01-18: Inputs: Added mapping for ImGuiKey_Insert. -// 2017-09-01: OpenGL: Save and restore current polygon mode. +// 2018-01-07: OpenGL: Changed GLSL shader version from 330 to 150. +// 2017-09-01: OpenGL: Save and restore current bound sampler. Save and restore current polygon mode. // 2017-08-25: Inputs: MousePos set to -FLT_MAX,-FLT_MAX when mouse is unavailable/missing (instead of -1,-1). +// 2017-05-01: OpenGL: Fixed save and restore of current blend func state. +// 2017-05-01: OpenGL: Fixed save and restore of current GL_ACTIVE_TEXTURE. // 2016-10-15: Misc: Added a void* user_data parameter to Clipboard function handlers. // 2016-09-05: OpenGL: Fixed save and restore of current scissor rectangle. // 2016-07-29: OpenGL: Explicitly setting GL_UNPACK_ROW_LENGTH to reduce issues because SDL changes it. (#752) +#include "imgui.h" +#include "imgui_impl_sdl2.h" + +// SDL,GL3W #include #include -#include -#include "imgui.h" -#include "imgui_impl_sdl_gl2.h" // Data static Uint64 g_Time = 0; static bool g_MousePressed[3] = { false, false, false }; -static GLuint g_FontTexture = 0; -static SDL_Cursor* g_SdlCursors[ImGuiMouseCursor_Count_] = { 0 }; - -// OpenGL2 Render function. -// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop) -// Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly, in order to be able to run within any OpenGL engine that doesn't do so. -void ImGui_ImplSdlGL2_RenderDrawData(ImDrawData* draw_data) -{ - // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) - ImGuiIO& io = ImGui::GetIO(); - int fb_width = (int)(io.DisplaySize.x * io.DisplayFramebufferScale.x); - int fb_height = (int)(io.DisplaySize.y * io.DisplayFramebufferScale.y); - if (fb_width == 0 || fb_height == 0) - return; - draw_data->ScaleClipRects(io.DisplayFramebufferScale); - - // We are using the OpenGL fixed pipeline to make the example code simpler to read! - // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, vertex/texcoord/color pointers, polygon fill. - GLint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); - GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode); - GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport); - GLint last_scissor_box[4]; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box); - glPushAttrib(GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT | GL_TRANSFORM_BIT); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glDisable(GL_CULL_FACE); - glDisable(GL_DEPTH_TEST); - glEnable(GL_SCISSOR_TEST); - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glEnableClientState(GL_COLOR_ARRAY); - glEnable(GL_TEXTURE_2D); - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - //glUseProgram(0); // You may want this if using this code in an OpenGL 3+ context where shaders may be bound - - // Setup viewport, orthographic projection matrix - glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height); - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - glOrtho(0.0f, io.DisplaySize.x, io.DisplaySize.y, 0.0f, -1.0f, +1.0f); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - - // Render command lists - for (int n = 0; n < draw_data->CmdListsCount; n++) - { - const ImDrawList* cmd_list = draw_data->CmdLists[n]; - const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data; - const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data; - glVertexPointer(2, GL_FLOAT, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, pos))); - glTexCoordPointer(2, GL_FLOAT, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, uv))); - glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, col))); - - for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) - { - const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; - if (pcmd->UserCallback) - { - pcmd->UserCallback(cmd_list, pcmd); - } - else - { - glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId); - glScissor((int)pcmd->ClipRect.x, (int)(fb_height - pcmd->ClipRect.w), (int)(pcmd->ClipRect.z - pcmd->ClipRect.x), (int)(pcmd->ClipRect.w - pcmd->ClipRect.y)); - glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer); - } - idx_buffer += pcmd->ElemCount; - } - } - - // Restore modified state - glDisableClientState(GL_COLOR_ARRAY); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - glDisableClientState(GL_VERTEX_ARRAY); - glBindTexture(GL_TEXTURE_2D, (GLuint)last_texture); - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glPopAttrib(); - glPolygonMode(GL_FRONT, last_polygon_mode[0]); glPolygonMode(GL_BACK, last_polygon_mode[1]); - glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]); - glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]); -} +static SDL_Cursor* g_MouseCursors[ImGuiMouseCursor_Count_] = { 0 }; -static const char* ImGui_ImplSdlGL2_GetClipboardText(void*) +static const char* ImGui_ImplSDL2_GetClipboardText(void*) { return SDL_GetClipboardText(); } -static void ImGui_ImplSdlGL2_SetClipboardText(void*, const char* text) +static void ImGui_ImplSDL2_SetClipboardText(void*, const char* text) { SDL_SetClipboardText(text); } @@ -144,7 +56,7 @@ static void ImGui_ImplSdlGL2_SetClipboardText(void*, const char* text) // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. -bool ImGui_ImplSdlGL2_ProcessEvent(SDL_Event* event) +bool ImGui_ImplSDL2_ProcessEvent(SDL_Event* event) { ImGuiIO& io = ImGui::GetIO(); switch (event->type) @@ -185,44 +97,7 @@ bool ImGui_ImplSdlGL2_ProcessEvent(SDL_Event* event) return false; } -bool ImGui_ImplSdlGL2_CreateDeviceObjects() -{ - // Build texture atlas - ImGuiIO& io = ImGui::GetIO(); - unsigned char* pixels; - int width, height; - io.Fonts->GetTexDataAsAlpha8(&pixels, &width, &height); - - // Upload texture to graphics system - GLint last_texture; - glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); - glGenTextures(1, &g_FontTexture); - glBindTexture(GL_TEXTURE_2D, g_FontTexture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, pixels); - - // Store our identifier - io.Fonts->TexID = (void *)(intptr_t)g_FontTexture; - - // Restore state - glBindTexture(GL_TEXTURE_2D, last_texture); - - return true; -} - -void ImGui_ImplSdlGL2_InvalidateDeviceObjects() -{ - if (g_FontTexture) - { - glDeleteTextures(1, &g_FontTexture); - ImGui::GetIO().Fonts->TexID = 0; - g_FontTexture = 0; - } -} - -bool ImGui_ImplSdlGL2_Init(SDL_Window* window) +bool ImGui_ImplSDL2_Init(SDL_Window* window) { // Keyboard mapping. ImGui will use those indices to peek into the io.KeyDown[] array. ImGuiIO& io = ImGui::GetIO(); @@ -248,17 +123,17 @@ bool ImGui_ImplSdlGL2_Init(SDL_Window* window) io.KeyMap[ImGuiKey_Y] = SDL_SCANCODE_Y; io.KeyMap[ImGuiKey_Z] = SDL_SCANCODE_Z; - io.SetClipboardTextFn = ImGui_ImplSdlGL2_SetClipboardText; - io.GetClipboardTextFn = ImGui_ImplSdlGL2_GetClipboardText; + io.SetClipboardTextFn = ImGui_ImplSDL2_SetClipboardText; + io.GetClipboardTextFn = ImGui_ImplSDL2_GetClipboardText; io.ClipboardUserData = NULL; - g_SdlCursors[ImGuiMouseCursor_Arrow] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW); - g_SdlCursors[ImGuiMouseCursor_TextInput] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_IBEAM); - g_SdlCursors[ImGuiMouseCursor_ResizeAll] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEALL); - g_SdlCursors[ImGuiMouseCursor_ResizeNS] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENS); - g_SdlCursors[ImGuiMouseCursor_ResizeEW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEWE); - g_SdlCursors[ImGuiMouseCursor_ResizeNESW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENESW); - g_SdlCursors[ImGuiMouseCursor_ResizeNWSE] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENWSE); + g_MouseCursors[ImGuiMouseCursor_Arrow] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW); + g_MouseCursors[ImGuiMouseCursor_TextInput] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_IBEAM); + g_MouseCursors[ImGuiMouseCursor_ResizeAll] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEALL); + g_MouseCursors[ImGuiMouseCursor_ResizeNS] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENS); + g_MouseCursors[ImGuiMouseCursor_ResizeEW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEWE); + g_MouseCursors[ImGuiMouseCursor_ResizeNESW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENESW); + g_MouseCursors[ImGuiMouseCursor_ResizeNWSE] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENWSE); #ifdef _WIN32 SDL_SysWMinfo wmInfo; @@ -272,20 +147,16 @@ bool ImGui_ImplSdlGL2_Init(SDL_Window* window) return true; } -void ImGui_ImplSdlGL2_Shutdown() +void ImGui_ImplSDL2_Shutdown() { - ImGui_ImplSdlGL2_InvalidateDeviceObjects(); - for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_Count_; cursor_n++) - SDL_FreeCursor(g_SdlCursors[cursor_n]); + SDL_FreeCursor(g_MouseCursors[cursor_n]); } -void ImGui_ImplSdlGL2_NewFrame(SDL_Window *window) +void ImGui_ImplSDL2_NewFrame(SDL_Window* window) { - if (!g_FontTexture) - ImGui_ImplSdlGL2_CreateDeviceObjects(); - ImGuiIO& io = ImGui::GetIO(); + IM_ASSERT(io.Fonts->IsBuilt()); // Font atlas needs to be built, call renderer _NewFrame() function e.g. ImGui_ImplOpenGL3_NewFrame() // Setup display size (every frame to accommodate for window resizing) int w, h; @@ -334,7 +205,7 @@ void ImGui_ImplSdlGL2_NewFrame(SDL_Window *window) } else { - SDL_SetCursor(g_SdlCursors[cursor]); + SDL_SetCursor(g_MouseCursors[cursor]); SDL_ShowCursor(1); } diff --git a/examples/sdl_opengl3_example/imgui_impl_sdl_gl3.h b/examples/imgui_impl_sdl2.h similarity index 63% rename from examples/sdl_opengl3_example/imgui_impl_sdl_gl3.h rename to examples/imgui_impl_sdl2.h index fa111a1793b7..b19c0f5e2bf4 100644 --- a/examples/sdl_opengl3_example/imgui_impl_sdl_gl3.h +++ b/examples/imgui_impl_sdl2.h @@ -13,12 +13,7 @@ struct SDL_Window; typedef union SDL_Event SDL_Event; -IMGUI_API bool ImGui_ImplSdlGL3_Init(SDL_Window* window); -IMGUI_API void ImGui_ImplSdlGL3_Shutdown(); -IMGUI_API void ImGui_ImplSdlGL3_NewFrame(SDL_Window* window); -IMGUI_API void ImGui_ImplSdlGL3_RenderDrawData(ImDrawData* draw_data); -IMGUI_API bool ImGui_ImplSdlGL3_ProcessEvent(SDL_Event* event); - -// Use if you want to reset your rendering device without losing ImGui state. -IMGUI_API void ImGui_ImplSdlGL3_InvalidateDeviceObjects(); -IMGUI_API bool ImGui_ImplSdlGL3_CreateDeviceObjects(); +IMGUI_API bool ImGui_ImplSDL2_Init(SDL_Window* window); +IMGUI_API void ImGui_ImplSDL2_Shutdown(); +IMGUI_API void ImGui_ImplSDL2_NewFrame(SDL_Window* window); +IMGUI_API bool ImGui_ImplSDL2_ProcessEvent(SDL_Event* event); diff --git a/examples/sdl_opengl2_example/imgui_impl_sdl_gl2.h b/examples/sdl_opengl2_example/imgui_impl_sdl_gl2.h deleted file mode 100644 index 4df1c169c4ee..000000000000 --- a/examples/sdl_opengl2_example/imgui_impl_sdl_gl2.h +++ /dev/null @@ -1,27 +0,0 @@ -// ImGui SDL2 binding with OpenGL (legacy, fixed pipeline) -// (SDL is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) - -// Implemented features: -// [X] User texture binding. Cast 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. - -// **DO NOT USE THIS CODE IF YOUR CODE/ENGINE IS USING MODERN OPENGL (SHADERS, VBO, VAO, etc.)** -// **Prefer using the code in the sdl_opengl3_example/ folder** -// See imgui_impl_sdl.cpp for details. - -// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. -// If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). -// If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp. -// https://github.com/ocornut/imgui - -struct SDL_Window; -typedef union SDL_Event SDL_Event; - -IMGUI_API bool ImGui_ImplSdlGL2_Init(SDL_Window* window); -IMGUI_API void ImGui_ImplSdlGL2_Shutdown(); -IMGUI_API void ImGui_ImplSdlGL2_NewFrame(SDL_Window* window); -IMGUI_API void ImGui_ImplSdlGL2_RenderDrawData(ImDrawData* draw_data); -IMGUI_API bool ImGui_ImplSdlGL2_ProcessEvent(SDL_Event* event); - -// Use if you want to reset your rendering device without losing ImGui state. -IMGUI_API void ImGui_ImplSdlGL2_InvalidateDeviceObjects(); -IMGUI_API bool ImGui_ImplSdlGL2_CreateDeviceObjects(); diff --git a/examples/sdl_opengl2_example/main.cpp b/examples/sdl_opengl2_example/main.cpp index 8f4ca93dcb13..6c8dd1d3b3f2 100644 --- a/examples/sdl_opengl2_example/main.cpp +++ b/examples/sdl_opengl2_example/main.cpp @@ -7,7 +7,8 @@ // See imgui_impl_sdl.cpp for details. #include "imgui.h" -#include "imgui_impl_sdl_gl2.h" +#include "../imgui_impl_sdl2.h" +#include "../imgui_impl_opengl2.h" #include #include #include @@ -36,7 +37,8 @@ int main(int, char**) // Setup ImGui binding ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - ImGui_ImplSdlGL2_Init(window); + ImGui_ImplSDL2_Init(window); + ImGui_ImplOpenGL2_Init(); //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; // Enable Keyboard Controls // Setup style @@ -73,11 +75,12 @@ int main(int, char**) SDL_Event event; while (SDL_PollEvent(&event)) { - ImGui_ImplSdlGL2_ProcessEvent(&event); + ImGui_ImplSDL2_ProcessEvent(&event); if (event.type == SDL_QUIT) done = true; } - ImGui_ImplSdlGL2_NewFrame(window); + ImGui_ImplOpenGL2_NewFrame(); + ImGui_ImplSDL2_NewFrame(window); // 1. Show a simple window. // Tip: if we don't call ImGui::Begin()/ImGui::End() the widgets automatically appears in a window called "Debug". @@ -122,12 +125,13 @@ int main(int, char**) glClear(GL_COLOR_BUFFER_BIT); //glUseProgram(0); // You may want this if using this code in an OpenGL 3+ context where shaders may be bound ImGui::Render(); - ImGui_ImplSdlGL2_RenderDrawData(ImGui::GetDrawData()); + ImGui_ImplOpenGL2_RenderDrawData(ImGui::GetDrawData()); SDL_GL_SwapWindow(window); } // Cleanup - ImGui_ImplSdlGL2_Shutdown(); + ImGui_ImplOpenGL2_Shutdown(); + ImGui_ImplSDL2_Shutdown(); ImGui::DestroyContext(); SDL_GL_DeleteContext(glcontext); diff --git a/examples/sdl_opengl2_example/sdl_opengl2_example.vcxproj b/examples/sdl_opengl2_example/sdl_opengl2_example.vcxproj index ad83c48150df..776cde6cba71 100644 --- a/examples/sdl_opengl2_example/sdl_opengl2_example.vcxproj +++ b/examples/sdl_opengl2_example/sdl_opengl2_example.vcxproj @@ -153,14 +153,16 @@ - + + - + + diff --git a/examples/sdl_opengl2_example/sdl_opengl2_example.vcxproj.filters b/examples/sdl_opengl2_example/sdl_opengl2_example.vcxproj.filters index 1c505f4e579f..f129cc255a9a 100644 --- a/examples/sdl_opengl2_example/sdl_opengl2_example.vcxproj.filters +++ b/examples/sdl_opengl2_example/sdl_opengl2_example.vcxproj.filters @@ -22,7 +22,10 @@ sources - + + sources + + sources @@ -36,7 +39,10 @@ imgui - + + sources + + sources diff --git a/examples/sdl_opengl3_example/imgui_impl_sdl_gl3.cpp b/examples/sdl_opengl3_example/imgui_impl_sdl_gl3.cpp deleted file mode 100644 index 585439abdcc4..000000000000 --- a/examples/sdl_opengl3_example/imgui_impl_sdl_gl3.cpp +++ /dev/null @@ -1,457 +0,0 @@ -// ImGui SDL2 binding with OpenGL3 -// (SDL is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) -// (GL3W is a helper library to access OpenGL functions since there is no standard header to access modern OpenGL functions easily. Alternatives are GLEW, Glad, etc.) - -// Implemented features: -// [X] User texture binding. Cast 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. - -// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. -// If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). -// If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp. -// https://github.com/ocornut/imgui - -// CHANGELOG -// (minor and older changes stripped away, please see git history for details) -// 2018-02-16: Inputs: Added support for mouse cursors, honoring ImGui::GetMouseCursor() value. -// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplSdlGL3_RenderDrawData() in the .h file so you can call it yourself. -// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. -// 2018-02-06: Inputs: Added mapping for ImGuiKey_Space. -// 2018-02-05: Misc: Using SDL_GetPerformanceCounter() instead of SDL_GetTicks() to be able to handle very high framerate (1000+ FPS). -// 2018-02-05: Inputs: Keyboard mapping is using scancodes everywhere instead of a confusing mixture of keycodes and scancodes. -// 2018-01-20: Inputs: Added Horizontal Mouse Wheel support. -// 2018-01-19: Inputs: When available (SDL 2.0.4+) using SDL_CaptureMouse() to retrieve coordinates outside of client area when dragging. Otherwise (SDL 2.0.3 and before) testing for SDL_WINDOW_INPUT_FOCUS instead of SDL_WINDOW_MOUSE_FOCUS. -// 2018-01-18: Inputs: Added mapping for ImGuiKey_Insert. -// 2018-01-07: OpenGL: Changed GLSL shader version from 330 to 150. -// 2017-09-01: OpenGL: Save and restore current bound sampler. Save and restore current polygon mode. -// 2017-08-25: Inputs: MousePos set to -FLT_MAX,-FLT_MAX when mouse is unavailable/missing (instead of -1,-1). -// 2017-05-01: OpenGL: Fixed save and restore of current blend func state. -// 2017-05-01: OpenGL: Fixed save and restore of current GL_ACTIVE_TEXTURE. -// 2016-10-15: Misc: Added a void* user_data parameter to Clipboard function handlers. -// 2016-09-05: OpenGL: Fixed save and restore of current scissor rectangle. -// 2016-07-29: OpenGL: Explicitly setting GL_UNPACK_ROW_LENGTH to reduce issues because SDL changes it. (#752) - -#include "imgui.h" -#include "imgui_impl_sdl_gl3.h" - -// SDL,GL3W -#include -#include -#include // This example is using gl3w to access OpenGL functions (because it is small). You may use glew/glad/glLoadGen/etc. whatever already works for you. - -// Data -static Uint64 g_Time = 0; -static bool g_MousePressed[3] = { false, false, false }; -static GLuint g_FontTexture = 0; -static int g_ShaderHandle = 0, g_VertHandle = 0, g_FragHandle = 0; -static int g_AttribLocationTex = 0, g_AttribLocationProjMtx = 0; -static int g_AttribLocationPosition = 0, g_AttribLocationUV = 0, g_AttribLocationColor = 0; -static unsigned int g_VboHandle = 0, g_VaoHandle = 0, g_ElementsHandle = 0; -static SDL_Cursor* g_SdlCursors[ImGuiMouseCursor_Count_] = { 0 }; - -// This is the main rendering function that you have to implement and provide to ImGui (via setting up 'RenderDrawListsFn' in the ImGuiIO structure) -// Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly, in order to be able to run within any OpenGL engine that doesn't do so. -// If text or lines are blurry when integrating ImGui in your engine: in your Render function, try translating your projection matrix by (0.5f,0.5f) or (0.375f,0.375f) -void ImGui_ImplSdlGL3_RenderDrawData(ImDrawData* draw_data) -{ - // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) - ImGuiIO& io = ImGui::GetIO(); - int fb_width = (int)(io.DisplaySize.x * io.DisplayFramebufferScale.x); - int fb_height = (int)(io.DisplaySize.y * io.DisplayFramebufferScale.y); - if (fb_width == 0 || fb_height == 0) - return; - draw_data->ScaleClipRects(io.DisplayFramebufferScale); - - // Backup GL state - GLenum last_active_texture; glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint*)&last_active_texture); - glActiveTexture(GL_TEXTURE0); - GLint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, &last_program); - GLint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); - GLint last_sampler; glGetIntegerv(GL_SAMPLER_BINDING, &last_sampler); - GLint last_array_buffer; glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer); - GLint last_element_array_buffer; glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &last_element_array_buffer); - GLint last_vertex_array; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array); - GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode); - GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport); - GLint last_scissor_box[4]; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box); - GLenum last_blend_src_rgb; glGetIntegerv(GL_BLEND_SRC_RGB, (GLint*)&last_blend_src_rgb); - GLenum last_blend_dst_rgb; glGetIntegerv(GL_BLEND_DST_RGB, (GLint*)&last_blend_dst_rgb); - GLenum last_blend_src_alpha; glGetIntegerv(GL_BLEND_SRC_ALPHA, (GLint*)&last_blend_src_alpha); - GLenum last_blend_dst_alpha; glGetIntegerv(GL_BLEND_DST_ALPHA, (GLint*)&last_blend_dst_alpha); - GLenum last_blend_equation_rgb; glGetIntegerv(GL_BLEND_EQUATION_RGB, (GLint*)&last_blend_equation_rgb); - GLenum last_blend_equation_alpha; glGetIntegerv(GL_BLEND_EQUATION_ALPHA, (GLint*)&last_blend_equation_alpha); - GLboolean last_enable_blend = glIsEnabled(GL_BLEND); - GLboolean last_enable_cull_face = glIsEnabled(GL_CULL_FACE); - GLboolean last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST); - GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST); - - // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill - glEnable(GL_BLEND); - glBlendEquation(GL_FUNC_ADD); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glDisable(GL_CULL_FACE); - glDisable(GL_DEPTH_TEST); - glEnable(GL_SCISSOR_TEST); - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - - // Setup viewport, orthographic projection matrix - glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height); - const float ortho_projection[4][4] = - { - { 2.0f/io.DisplaySize.x, 0.0f, 0.0f, 0.0f }, - { 0.0f, 2.0f/-io.DisplaySize.y, 0.0f, 0.0f }, - { 0.0f, 0.0f, -1.0f, 0.0f }, - {-1.0f, 1.0f, 0.0f, 1.0f }, - }; - glUseProgram(g_ShaderHandle); - glUniform1i(g_AttribLocationTex, 0); - glUniformMatrix4fv(g_AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]); - glBindVertexArray(g_VaoHandle); - glBindSampler(0, 0); // Rely on combined texture/sampler state. - - for (int n = 0; n < draw_data->CmdListsCount; n++) - { - const ImDrawList* cmd_list = draw_data->CmdLists[n]; - const ImDrawIdx* idx_buffer_offset = 0; - - glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle); - glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)cmd_list->VtxBuffer.Size * sizeof(ImDrawVert), (const GLvoid*)cmd_list->VtxBuffer.Data, GL_STREAM_DRAW); - - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_ElementsHandle); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, (GLsizeiptr)cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx), (const GLvoid*)cmd_list->IdxBuffer.Data, GL_STREAM_DRAW); - - for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) - { - const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; - if (pcmd->UserCallback) - { - pcmd->UserCallback(cmd_list, pcmd); - } - else - { - glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId); - glScissor((int)pcmd->ClipRect.x, (int)(fb_height - pcmd->ClipRect.w), (int)(pcmd->ClipRect.z - pcmd->ClipRect.x), (int)(pcmd->ClipRect.w - pcmd->ClipRect.y)); - glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset); - } - idx_buffer_offset += pcmd->ElemCount; - } - } - - // Restore modified GL state - glUseProgram(last_program); - glBindTexture(GL_TEXTURE_2D, last_texture); - glBindSampler(0, last_sampler); - glActiveTexture(last_active_texture); - glBindVertexArray(last_vertex_array); - glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, last_element_array_buffer); - glBlendEquationSeparate(last_blend_equation_rgb, last_blend_equation_alpha); - glBlendFuncSeparate(last_blend_src_rgb, last_blend_dst_rgb, last_blend_src_alpha, last_blend_dst_alpha); - if (last_enable_blend) glEnable(GL_BLEND); else glDisable(GL_BLEND); - if (last_enable_cull_face) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE); - if (last_enable_depth_test) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST); - if (last_enable_scissor_test) glEnable(GL_SCISSOR_TEST); else glDisable(GL_SCISSOR_TEST); - glPolygonMode(GL_FRONT_AND_BACK, last_polygon_mode[0]); - glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]); - glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]); -} - -static const char* ImGui_ImplSdlGL3_GetClipboardText(void*) -{ - return SDL_GetClipboardText(); -} - -static void ImGui_ImplSdlGL3_SetClipboardText(void*, const char* text) -{ - SDL_SetClipboardText(text); -} - -// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. -// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application. -// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. -// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. -bool ImGui_ImplSdlGL3_ProcessEvent(SDL_Event* event) -{ - ImGuiIO& io = ImGui::GetIO(); - switch (event->type) - { - case SDL_MOUSEWHEEL: - { - if (event->wheel.x > 0) io.MouseWheelH += 1; - if (event->wheel.x < 0) io.MouseWheelH -= 1; - if (event->wheel.y > 0) io.MouseWheel += 1; - if (event->wheel.y < 0) io.MouseWheel -= 1; - return true; - } - case SDL_MOUSEBUTTONDOWN: - { - if (event->button.button == SDL_BUTTON_LEFT) g_MousePressed[0] = true; - if (event->button.button == SDL_BUTTON_RIGHT) g_MousePressed[1] = true; - if (event->button.button == SDL_BUTTON_MIDDLE) g_MousePressed[2] = true; - return true; - } - case SDL_TEXTINPUT: - { - io.AddInputCharactersUTF8(event->text.text); - return true; - } - case SDL_KEYDOWN: - case SDL_KEYUP: - { - int key = event->key.keysym.scancode; - IM_ASSERT(key >= 0 && key < IM_ARRAYSIZE(io.KeysDown)); - io.KeysDown[key] = (event->type == SDL_KEYDOWN); - io.KeyShift = ((SDL_GetModState() & KMOD_SHIFT) != 0); - io.KeyCtrl = ((SDL_GetModState() & KMOD_CTRL) != 0); - io.KeyAlt = ((SDL_GetModState() & KMOD_ALT) != 0); - io.KeySuper = ((SDL_GetModState() & KMOD_GUI) != 0); - return true; - } - } - return false; -} - -void ImGui_ImplSdlGL3_CreateFontsTexture() -{ - // Build texture atlas - ImGuiIO& io = ImGui::GetIO(); - unsigned char* pixels; - int width, height; - io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bits for OpenGL3 demo because it is more likely to be compatible with user's existing shader. - - // Upload texture to graphics system - GLint last_texture; - glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); - glGenTextures(1, &g_FontTexture); - glBindTexture(GL_TEXTURE_2D, g_FontTexture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); - - // Store our identifier - io.Fonts->TexID = (void *)(intptr_t)g_FontTexture; - - // Restore state - glBindTexture(GL_TEXTURE_2D, last_texture); -} - -bool ImGui_ImplSdlGL3_CreateDeviceObjects() -{ - // Backup GL state - GLint last_texture, last_array_buffer, last_vertex_array; - glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); - glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer); - glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array); - - const GLchar *vertex_shader = - "#version 150\n" - "uniform mat4 ProjMtx;\n" - "in vec2 Position;\n" - "in vec2 UV;\n" - "in vec4 Color;\n" - "out vec2 Frag_UV;\n" - "out vec4 Frag_Color;\n" - "void main()\n" - "{\n" - " Frag_UV = UV;\n" - " Frag_Color = Color;\n" - " gl_Position = ProjMtx * vec4(Position.xy,0,1);\n" - "}\n"; - - const GLchar* fragment_shader = - "#version 150\n" - "uniform sampler2D Texture;\n" - "in vec2 Frag_UV;\n" - "in vec4 Frag_Color;\n" - "out vec4 Out_Color;\n" - "void main()\n" - "{\n" - " Out_Color = Frag_Color * texture( Texture, Frag_UV.st);\n" - "}\n"; - - g_ShaderHandle = glCreateProgram(); - g_VertHandle = glCreateShader(GL_VERTEX_SHADER); - g_FragHandle = glCreateShader(GL_FRAGMENT_SHADER); - glShaderSource(g_VertHandle, 1, &vertex_shader, 0); - glShaderSource(g_FragHandle, 1, &fragment_shader, 0); - glCompileShader(g_VertHandle); - glCompileShader(g_FragHandle); - glAttachShader(g_ShaderHandle, g_VertHandle); - glAttachShader(g_ShaderHandle, g_FragHandle); - glLinkProgram(g_ShaderHandle); - - g_AttribLocationTex = glGetUniformLocation(g_ShaderHandle, "Texture"); - g_AttribLocationProjMtx = glGetUniformLocation(g_ShaderHandle, "ProjMtx"); - g_AttribLocationPosition = glGetAttribLocation(g_ShaderHandle, "Position"); - g_AttribLocationUV = glGetAttribLocation(g_ShaderHandle, "UV"); - g_AttribLocationColor = glGetAttribLocation(g_ShaderHandle, "Color"); - - glGenBuffers(1, &g_VboHandle); - glGenBuffers(1, &g_ElementsHandle); - - glGenVertexArrays(1, &g_VaoHandle); - glBindVertexArray(g_VaoHandle); - glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle); - glEnableVertexAttribArray(g_AttribLocationPosition); - glEnableVertexAttribArray(g_AttribLocationUV); - glEnableVertexAttribArray(g_AttribLocationColor); - - glVertexAttribPointer(g_AttribLocationPosition, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, pos)); - glVertexAttribPointer(g_AttribLocationUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, uv)); - glVertexAttribPointer(g_AttribLocationColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, col)); - - ImGui_ImplSdlGL3_CreateFontsTexture(); - - // Restore modified GL state - glBindTexture(GL_TEXTURE_2D, last_texture); - glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer); - glBindVertexArray(last_vertex_array); - - return true; -} - -void ImGui_ImplSdlGL3_InvalidateDeviceObjects() -{ - if (g_VaoHandle) glDeleteVertexArrays(1, &g_VaoHandle); - if (g_VboHandle) glDeleteBuffers(1, &g_VboHandle); - if (g_ElementsHandle) glDeleteBuffers(1, &g_ElementsHandle); - g_VaoHandle = g_VboHandle = g_ElementsHandle = 0; - - if (g_ShaderHandle && g_VertHandle) glDetachShader(g_ShaderHandle, g_VertHandle); - if (g_VertHandle) glDeleteShader(g_VertHandle); - g_VertHandle = 0; - - if (g_ShaderHandle && g_FragHandle) glDetachShader(g_ShaderHandle, g_FragHandle); - if (g_FragHandle) glDeleteShader(g_FragHandle); - g_FragHandle = 0; - - if (g_ShaderHandle) glDeleteProgram(g_ShaderHandle); - g_ShaderHandle = 0; - - if (g_FontTexture) - { - glDeleteTextures(1, &g_FontTexture); - ImGui::GetIO().Fonts->TexID = 0; - g_FontTexture = 0; - } -} - -bool ImGui_ImplSdlGL3_Init(SDL_Window* window) -{ - // Keyboard mapping. ImGui will use those indices to peek into the io.KeyDown[] array. - ImGuiIO& io = ImGui::GetIO(); - io.KeyMap[ImGuiKey_Tab] = SDL_SCANCODE_TAB; - io.KeyMap[ImGuiKey_LeftArrow] = SDL_SCANCODE_LEFT; - io.KeyMap[ImGuiKey_RightArrow] = SDL_SCANCODE_RIGHT; - io.KeyMap[ImGuiKey_UpArrow] = SDL_SCANCODE_UP; - io.KeyMap[ImGuiKey_DownArrow] = SDL_SCANCODE_DOWN; - io.KeyMap[ImGuiKey_PageUp] = SDL_SCANCODE_PAGEUP; - io.KeyMap[ImGuiKey_PageDown] = SDL_SCANCODE_PAGEDOWN; - io.KeyMap[ImGuiKey_Home] = SDL_SCANCODE_HOME; - io.KeyMap[ImGuiKey_End] = SDL_SCANCODE_END; - io.KeyMap[ImGuiKey_Insert] = SDL_SCANCODE_INSERT; - io.KeyMap[ImGuiKey_Delete] = SDL_SCANCODE_DELETE; - io.KeyMap[ImGuiKey_Backspace] = SDL_SCANCODE_BACKSPACE; - io.KeyMap[ImGuiKey_Space] = SDL_SCANCODE_SPACE; - io.KeyMap[ImGuiKey_Enter] = SDL_SCANCODE_RETURN; - io.KeyMap[ImGuiKey_Escape] = SDL_SCANCODE_ESCAPE; - io.KeyMap[ImGuiKey_A] = SDL_SCANCODE_A; - io.KeyMap[ImGuiKey_C] = SDL_SCANCODE_C; - io.KeyMap[ImGuiKey_V] = SDL_SCANCODE_V; - io.KeyMap[ImGuiKey_X] = SDL_SCANCODE_X; - io.KeyMap[ImGuiKey_Y] = SDL_SCANCODE_Y; - io.KeyMap[ImGuiKey_Z] = SDL_SCANCODE_Z; - - io.SetClipboardTextFn = ImGui_ImplSdlGL3_SetClipboardText; - io.GetClipboardTextFn = ImGui_ImplSdlGL3_GetClipboardText; - io.ClipboardUserData = NULL; - - g_SdlCursors[ImGuiMouseCursor_Arrow] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW); - g_SdlCursors[ImGuiMouseCursor_TextInput] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_IBEAM); - g_SdlCursors[ImGuiMouseCursor_ResizeAll] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEALL); - g_SdlCursors[ImGuiMouseCursor_ResizeNS] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENS); - g_SdlCursors[ImGuiMouseCursor_ResizeEW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEWE); - g_SdlCursors[ImGuiMouseCursor_ResizeNESW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENESW); - g_SdlCursors[ImGuiMouseCursor_ResizeNWSE] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENWSE); - -#ifdef _WIN32 - SDL_SysWMinfo wmInfo; - SDL_VERSION(&wmInfo.version); - SDL_GetWindowWMInfo(window, &wmInfo); - io.ImeWindowHandle = wmInfo.info.win.window; -#else - (void)window; -#endif - - return true; -} - -void ImGui_ImplSdlGL3_Shutdown() -{ - ImGui_ImplSdlGL3_InvalidateDeviceObjects(); - - for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_Count_; cursor_n++) - SDL_FreeCursor(g_SdlCursors[cursor_n]); -} - -void ImGui_ImplSdlGL3_NewFrame(SDL_Window* window) -{ - if (!g_FontTexture) - ImGui_ImplSdlGL3_CreateDeviceObjects(); - - ImGuiIO& io = ImGui::GetIO(); - - // Setup display size (every frame to accommodate for window resizing) - int w, h; - int display_w, display_h; - SDL_GetWindowSize(window, &w, &h); - SDL_GL_GetDrawableSize(window, &display_w, &display_h); - io.DisplaySize = ImVec2((float)w, (float)h); - io.DisplayFramebufferScale = ImVec2(w > 0 ? ((float)display_w / w) : 0, h > 0 ? ((float)display_h / h) : 0); - - // Setup time step (we don't use SDL_GetTicks() because it is using millisecond resolution) - static Uint64 frequency = SDL_GetPerformanceFrequency(); - Uint64 current_time = SDL_GetPerformanceCounter(); - io.DeltaTime = g_Time > 0 ? (float)((double)(current_time - g_Time) / frequency) : (float)(1.0f / 60.0f); - g_Time = current_time; - - // Setup mouse inputs (we already got mouse wheel, keyboard keys & characters from our event handler) - int mx, my; - Uint32 mouse_buttons = SDL_GetMouseState(&mx, &my); - io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); - io.MouseDown[0] = g_MousePressed[0] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_LEFT)) != 0; // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame. - io.MouseDown[1] = g_MousePressed[1] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_RIGHT)) != 0; - io.MouseDown[2] = g_MousePressed[2] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_MIDDLE)) != 0; - g_MousePressed[0] = g_MousePressed[1] = g_MousePressed[2] = false; - - // We need to use SDL_CaptureMouse() to easily retrieve mouse coordinates outside of the client area. This is only supported from SDL 2.0.4 (released Jan 2016) -#if (SDL_MAJOR_VERSION >= 2) && (SDL_MINOR_VERSION >= 0) && (SDL_PATCHLEVEL >= 4) - if ((SDL_GetWindowFlags(window) & (SDL_WINDOW_MOUSE_FOCUS | SDL_WINDOW_MOUSE_CAPTURE)) != 0) - io.MousePos = ImVec2((float)mx, (float)my); - bool any_mouse_button_down = false; - for (int n = 0; n < IM_ARRAYSIZE(io.MouseDown); n++) - any_mouse_button_down |= io.MouseDown[n]; - if (any_mouse_button_down && (SDL_GetWindowFlags(window) & SDL_WINDOW_MOUSE_CAPTURE) == 0) - SDL_CaptureMouse(SDL_TRUE); - if (!any_mouse_button_down && (SDL_GetWindowFlags(window) & SDL_WINDOW_MOUSE_CAPTURE) != 0) - SDL_CaptureMouse(SDL_FALSE); -#else - if ((SDL_GetWindowFlags(window) & SDL_WINDOW_INPUT_FOCUS) != 0) - io.MousePos = ImVec2((float)mx, (float)my); -#endif - - // Hide OS mouse cursor if ImGui is drawing it - ImGuiMouseCursor cursor = ImGui::GetMouseCursor(); - if (io.MouseDrawCursor || cursor == ImGuiMouseCursor_None) - { - SDL_ShowCursor(0); - } - else - { - SDL_SetCursor(g_SdlCursors[cursor]); - SDL_ShowCursor(1); - } - - // Start the frame. This call will update the io.WantCaptureMouse, io.WantCaptureKeyboard flag that you can use to dispatch inputs (or not) to your application. - ImGui::NewFrame(); -} diff --git a/examples/sdl_opengl3_example/main.cpp b/examples/sdl_opengl3_example/main.cpp index a3fc625c2512..24f2f8dc51d2 100644 --- a/examples/sdl_opengl3_example/main.cpp +++ b/examples/sdl_opengl3_example/main.cpp @@ -4,7 +4,8 @@ // (GL3W is a helper library to access OpenGL functions since there is no standard header to access modern OpenGL functions easily. Alternatives are GLEW, Glad, etc.) #include "imgui.h" -#include "imgui_impl_sdl_gl3.h" +#include "../imgui_impl_sdl2.h" +#include "../imgui_impl_opengl3.h" #include #include // This example is using gl3w to access OpenGL functions (because it is small). You may use glew/glad/glLoadGen/etc. whatever already works for you. #include @@ -36,7 +37,8 @@ int main(int, char**) // Setup ImGui binding ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - ImGui_ImplSdlGL3_Init(window); + ImGui_ImplSDL2_Init(window); + ImGui_ImplOpenGL3_Init(); //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; // Setup style @@ -73,11 +75,12 @@ int main(int, char**) SDL_Event event; while (SDL_PollEvent(&event)) { - ImGui_ImplSdlGL3_ProcessEvent(&event); + ImGui_ImplSDL2_ProcessEvent(&event); if (event.type == SDL_QUIT) done = true; } - ImGui_ImplSdlGL3_NewFrame(window); + ImGui_ImplOpenGL3_NewFrame(); + ImGui_ImplSDL2_NewFrame(window); // 1. Show a simple window. // Tip: if we don't call ImGui::Begin()/ImGui::End() the widgets automatically appears in a window called "Debug". @@ -121,12 +124,13 @@ int main(int, char**) glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w); glClear(GL_COLOR_BUFFER_BIT); ImGui::Render(); - ImGui_ImplSdlGL3_RenderDrawData(ImGui::GetDrawData()); + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); SDL_GL_SwapWindow(window); } // Cleanup - ImGui_ImplSdlGL3_Shutdown(); + ImGui_ImplOpenGL3_Shutdown(); + ImGui_ImplSDL2_Shutdown(); ImGui::DestroyContext(); SDL_GL_DeleteContext(glcontext); diff --git a/examples/sdl_opengl3_example/sdl_opengl3_example.vcxproj b/examples/sdl_opengl3_example/sdl_opengl3_example.vcxproj index 1ac4d0acc893..8d17b2255987 100644 --- a/examples/sdl_opengl3_example/sdl_opengl3_example.vcxproj +++ b/examples/sdl_opengl3_example/sdl_opengl3_example.vcxproj @@ -153,17 +153,19 @@ + + - + + - diff --git a/examples/sdl_opengl3_example/sdl_opengl3_example.vcxproj.filters b/examples/sdl_opengl3_example/sdl_opengl3_example.vcxproj.filters index f8f434155c3f..93d321a2a8fd 100644 --- a/examples/sdl_opengl3_example/sdl_opengl3_example.vcxproj.filters +++ b/examples/sdl_opengl3_example/sdl_opengl3_example.vcxproj.filters @@ -22,15 +22,18 @@ imgui - - sources - sources gl3w + + sources + + + sources + @@ -42,15 +45,18 @@ imgui - - sources - gl3w gl3w + + sources + + + sources + From 90dffb5a063861355afd036db85b5f7cb32f7037 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 16 Feb 2018 22:50:19 +0100 Subject: [PATCH 006/828] Examples: Vulkan: Extracted into imgui_impl_vulkan.*, reused imgui_impl_glfw* files. --- ..._glfw_vulkan.cpp => imgui_impl_vulkan.cpp} | 232 ++++-------------- examples/imgui_impl_vulkan.h | 35 +++ .../vulkan_example/imgui_impl_glfw_vulkan.h | 46 ---- examples/vulkan_example/main.cpp | 22 +- .../vulkan_example/vulkan_example.vcxproj | 6 +- .../vulkan_example.vcxproj.filters | 10 +- 6 files changed, 105 insertions(+), 246 deletions(-) rename examples/{vulkan_example/imgui_impl_glfw_vulkan.cpp => imgui_impl_vulkan.cpp} (81%) create mode 100644 examples/imgui_impl_vulkan.h delete mode 100644 examples/vulkan_example/imgui_impl_glfw_vulkan.h diff --git a/examples/vulkan_example/imgui_impl_glfw_vulkan.cpp b/examples/imgui_impl_vulkan.cpp similarity index 81% rename from examples/vulkan_example/imgui_impl_glfw_vulkan.cpp rename to examples/imgui_impl_vulkan.cpp index 31f22e8627b8..1162920fa4ac 100644 --- a/examples/vulkan_example/imgui_impl_glfw_vulkan.cpp +++ b/examples/imgui_impl_vulkan.cpp @@ -10,7 +10,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback, ImGui_ImplGlfwVulkan_Render() calls ImGui_ImplGlfwVulkan_RenderDrawData() itself. +// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback, ImGui_ImplVulkan_Render() calls ImGui_ImplVulkan_RenderDrawData() itself. // 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. // 2018-02-06: Inputs: Added mapping for ImGuiKey_Space. // 2018-01-20: Inputs: Added Horizontal Mouse Wheel support. @@ -23,23 +23,7 @@ // 2016-08-27: Vulkan: Fix Vulkan example for use when a depth buffer is active. #include "imgui.h" -#include "imgui_impl_glfw_vulkan.h" - -// GLFW -#define GLFW_INCLUDE_NONE -#define GLFW_INCLUDE_VULKAN -#include -#ifdef _WIN32 -#undef APIENTRY -#define GLFW_EXPOSE_NATIVE_WIN32 -#define GLFW_EXPOSE_NATIVE_WGL -#include -#endif - -// GLFW Data -static GLFWwindow* g_Window = NULL; -static double g_Time = 0.0f; -static bool g_MouseJustPressed[3] = { false, false, false }; +#include "imgui_impl_vulkan.h" // Vulkan Data static VkAllocationCallbacks* g_Allocator = NULL; @@ -149,7 +133,7 @@ static uint32_t __glsl_shader_frag_spv[] = 0x00010038 }; -static uint32_t ImGui_ImplGlfwVulkan_MemoryType(VkMemoryPropertyFlags properties, uint32_t type_bits) +static uint32_t ImGui_ImplVulkan_MemoryType(VkMemoryPropertyFlags properties, uint32_t type_bits) { VkPhysicalDeviceMemoryProperties prop; vkGetPhysicalDeviceMemoryProperties(g_Gpu, &prop); @@ -159,14 +143,14 @@ static uint32_t ImGui_ImplGlfwVulkan_MemoryType(VkMemoryPropertyFlags properties return 0xffffffff; // Unable to find memoryType } -static void ImGui_ImplGlfwVulkan_VkResult(VkResult err) +static void ImGui_ImplVulkan_VkResult(VkResult err) { if (g_CheckVkResult) g_CheckVkResult(err); } // This is the main rendering function that you have to implement and provide to ImGui (via setting up 'RenderDrawListsFn' in the ImGuiIO structure) -void ImGui_ImplGlfwVulkan_RenderDrawData(ImDrawData* draw_data) +void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data) { VkResult err; ImGuiIO& io = ImGui::GetIO(); @@ -188,18 +172,18 @@ void ImGui_ImplGlfwVulkan_RenderDrawData(ImDrawData* draw_data) buffer_info.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; err = vkCreateBuffer(g_Device, &buffer_info, g_Allocator, &g_VertexBuffer[g_FrameIndex]); - ImGui_ImplGlfwVulkan_VkResult(err); + ImGui_ImplVulkan_VkResult(err); VkMemoryRequirements req; vkGetBufferMemoryRequirements(g_Device, g_VertexBuffer[g_FrameIndex], &req); g_BufferMemoryAlignment = (g_BufferMemoryAlignment > req.alignment) ? g_BufferMemoryAlignment : req.alignment; VkMemoryAllocateInfo alloc_info = {}; alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; alloc_info.allocationSize = req.size; - alloc_info.memoryTypeIndex = ImGui_ImplGlfwVulkan_MemoryType(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, req.memoryTypeBits); + alloc_info.memoryTypeIndex = ImGui_ImplVulkan_MemoryType(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, req.memoryTypeBits); err = vkAllocateMemory(g_Device, &alloc_info, g_Allocator, &g_VertexBufferMemory[g_FrameIndex]); - ImGui_ImplGlfwVulkan_VkResult(err); + ImGui_ImplVulkan_VkResult(err); err = vkBindBufferMemory(g_Device, g_VertexBuffer[g_FrameIndex], g_VertexBufferMemory[g_FrameIndex], 0); - ImGui_ImplGlfwVulkan_VkResult(err); + ImGui_ImplVulkan_VkResult(err); g_VertexBufferSize[g_FrameIndex] = vertex_buffer_size; } @@ -218,18 +202,18 @@ void ImGui_ImplGlfwVulkan_RenderDrawData(ImDrawData* draw_data) buffer_info.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT; buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; err = vkCreateBuffer(g_Device, &buffer_info, g_Allocator, &g_IndexBuffer[g_FrameIndex]); - ImGui_ImplGlfwVulkan_VkResult(err); + ImGui_ImplVulkan_VkResult(err); VkMemoryRequirements req; vkGetBufferMemoryRequirements(g_Device, g_IndexBuffer[g_FrameIndex], &req); g_BufferMemoryAlignment = (g_BufferMemoryAlignment > req.alignment) ? g_BufferMemoryAlignment : req.alignment; VkMemoryAllocateInfo alloc_info = {}; alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; alloc_info.allocationSize = req.size; - alloc_info.memoryTypeIndex = ImGui_ImplGlfwVulkan_MemoryType(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, req.memoryTypeBits); + alloc_info.memoryTypeIndex = ImGui_ImplVulkan_MemoryType(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, req.memoryTypeBits); err = vkAllocateMemory(g_Device, &alloc_info, g_Allocator, &g_IndexBufferMemory[g_FrameIndex]); - ImGui_ImplGlfwVulkan_VkResult(err); + ImGui_ImplVulkan_VkResult(err); err = vkBindBufferMemory(g_Device, g_IndexBuffer[g_FrameIndex], g_IndexBufferMemory[g_FrameIndex], 0); - ImGui_ImplGlfwVulkan_VkResult(err); + ImGui_ImplVulkan_VkResult(err); g_IndexBufferSize[g_FrameIndex] = index_buffer_size; } @@ -238,9 +222,9 @@ void ImGui_ImplGlfwVulkan_RenderDrawData(ImDrawData* draw_data) ImDrawVert* vtx_dst; ImDrawIdx* idx_dst; err = vkMapMemory(g_Device, g_VertexBufferMemory[g_FrameIndex], 0, vertex_size, 0, (void**)(&vtx_dst)); - ImGui_ImplGlfwVulkan_VkResult(err); + ImGui_ImplVulkan_VkResult(err); err = vkMapMemory(g_Device, g_IndexBufferMemory[g_FrameIndex], 0, index_size, 0, (void**)(&idx_dst)); - ImGui_ImplGlfwVulkan_VkResult(err); + ImGui_ImplVulkan_VkResult(err); for (int n = 0; n < draw_data->CmdListsCount; n++) { const ImDrawList* cmd_list = draw_data->CmdLists[n]; @@ -257,7 +241,7 @@ void ImGui_ImplGlfwVulkan_RenderDrawData(ImDrawData* draw_data) range[1].memory = g_IndexBufferMemory[g_FrameIndex]; range[1].size = VK_WHOLE_SIZE; err = vkFlushMappedMemoryRanges(g_Device, 2, range); - ImGui_ImplGlfwVulkan_VkResult(err); + ImGui_ImplVulkan_VkResult(err); vkUnmapMemory(g_Device, g_VertexBufferMemory[g_FrameIndex]); vkUnmapMemory(g_Device, g_IndexBufferMemory[g_FrameIndex]); } @@ -330,52 +314,7 @@ void ImGui_ImplGlfwVulkan_RenderDrawData(ImDrawData* draw_data) } } -static const char* ImGui_ImplGlfwVulkan_GetClipboardText(void* user_data) -{ - return glfwGetClipboardString((GLFWwindow*)user_data); -} - -static void ImGui_ImplGlfwVulkan_SetClipboardText(void* user_data, const char* text) -{ - glfwSetClipboardString((GLFWwindow*)user_data, text); -} - -void ImGui_ImplGlfwVulkan_MouseButtonCallback(GLFWwindow*, int button, int action, int /*mods*/) -{ - if (action == GLFW_PRESS && button >= 0 && button < 3) - g_MouseJustPressed[button] = true; -} - -void ImGui_ImplGlfwVulkan_ScrollCallback(GLFWwindow*, double xoffset, double yoffset) -{ - ImGuiIO& io = ImGui::GetIO(); - io.MouseWheelH += (float)xoffset; - io.MouseWheel += (float)yoffset; -} - -void ImGui_ImplGlfwVulkan_KeyCallback(GLFWwindow*, int key, int, int action, int mods) -{ - ImGuiIO& io = ImGui::GetIO(); - if (action == GLFW_PRESS) - io.KeysDown[key] = true; - if (action == GLFW_RELEASE) - io.KeysDown[key] = false; - - (void)mods; // Modifiers are not reliable across systems - io.KeyCtrl = io.KeysDown[GLFW_KEY_LEFT_CONTROL] || io.KeysDown[GLFW_KEY_RIGHT_CONTROL]; - io.KeyShift = io.KeysDown[GLFW_KEY_LEFT_SHIFT] || io.KeysDown[GLFW_KEY_RIGHT_SHIFT]; - io.KeyAlt = io.KeysDown[GLFW_KEY_LEFT_ALT] || io.KeysDown[GLFW_KEY_RIGHT_ALT]; - io.KeySuper = io.KeysDown[GLFW_KEY_LEFT_SUPER] || io.KeysDown[GLFW_KEY_RIGHT_SUPER]; -} - -void ImGui_ImplGlfwVulkan_CharCallback(GLFWwindow*, unsigned int c) -{ - ImGuiIO& io = ImGui::GetIO(); - if (c > 0 && c < 0x10000) - io.AddInputCharacter((unsigned short)c); -} - -bool ImGui_ImplGlfwVulkan_CreateFontsTexture(VkCommandBuffer command_buffer) +bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer) { ImGuiIO& io = ImGui::GetIO(); @@ -403,17 +342,17 @@ bool ImGui_ImplGlfwVulkan_CreateFontsTexture(VkCommandBuffer command_buffer) info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; err = vkCreateImage(g_Device, &info, g_Allocator, &g_FontImage); - ImGui_ImplGlfwVulkan_VkResult(err); + ImGui_ImplVulkan_VkResult(err); VkMemoryRequirements req; vkGetImageMemoryRequirements(g_Device, g_FontImage, &req); VkMemoryAllocateInfo alloc_info = {}; alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; alloc_info.allocationSize = req.size; - alloc_info.memoryTypeIndex = ImGui_ImplGlfwVulkan_MemoryType(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, req.memoryTypeBits); + alloc_info.memoryTypeIndex = ImGui_ImplVulkan_MemoryType(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, req.memoryTypeBits); err = vkAllocateMemory(g_Device, &alloc_info, g_Allocator, &g_FontMemory); - ImGui_ImplGlfwVulkan_VkResult(err); + ImGui_ImplVulkan_VkResult(err); err = vkBindImageMemory(g_Device, g_FontImage, g_FontMemory, 0); - ImGui_ImplGlfwVulkan_VkResult(err); + ImGui_ImplVulkan_VkResult(err); } // Create the Image View: @@ -427,7 +366,7 @@ bool ImGui_ImplGlfwVulkan_CreateFontsTexture(VkCommandBuffer command_buffer) info.subresourceRange.levelCount = 1; info.subresourceRange.layerCount = 1; err = vkCreateImageView(g_Device, &info, g_Allocator, &g_FontView); - ImGui_ImplGlfwVulkan_VkResult(err); + ImGui_ImplVulkan_VkResult(err); } // Update the Descriptor Set: @@ -453,32 +392,32 @@ bool ImGui_ImplGlfwVulkan_CreateFontsTexture(VkCommandBuffer command_buffer) buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; err = vkCreateBuffer(g_Device, &buffer_info, g_Allocator, &g_UploadBuffer); - ImGui_ImplGlfwVulkan_VkResult(err); + ImGui_ImplVulkan_VkResult(err); VkMemoryRequirements req; vkGetBufferMemoryRequirements(g_Device, g_UploadBuffer, &req); g_BufferMemoryAlignment = (g_BufferMemoryAlignment > req.alignment) ? g_BufferMemoryAlignment : req.alignment; VkMemoryAllocateInfo alloc_info = {}; alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; alloc_info.allocationSize = req.size; - alloc_info.memoryTypeIndex = ImGui_ImplGlfwVulkan_MemoryType(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, req.memoryTypeBits); + alloc_info.memoryTypeIndex = ImGui_ImplVulkan_MemoryType(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, req.memoryTypeBits); err = vkAllocateMemory(g_Device, &alloc_info, g_Allocator, &g_UploadBufferMemory); - ImGui_ImplGlfwVulkan_VkResult(err); + ImGui_ImplVulkan_VkResult(err); err = vkBindBufferMemory(g_Device, g_UploadBuffer, g_UploadBufferMemory, 0); - ImGui_ImplGlfwVulkan_VkResult(err); + ImGui_ImplVulkan_VkResult(err); } // Upload to Buffer: { char* map = NULL; err = vkMapMemory(g_Device, g_UploadBufferMemory, 0, upload_size, 0, (void**)(&map)); - ImGui_ImplGlfwVulkan_VkResult(err); + ImGui_ImplVulkan_VkResult(err); memcpy(map, pixels, upload_size); VkMappedMemoryRange range[1] = {}; range[0].sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; range[0].memory = g_UploadBufferMemory; range[0].size = upload_size; err = vkFlushMappedMemoryRanges(g_Device, 1, range); - ImGui_ImplGlfwVulkan_VkResult(err); + ImGui_ImplVulkan_VkResult(err); vkUnmapMemory(g_Device, g_UploadBufferMemory); } // Copy to Image: @@ -525,7 +464,7 @@ bool ImGui_ImplGlfwVulkan_CreateFontsTexture(VkCommandBuffer command_buffer) return true; } -bool ImGui_ImplGlfwVulkan_CreateDeviceObjects() +bool ImGui_ImplVulkan_CreateDeviceObjects() { VkResult err; VkShaderModule vert_module; @@ -538,13 +477,13 @@ bool ImGui_ImplGlfwVulkan_CreateDeviceObjects() vert_info.codeSize = sizeof(__glsl_shader_vert_spv); vert_info.pCode = (uint32_t*)__glsl_shader_vert_spv; err = vkCreateShaderModule(g_Device, &vert_info, g_Allocator, &vert_module); - ImGui_ImplGlfwVulkan_VkResult(err); + ImGui_ImplVulkan_VkResult(err); VkShaderModuleCreateInfo frag_info = {}; frag_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; frag_info.codeSize = sizeof(__glsl_shader_frag_spv); frag_info.pCode = (uint32_t*)__glsl_shader_frag_spv; err = vkCreateShaderModule(g_Device, &frag_info, g_Allocator, &frag_module); - ImGui_ImplGlfwVulkan_VkResult(err); + ImGui_ImplVulkan_VkResult(err); } if (!g_FontSampler) @@ -561,7 +500,7 @@ bool ImGui_ImplGlfwVulkan_CreateDeviceObjects() info.maxLod = 1000; info.maxAnisotropy = 1.0f; err = vkCreateSampler(g_Device, &info, g_Allocator, &g_FontSampler); - ImGui_ImplGlfwVulkan_VkResult(err); + ImGui_ImplVulkan_VkResult(err); } if (!g_DescriptorSetLayout) @@ -577,7 +516,7 @@ bool ImGui_ImplGlfwVulkan_CreateDeviceObjects() info.bindingCount = 1; info.pBindings = binding; err = vkCreateDescriptorSetLayout(g_Device, &info, g_Allocator, &g_DescriptorSetLayout); - ImGui_ImplGlfwVulkan_VkResult(err); + ImGui_ImplVulkan_VkResult(err); } // Create Descriptor Set: @@ -588,7 +527,7 @@ bool ImGui_ImplGlfwVulkan_CreateDeviceObjects() alloc_info.descriptorSetCount = 1; alloc_info.pSetLayouts = &g_DescriptorSetLayout; err = vkAllocateDescriptorSets(g_Device, &alloc_info, &g_DescriptorSet); - ImGui_ImplGlfwVulkan_VkResult(err); + ImGui_ImplVulkan_VkResult(err); } if (!g_PipelineLayout) @@ -605,7 +544,7 @@ bool ImGui_ImplGlfwVulkan_CreateDeviceObjects() layout_info.pushConstantRangeCount = 1; layout_info.pPushConstantRanges = push_constants; err = vkCreatePipelineLayout(g_Device, &layout_info, g_Allocator, &g_PipelineLayout); - ImGui_ImplGlfwVulkan_VkResult(err); + ImGui_ImplVulkan_VkResult(err); } VkPipelineShaderStageCreateInfo stage[2] = {}; @@ -703,7 +642,7 @@ bool ImGui_ImplGlfwVulkan_CreateDeviceObjects() info.layout = g_PipelineLayout; info.renderPass = g_RenderPass; err = vkCreateGraphicsPipelines(g_Device, g_PipelineCache, 1, &info, g_Allocator, &g_Pipeline); - ImGui_ImplGlfwVulkan_VkResult(err); + ImGui_ImplVulkan_VkResult(err); vkDestroyShaderModule(g_Device, vert_module, g_Allocator); vkDestroyShaderModule(g_Device, frag_module, g_Allocator); @@ -711,7 +650,7 @@ bool ImGui_ImplGlfwVulkan_CreateDeviceObjects() return true; } -void ImGui_ImplGlfwVulkan_InvalidateFontUploadObjects() +void ImGui_ImplVulkan_InvalidateFontUploadObjects() { if (g_UploadBuffer) { @@ -725,9 +664,9 @@ void ImGui_ImplGlfwVulkan_InvalidateFontUploadObjects() } } -void ImGui_ImplGlfwVulkan_InvalidateDeviceObjects() +void ImGui_ImplVulkan_InvalidateDeviceObjects() { - ImGui_ImplGlfwVulkan_InvalidateFontUploadObjects(); + ImGui_ImplVulkan_InvalidateFontUploadObjects(); for (int i = 0; i < IMGUI_VK_QUEUED_FRAMES; i++) { @@ -746,7 +685,7 @@ void ImGui_ImplGlfwVulkan_InvalidateDeviceObjects() if (g_Pipeline) { vkDestroyPipeline(g_Device, g_Pipeline, g_Allocator); g_Pipeline = VK_NULL_HANDLE; } } -bool ImGui_ImplGlfwVulkan_Init(GLFWwindow* window, bool install_callbacks, ImGui_ImplGlfwVulkan_Init_Data *init_data) +bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitData *init_data) { g_Allocator = init_data->allocator; g_Gpu = init_data->gpu; @@ -756,104 +695,25 @@ bool ImGui_ImplGlfwVulkan_Init(GLFWwindow* window, bool install_callbacks, Im g_DescriptorPool = init_data->descriptor_pool; g_CheckVkResult = init_data->check_vk_result; - g_Window = window; - - ImGuiIO& io = ImGui::GetIO(); - io.KeyMap[ImGuiKey_Tab] = GLFW_KEY_TAB; // Keyboard mapping. ImGui will use those indices to peek into the io.KeyDown[] array. - io.KeyMap[ImGuiKey_LeftArrow] = GLFW_KEY_LEFT; - io.KeyMap[ImGuiKey_RightArrow] = GLFW_KEY_RIGHT; - io.KeyMap[ImGuiKey_UpArrow] = GLFW_KEY_UP; - io.KeyMap[ImGuiKey_DownArrow] = GLFW_KEY_DOWN; - io.KeyMap[ImGuiKey_PageUp] = GLFW_KEY_PAGE_UP; - io.KeyMap[ImGuiKey_PageDown] = GLFW_KEY_PAGE_DOWN; - io.KeyMap[ImGuiKey_Home] = GLFW_KEY_HOME; - io.KeyMap[ImGuiKey_End] = GLFW_KEY_END; - io.KeyMap[ImGuiKey_Insert] = GLFW_KEY_INSERT; - io.KeyMap[ImGuiKey_Delete] = GLFW_KEY_DELETE; - io.KeyMap[ImGuiKey_Backspace] = GLFW_KEY_BACKSPACE; - io.KeyMap[ImGuiKey_Space] = GLFW_KEY_SPACE; - io.KeyMap[ImGuiKey_Enter] = GLFW_KEY_ENTER; - io.KeyMap[ImGuiKey_Escape] = GLFW_KEY_ESCAPE; - io.KeyMap[ImGuiKey_A] = GLFW_KEY_A; - io.KeyMap[ImGuiKey_C] = GLFW_KEY_C; - io.KeyMap[ImGuiKey_V] = GLFW_KEY_V; - io.KeyMap[ImGuiKey_X] = GLFW_KEY_X; - io.KeyMap[ImGuiKey_Y] = GLFW_KEY_Y; - io.KeyMap[ImGuiKey_Z] = GLFW_KEY_Z; - - io.SetClipboardTextFn = ImGui_ImplGlfwVulkan_SetClipboardText; - io.GetClipboardTextFn = ImGui_ImplGlfwVulkan_GetClipboardText; - io.ClipboardUserData = g_Window; -#ifdef _WIN32 - io.ImeWindowHandle = glfwGetWin32Window(g_Window); -#endif - - if (install_callbacks) - { - glfwSetMouseButtonCallback(window, ImGui_ImplGlfwVulkan_MouseButtonCallback); - glfwSetScrollCallback(window, ImGui_ImplGlfwVulkan_ScrollCallback); - glfwSetKeyCallback(window, ImGui_ImplGlfwVulkan_KeyCallback); - glfwSetCharCallback(window, ImGui_ImplGlfwVulkan_CharCallback); - } - - ImGui_ImplGlfwVulkan_CreateDeviceObjects(); + ImGui_ImplVulkan_CreateDeviceObjects(); return true; } -void ImGui_ImplGlfwVulkan_Shutdown() +void ImGui_ImplVulkan_Shutdown() { - ImGui_ImplGlfwVulkan_InvalidateDeviceObjects(); + ImGui_ImplVulkan_InvalidateDeviceObjects(); } -void ImGui_ImplGlfwVulkan_NewFrame() +void ImGui_ImplVulkan_NewFrame() { - ImGuiIO& io = ImGui::GetIO(); - - // Setup display size (every frame to accommodate for window resizing) - int w, h; - int display_w, display_h; - glfwGetWindowSize(g_Window, &w, &h); - glfwGetFramebufferSize(g_Window, &display_w, &display_h); - io.DisplaySize = ImVec2((float)w, (float)h); - io.DisplayFramebufferScale = ImVec2(w > 0 ? ((float)display_w / w) : 0, h > 0 ? ((float)display_h / h) : 0); - - // Setup time step - double current_time = glfwGetTime(); - io.DeltaTime = g_Time > 0.0 ? (float)(current_time - g_Time) : (float)(1.0f/60.0f); - g_Time = current_time; - - // Setup inputs - // (we already got mouse wheel, keyboard keys & characters from glfw callbacks polled in glfwPollEvents()) - if (glfwGetWindowAttrib(g_Window, GLFW_FOCUSED)) - { - double mouse_x, mouse_y; - glfwGetCursorPos(g_Window, &mouse_x, &mouse_y); - io.MousePos = ImVec2((float)mouse_x, (float)mouse_y); - } - else - { - io.MousePos = ImVec2(-FLT_MAX,-FLT_MAX); - } - - for (int i = 0; i < 3; i++) - { - io.MouseDown[i] = g_MouseJustPressed[i] || glfwGetMouseButton(g_Window, i) != 0; // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame. - g_MouseJustPressed[i] = false; - } - - // Hide OS mouse cursor if ImGui is drawing it - glfwSetInputMode(g_Window, GLFW_CURSOR, io.MouseDrawCursor ? GLFW_CURSOR_HIDDEN : GLFW_CURSOR_NORMAL); - - // Start the frame. This call will update the io.WantCaptureMouse, io.WantCaptureKeyboard flag that you can use to dispatch inputs (or not) to your application. - ImGui::NewFrame(); } -void ImGui_ImplGlfwVulkan_Render(VkCommandBuffer command_buffer) +void ImGui_ImplVulkan_Render(VkCommandBuffer command_buffer) { g_CommandBuffer = command_buffer; ImGui::Render(); - ImGui_ImplGlfwVulkan_RenderDrawData(ImGui::GetDrawData()); + ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData()); g_CommandBuffer = VK_NULL_HANDLE; g_FrameIndex = (g_FrameIndex + 1) % IMGUI_VK_QUEUED_FRAMES; } diff --git a/examples/imgui_impl_vulkan.h b/examples/imgui_impl_vulkan.h new file mode 100644 index 000000000000..0a0e9be0eb0e --- /dev/null +++ b/examples/imgui_impl_vulkan.h @@ -0,0 +1,35 @@ +// ImGui GLFW binding with Vulkan + shaders + +// Missing features: +// [ ] User texture binding. Changes of ImTextureID aren't supported by this binding! See https://github.com/ocornut/imgui/pull/914 + +// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. +// If you use this binding you'll need to call 5 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXX_CreateFontsTexture(), ImGui_ImplXXXX_NewFrame(), ImGui_ImplXXXX_Render() and ImGui_ImplXXXX_Shutdown(). +// If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp. +// https://github.com/ocornut/imgui + +#include + +#define IMGUI_VK_QUEUED_FRAMES 2 + +struct ImGui_ImplVulkan_InitData +{ + VkAllocationCallbacks* allocator; + VkPhysicalDevice gpu; + VkDevice device; + VkRenderPass render_pass; + VkPipelineCache pipeline_cache; + VkDescriptorPool descriptor_pool; + void (*check_vk_result)(VkResult err); +}; + +IMGUI_API bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitData *init_data); +IMGUI_API void ImGui_ImplVulkan_Shutdown(); +IMGUI_API void ImGui_ImplVulkan_NewFrame(); +IMGUI_API void ImGui_ImplVulkan_Render(VkCommandBuffer command_buffer); + +// Called by Init/NewFrame/Shutdown +IMGUI_API void ImGui_ImplVulkan_InvalidateFontUploadObjects(); +IMGUI_API void ImGui_ImplVulkan_InvalidateDeviceObjects(); +IMGUI_API bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer); +IMGUI_API bool ImGui_ImplVulkan_CreateDeviceObjects(); diff --git a/examples/vulkan_example/imgui_impl_glfw_vulkan.h b/examples/vulkan_example/imgui_impl_glfw_vulkan.h deleted file mode 100644 index 54918d34a69b..000000000000 --- a/examples/vulkan_example/imgui_impl_glfw_vulkan.h +++ /dev/null @@ -1,46 +0,0 @@ -// ImGui GLFW binding with Vulkan + shaders - -// Missing features: -// [ ] User texture binding. Changes of ImTextureID aren't supported by this binding! See https://github.com/ocornut/imgui/pull/914 - -// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. -// If you use this binding you'll need to call 5 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXX_CreateFontsTexture(), ImGui_ImplXXXX_NewFrame(), ImGui_ImplXXXX_Render() and ImGui_ImplXXXX_Shutdown(). -// If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp. -// https://github.com/ocornut/imgui - -struct GLFWwindow; - -#include - -#define IMGUI_VK_QUEUED_FRAMES 2 - -struct ImGui_ImplGlfwVulkan_Init_Data -{ - VkAllocationCallbacks* allocator; - VkPhysicalDevice gpu; - VkDevice device; - VkRenderPass render_pass; - VkPipelineCache pipeline_cache; - VkDescriptorPool descriptor_pool; - void (*check_vk_result)(VkResult err); -}; - -IMGUI_API bool ImGui_ImplGlfwVulkan_Init(GLFWwindow* window, bool install_callbacks, ImGui_ImplGlfwVulkan_Init_Data *init_data); -IMGUI_API void ImGui_ImplGlfwVulkan_Shutdown(); -IMGUI_API void ImGui_ImplGlfwVulkan_NewFrame(); -IMGUI_API void ImGui_ImplGlfwVulkan_Render(VkCommandBuffer command_buffer); - -// Use if you want to reset your rendering device without losing ImGui state. -IMGUI_API void ImGui_ImplGlfwVulkan_InvalidateFontUploadObjects(); -IMGUI_API void ImGui_ImplGlfwVulkan_InvalidateDeviceObjects(); -IMGUI_API bool ImGui_ImplGlfwVulkan_CreateFontsTexture(VkCommandBuffer command_buffer); -IMGUI_API bool ImGui_ImplGlfwVulkan_CreateDeviceObjects(); - -// GLFW callbacks (installed by default if you enable 'install_callbacks' during initialization) -// Provided here if you want to chain callbacks. -// You can also handle inputs yourself and use those as a reference. -IMGUI_API void ImGui_ImplGlfwVulkan_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods); -IMGUI_API void ImGui_ImplGlfwVulkan_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset); -IMGUI_API void ImGui_ImplGlfwVulkan_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods); -IMGUI_API void ImGui_ImplGlfwVulkan_CharCallback(GLFWwindow* window, unsigned int c); - diff --git a/examples/vulkan_example/main.cpp b/examples/vulkan_example/main.cpp index be5fe528498f..3a10afd2fe9b 100644 --- a/examples/vulkan_example/main.cpp +++ b/examples/vulkan_example/main.cpp @@ -2,7 +2,8 @@ // If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp. #include "imgui.h" -#include "imgui_impl_glfw_vulkan.h" +#include "../imgui_impl_glfw.h" +#include "../imgui_impl_vulkan.h" #include // printf, fprintf #include // abort @@ -616,7 +617,7 @@ int main(int, char**) // Setup ImGui binding ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - ImGui_ImplGlfwVulkan_Init_Data init_data = {}; + ImGui_ImplVulkan_InitData init_data = {}; init_data.allocator = g_Allocator; init_data.gpu = g_Gpu; init_data.device = g_Device; @@ -624,7 +625,8 @@ int main(int, char**) init_data.pipeline_cache = g_PipelineCache; init_data.descriptor_pool = g_DescriptorPool; init_data.check_vk_result = check_vk_result; - ImGui_ImplGlfwVulkan_Init(window, true, &init_data); + ImGui_ImplVulkan_Init(&init_data); + ImGui_ImplGlfw_Init(window, true); //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; // Enable Keyboard Controls // Setup style @@ -657,7 +659,7 @@ int main(int, char**) err = vkBeginCommandBuffer(g_CommandBuffer[g_FrameIndex], &begin_info); check_vk_result(err); - ImGui_ImplGlfwVulkan_CreateFontsTexture(g_CommandBuffer[g_FrameIndex]); + ImGui_ImplVulkan_CreateFontsTexture(g_CommandBuffer[g_FrameIndex]); VkSubmitInfo end_info = {}; end_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; @@ -670,7 +672,7 @@ int main(int, char**) err = vkDeviceWaitIdle(g_Device); check_vk_result(err); - ImGui_ImplGlfwVulkan_InvalidateFontUploadObjects(); + ImGui_ImplVulkan_InvalidateFontUploadObjects(); } bool show_demo_window = true; @@ -681,9 +683,9 @@ int main(int, char**) // Hence we must render once and increase the g_FrameIndex without presenting, which we do before entering the render loop. // This is also the reason why frame_end() is split into frame_end() and frame_present(), the later one not being called here. #ifdef IMGUI_UNLIMITED_FRAME_RATE - ImGui_ImplGlfwVulkan_NewFrame(); + ImGui_ImplVulkan_NewFrame(); frame_begin(); - ImGui_ImplGlfwVulkan_Render(g_CommandBuffer[g_FrameIndex]); + ImGui_ImplVulkan_Render(g_CommandBuffer[g_FrameIndex]); frame_end(); g_FrameIndex = (g_FrameIndex + 1) % IMGUI_VK_QUEUED_FRAMES; #endif // IMGUI_UNLIMITED_FRAME_RATE @@ -696,7 +698,7 @@ int main(int, char**) // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. glfwPollEvents(); - ImGui_ImplGlfwVulkan_NewFrame(); + ImGui_ImplVulkan_NewFrame(); // 1. Show a simple window. // Tip: if we don't call ImGui::Begin()/ImGui::End() the widgets automatically appears in a window called "Debug". @@ -737,7 +739,7 @@ int main(int, char**) memcpy(&g_ClearValue.color.float32[0], &clear_color, 4 * sizeof(float)); frame_begin(); - ImGui_ImplGlfwVulkan_Render(g_CommandBuffer[g_FrameIndex]); + ImGui_ImplVulkan_Render(g_CommandBuffer[g_FrameIndex]); frame_end(); frame_present(); } @@ -745,7 +747,7 @@ int main(int, char**) // Cleanup VkResult err = vkDeviceWaitIdle(g_Device); check_vk_result(err); - ImGui_ImplGlfwVulkan_Shutdown(); + ImGui_ImplVulkan_Shutdown(); ImGui::DestroyContext(); cleanup_vulkan(); glfwTerminate(); diff --git a/examples/vulkan_example/vulkan_example.vcxproj b/examples/vulkan_example/vulkan_example.vcxproj index a315d69ea27e..299b26f4f3c5 100644 --- a/examples/vulkan_example/vulkan_example.vcxproj +++ b/examples/vulkan_example/vulkan_example.vcxproj @@ -153,14 +153,16 @@ - + + - + + diff --git a/examples/vulkan_example/vulkan_example.vcxproj.filters b/examples/vulkan_example/vulkan_example.vcxproj.filters index f3ed8ede4db6..0fa42d7f7481 100644 --- a/examples/vulkan_example/vulkan_example.vcxproj.filters +++ b/examples/vulkan_example/vulkan_example.vcxproj.filters @@ -22,7 +22,10 @@ sources - + + sources + + sources @@ -36,7 +39,10 @@ imgui - + + sources + + sources From 19540479d4d8d8512c7ea03eaf16b6fb013a77b8 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 16 Feb 2018 23:04:17 +0100 Subject: [PATCH 007/828] Examples: DirectX11: Extracted imgui_impl_dx11.* instead imgui_impl_dx11 and imgui_impl_win32 --- .../directx11_example.vcxproj | 6 +- .../directx11_example.vcxproj.filters | 18 +- examples/directx11_example/main.cpp | 8 +- .../imgui_impl_dx11.cpp | 148 +--------------- .../{directx11_example => }/imgui_impl_dx11.h | 9 +- examples/imgui_impl_win32.cpp | 161 ++++++++++++++++++ examples/imgui_impl_win32.h | 11 ++ 7 files changed, 196 insertions(+), 165 deletions(-) rename examples/{directx11_example => }/imgui_impl_dx11.cpp (78%) rename examples/{directx11_example => }/imgui_impl_dx11.h (66%) create mode 100644 examples/imgui_impl_win32.cpp create mode 100644 examples/imgui_impl_win32.h diff --git a/examples/directx11_example/directx11_example.vcxproj b/examples/directx11_example/directx11_example.vcxproj index c07939f776b4..e38bb7a95cdc 100644 --- a/examples/directx11_example/directx11_example.vcxproj +++ b/examples/directx11_example/directx11_example.vcxproj @@ -143,13 +143,15 @@ - + + - + + diff --git a/examples/directx11_example/directx11_example.vcxproj.filters b/examples/directx11_example/directx11_example.vcxproj.filters index 57b74dee4985..ab1e3fba2ef5 100644 --- a/examples/directx11_example/directx11_example.vcxproj.filters +++ b/examples/directx11_example/directx11_example.vcxproj.filters @@ -15,12 +15,15 @@ imgui - - sources - imgui + + sources + + + sources + @@ -29,15 +32,18 @@ sources - - sources - imgui imgui + + sources + + + sources + diff --git a/examples/directx11_example/main.cpp b/examples/directx11_example/main.cpp index f6dc6f74c828..c61abc2de663 100644 --- a/examples/directx11_example/main.cpp +++ b/examples/directx11_example/main.cpp @@ -2,7 +2,8 @@ // If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp. #include "imgui.h" -#include "imgui_impl_dx11.h" +#include "../imgui_impl_win32.h" +#include "../imgui_impl_dx11.h" #include #define DIRECTINPUT_VERSION 0x0800 #include @@ -125,7 +126,8 @@ int main(int, char**) // Setup ImGui binding ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - ImGui_ImplDX11_Init(hwnd, g_pd3dDevice, g_pd3dDeviceContext); + ImGui_ImplWin32_Init(hwnd); + ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext); //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; // Enable Keyboard Controls // Setup style @@ -167,6 +169,7 @@ int main(int, char**) continue; } ImGui_ImplDX11_NewFrame(); + ImGui_ImplWin32_NewFrame(); // 1. Show a simple window. // Tip: if we don't call ImGui::Begin()/ImGui::End() the widgets automatically appears in a window called "Debug". @@ -216,6 +219,7 @@ int main(int, char**) } ImGui_ImplDX11_Shutdown(); + ImGui_ImplWin32_Shutdown(); ImGui::DestroyContext(); CleanupDeviceD3D(); diff --git a/examples/directx11_example/imgui_impl_dx11.cpp b/examples/imgui_impl_dx11.cpp similarity index 78% rename from examples/directx11_example/imgui_impl_dx11.cpp rename to examples/imgui_impl_dx11.cpp index a0e9d7f3809c..7d3ded90d293 100644 --- a/examples/directx11_example/imgui_impl_dx11.cpp +++ b/examples/imgui_impl_dx11.cpp @@ -31,11 +31,6 @@ #define DIRECTINPUT_VERSION 0x0800 #include -// Data -static INT64 g_Time = 0; -static INT64 g_TicksPerSecond = 0; - -static HWND g_hWnd = 0; static ID3D11Device* g_pd3dDevice = NULL; static ID3D11DeviceContext* g_pd3dDeviceContext = NULL; static ID3D11Buffer* g_pVB = NULL; @@ -247,76 +242,6 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) ctx->IASetInputLayout(old.InputLayout); if (old.InputLayout) old.InputLayout->Release(); } -// Process Win32 mouse/keyboard inputs. -// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. -// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application. -// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. -// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. -// PS: In this Win32 handler, we use the capture API (GetCapture/SetCapture/ReleaseCapture) to be able to read mouse coordinations when dragging mouse outside of our window bounds. -// PS: We treat DBLCLK messages as regular mouse down messages, so this code will work on windows classes that have the CS_DBLCLKS flag set. Our own example app code doesn't set this flag. -IMGUI_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - if (ImGui::GetCurrentContext() == NULL) - return 0; - - ImGuiIO& io = ImGui::GetIO(); - switch (msg) - { - case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK: - case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK: - case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK: - { - int button = 0; - if (msg == WM_LBUTTONDOWN || msg == WM_LBUTTONDBLCLK) button = 0; - if (msg == WM_RBUTTONDOWN || msg == WM_RBUTTONDBLCLK) button = 1; - if (msg == WM_MBUTTONDOWN || msg == WM_MBUTTONDBLCLK) button = 2; - if (!ImGui::IsAnyMouseDown() && ::GetCapture() == NULL) - ::SetCapture(hwnd); - io.MouseDown[button] = true; - return 0; - } - case WM_LBUTTONUP: - case WM_RBUTTONUP: - case WM_MBUTTONUP: - { - int button = 0; - if (msg == WM_LBUTTONUP) button = 0; - if (msg == WM_RBUTTONUP) button = 1; - if (msg == WM_MBUTTONUP) button = 2; - io.MouseDown[button] = false; - if (!ImGui::IsAnyMouseDown() && ::GetCapture() == hwnd) - ::ReleaseCapture(); - return 0; - } - case WM_MOUSEWHEEL: - io.MouseWheel += GET_WHEEL_DELTA_WPARAM(wParam) > 0 ? +1.0f : -1.0f; - return 0; - case WM_MOUSEHWHEEL: - io.MouseWheelH += GET_WHEEL_DELTA_WPARAM(wParam) > 0 ? +1.0f : -1.0f; - return 0; - case WM_MOUSEMOVE: - io.MousePos.x = (signed short)(lParam); - io.MousePos.y = (signed short)(lParam >> 16); - return 0; - case WM_KEYDOWN: - case WM_SYSKEYDOWN: - if (wParam < 256) - io.KeysDown[wParam] = 1; - return 0; - case WM_KEYUP: - case WM_SYSKEYUP: - if (wParam < 256) - io.KeysDown[wParam] = 0; - return 0; - case WM_CHAR: - // You can also use ToAscii()+GetKeyboardState() to retrieve characters. - if (wParam > 0 && wParam < 0x10000) - io.AddInputCharacter((unsigned short)wParam); - return 0; - } - return 0; -} - static void ImGui_ImplDX11_CreateFontsTexture() { // Build texture atlas @@ -538,42 +463,10 @@ void ImGui_ImplDX11_InvalidateDeviceObjects() if (g_pVertexShaderBlob) { g_pVertexShaderBlob->Release(); g_pVertexShaderBlob = NULL; } } -bool ImGui_ImplDX11_Init(void* hwnd, ID3D11Device* device, ID3D11DeviceContext* device_context) +bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_context) { - g_hWnd = (HWND)hwnd; g_pd3dDevice = device; g_pd3dDeviceContext = device_context; - - if (!QueryPerformanceFrequency((LARGE_INTEGER *)&g_TicksPerSecond)) - return false; - if (!QueryPerformanceCounter((LARGE_INTEGER *)&g_Time)) - return false; - - ImGuiIO& io = ImGui::GetIO(); - io.KeyMap[ImGuiKey_Tab] = VK_TAB; // Keyboard mapping. ImGui will use those indices to peek into the io.KeyDown[] array that we will update during the application lifetime. - io.KeyMap[ImGuiKey_LeftArrow] = VK_LEFT; - io.KeyMap[ImGuiKey_RightArrow] = VK_RIGHT; - io.KeyMap[ImGuiKey_UpArrow] = VK_UP; - io.KeyMap[ImGuiKey_DownArrow] = VK_DOWN; - io.KeyMap[ImGuiKey_PageUp] = VK_PRIOR; - io.KeyMap[ImGuiKey_PageDown] = VK_NEXT; - io.KeyMap[ImGuiKey_Home] = VK_HOME; - io.KeyMap[ImGuiKey_End] = VK_END; - io.KeyMap[ImGuiKey_Insert] = VK_INSERT; - io.KeyMap[ImGuiKey_Delete] = VK_DELETE; - io.KeyMap[ImGuiKey_Backspace] = VK_BACK; - io.KeyMap[ImGuiKey_Space] = VK_SPACE; - io.KeyMap[ImGuiKey_Enter] = VK_RETURN; - io.KeyMap[ImGuiKey_Escape] = VK_ESCAPE; - io.KeyMap[ImGuiKey_A] = 'A'; - io.KeyMap[ImGuiKey_C] = 'C'; - io.KeyMap[ImGuiKey_V] = 'V'; - io.KeyMap[ImGuiKey_X] = 'X'; - io.KeyMap[ImGuiKey_Y] = 'Y'; - io.KeyMap[ImGuiKey_Z] = 'Z'; - - io.ImeWindowHandle = g_hWnd; - return true; } @@ -582,49 +475,10 @@ void ImGui_ImplDX11_Shutdown() ImGui_ImplDX11_InvalidateDeviceObjects(); g_pd3dDevice = NULL; g_pd3dDeviceContext = NULL; - g_hWnd = (HWND)0; } void ImGui_ImplDX11_NewFrame() { if (!g_pFontSampler) ImGui_ImplDX11_CreateDeviceObjects(); - - ImGuiIO& io = ImGui::GetIO(); - - // Setup display size (every frame to accommodate for window resizing) - RECT rect; - GetClientRect(g_hWnd, &rect); - io.DisplaySize = ImVec2((float)(rect.right - rect.left), (float)(rect.bottom - rect.top)); - - // Setup time step - INT64 current_time; - QueryPerformanceCounter((LARGE_INTEGER *)¤t_time); - io.DeltaTime = (float)(current_time - g_Time) / g_TicksPerSecond; - g_Time = current_time; - - // Read keyboard modifiers inputs - io.KeyCtrl = (GetKeyState(VK_CONTROL) & 0x8000) != 0; - io.KeyShift = (GetKeyState(VK_SHIFT) & 0x8000) != 0; - io.KeyAlt = (GetKeyState(VK_MENU) & 0x8000) != 0; - io.KeySuper = false; - // io.KeysDown : filled by WM_KEYDOWN/WM_KEYUP events - // io.MousePos : filled by WM_MOUSEMOVE events - // io.MouseDown : filled by WM_*BUTTON* events - // io.MouseWheel : filled by WM_MOUSEWHEEL events - - // Set OS mouse position if requested last frame by io.WantMoveMouse flag (used when io.NavMovesTrue is enabled by user and using directional navigation) - if (io.WantMoveMouse) - { - POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y }; - ClientToScreen(g_hWnd, &pos); - SetCursorPos(pos.x, pos.y); - } - - // Hide OS mouse cursor if ImGui is drawing it - if (io.MouseDrawCursor) - SetCursor(NULL); - - // Start the frame. This call will update the io.WantCaptureMouse, io.WantCaptureKeyboard flag that you can use to dispatch inputs (or not) to your application. - ImGui::NewFrame(); } diff --git a/examples/directx11_example/imgui_impl_dx11.h b/examples/imgui_impl_dx11.h similarity index 66% rename from examples/directx11_example/imgui_impl_dx11.h rename to examples/imgui_impl_dx11.h index 9364b0cabf2f..10e0d9637b7d 100644 --- a/examples/directx11_example/imgui_impl_dx11.h +++ b/examples/imgui_impl_dx11.h @@ -11,7 +11,7 @@ struct ID3D11Device; struct ID3D11DeviceContext; -IMGUI_API bool ImGui_ImplDX11_Init(void* hwnd, ID3D11Device* device, ID3D11DeviceContext* device_context); +IMGUI_API bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_context); IMGUI_API void ImGui_ImplDX11_Shutdown(); IMGUI_API void ImGui_ImplDX11_NewFrame(); IMGUI_API void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data); @@ -19,10 +19,3 @@ IMGUI_API void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data); // Use if you want to reset your rendering device without losing ImGui state. IMGUI_API void ImGui_ImplDX11_InvalidateDeviceObjects(); IMGUI_API bool ImGui_ImplDX11_CreateDeviceObjects(); - -// Handler for Win32 messages, update mouse/keyboard data. -// You may or not need this for your implementation, but it can serve as reference for handling inputs. -// Commented out to avoid dragging dependencies on types. You can copy the extern declaration in your code. -/* -IMGUI_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); -*/ diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp new file mode 100644 index 000000000000..c2d6d9d730ee --- /dev/null +++ b/examples/imgui_impl_win32.cpp @@ -0,0 +1,161 @@ +#include "imgui.h" +#include "imgui_impl_win32.h" +#define WIN32_LEAN_AND_MEAN +#include + +// Data +static HWND g_hWnd = 0; +static INT64 g_Time = 0; +static INT64 g_TicksPerSecond = 0; + +// Functions +bool ImGui_ImplWin32_Init(void* hwnd) +{ + if (!QueryPerformanceFrequency((LARGE_INTEGER *)&g_TicksPerSecond)) + return false; + if (!QueryPerformanceCounter((LARGE_INTEGER *)&g_Time)) + return false; + + g_hWnd = (HWND)hwnd; + + ImGuiIO& io = ImGui::GetIO(); + io.KeyMap[ImGuiKey_Tab] = VK_TAB; // Keyboard mapping. ImGui will use those indices to peek into the io.KeyDown[] array that we will update during the application lifetime. + io.KeyMap[ImGuiKey_LeftArrow] = VK_LEFT; + io.KeyMap[ImGuiKey_RightArrow] = VK_RIGHT; + io.KeyMap[ImGuiKey_UpArrow] = VK_UP; + io.KeyMap[ImGuiKey_DownArrow] = VK_DOWN; + io.KeyMap[ImGuiKey_PageUp] = VK_PRIOR; + io.KeyMap[ImGuiKey_PageDown] = VK_NEXT; + io.KeyMap[ImGuiKey_Home] = VK_HOME; + io.KeyMap[ImGuiKey_End] = VK_END; + io.KeyMap[ImGuiKey_Insert] = VK_INSERT; + io.KeyMap[ImGuiKey_Delete] = VK_DELETE; + io.KeyMap[ImGuiKey_Backspace] = VK_BACK; + io.KeyMap[ImGuiKey_Space] = VK_SPACE; + io.KeyMap[ImGuiKey_Enter] = VK_RETURN; + io.KeyMap[ImGuiKey_Escape] = VK_ESCAPE; + io.KeyMap[ImGuiKey_A] = 'A'; + io.KeyMap[ImGuiKey_C] = 'C'; + io.KeyMap[ImGuiKey_V] = 'V'; + io.KeyMap[ImGuiKey_X] = 'X'; + io.KeyMap[ImGuiKey_Y] = 'Y'; + io.KeyMap[ImGuiKey_Z] = 'Z'; + + io.ImeWindowHandle = g_hWnd; return true; +} + +void ImGui_ImplWin32_Shutdown() +{ + g_hWnd = (HWND)0; +} + +void ImGui_ImplWin32_NewFrame() +{ + ImGuiIO& io = ImGui::GetIO(); + + // Setup display size (every frame to accommodate for window resizing) + RECT rect; + GetClientRect(g_hWnd, &rect); + io.DisplaySize = ImVec2((float)(rect.right - rect.left), (float)(rect.bottom - rect.top)); + + // Setup time step + INT64 current_time; + QueryPerformanceCounter((LARGE_INTEGER *)¤t_time); + io.DeltaTime = (float)(current_time - g_Time) / g_TicksPerSecond; + g_Time = current_time; + + // Read keyboard modifiers inputs + io.KeyCtrl = (GetKeyState(VK_CONTROL) & 0x8000) != 0; + io.KeyShift = (GetKeyState(VK_SHIFT) & 0x8000) != 0; + io.KeyAlt = (GetKeyState(VK_MENU) & 0x8000) != 0; + io.KeySuper = false; + // io.KeysDown : filled by WM_KEYDOWN/WM_KEYUP events + // io.MousePos : filled by WM_MOUSEMOVE events + // io.MouseDown : filled by WM_*BUTTON* events + // io.MouseWheel : filled by WM_MOUSEWHEEL events + + // Set OS mouse position if requested last frame by io.WantMoveMouse flag (used when io.NavMovesTrue is enabled by user and using directional navigation) + if (io.WantMoveMouse) + { + POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y }; + ClientToScreen(g_hWnd, &pos); + SetCursorPos(pos.x, pos.y); + } + + // Hide OS mouse cursor if ImGui is drawing it + if (io.MouseDrawCursor) + SetCursor(NULL); + + // Start the frame. This call will update the io.WantCaptureMouse, io.WantCaptureKeyboard flag that you can use to dispatch inputs (or not) to your application. + ImGui::NewFrame(); +} + +// Process Win32 mouse/keyboard inputs. +// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. +// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application. +// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. +// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. +// PS: In this Win32 handler, we use the capture API (GetCapture/SetCapture/ReleaseCapture) to be able to read mouse coordinations when dragging mouse outside of our window bounds. +// PS: We treat DBLCLK messages as regular mouse down messages, so this code will work on windows classes that have the CS_DBLCLKS flag set. Our own example app code doesn't set this flag. +IMGUI_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + if (ImGui::GetCurrentContext() == NULL) + return 0; + + ImGuiIO& io = ImGui::GetIO(); + switch (msg) + { + case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK: + case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK: + case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK: + { + int button = 0; + if (msg == WM_LBUTTONDOWN || msg == WM_LBUTTONDBLCLK) button = 0; + if (msg == WM_RBUTTONDOWN || msg == WM_RBUTTONDBLCLK) button = 1; + if (msg == WM_MBUTTONDOWN || msg == WM_MBUTTONDBLCLK) button = 2; + if (!ImGui::IsAnyMouseDown() && ::GetCapture() == NULL) + ::SetCapture(hwnd); + io.MouseDown[button] = true; + return 0; + } + case WM_LBUTTONUP: + case WM_RBUTTONUP: + case WM_MBUTTONUP: + { + int button = 0; + if (msg == WM_LBUTTONUP) button = 0; + if (msg == WM_RBUTTONUP) button = 1; + if (msg == WM_MBUTTONUP) button = 2; + io.MouseDown[button] = false; + if (!ImGui::IsAnyMouseDown() && ::GetCapture() == hwnd) + ::ReleaseCapture(); + return 0; + } + case WM_MOUSEWHEEL: + io.MouseWheel += GET_WHEEL_DELTA_WPARAM(wParam) > 0 ? +1.0f : -1.0f; + return 0; + case WM_MOUSEHWHEEL: + io.MouseWheelH += GET_WHEEL_DELTA_WPARAM(wParam) > 0 ? +1.0f : -1.0f; + return 0; + case WM_MOUSEMOVE: + io.MousePos.x = (signed short)(lParam); + io.MousePos.y = (signed short)(lParam >> 16); + return 0; + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + if (wParam < 256) + io.KeysDown[wParam] = 1; + return 0; + case WM_KEYUP: + case WM_SYSKEYUP: + if (wParam < 256) + io.KeysDown[wParam] = 0; + return 0; + case WM_CHAR: + // You can also use ToAscii()+GetKeyboardState() to retrieve characters. + if (wParam > 0 && wParam < 0x10000) + io.AddInputCharacter((unsigned short)wParam); + return 0; + } + return 0; +} \ No newline at end of file diff --git a/examples/imgui_impl_win32.h b/examples/imgui_impl_win32.h new file mode 100644 index 000000000000..086cf032a0d7 --- /dev/null +++ b/examples/imgui_impl_win32.h @@ -0,0 +1,11 @@ + +IMGUI_API bool ImGui_ImplWin32_Init(void* hwnd); +IMGUI_API void ImGui_ImplWin32_Shutdown(); +IMGUI_API void ImGui_ImplWin32_NewFrame(); + +// Handler for Win32 messages, update mouse/keyboard data. +// You may or not need this for your implementation, but it can serve as reference for handling inputs. +// Commented out to avoid dragging dependencies on types. You can copy the extern declaration in your code. +/* +IMGUI_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); +*/ From 6cd4e30b5812352d600404c84047a5916f1aedc4 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 16 Feb 2018 23:11:43 +0100 Subject: [PATCH 008/828] Examples: DirectX10: Reusing imgui_impl_win32, removed that code from imgui_impl_dx10 --- .../directx10_example.vcxproj | 6 +- .../directx10_example.vcxproj.filters | 18 +- examples/directx10_example/main.cpp | 8 +- .../imgui_impl_dx10.cpp | 168 +----------------- .../{directx10_example => }/imgui_impl_dx10.h | 9 +- examples/imgui_impl_dx11.cpp | 8 +- 6 files changed, 35 insertions(+), 182 deletions(-) rename examples/{directx10_example => }/imgui_impl_dx10.cpp (76%) rename examples/{directx10_example => }/imgui_impl_dx10.h (67%) diff --git a/examples/directx10_example/directx10_example.vcxproj b/examples/directx10_example/directx10_example.vcxproj index 64b57ddf172a..2013d0e7af17 100644 --- a/examples/directx10_example/directx10_example.vcxproj +++ b/examples/directx10_example/directx10_example.vcxproj @@ -143,13 +143,15 @@ - + + - + + diff --git a/examples/directx10_example/directx10_example.vcxproj.filters b/examples/directx10_example/directx10_example.vcxproj.filters index aef010adf011..c3422a5838aa 100644 --- a/examples/directx10_example/directx10_example.vcxproj.filters +++ b/examples/directx10_example/directx10_example.vcxproj.filters @@ -15,12 +15,15 @@ imgui - - sources - imgui + + sources + + + sources + @@ -29,15 +32,18 @@ sources - - sources - imgui imgui + + sources + + + sources + diff --git a/examples/directx10_example/main.cpp b/examples/directx10_example/main.cpp index f20f6881e0bb..a5541554897e 100644 --- a/examples/directx10_example/main.cpp +++ b/examples/directx10_example/main.cpp @@ -2,7 +2,8 @@ // If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp. #include "imgui.h" -#include "imgui_impl_dx10.h" +#include "../imgui_impl_win32.h" +#include "../imgui_impl_dx10.h" #include #include #define DIRECTINPUT_VERSION 0x0800 @@ -122,7 +123,8 @@ int main(int, char**) // Setup ImGui binding ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - ImGui_ImplDX10_Init(hwnd, g_pd3dDevice); + ImGui_ImplWin32_Init(hwnd); + ImGui_ImplDX10_Init(g_pd3dDevice); //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; // Enable Keyboard Controls // Setup style @@ -164,6 +166,7 @@ int main(int, char**) continue; } ImGui_ImplDX10_NewFrame(); + ImGui_ImplWin32_NewFrame(); // 1. Show a simple window. // Tip: if we don't call ImGui::Begin()/ImGui::End() the widgets automatically appears in a window called "Debug". @@ -213,6 +216,7 @@ int main(int, char**) } ImGui_ImplDX10_Shutdown(); + ImGui_ImplWin32_Shutdown(); ImGui::DestroyContext(); CleanupDeviceD3D(); diff --git a/examples/directx10_example/imgui_impl_dx10.cpp b/examples/imgui_impl_dx10.cpp similarity index 76% rename from examples/directx10_example/imgui_impl_dx10.cpp rename to examples/imgui_impl_dx10.cpp index 10da969e2c38..8cf6c8fd32c7 100644 --- a/examples/directx10_example/imgui_impl_dx10.cpp +++ b/examples/imgui_impl_dx10.cpp @@ -29,14 +29,8 @@ #include #include #include -#define DIRECTINPUT_VERSION 0x0800 -#include // Data -static INT64 g_Time = 0; -static INT64 g_TicksPerSecond = 0; - -static HWND g_hWnd = 0; static ID3D10Device* g_pd3dDevice = NULL; static ID3D10Buffer* g_pVB = NULL; static ID3D10Buffer* g_pIB = NULL; @@ -116,11 +110,11 @@ void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data) if (g_pVertexConstantBuffer->Map(D3D10_MAP_WRITE_DISCARD, 0, &mapped_resource) != S_OK) return; VERTEX_CONSTANT_BUFFER* constant_buffer = (VERTEX_CONSTANT_BUFFER*)mapped_resource; - const float L = 0.0f; - const float R = ImGui::GetIO().DisplaySize.x; - const float B = ImGui::GetIO().DisplaySize.y; - const float T = 0.0f; - const float mvp[4][4] = + float L = 0.0f; + float R = ImGui::GetIO().DisplaySize.x; + float B = ImGui::GetIO().DisplaySize.y; + float T = 0.0f; + float mvp[4][4] = { { 2.0f/(R-L), 0.0f, 0.0f, 0.0f }, { 0.0f, 2.0f/(T-B), 0.0f, 0.0f }, @@ -240,86 +234,15 @@ void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data) ctx->IASetInputLayout(old.InputLayout); if (old.InputLayout) old.InputLayout->Release(); } -// Process Win32 mouse/keyboard inputs. -// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. -// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application. -// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. -// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. -// PS: In this Win32 handler, we use the capture API (GetCapture/SetCapture/ReleaseCapture) to be able to read mouse coordinations when dragging mouse outside of our window bounds. -// PS: We treat DBLCLK messages as regular mouse down messages, so this code will work on windows classes that have the CS_DBLCLKS flag set. Our own example app code doesn't set this flag. -IMGUI_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - if (ImGui::GetCurrentContext() == NULL) - return 0; - - ImGuiIO& io = ImGui::GetIO(); - switch (msg) - { - case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK: - case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK: - case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK: - { - int button = 0; - if (msg == WM_LBUTTONDOWN || msg == WM_LBUTTONDBLCLK) button = 0; - if (msg == WM_RBUTTONDOWN || msg == WM_RBUTTONDBLCLK) button = 1; - if (msg == WM_MBUTTONDOWN || msg == WM_MBUTTONDBLCLK) button = 2; - if (!ImGui::IsAnyMouseDown() && ::GetCapture() == NULL) - ::SetCapture(hwnd); - io.MouseDown[button] = true; - return 0; - } - case WM_LBUTTONUP: - case WM_RBUTTONUP: - case WM_MBUTTONUP: - { - int button = 0; - if (msg == WM_LBUTTONUP) button = 0; - if (msg == WM_RBUTTONUP) button = 1; - if (msg == WM_MBUTTONUP) button = 2; - io.MouseDown[button] = false; - if (!ImGui::IsAnyMouseDown() && ::GetCapture() == hwnd) - ::ReleaseCapture(); - return 0; - } - case WM_MOUSEWHEEL: - io.MouseWheel += GET_WHEEL_DELTA_WPARAM(wParam) > 0 ? +1.0f : -1.0f; - return 0; - case WM_MOUSEHWHEEL: - io.MouseWheelH += GET_WHEEL_DELTA_WPARAM(wParam) > 0 ? +1.0f : -1.0f; - return 0; - case WM_MOUSEMOVE: - io.MousePos.x = (signed short)(lParam); - io.MousePos.y = (signed short)(lParam >> 16); - return 0; - case WM_KEYDOWN: - case WM_SYSKEYDOWN: - if (wParam < 256) - io.KeysDown[wParam] = 1; - return 0; - case WM_KEYUP: - case WM_SYSKEYUP: - if (wParam < 256) - io.KeysDown[wParam] = 0; - return 0; - case WM_CHAR: - // You can also use ToAscii()+GetKeyboardState() to retrieve characters. - if (wParam > 0 && wParam < 0x10000) - io.AddInputCharacter((unsigned short)wParam); - return 0; - } - return 0; -} - static void ImGui_ImplDX10_CreateFontsTexture() { + // Build texture atlas ImGuiIO& io = ImGui::GetIO(); - - // Build unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); - // Create DX10 texture + // Upload texture to graphics system { D3D10_TEXTURE2D_DESC desc; ZeroMemory(&desc, sizeof(desc)); @@ -368,10 +291,6 @@ static void ImGui_ImplDX10_CreateFontsTexture() desc.MaxLOD = 0.f; g_pd3dDevice->CreateSamplerState(&desc, &g_pFontSampler); } - - // Cleanup (don't clear the input data if you want to append new fonts later) - io.Fonts->ClearInputData(); - io.Fonts->ClearTexData(); } bool ImGui_ImplDX10_CreateDeviceObjects() @@ -537,41 +456,9 @@ void ImGui_ImplDX10_InvalidateDeviceObjects() if (g_pVertexShaderBlob) { g_pVertexShaderBlob->Release(); g_pVertexShaderBlob = NULL; } } -bool ImGui_ImplDX10_Init(void* hwnd, ID3D10Device* device) +bool ImGui_ImplDX10_Init(ID3D10Device* device) { - g_hWnd = (HWND)hwnd; g_pd3dDevice = device; - - if (!QueryPerformanceFrequency((LARGE_INTEGER *)&g_TicksPerSecond)) - return false; - if (!QueryPerformanceCounter((LARGE_INTEGER *)&g_Time)) - return false; - - ImGuiIO& io = ImGui::GetIO(); - io.KeyMap[ImGuiKey_Tab] = VK_TAB; // Keyboard mapping. ImGui will use those indices to peek into the io.KeyDown[] array that we will update during the application lifetime. - io.KeyMap[ImGuiKey_LeftArrow] = VK_LEFT; - io.KeyMap[ImGuiKey_RightArrow] = VK_RIGHT; - io.KeyMap[ImGuiKey_UpArrow] = VK_UP; - io.KeyMap[ImGuiKey_DownArrow] = VK_DOWN; - io.KeyMap[ImGuiKey_PageUp] = VK_PRIOR; - io.KeyMap[ImGuiKey_PageDown] = VK_NEXT; - io.KeyMap[ImGuiKey_Home] = VK_HOME; - io.KeyMap[ImGuiKey_End] = VK_END; - io.KeyMap[ImGuiKey_Insert] = VK_INSERT; - io.KeyMap[ImGuiKey_Delete] = VK_DELETE; - io.KeyMap[ImGuiKey_Backspace] = VK_BACK; - io.KeyMap[ImGuiKey_Space] = VK_SPACE; - io.KeyMap[ImGuiKey_Enter] = VK_RETURN; - io.KeyMap[ImGuiKey_Escape] = VK_ESCAPE; - io.KeyMap[ImGuiKey_A] = 'A'; - io.KeyMap[ImGuiKey_C] = 'C'; - io.KeyMap[ImGuiKey_V] = 'V'; - io.KeyMap[ImGuiKey_X] = 'X'; - io.KeyMap[ImGuiKey_Y] = 'Y'; - io.KeyMap[ImGuiKey_Z] = 'Z'; - - io.ImeWindowHandle = g_hWnd; - return true; } @@ -579,49 +466,10 @@ void ImGui_ImplDX10_Shutdown() { ImGui_ImplDX10_InvalidateDeviceObjects(); g_pd3dDevice = NULL; - g_hWnd = (HWND)0; } void ImGui_ImplDX10_NewFrame() { if (!g_pFontSampler) ImGui_ImplDX10_CreateDeviceObjects(); - - ImGuiIO& io = ImGui::GetIO(); - - // Setup display size (every frame to accommodate for window resizing) - RECT rect; - GetClientRect(g_hWnd, &rect); - io.DisplaySize = ImVec2((float)(rect.right - rect.left), (float)(rect.bottom - rect.top)); - - // Setup time step - INT64 current_time; - QueryPerformanceCounter((LARGE_INTEGER *)¤t_time); - io.DeltaTime = (float)(current_time - g_Time) / g_TicksPerSecond; - g_Time = current_time; - - // Read keyboard modifiers inputs - io.KeyCtrl = (GetKeyState(VK_CONTROL) & 0x8000) != 0; - io.KeyShift = (GetKeyState(VK_SHIFT) & 0x8000) != 0; - io.KeyAlt = (GetKeyState(VK_MENU) & 0x8000) != 0; - io.KeySuper = false; - // io.KeysDown : filled by WM_KEYDOWN/WM_KEYUP events - // io.MousePos : filled by WM_MOUSEMOVE events - // io.MouseDown : filled by WM_*BUTTON* events - // io.MouseWheel : filled by WM_MOUSEWHEEL events - - // Set OS mouse position if requested last frame by io.WantMoveMouse flag (used when io.NavMovesTrue is enabled by user and using directional navigation) - if (io.WantMoveMouse) - { - POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y }; - ClientToScreen(g_hWnd, &pos); - SetCursorPos(pos.x, pos.y); - } - - // Hide OS mouse cursor if ImGui is drawing it - if (io.MouseDrawCursor) - SetCursor(NULL); - - // Start the frame. This call will update the io.WantCaptureMouse, io.WantCaptureKeyboard flag that you can use to dispatch inputs (or not) to your application. - ImGui::NewFrame(); } diff --git a/examples/directx10_example/imgui_impl_dx10.h b/examples/imgui_impl_dx10.h similarity index 67% rename from examples/directx10_example/imgui_impl_dx10.h rename to examples/imgui_impl_dx10.h index a47418476589..c375351bf9d9 100644 --- a/examples/directx10_example/imgui_impl_dx10.h +++ b/examples/imgui_impl_dx10.h @@ -10,7 +10,7 @@ struct ID3D10Device; -IMGUI_API bool ImGui_ImplDX10_Init(void* hwnd, ID3D10Device* device); +IMGUI_API bool ImGui_ImplDX10_Init(ID3D10Device* device); IMGUI_API void ImGui_ImplDX10_Shutdown(); IMGUI_API void ImGui_ImplDX10_NewFrame(); IMGUI_API void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data); @@ -18,10 +18,3 @@ IMGUI_API void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data); // Use if you want to reset your rendering device without losing ImGui state. IMGUI_API void ImGui_ImplDX10_InvalidateDeviceObjects(); IMGUI_API bool ImGui_ImplDX10_CreateDeviceObjects(); - -// Handler for Win32 messages, update mouse/keyboard data. -// You may or not need this for your implementation, but it can serve as reference for handling inputs. -// Commented out to avoid dragging dependencies on types. You can copy the extern declaration in your code. -/* -IMGUI_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); -*/ diff --git a/examples/imgui_impl_dx11.cpp b/examples/imgui_impl_dx11.cpp index 7d3ded90d293..b2d67911db91 100644 --- a/examples/imgui_impl_dx11.cpp +++ b/examples/imgui_impl_dx11.cpp @@ -28,9 +28,8 @@ // DirectX #include #include -#define DIRECTINPUT_VERSION 0x0800 -#include +// Data static ID3D11Device* g_pd3dDevice = NULL; static ID3D11DeviceContext* g_pd3dDeviceContext = NULL; static ID3D11Buffer* g_pVB = NULL; @@ -177,7 +176,7 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) vp.Height = ImGui::GetIO().DisplaySize.y; vp.MinDepth = 0.0f; vp.MaxDepth = 1.0f; - vp.TopLeftX = vp.TopLeftY = 0.0f; + vp.TopLeftX = vp.TopLeftY = 0; ctx->RSSetViewports(1, &vp); // Bind shader and vertex buffers @@ -351,7 +350,8 @@ bool ImGui_ImplDX11_CreateDeviceObjects() return false; // Create the input layout - D3D11_INPUT_ELEMENT_DESC local_layout[] = { + D3D11_INPUT_ELEMENT_DESC local_layout[] = + { { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (size_t)(&((ImDrawVert*)0)->pos), D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (size_t)(&((ImDrawVert*)0)->uv), D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, (size_t)(&((ImDrawVert*)0)->col), D3D11_INPUT_PER_VERTEX_DATA, 0 }, From 4f0db01f7cc090a2e118b1207b9b7c6a4cc865ef Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 16 Feb 2018 23:15:14 +0100 Subject: [PATCH 009/828] Examples: DirectX9: Reusing imgui_impl_win32, removed that code from imgui_impl_dx9 --- .../directx9_example/directx9_example.vcxproj | 6 +- .../directx9_example.vcxproj.filters | 18 ++- examples/directx9_example/main.cpp | 8 +- .../{directx9_example => }/imgui_impl_dx9.cpp | 146 +----------------- .../{directx9_example => }/imgui_impl_dx9.h | 9 +- examples/imgui_impl_win32.cpp | 2 +- 6 files changed, 25 insertions(+), 164 deletions(-) rename examples/{directx9_example => }/imgui_impl_dx9.cpp (62%) rename examples/{directx9_example => }/imgui_impl_dx9.h (67%) diff --git a/examples/directx9_example/directx9_example.vcxproj b/examples/directx9_example/directx9_example.vcxproj index 89f3ae6ecc61..0b7bfa6808f2 100644 --- a/examples/directx9_example/directx9_example.vcxproj +++ b/examples/directx9_example/directx9_example.vcxproj @@ -143,14 +143,16 @@ - + + - + + diff --git a/examples/directx9_example/directx9_example.vcxproj.filters b/examples/directx9_example/directx9_example.vcxproj.filters index 555ef06d7e1d..2ac28d181b4d 100644 --- a/examples/directx9_example/directx9_example.vcxproj.filters +++ b/examples/directx9_example/directx9_example.vcxproj.filters @@ -16,15 +16,18 @@ imgui - - sources - imgui imgui + + sources + + + sources + @@ -33,12 +36,15 @@ imgui - - sources - imgui + + sources + + + sources + diff --git a/examples/directx9_example/main.cpp b/examples/directx9_example/main.cpp index 16482b3aa8fa..c1e2db96fcfe 100644 --- a/examples/directx9_example/main.cpp +++ b/examples/directx9_example/main.cpp @@ -2,7 +2,8 @@ // If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp. #include "imgui.h" -#include "imgui_impl_dx9.h" +#include "../imgui_impl_dx9.h" +#include "../imgui_impl_win32.h" #include #define DIRECTINPUT_VERSION 0x0800 #include @@ -77,7 +78,8 @@ int main(int, char**) // Setup ImGui binding ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - ImGui_ImplDX9_Init(hwnd, g_pd3dDevice); + ImGui_ImplWin32_Init(hwnd); + ImGui_ImplDX9_Init(g_pd3dDevice); //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; // Enable Keyboard Controls // Setup style @@ -121,6 +123,7 @@ int main(int, char**) continue; } ImGui_ImplDX9_NewFrame(); + ImGui_ImplWin32_NewFrame(); // 1. Show a simple window. // Tip: if we don't call ImGui::Begin()/ImGui::End() the widgets automatically appears in a window called "Debug". @@ -184,6 +187,7 @@ int main(int, char**) } ImGui_ImplDX9_Shutdown(); + ImGui_ImplWin32_Shutdown(); ImGui::DestroyContext(); if (g_pd3dDevice) g_pd3dDevice->Release(); diff --git a/examples/directx9_example/imgui_impl_dx9.cpp b/examples/imgui_impl_dx9.cpp similarity index 62% rename from examples/directx9_example/imgui_impl_dx9.cpp rename to examples/imgui_impl_dx9.cpp index e960b7c51850..6e16b5be1427 100644 --- a/examples/directx9_example/imgui_impl_dx9.cpp +++ b/examples/imgui_impl_dx9.cpp @@ -23,9 +23,6 @@ #include // Data -static HWND g_hWnd = 0; -static INT64 g_Time = 0; -static INT64 g_TicksPerSecond = 0; static LPDIRECT3DDEVICE9 g_pd3dDevice = NULL; static LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL; static LPDIRECT3DINDEXBUFFER9 g_pIB = NULL; @@ -178,111 +175,9 @@ void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data) d3d9_state_block->Release(); } -// Process Win32 mouse/keyboard inputs. -// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. -// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application. -// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. -// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. -// PS: In this Win32 handler, we use the capture API (GetCapture/SetCapture/ReleaseCapture) to be able to read mouse coordinations when dragging mouse outside of our window bounds. -// PS: We treat DBLCLK messages as regular mouse down messages, so this code will work on windows classes that have the CS_DBLCLKS flag set. Our own example app code doesn't set this flag. -IMGUI_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +bool ImGui_ImplDX9_Init(IDirect3DDevice9* device) { - if (ImGui::GetCurrentContext() == NULL) - return 0; - - ImGuiIO& io = ImGui::GetIO(); - switch (msg) - { - case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK: - case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK: - case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK: - { - int button = 0; - if (msg == WM_LBUTTONDOWN || msg == WM_LBUTTONDBLCLK) button = 0; - if (msg == WM_RBUTTONDOWN || msg == WM_RBUTTONDBLCLK) button = 1; - if (msg == WM_MBUTTONDOWN || msg == WM_MBUTTONDBLCLK) button = 2; - if (!ImGui::IsAnyMouseDown() && ::GetCapture() == NULL) - ::SetCapture(hwnd); - io.MouseDown[button] = true; - return 0; - } - case WM_LBUTTONUP: - case WM_RBUTTONUP: - case WM_MBUTTONUP: - { - int button = 0; - if (msg == WM_LBUTTONUP) button = 0; - if (msg == WM_RBUTTONUP) button = 1; - if (msg == WM_MBUTTONUP) button = 2; - io.MouseDown[button] = false; - if (!ImGui::IsAnyMouseDown() && ::GetCapture() == hwnd) - ::ReleaseCapture(); - return 0; - } - case WM_MOUSEWHEEL: - io.MouseWheel += GET_WHEEL_DELTA_WPARAM(wParam) > 0 ? +1.0f : -1.0f; - return 0; - case WM_MOUSEHWHEEL: - io.MouseWheelH += GET_WHEEL_DELTA_WPARAM(wParam) > 0 ? +1.0f : -1.0f; - return 0; - case WM_MOUSEMOVE: - io.MousePos.x = (signed short)(lParam); - io.MousePos.y = (signed short)(lParam >> 16); - return 0; - case WM_KEYDOWN: - case WM_SYSKEYDOWN: - if (wParam < 256) - io.KeysDown[wParam] = 1; - return 0; - case WM_KEYUP: - case WM_SYSKEYUP: - if (wParam < 256) - io.KeysDown[wParam] = 0; - return 0; - case WM_CHAR: - // You can also use ToAscii()+GetKeyboardState() to retrieve characters. - if (wParam > 0 && wParam < 0x10000) - io.AddInputCharacter((unsigned short)wParam); - return 0; - } - return 0; -} - -bool ImGui_ImplDX9_Init(void* hwnd, IDirect3DDevice9* device) -{ - g_hWnd = (HWND)hwnd; g_pd3dDevice = device; - - if (!QueryPerformanceFrequency((LARGE_INTEGER *)&g_TicksPerSecond)) - return false; - if (!QueryPerformanceCounter((LARGE_INTEGER *)&g_Time)) - return false; - - ImGuiIO& io = ImGui::GetIO(); - io.KeyMap[ImGuiKey_Tab] = VK_TAB; // Keyboard mapping. ImGui will use those indices to peek into the io.KeyDown[] array that we will update during the application lifetime. - io.KeyMap[ImGuiKey_LeftArrow] = VK_LEFT; - io.KeyMap[ImGuiKey_RightArrow] = VK_RIGHT; - io.KeyMap[ImGuiKey_UpArrow] = VK_UP; - io.KeyMap[ImGuiKey_DownArrow] = VK_DOWN; - io.KeyMap[ImGuiKey_PageUp] = VK_PRIOR; - io.KeyMap[ImGuiKey_PageDown] = VK_NEXT; - io.KeyMap[ImGuiKey_Home] = VK_HOME; - io.KeyMap[ImGuiKey_End] = VK_END; - io.KeyMap[ImGuiKey_Insert] = VK_INSERT; - io.KeyMap[ImGuiKey_Delete] = VK_DELETE; - io.KeyMap[ImGuiKey_Backspace] = VK_BACK; - io.KeyMap[ImGuiKey_Space] = VK_SPACE; - io.KeyMap[ImGuiKey_Enter] = VK_RETURN; - io.KeyMap[ImGuiKey_Escape] = VK_ESCAPE; - io.KeyMap[ImGuiKey_A] = 'A'; - io.KeyMap[ImGuiKey_C] = 'C'; - io.KeyMap[ImGuiKey_V] = 'V'; - io.KeyMap[ImGuiKey_X] = 'X'; - io.KeyMap[ImGuiKey_Y] = 'Y'; - io.KeyMap[ImGuiKey_Z] = 'Z'; - - io.ImeWindowHandle = g_hWnd; - return true; } @@ -290,7 +185,6 @@ void ImGui_ImplDX9_Shutdown() { ImGui_ImplDX9_InvalidateDeviceObjects(); g_pd3dDevice = NULL; - g_hWnd = 0; } static bool ImGui_ImplDX9_CreateFontsTexture() @@ -355,42 +249,4 @@ void ImGui_ImplDX9_NewFrame() { if (!g_FontTexture) ImGui_ImplDX9_CreateDeviceObjects(); - - ImGuiIO& io = ImGui::GetIO(); - - // Setup display size (every frame to accommodate for window resizing) - RECT rect; - GetClientRect(g_hWnd, &rect); - io.DisplaySize = ImVec2((float)(rect.right - rect.left), (float)(rect.bottom - rect.top)); - - // Setup time step - INT64 current_time; - QueryPerformanceCounter((LARGE_INTEGER *)¤t_time); - io.DeltaTime = (float)(current_time - g_Time) / g_TicksPerSecond; - g_Time = current_time; - - // Read keyboard modifiers inputs - io.KeyCtrl = (GetKeyState(VK_CONTROL) & 0x8000) != 0; - io.KeyShift = (GetKeyState(VK_SHIFT) & 0x8000) != 0; - io.KeyAlt = (GetKeyState(VK_MENU) & 0x8000) != 0; - io.KeySuper = false; - // io.KeysDown : filled by WM_KEYDOWN/WM_KEYUP events - // io.MousePos : filled by WM_MOUSEMOVE events - // io.MouseDown : filled by WM_*BUTTON* events - // io.MouseWheel : filled by WM_MOUSEWHEEL events - - // Set OS mouse position if requested last frame by io.WantMoveMouse flag (used when io.NavMovesTrue is enabled by user and using directional navigation) - if (io.WantMoveMouse) - { - POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y }; - ClientToScreen(g_hWnd, &pos); - SetCursorPos(pos.x, pos.y); - } - - // Hide OS mouse cursor if ImGui is drawing it - if (io.MouseDrawCursor) - SetCursor(NULL); - - // Start the frame. This call will update the io.WantCaptureMouse, io.WantCaptureKeyboard flag that you can use to dispatch inputs (or not) to your application. - ImGui::NewFrame(); } diff --git a/examples/directx9_example/imgui_impl_dx9.h b/examples/imgui_impl_dx9.h similarity index 67% rename from examples/directx9_example/imgui_impl_dx9.h rename to examples/imgui_impl_dx9.h index e0ea2deae90c..eb52eabefd99 100644 --- a/examples/directx9_example/imgui_impl_dx9.h +++ b/examples/imgui_impl_dx9.h @@ -10,7 +10,7 @@ struct IDirect3DDevice9; -IMGUI_API bool ImGui_ImplDX9_Init(void* hwnd, IDirect3DDevice9* device); +IMGUI_API bool ImGui_ImplDX9_Init(IDirect3DDevice9* device); IMGUI_API void ImGui_ImplDX9_Shutdown(); IMGUI_API void ImGui_ImplDX9_NewFrame(); IMGUI_API void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data); @@ -18,10 +18,3 @@ IMGUI_API void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data); // Use if you want to reset your rendering device without losing ImGui state. IMGUI_API void ImGui_ImplDX9_InvalidateDeviceObjects(); IMGUI_API bool ImGui_ImplDX9_CreateDeviceObjects(); - -// Handler for Win32 messages, update mouse/keyboard data. -// You may or not need this for your implementation, but it can serve as reference for handling inputs. -// Commented out to avoid dragging dependencies on types. You can copy the extern declaration in your code. -/* -IMGUI_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); -*/ diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index c2d6d9d730ee..3547b98ce16c 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -158,4 +158,4 @@ IMGUI_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARAM wPa return 0; } return 0; -} \ No newline at end of file +} From 80a8aea7e350b3e426e9569134306a7c5c8336ae Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 16 Feb 2018 23:16:25 +0100 Subject: [PATCH 010/828] Examples: Added SDL, Vulkan examples to .sln file. --- examples/imgui_examples.sln | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/examples/imgui_examples.sln b/examples/imgui_examples.sln index 8c1cd2a397b4..f0aca4326bcc 100644 --- a/examples/imgui_examples.sln +++ b/examples/imgui_examples.sln @@ -1,6 +1,8 @@  -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "opengl2_example", "opengl2_example\opengl2_example.vcxproj", "{9CDA7840-B7A5-496D-A527-E95571496D18}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "directx9_example", "directx9_example\directx9_example.vcxproj", "{4165A294-21F2-44CA-9B38-E3F935ABADF5}" @@ -11,6 +13,12 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "opengl3_example", "opengl3_ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "directx10_example", "directx10_example\directx10_example.vcxproj", "{345A953E-A004-4648-B442-DC5F9F11068C}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sdl_opengl2_example", "sdl_opengl2_example\sdl_opengl2_example.vcxproj", "{2AE17FDE-F7F3-4CAC-ADAB-0710EDA4F741}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sdl_opengl3_example", "sdl_opengl3_example\sdl_opengl3_example.vcxproj", "{BBAEB705-1669-40F3-8567-04CF6A991F4C}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vulkan_example", "vulkan_example\vulkan_example.vcxproj", "{57E2DF5A-6FC8-45BB-99DD-91A18C646E80}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -59,6 +67,30 @@ Global {345A953E-A004-4648-B442-DC5F9F11068C}.Release|Win32.Build.0 = Release|Win32 {345A953E-A004-4648-B442-DC5F9F11068C}.Release|x64.ActiveCfg = Release|x64 {345A953E-A004-4648-B442-DC5F9F11068C}.Release|x64.Build.0 = Release|x64 + {2AE17FDE-F7F3-4CAC-ADAB-0710EDA4F741}.Debug|Win32.ActiveCfg = Debug|Win32 + {2AE17FDE-F7F3-4CAC-ADAB-0710EDA4F741}.Debug|Win32.Build.0 = Debug|Win32 + {2AE17FDE-F7F3-4CAC-ADAB-0710EDA4F741}.Debug|x64.ActiveCfg = Debug|x64 + {2AE17FDE-F7F3-4CAC-ADAB-0710EDA4F741}.Debug|x64.Build.0 = Debug|x64 + {2AE17FDE-F7F3-4CAC-ADAB-0710EDA4F741}.Release|Win32.ActiveCfg = Release|Win32 + {2AE17FDE-F7F3-4CAC-ADAB-0710EDA4F741}.Release|Win32.Build.0 = Release|Win32 + {2AE17FDE-F7F3-4CAC-ADAB-0710EDA4F741}.Release|x64.ActiveCfg = Release|x64 + {2AE17FDE-F7F3-4CAC-ADAB-0710EDA4F741}.Release|x64.Build.0 = Release|x64 + {BBAEB705-1669-40F3-8567-04CF6A991F4C}.Debug|Win32.ActiveCfg = Debug|Win32 + {BBAEB705-1669-40F3-8567-04CF6A991F4C}.Debug|Win32.Build.0 = Debug|Win32 + {BBAEB705-1669-40F3-8567-04CF6A991F4C}.Debug|x64.ActiveCfg = Debug|x64 + {BBAEB705-1669-40F3-8567-04CF6A991F4C}.Debug|x64.Build.0 = Debug|x64 + {BBAEB705-1669-40F3-8567-04CF6A991F4C}.Release|Win32.ActiveCfg = Release|Win32 + {BBAEB705-1669-40F3-8567-04CF6A991F4C}.Release|Win32.Build.0 = Release|Win32 + {BBAEB705-1669-40F3-8567-04CF6A991F4C}.Release|x64.ActiveCfg = Release|x64 + {BBAEB705-1669-40F3-8567-04CF6A991F4C}.Release|x64.Build.0 = Release|x64 + {57E2DF5A-6FC8-45BB-99DD-91A18C646E80}.Debug|Win32.ActiveCfg = Debug|Win32 + {57E2DF5A-6FC8-45BB-99DD-91A18C646E80}.Debug|Win32.Build.0 = Debug|Win32 + {57E2DF5A-6FC8-45BB-99DD-91A18C646E80}.Debug|x64.ActiveCfg = Debug|x64 + {57E2DF5A-6FC8-45BB-99DD-91A18C646E80}.Debug|x64.Build.0 = Debug|x64 + {57E2DF5A-6FC8-45BB-99DD-91A18C646E80}.Release|Win32.ActiveCfg = Release|Win32 + {57E2DF5A-6FC8-45BB-99DD-91A18C646E80}.Release|Win32.Build.0 = Release|Win32 + {57E2DF5A-6FC8-45BB-99DD-91A18C646E80}.Release|x64.ActiveCfg = Release|x64 + {57E2DF5A-6FC8-45BB-99DD-91A18C646E80}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 6d0f9244b842ad082f6f347e9634f0c3c5fa93d9 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 16 Feb 2018 23:54:57 +0100 Subject: [PATCH 011/828] Examples: Allegro5, Marmalade: Moved bindings to parent folder. Renamed Allegro stuff from *A5_ to *Allegro5_ --- examples/allegro5_example/main.cpp | 16 +++++++------- ...ui_impl_a5.cpp => imgui_impl_allegro5.cpp} | 22 +++++++++---------- .../imgui_impl_a5.h => imgui_impl_allegro5.h} | 14 ++++++------ .../imgui_impl_marmalade.cpp | 0 .../imgui_impl_marmalade.h | 0 examples/marmalade_example/main.cpp | 2 +- .../marmalade_example/marmalade_example.mkb | 6 ++--- 7 files changed, 30 insertions(+), 30 deletions(-) rename examples/{allegro5_example/imgui_impl_a5.cpp => imgui_impl_allegro5.cpp} (95%) rename examples/{allegro5_example/imgui_impl_a5.h => imgui_impl_allegro5.h} (65%) rename examples/{marmalade_example => }/imgui_impl_marmalade.cpp (100%) rename examples/{marmalade_example => }/imgui_impl_marmalade.h (100%) diff --git a/examples/allegro5_example/main.cpp b/examples/allegro5_example/main.cpp index 1478573cce9b..4a53461027e6 100644 --- a/examples/allegro5_example/main.cpp +++ b/examples/allegro5_example/main.cpp @@ -5,7 +5,7 @@ #include #include #include "imgui.h" -#include "imgui_impl_a5.h" +#include "../imgui_impl_allegro5.h" int main(int, char**) { @@ -25,7 +25,7 @@ int main(int, char**) // Setup ImGui binding ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - ImGui_ImplA5_Init(display); + ImGui_ImplAllegro5_Init(display); //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; // Enable Keyboard Controls // Setup style @@ -62,17 +62,17 @@ int main(int, char**) ALLEGRO_EVENT ev; while (al_get_next_event(queue, &ev)) { - ImGui_ImplA5_ProcessEvent(&ev); + ImGui_ImplAllegro5_ProcessEvent(&ev); if (ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE) running = false; if (ev.type == ALLEGRO_EVENT_DISPLAY_RESIZE) { - ImGui_ImplA5_InvalidateDeviceObjects(); + ImGui_ImplAllegro5_InvalidateDeviceObjects(); al_acknowledge_resize(display); - Imgui_ImplA5_CreateDeviceObjects(); + ImGui_ImplAllegro5_CreateDeviceObjects(); } } - ImGui_ImplA5_NewFrame(); + ImGui_ImplAllegro5_NewFrame(); // 1. Show a simple window. // Tip: if we don't call ImGui::Begin()/ImGui::End() the widgets automatically appears in a window called "Debug". @@ -114,12 +114,12 @@ int main(int, char**) // Rendering al_clear_to_color(al_map_rgba_f(clear_color.x, clear_color.y, clear_color.z, clear_color.w)); ImGui::Render(); - ImGui_ImplA5_RenderDrawData(ImGui::GetDrawData()); + ImGui_ImplAllegro5_RenderDrawData(ImGui::GetDrawData()); al_flip_display(); } // Cleanup - ImGui_ImplA5_Shutdown(); + ImGui_ImplAllegro5_Shutdown(); ImGui::DestroyContext(); al_destroy_event_queue(queue); al_destroy_display(display); diff --git a/examples/allegro5_example/imgui_impl_a5.cpp b/examples/imgui_impl_allegro5.cpp similarity index 95% rename from examples/allegro5_example/imgui_impl_a5.cpp rename to examples/imgui_impl_allegro5.cpp index 127bf196a0e1..228ae0cb4f70 100644 --- a/examples/allegro5_example/imgui_impl_a5.cpp +++ b/examples/imgui_impl_allegro5.cpp @@ -12,14 +12,14 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplA5_RenderDrawData() in the .h file so you can call it yourself. +// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplAllegro5_RenderDrawData() in the .h file so you can call it yourself. // 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. // 2018-02-06: Inputs: Added mapping for ImGuiKey_Space. #include // uint64_t #include // memcpy #include "imgui.h" -#include "imgui_impl_a5.h" +#include "imgui_impl_allegro5.h" #include #include @@ -43,7 +43,7 @@ struct ImDrawVertAllegro // Render function. // (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop) -void ImGui_ImplA5_RenderDrawData(ImDrawData* draw_data) +void ImGui_ImplAllegro5_RenderDrawData(ImDrawData* draw_data) { int op, src, dst; al_get_blender(&op, &src, &dst); @@ -97,7 +97,7 @@ void ImGui_ImplA5_RenderDrawData(ImDrawData* draw_data) al_set_clipping_rectangle(0, 0, al_get_display_width(g_Display), al_get_display_height(g_Display)); } -bool Imgui_ImplA5_CreateDeviceObjects() +bool ImGui_ImplAllegro5_CreateDeviceObjects() { // Build texture atlas ImGuiIO &io = ImGui::GetIO(); @@ -144,7 +144,7 @@ bool Imgui_ImplA5_CreateDeviceObjects() return true; } -void ImGui_ImplA5_InvalidateDeviceObjects() +void ImGui_ImplAllegro5_InvalidateDeviceObjects() { if (g_Texture) { @@ -159,7 +159,7 @@ void ImGui_ImplA5_InvalidateDeviceObjects() } } -bool ImGui_ImplA5_Init(ALLEGRO_DISPLAY* display) +bool ImGui_ImplAllegro5_Init(ALLEGRO_DISPLAY* display) { g_Display = display; @@ -205,16 +205,16 @@ bool ImGui_ImplA5_Init(ALLEGRO_DISPLAY* display) return true; } -void ImGui_ImplA5_Shutdown() +void ImGui_ImplAllegro5_Shutdown() { - ImGui_ImplA5_InvalidateDeviceObjects(); + ImGui_ImplAllegro5_InvalidateDeviceObjects(); } // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. -bool ImGui_ImplA5_ProcessEvent(ALLEGRO_EVENT *ev) +bool ImGui_ImplAllegro5_ProcessEvent(ALLEGRO_EVENT *ev) { ImGuiIO &io = ImGui::GetIO(); @@ -238,10 +238,10 @@ bool ImGui_ImplA5_ProcessEvent(ALLEGRO_EVENT *ev) return false; } -void ImGui_ImplA5_NewFrame() +void ImGui_ImplAllegro5_NewFrame() { if (!g_Texture) - Imgui_ImplA5_CreateDeviceObjects(); + ImGui_ImplAllegro5_CreateDeviceObjects(); ImGuiIO &io = ImGui::GetIO(); diff --git a/examples/allegro5_example/imgui_impl_a5.h b/examples/imgui_impl_allegro5.h similarity index 65% rename from examples/allegro5_example/imgui_impl_a5.h rename to examples/imgui_impl_allegro5.h index ccc3ac45f102..4b75dffa6003 100644 --- a/examples/allegro5_example/imgui_impl_a5.h +++ b/examples/imgui_impl_allegro5.h @@ -15,12 +15,12 @@ struct ALLEGRO_DISPLAY; union ALLEGRO_EVENT; -IMGUI_API bool ImGui_ImplA5_Init(ALLEGRO_DISPLAY* display); -IMGUI_API void ImGui_ImplA5_Shutdown(); -IMGUI_API void ImGui_ImplA5_NewFrame(); -IMGUI_API void ImGui_ImplA5_RenderDrawData(ImDrawData* draw_data); -IMGUI_API bool ImGui_ImplA5_ProcessEvent(ALLEGRO_EVENT* event); +IMGUI_API bool ImGui_ImplAllegro5_Init(ALLEGRO_DISPLAY* display); +IMGUI_API void ImGui_ImplAllegro5_Shutdown(); +IMGUI_API void ImGui_ImplAllegro5_NewFrame(); +IMGUI_API void ImGui_ImplAllegro5_RenderDrawData(ImDrawData* draw_data); +IMGUI_API bool ImGui_ImplAllegro5_ProcessEvent(ALLEGRO_EVENT* event); // Use if you want to reset your rendering device without losing ImGui state. -IMGUI_API bool Imgui_ImplA5_CreateDeviceObjects(); -IMGUI_API void ImGui_ImplA5_InvalidateDeviceObjects(); +IMGUI_API bool ImGui_ImplAllegro5_CreateDeviceObjects(); +IMGUI_API void ImGui_ImplAllegro5_InvalidateDeviceObjects(); diff --git a/examples/marmalade_example/imgui_impl_marmalade.cpp b/examples/imgui_impl_marmalade.cpp similarity index 100% rename from examples/marmalade_example/imgui_impl_marmalade.cpp rename to examples/imgui_impl_marmalade.cpp diff --git a/examples/marmalade_example/imgui_impl_marmalade.h b/examples/imgui_impl_marmalade.h similarity index 100% rename from examples/marmalade_example/imgui_impl_marmalade.h rename to examples/imgui_impl_marmalade.h diff --git a/examples/marmalade_example/main.cpp b/examples/marmalade_example/main.cpp index f2ddae404829..08f8b1d5ce16 100644 --- a/examples/marmalade_example/main.cpp +++ b/examples/marmalade_example/main.cpp @@ -5,7 +5,7 @@ // This file is part of ImGui #include "imgui.h" -#include "imgui_impl_marmalade.h" +#include "../imgui_impl_marmalade.h" #include #include diff --git a/examples/marmalade_example/marmalade_example.mkb b/examples/marmalade_example/marmalade_example.mkb index 9f8ea44ae8ba..11b0392f9701 100644 --- a/examples/marmalade_example/marmalade_example.mkb +++ b/examples/marmalade_example/marmalade_example.mkb @@ -34,11 +34,11 @@ files ../../imgui_draw.cpp ../../imconfig.h ../../imgui.h - ../../imgui_internal.h + ../../imgui_internal.h ["imgui","Marmalade binding"] - imgui_impl_marmalade.h - imgui_impl_marmalade.cpp + ../imgui_impl_marmalade.h + ../imgui_impl_marmalade.cpp main.cpp } From 45cbebad64be1d48bb9218203542924b6812adae Mon Sep 17 00:00:00 2001 From: omar Date: Sun, 18 Feb 2018 21:04:17 +0100 Subject: [PATCH 012/828] Added dummy io.DisplayPos field. --- imgui.cpp | 2 +- imgui.h | 3 ++- imgui_demo.cpp | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index a2a7826dc866..f23e6f949199 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11024,7 +11024,7 @@ bool ImGui::MenuItem(const char* label, const char* shortcut, bool* p_selected, bool ImGui::BeginMainMenuBar() { ImGuiContext& g = *GImGui; - SetNextWindowPos(ImVec2(0.0f, 0.0f)); + SetNextWindowPos(g.IO.DisplayPos); SetNextWindowSize(ImVec2(g.IO.DisplaySize.x, g.FontBaseSize + g.Style.FramePadding.y * 2.0f)); PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0,0)); diff --git a/imgui.h b/imgui.h index f075d72d3f5c..bd96d8c90cbc 100644 --- a/imgui.h +++ b/imgui.h @@ -1024,6 +1024,7 @@ struct ImGuiIO // Output - Retrieve after calling NewFrame() //------------------------------------------------------------------ + ImVec2 DisplayPos; // Always ImVec2(0,0) for now. (In upcoming multiple viewports branch, this will be repositioned by API on a per-viewport basis). The display area goes from DisplayPos to DisplayPos+DisplaySize. bool WantCaptureMouse; // When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application. This is set by ImGui when it wants to use your mouse (e.g. unclicked mouse is hovering a window, or a widget is active). bool WantCaptureKeyboard; // When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. This is set by ImGui when it wants to use your keyboard inputs. bool WantTextInput; // Mobile/console: when io.WantTextInput is true, you may display an on-screen keyboard. This is set by ImGui when it wants textual keyboard input to happen (e.g. when a InputText widget is active). @@ -1080,7 +1081,7 @@ namespace ImGui bool Begin(const char* name, bool* p_open, const ImVec2& size_on_first_use, float bg_alpha_override = -1.0f, ImGuiWindowFlags flags = 0); // Use SetNextWindowSize(size, ImGuiCond_FirstUseEver) + SetNextWindowBgAlpha() instead. static inline bool IsRootWindowOrAnyChildHovered() { return IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows); } static inline void AlignFirstTextHeightToWidgets() { AlignTextToFramePadding(); } - static inline void SetNextWindowPosCenter(ImGuiCond c=0) { ImGuiIO& io = GetIO(); SetNextWindowPos(ImVec2(io.DisplaySize.x * 0.5f, io.DisplaySize.y * 0.5f), c, ImVec2(0.5f, 0.5f)); } + static inline void SetNextWindowPosCenter(ImGuiCond c=0) { ImGuiIO& io = GetIO(); SetNextWindowPos(ImVec2(io.DisplayPos.x + io.DisplaySize.x * 0.5f, io.DisplayPos.y + io.DisplaySize.y * 0.5f), c, ImVec2(0.5f, 0.5f)); } // OBSOLETED in 1.51 (between Jun 2017 and Aug 2017) static inline bool IsItemHoveredRect() { return IsItemHovered(ImGuiHoveredFlags_RectOnly); } static inline bool IsPosHoveringAnyWindow(const ImVec2&) { IM_ASSERT(0); return false; } // This was misleading and partly broken. You probably want to use the ImGui::GetIO().WantCaptureMouse flag instead. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 2a1972781397..00cd299dcaac 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2429,7 +2429,8 @@ static void ShowExampleAppFixedOverlay(bool* p_open) { const float DISTANCE = 10.0f; static int corner = 0; - ImVec2 window_pos = ImVec2((corner & 1) ? ImGui::GetIO().DisplaySize.x - DISTANCE : DISTANCE, (corner & 2) ? ImGui::GetIO().DisplaySize.y - DISTANCE : DISTANCE); + ImGuiIO& io = ImGui::GetIO(); + ImVec2 window_pos = ImVec2(io.DisplayPos.x + ((corner & 1) ? io.DisplaySize.x - DISTANCE : DISTANCE), io.DisplayPos.y + ((corner & 2) ? io.DisplaySize.y - DISTANCE : DISTANCE)); ImVec2 window_pos_pivot = ImVec2((corner & 1) ? 1.0f : 0.0f, (corner & 2) ? 1.0f : 0.0f); ImGui::SetNextWindowPos(window_pos, ImGuiCond_Always, window_pos_pivot); ImGui::SetNextWindowBgAlpha(0.3f); // Transparent background From 3bd3693fb799e1d6000663072cfea1bc82627ae0 Mon Sep 17 00:00:00 2001 From: omar Date: Sun, 18 Feb 2018 21:09:28 +0100 Subject: [PATCH 013/828] Examples: DirectX10, DirectX11, OpenGL2, OpenGL3: Added support for io.DisplayPos. --- examples/imgui_impl_dx10.cpp | 24 ++++++++++++++++-------- examples/imgui_impl_dx11.cpp | 24 ++++++++++++++++-------- examples/imgui_impl_opengl2.cpp | 21 +++++++++++++++++---- examples/imgui_impl_opengl3.cpp | 31 ++++++++++++++++++++++++------- 4 files changed, 73 insertions(+), 27 deletions(-) diff --git a/examples/imgui_impl_dx10.cpp b/examples/imgui_impl_dx10.cpp index 8cf6c8fd32c7..8896e7d7892c 100644 --- a/examples/imgui_impl_dx10.cpp +++ b/examples/imgui_impl_dx10.cpp @@ -10,6 +10,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2018-XX-XX: Draw: Offset projection matrix and clipping rectangle by io.DisplayPos (which will be non-zero for multi-viewport applications). // 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplDX10_RenderDrawData() in the .h file so you can call it yourself. // 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. // 2018-02-06: Inputs: Added mapping for ImGuiKey_Space. @@ -105,15 +106,17 @@ void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data) g_pIB->Unmap(); // Setup orthographic projection matrix into our constant buffer + // Our visible imgui space lies from io.DisplayPos (top left) to io.DisplayPos+io.DisplaySize (bottom right). io.DisplayPos is typically (0,0) for single viewport applications. + ImGuiIO& io = ImGui::GetIO(); { void* mapped_resource; if (g_pVertexConstantBuffer->Map(D3D10_MAP_WRITE_DISCARD, 0, &mapped_resource) != S_OK) return; VERTEX_CONSTANT_BUFFER* constant_buffer = (VERTEX_CONSTANT_BUFFER*)mapped_resource; - float L = 0.0f; - float R = ImGui::GetIO().DisplaySize.x; - float B = ImGui::GetIO().DisplaySize.y; - float T = 0.0f; + float L = io.DisplayPos.x; + float R = io.DisplayPos.x + io.DisplaySize.x; + float T = io.DisplayPos.y; + float B = io.DisplayPos.y + io.DisplaySize.y; float mvp[4][4] = { { 2.0f/(R-L), 0.0f, 0.0f, 0.0f }, @@ -167,8 +170,8 @@ void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data) // Setup viewport D3D10_VIEWPORT vp; memset(&vp, 0, sizeof(D3D10_VIEWPORT)); - vp.Width = (UINT)ImGui::GetIO().DisplaySize.x; - vp.Height = (UINT)ImGui::GetIO().DisplaySize.y; + vp.Width = (UINT)io.DisplaySize.x; + vp.Height = (UINT)io.DisplaySize.y; vp.MinDepth = 0.0f; vp.MaxDepth = 1.0f; vp.TopLeftX = vp.TopLeftY = 0; @@ -203,13 +206,18 @@ void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data) const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; if (pcmd->UserCallback) { + // User callback (registered via ImDrawList::AddCallback) pcmd->UserCallback(cmd_list, pcmd); } else { - const D3D10_RECT r = { (LONG)pcmd->ClipRect.x, (LONG)pcmd->ClipRect.y, (LONG)pcmd->ClipRect.z, (LONG)pcmd->ClipRect.w }; + // Apply scissor/clipping rectangle + const ImVec4 clip_rect = ImVec4(pcmd->ClipRect.x - io.DisplayPos.x, pcmd->ClipRect.y - io.DisplayPos.y, pcmd->ClipRect.z - io.DisplayPos.x, pcmd->ClipRect.w - io.DisplayPos.y); + const D3D10_RECT clip_rect_dx = { (LONG)clip_rect.x, (LONG)clip_rect.y, (LONG)clip_rect.z, (LONG)clip_rect.w }; + ctx->RSSetScissorRects(1, &clip_rect_dx); + + // Bind texture, Draw ctx->PSSetShaderResources(0, 1, (ID3D10ShaderResourceView**)&pcmd->TextureId); - ctx->RSSetScissorRects(1, &r); ctx->DrawIndexed(pcmd->ElemCount, idx_offset, vtx_offset); } idx_offset += pcmd->ElemCount; diff --git a/examples/imgui_impl_dx11.cpp b/examples/imgui_impl_dx11.cpp index b2d67911db91..5b8326a880b1 100644 --- a/examples/imgui_impl_dx11.cpp +++ b/examples/imgui_impl_dx11.cpp @@ -10,6 +10,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2018-XX-XX: Draw: Offset projection matrix and clipping rectangle by io.DisplayPos (which will be non-zero for multi-viewport applications). // 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplDX11_RenderDrawData() in the .h file so you can call it yourself. // 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. // 2018-02-06: Inputs: Added mapping for ImGuiKey_Space. @@ -107,15 +108,17 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) ctx->Unmap(g_pIB, 0); // Setup orthographic projection matrix into our constant buffer + // Our visible imgui space lies from io.DisplayPos (top left) to io.DisplayPos+io.DisplaySize (bottom right). io.DisplayPos is typically (0,0) for single viewport applications. + ImGuiIO& io = ImGui::GetIO(); { D3D11_MAPPED_SUBRESOURCE mapped_resource; if (ctx->Map(g_pVertexConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped_resource) != S_OK) return; VERTEX_CONSTANT_BUFFER* constant_buffer = (VERTEX_CONSTANT_BUFFER*)mapped_resource.pData; - float L = 0.0f; - float R = ImGui::GetIO().DisplaySize.x; - float B = ImGui::GetIO().DisplaySize.y; - float T = 0.0f; + float L = io.DisplayPos.x; + float R = io.DisplayPos.x + io.DisplaySize.x; + float T = io.DisplayPos.y; + float B = io.DisplayPos.y + io.DisplaySize.y; float mvp[4][4] = { { 2.0f/(R-L), 0.0f, 0.0f, 0.0f }, @@ -172,8 +175,8 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) // Setup viewport D3D11_VIEWPORT vp; memset(&vp, 0, sizeof(D3D11_VIEWPORT)); - vp.Width = ImGui::GetIO().DisplaySize.x; - vp.Height = ImGui::GetIO().DisplaySize.y; + vp.Width = io.DisplaySize.x; + vp.Height = io.DisplaySize.y; vp.MinDepth = 0.0f; vp.MaxDepth = 1.0f; vp.TopLeftX = vp.TopLeftY = 0; @@ -208,13 +211,18 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; if (pcmd->UserCallback) { + // User callback (registered via ImDrawList::AddCallback) pcmd->UserCallback(cmd_list, pcmd); } else { - const D3D11_RECT r = { (LONG)pcmd->ClipRect.x, (LONG)pcmd->ClipRect.y, (LONG)pcmd->ClipRect.z, (LONG)pcmd->ClipRect.w }; + // Apply scissor/clipping rectangle + const ImVec4 clip_rect = ImVec4(pcmd->ClipRect.x - io.DisplayPos.x, pcmd->ClipRect.y - io.DisplayPos.y, pcmd->ClipRect.z - io.DisplayPos.x, pcmd->ClipRect.w - io.DisplayPos.y); + const D3D11_RECT clip_rect_dx = { (LONG)clip_rect.x, (LONG)clip_rect.y, (LONG)clip_rect.z, (LONG)clip_rect.w }; + ctx->RSSetScissorRects(1, &clip_rect_dx); + + // Bind texture, Draw ctx->PSSetShaderResources(0, 1, (ID3D11ShaderResourceView**)&pcmd->TextureId); - ctx->RSSetScissorRects(1, &r); ctx->DrawIndexed(pcmd->ElemCount, idx_offset, vtx_offset); } idx_offset += pcmd->ElemCount; diff --git a/examples/imgui_impl_opengl2.cpp b/examples/imgui_impl_opengl2.cpp index 8810b094d5a5..cd1557d5fe39 100644 --- a/examples/imgui_impl_opengl2.cpp +++ b/examples/imgui_impl_opengl2.cpp @@ -6,6 +6,10 @@ // confuse your GPU driver. // The GL2 code is unable to reset attributes or even call e.g. "glUseProgram(0)" because they don't exist in that API. +// CHANGELOG +// (minor and older changes stripped away, please see git history for details) +// 2018-XX-XX: Draw: Offset projection matrix and clipping rectangle by io.DisplayPos (which will be non-zero for multi-viewport applications). + #include "imgui.h" #include "imgui_impl_opengl2.h" @@ -75,11 +79,12 @@ void ImGui_ImplOpenGL2_RenderDrawData(ImDrawData* draw_data) //glUseProgram(0); // You may want this if using this code in an OpenGL 3+ context where shaders may be bound // Setup viewport, orthographic projection matrix + // Our visible imgui space lies from io.DisplayPos (top left) to io.DisplayPos+io.DisplaySize (bottom right). io.DisplayPos is typically (0,0) for single viewport applications. glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); - glOrtho(0.0f, io.DisplaySize.x, io.DisplaySize.y, 0.0f, -1.0f, +1.0f); + glOrtho(io.DisplayPos.x, io.DisplayPos.x + io.DisplaySize.x, io.DisplayPos.y + io.DisplaySize.y, io.DisplayPos.y, -1.0f, +1.0f); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); @@ -99,13 +104,21 @@ void ImGui_ImplOpenGL2_RenderDrawData(ImDrawData* draw_data) const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; if (pcmd->UserCallback) { + // User callback (registered via ImDrawList::AddCallback) pcmd->UserCallback(cmd_list, pcmd); } else { - glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId); - glScissor((int)pcmd->ClipRect.x, (int)(fb_height - pcmd->ClipRect.w), (int)(pcmd->ClipRect.z - pcmd->ClipRect.x), (int)(pcmd->ClipRect.w - pcmd->ClipRect.y)); - glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer); + ImVec4 clip_rect = ImVec4(pcmd->ClipRect.x - io.DisplayPos.x, pcmd->ClipRect.y - io.DisplayPos.y, pcmd->ClipRect.z - io.DisplayPos.x, pcmd->ClipRect.w - io.DisplayPos.y); + if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f) + { + // Apply scissor/clipping rectangle + glScissor((int)clip_rect.x, (int)(fb_height - clip_rect.w), (int)(clip_rect.z - clip_rect.x), (int)(clip_rect.w - clip_rect.y)); + + // Bind texture, Draw + glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId); + glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer); + } } idx_buffer += pcmd->ElemCount; } diff --git a/examples/imgui_impl_opengl3.cpp b/examples/imgui_impl_opengl3.cpp index 241dc3826e7a..65dff4273778 100644 --- a/examples/imgui_impl_opengl3.cpp +++ b/examples/imgui_impl_opengl3.cpp @@ -1,3 +1,7 @@ +// CHANGELOG +// (minor and older changes stripped away, please see git history for details) +// 2018-XX-XX: Draw: Offset projection matrix and clipping rectangle by io.DisplayPos (which will be non-zero for multi-viewport applications). + #include "imgui.h" #include "imgui_impl_opengl3.h" #include // This example is using gl3w to access OpenGL functions (because it is small). You may use glew/glad/glLoadGen/etc. whatever already works for you. @@ -72,13 +76,18 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); // Setup viewport, orthographic projection matrix + // Our visible imgui space lies from io.DisplayPos (top left) to io.DisplayPos+io.DisplaySize (bottom right). io.DisplayPos is typically (0,0) for single viewport applications. glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height); + float L = io.DisplayPos.x; + float R = io.DisplayPos.x + io.DisplaySize.x; + float T = io.DisplayPos.y; + float B = io.DisplayPos.y + io.DisplaySize.y; const float ortho_projection[4][4] = { - { 2.0f/io.DisplaySize.x, 0.0f, 0.0f, 0.0f }, - { 0.0f, 2.0f/-io.DisplaySize.y, 0.0f, 0.0f }, - { 0.0f, 0.0f, -1.0f, 0.0f }, - { -1.0f, 1.0f, 0.0f, 1.0f }, + { 2.0f/(R-L), 0.0f, 0.0f, 0.0f }, + { 0.0f, 2.0f/(T-B), 0.0f, 0.0f }, + { 0.0f, 0.0f, -1.0f, 0.0f }, + { (R+L)/(L-R), (T+B)/(B-T), 0.0f, 1.0f }, }; glUseProgram(g_ShaderHandle); glUniform1i(g_AttribLocationTex, 0); @@ -102,13 +111,21 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; if (pcmd->UserCallback) { + // User callback (registered via ImDrawList::AddCallback) pcmd->UserCallback(cmd_list, pcmd); } else { - glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId); - glScissor((int)pcmd->ClipRect.x, (int)(fb_height - pcmd->ClipRect.w), (int)(pcmd->ClipRect.z - pcmd->ClipRect.x), (int)(pcmd->ClipRect.w - pcmd->ClipRect.y)); - glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset); + ImVec4 clip_rect = ImVec4(pcmd->ClipRect.x - io.DisplayPos.x, pcmd->ClipRect.y - io.DisplayPos.y, pcmd->ClipRect.z - io.DisplayPos.x, pcmd->ClipRect.w - io.DisplayPos.y); + if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f) + { + // Apply scissor/clipping rectangle + glScissor((int)clip_rect.x, (int)(fb_height - clip_rect.w), (int)(clip_rect.z - clip_rect.x), (int)(clip_rect.w - clip_rect.y)); + + // Bind texture, Draw + glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId); + glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset); + } } idx_buffer_offset += pcmd->ElemCount; } From 230c5ca735fa34dd82978a60d17f6bfeb39c7176 Mon Sep 17 00:00:00 2001 From: omar Date: Sun, 18 Feb 2018 21:15:41 +0100 Subject: [PATCH 014/828] Examples: Vulkan: Fixed GLFW calls. --- examples/vulkan_example/main.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/examples/vulkan_example/main.cpp b/examples/vulkan_example/main.cpp index 3a10afd2fe9b..37acdea2548f 100644 --- a/examples/vulkan_example/main.cpp +++ b/examples/vulkan_example/main.cpp @@ -684,6 +684,7 @@ int main(int, char**) // This is also the reason why frame_end() is split into frame_end() and frame_present(), the later one not being called here. #ifdef IMGUI_UNLIMITED_FRAME_RATE ImGui_ImplVulkan_NewFrame(); + ImGui_ImplGlfw_NewFrame(); frame_begin(); ImGui_ImplVulkan_Render(g_CommandBuffer[g_FrameIndex]); frame_end(); @@ -699,6 +700,7 @@ int main(int, char**) // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. glfwPollEvents(); ImGui_ImplVulkan_NewFrame(); + ImGui_ImplGlfw_NewFrame(); // 1. Show a simple window. // Tip: if we don't call ImGui::Begin()/ImGui::End() the widgets automatically appears in a window called "Debug". @@ -748,6 +750,7 @@ int main(int, char**) VkResult err = vkDeviceWaitIdle(g_Device); check_vk_result(err); ImGui_ImplVulkan_Shutdown(); + ImGui_ImplGlfw_Shutdown(); ImGui::DestroyContext(); cleanup_vulkan(); glfwTerminate(); From df9051ded2dc579943844a29a5c5267258289f9d Mon Sep 17 00:00:00 2001 From: omar Date: Sun, 18 Feb 2018 21:15:51 +0100 Subject: [PATCH 015/828] Examples: Vulkan: Added support for io.DisplayPos. --- examples/imgui_impl_vulkan.cpp | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/examples/imgui_impl_vulkan.cpp b/examples/imgui_impl_vulkan.cpp index 1162920fa4ac..db691a8db766 100644 --- a/examples/imgui_impl_vulkan.cpp +++ b/examples/imgui_impl_vulkan.cpp @@ -10,6 +10,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2018-XX-XX: Draw: Offset projection matrix and clipping rectangle by io.DisplayPos (which will be non-zero for multi-viewport applications). // 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback, ImGui_ImplVulkan_Render() calls ImGui_ImplVulkan_RenderDrawData() itself. // 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. // 2018-02-06: Inputs: Added mapping for ImGuiKey_Space. @@ -266,21 +267,22 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data) VkViewport viewport; viewport.x = 0; viewport.y = 0; - viewport.width = ImGui::GetIO().DisplaySize.x; - viewport.height = ImGui::GetIO().DisplaySize.y; + viewport.width = io.DisplaySize.x; + viewport.height = io.DisplaySize.y; viewport.minDepth = 0.0f; viewport.maxDepth = 1.0f; vkCmdSetViewport(g_CommandBuffer, 0, 1, &viewport); } // Setup scale and translation: + // (Our visible imgui space lies from io.DisplayPos (top left) to io.DisplayPos+io.DisplaySize (bottom right). io.DisplayPos is typically (0,0) for single viewport applications.) { float scale[2]; - scale[0] = 2.0f/io.DisplaySize.x; - scale[1] = 2.0f/io.DisplaySize.y; + scale[0] = 2.0f / io.DisplaySize.x; + scale[1] = 2.0f / io.DisplaySize.y; float translate[2]; - translate[0] = -1.0f; - translate[1] = -1.0f; + translate[0] = -1.0f - io.DisplayPos.x * scale[0]; + translate[1] = -1.0f - io.DisplayPos.y * scale[1]; vkCmdPushConstants(g_CommandBuffer, g_PipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, sizeof(float) * 0, sizeof(float) * 2, scale); vkCmdPushConstants(g_CommandBuffer, g_PipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, sizeof(float) * 2, sizeof(float) * 2, translate); } @@ -300,12 +302,16 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data) } else { + // Apply scissor/clipping rectangle + // FIXME: We could clamp width/height based on clamped min/max values. VkRect2D scissor; - scissor.offset.x = (int32_t)(pcmd->ClipRect.x) > 0 ? (int32_t)(pcmd->ClipRect.x) : 0; - scissor.offset.y = (int32_t)(pcmd->ClipRect.y) > 0 ? (int32_t)(pcmd->ClipRect.y) : 0; + scissor.offset.x = (int32_t)(pcmd->ClipRect.x - io.DisplayPos.x) > 0 ? (int32_t)(pcmd->ClipRect.x - io.DisplayPos.y) : 0; + scissor.offset.y = (int32_t)(pcmd->ClipRect.y - io.DisplayPos.y) > 0 ? (int32_t)(pcmd->ClipRect.y - io.DisplayPos.y) : 0; scissor.extent.width = (uint32_t)(pcmd->ClipRect.z - pcmd->ClipRect.x); scissor.extent.height = (uint32_t)(pcmd->ClipRect.w - pcmd->ClipRect.y + 1); // FIXME: Why +1 here? vkCmdSetScissor(g_CommandBuffer, 0, 1, &scissor); + + // Draw vkCmdDrawIndexed(g_CommandBuffer, pcmd->ElemCount, 1, idx_offset, vtx_offset, 0); } idx_offset += pcmd->ElemCount; @@ -420,6 +426,7 @@ bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer) ImGui_ImplVulkan_VkResult(err); vkUnmapMemory(g_Device, g_UploadBufferMemory); } + // Copy to Image: { VkImageMemoryBarrier copy_barrier[1] = {}; From 9fdf72e42be0b2d22b4ccd0a78e2a54793278b85 Mon Sep 17 00:00:00 2001 From: omar Date: Sun, 18 Feb 2018 21:23:11 +0100 Subject: [PATCH 016/828] Examples: WIn32: Prefixing every Win32 function calls with :: to denote global namespace in a consistent manner. --- examples/imgui_impl_win32.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index 3547b98ce16c..1737b7cc9493 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -11,9 +11,9 @@ static INT64 g_TicksPerSecond = 0; // Functions bool ImGui_ImplWin32_Init(void* hwnd) { - if (!QueryPerformanceFrequency((LARGE_INTEGER *)&g_TicksPerSecond)) + if (!::QueryPerformanceFrequency((LARGE_INTEGER *)&g_TicksPerSecond)) return false; - if (!QueryPerformanceCounter((LARGE_INTEGER *)&g_Time)) + if (!::QueryPerformanceCounter((LARGE_INTEGER *)&g_Time)) return false; g_hWnd = (HWND)hwnd; @@ -55,19 +55,19 @@ void ImGui_ImplWin32_NewFrame() // Setup display size (every frame to accommodate for window resizing) RECT rect; - GetClientRect(g_hWnd, &rect); + ::GetClientRect(g_hWnd, &rect); io.DisplaySize = ImVec2((float)(rect.right - rect.left), (float)(rect.bottom - rect.top)); // Setup time step INT64 current_time; - QueryPerformanceCounter((LARGE_INTEGER *)¤t_time); + ::QueryPerformanceCounter((LARGE_INTEGER *)¤t_time); io.DeltaTime = (float)(current_time - g_Time) / g_TicksPerSecond; g_Time = current_time; // Read keyboard modifiers inputs - io.KeyCtrl = (GetKeyState(VK_CONTROL) & 0x8000) != 0; - io.KeyShift = (GetKeyState(VK_SHIFT) & 0x8000) != 0; - io.KeyAlt = (GetKeyState(VK_MENU) & 0x8000) != 0; + io.KeyCtrl = (::GetKeyState(VK_CONTROL) & 0x8000) != 0; + io.KeyShift = (::GetKeyState(VK_SHIFT) & 0x8000) != 0; + io.KeyAlt = (::GetKeyState(VK_MENU) & 0x8000) != 0; io.KeySuper = false; // io.KeysDown : filled by WM_KEYDOWN/WM_KEYUP events // io.MousePos : filled by WM_MOUSEMOVE events @@ -78,13 +78,13 @@ void ImGui_ImplWin32_NewFrame() if (io.WantMoveMouse) { POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y }; - ClientToScreen(g_hWnd, &pos); - SetCursorPos(pos.x, pos.y); + ::ClientToScreen(g_hWnd, &pos); + ::SetCursorPos(pos.x, pos.y); } // Hide OS mouse cursor if ImGui is drawing it if (io.MouseDrawCursor) - SetCursor(NULL); + ::SetCursor(NULL); // Start the frame. This call will update the io.WantCaptureMouse, io.WantCaptureKeyboard flag that you can use to dispatch inputs (or not) to your application. ImGui::NewFrame(); From e660d92fa5a30a0478457d3f0f1042b7fb2c5072 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 19 Feb 2018 22:29:52 +0100 Subject: [PATCH 017/828] Examples: GLFW: Added mouse cursors support (#1495) --- examples/imgui_impl_glfw.cpp | 26 +++++++++++++++++++++++++- examples/imgui_impl_sdl2.cpp | 3 +++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index ab423ff3f4e9..578752c6729a 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -13,6 +13,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2018-XX-XX: Inputs: Added support for mouse cursors, honoring ImGui::GetMouseCursor() value. // 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplGlfwGL3_RenderDrawData() in the .h file so you can call it yourself. // 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. // 2018-02-06: Inputs: Added mapping for ImGuiKey_Space. @@ -43,6 +44,7 @@ static GLFWwindow* g_Window = NULL; static double g_Time = 0.0f; static bool g_MouseJustPressed[3] = { false, false, false }; +static GLFWcursor* g_MouseCursors[ImGuiMouseCursor_Count_] = { 0 }; static const char* ImGui_ImplGlfwGL3_GetClipboardText(void* user_data) { @@ -123,6 +125,14 @@ bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks) io.ImeWindowHandle = glfwGetWin32Window(g_Window); #endif + g_MouseCursors[ImGuiMouseCursor_Arrow] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); + g_MouseCursors[ImGuiMouseCursor_TextInput] = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR); + g_MouseCursors[ImGuiMouseCursor_ResizeAll] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); // FIXME: GLFW doesn't have this. + g_MouseCursors[ImGuiMouseCursor_ResizeNS] = glfwCreateStandardCursor(GLFW_VRESIZE_CURSOR); + g_MouseCursors[ImGuiMouseCursor_ResizeEW] = glfwCreateStandardCursor(GLFW_HRESIZE_CURSOR); + g_MouseCursors[ImGuiMouseCursor_ResizeNESW] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); // FIXME: GLFW doesn't have this. + g_MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); // FIXME: GLFW doesn't have this. + if (install_callbacks) { glfwSetMouseButtonCallback(window, ImGui_ImplGlfw_MouseButtonCallback); @@ -136,6 +146,11 @@ bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks) void ImGui_ImplGlfw_Shutdown() { + for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_Count_; cursor_n++) + { + glfwDestroyCursor(g_MouseCursors[cursor_n]); + g_MouseCursors[cursor_n] = NULL; + } } void ImGui_ImplGlfw_NewFrame() @@ -184,7 +199,16 @@ void ImGui_ImplGlfw_NewFrame() } // Hide OS mouse cursor if ImGui is drawing it - glfwSetInputMode(g_Window, GLFW_CURSOR, io.MouseDrawCursor ? GLFW_CURSOR_HIDDEN : GLFW_CURSOR_NORMAL); + ImGuiMouseCursor cursor = ImGui::GetMouseCursor(); + if (io.MouseDrawCursor || cursor == ImGuiMouseCursor_None) + { + glfwSetInputMode(g_Window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); + } + else + { + glfwSetInputMode(g_Window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + glfwSetCursor(g_Window, g_MouseCursors[cursor]); + } // Gamepad navigation mapping [BETA] memset(io.NavInputs, 0, sizeof(io.NavInputs)); diff --git a/examples/imgui_impl_sdl2.cpp b/examples/imgui_impl_sdl2.cpp index 6b39c3f8da0b..c302d1038841 100644 --- a/examples/imgui_impl_sdl2.cpp +++ b/examples/imgui_impl_sdl2.cpp @@ -150,7 +150,10 @@ bool ImGui_ImplSDL2_Init(SDL_Window* window) void ImGui_ImplSDL2_Shutdown() { for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_Count_; cursor_n++) + { SDL_FreeCursor(g_MouseCursors[cursor_n]); + g_MouseCursors[cursor_n] = NULL; + } } void ImGui_ImplSDL2_NewFrame(SDL_Window* window) From 387f724d33ea186a6846bd7c2ce15af111762aa0 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 21 Feb 2018 22:39:49 +0100 Subject: [PATCH 018/828] Examples: Vulkan: Formatting and tweaks (to match SDL's main). --- examples/vulkan_example/main.cpp | 54 ++++++++++++++++---------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/examples/vulkan_example/main.cpp b/examples/vulkan_example/main.cpp index 37acdea2548f..24135e2699cb 100644 --- a/examples/vulkan_example/main.cpp +++ b/examples/vulkan_example/main.cpp @@ -1,4 +1,4 @@ -// ImGui - standalone example application for Glfw + Vulkan, using programmable pipeline +// ImGui - standalone example application for Glfw + Vulkan // If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp. #include "imgui.h" @@ -10,6 +10,7 @@ #define GLFW_INCLUDE_NONE #define GLFW_INCLUDE_VULKAN #include +#include #define IMGUI_MAX_POSSIBLE_BACK_BUFFERS 16 #define IMGUI_UNLIMITED_FRAME_RATE @@ -29,7 +30,7 @@ static VkQueue g_Queue = VK_NULL_HANDLE; static VkDebugReportCallbackEXT g_Debug_Report = VK_NULL_HANDLE; static VkSurfaceFormatKHR g_SurfaceFormat; -static VkImageSubresourceRange g_ImageRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; +static VkImageSubresourceRange g_ImageRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; static VkPresentModeKHR g_PresentMode; static VkPipelineCache g_PipelineCache = VK_NULL_HANDLE; @@ -59,7 +60,7 @@ static void check_vk_result(VkResult err) abort(); } -static void resize_vulkan(GLFWwindow* /*window*/, int w, int h) +static void resize_vulkan(GLFWwindow*, int w, int h) { VkResult err; VkSwapchainKHR old_swapchain = g_Swapchain; @@ -194,37 +195,34 @@ static void resize_vulkan(GLFWwindow* /*window*/, int w, int h) static VKAPI_ATTR VkBool32 VKAPI_CALL debug_report( VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char* pLayerPrefix, const char* pMessage, void* pUserData) { - printf("[vulkan] ObjectType: %i\nMessage: %s\n\n", objectType, pMessage ); + printf("[vulkan] ObjectType: %i\nMessage: %s\n\n", objectType, pMessage); return VK_FALSE; } #endif // IMGUI_VULKAN_DEBUG_REPORT -static void setup_vulkan(GLFWwindow* window) +static void setup_vulkan(GLFWwindow* window, const char** extensions, uint32_t extensions_count) { VkResult err; // Create Vulkan Instance { - uint32_t extensions_count; - const char** glfw_extensions = glfwGetRequiredInstanceExtensions(&extensions_count); - VkInstanceCreateInfo create_info = {}; create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; create_info.enabledExtensionCount = extensions_count; - create_info.ppEnabledExtensionNames = glfw_extensions; + create_info.ppEnabledExtensionNames = extensions; #ifdef IMGUI_VULKAN_DEBUG_REPORT // enabling multiple validation layers grouped as lunarg standard validation - const char* layers[] = {"VK_LAYER_LUNARG_standard_validation"}; + const char* layers[] = { "VK_LAYER_LUNARG_standard_validation" }; create_info.enabledLayerCount = 1; create_info.ppEnabledLayerNames = layers; // need additional storage for char pointer to debug report extension const char** extensions = (const char**)malloc(sizeof(const char*) * (extensions_count + 1)); for (size_t i = 0; i < extensions_count; i++) - extensions[i] = glfw_extensions[i]; - extensions[ extensions_count ] = "VK_EXT_debug_report"; - create_info.enabledExtensionCount = extensions_count+1; + extensions[i] = extensions[i]; + extensions[extensions_count] = "VK_EXT_debug_report"; + create_info.enabledExtensionCount = extensions_count + 1; create_info.ppEnabledExtensionNames = extensions; #endif // IMGUI_VULKAN_DEBUG_REPORT @@ -235,7 +233,7 @@ static void setup_vulkan(GLFWwindow* window) free(extensions); // create the debug report callback - VkDebugReportCallbackCreateInfoEXT debug_report_ci ={}; + VkDebugReportCallbackCreateInfoEXT debug_report_ci = {}; debug_report_ci.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT; debug_report_ci.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT; debug_report_ci.pfnCallback = debug_report; @@ -245,12 +243,12 @@ static void setup_vulkan(GLFWwindow* window) PFN_vkCreateDebugReportCallbackEXT vkCreateDebugReportCallbackEXT = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(g_Instance, "vkCreateDebugReportCallbackEXT"); - err = vkCreateDebugReportCallbackEXT( g_Instance, &debug_report_ci, g_Allocator, &g_Debug_Report ); + err = vkCreateDebugReportCallbackEXT(g_Instance, &debug_report_ci, g_Allocator, &g_Debug_Report); check_vk_result(err); #endif // IMGUI_VULKAN_DEBUG_REPORT } - // Create Window Surface + // Create Window Surface (with GLFW) { err = glfwCreateWindowSurface(g_Instance, window, g_Allocator, &g_Surface); check_vk_result(err); @@ -315,7 +313,7 @@ static void setup_vulkan(GLFWwindow* window) // first check if only one format, VK_FORMAT_UNDEFINED, is available, which would imply that any format is available if (count == 1) { - if( formats[0].format == VK_FORMAT_UNDEFINED ) + if (formats[0].format == VK_FORMAT_UNDEFINED) { g_SurfaceFormat.format = VK_FORMAT_B8G8R8A8_UNORM; g_SurfaceFormat.colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; @@ -328,14 +326,13 @@ static void setup_vulkan(GLFWwindow* window) else { // request several formats, the first found will be used - VkFormat requestSurfaceImageFormat[] = {VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM}; + VkFormat requestSurfaceImageFormat[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM }; VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; bool requestedFound = false; for (size_t i = 0; i < sizeof(requestSurfaceImageFormat) / sizeof(requestSurfaceImageFormat[0]); i++) { - if( requestedFound ) { + if (requestedFound) break; - } for (uint32_t j = 0; j < count; j++) { if (formats[j].format == requestSurfaceImageFormat[i] && formats[j].colorSpace == requestSurfaceColorSpace) @@ -375,7 +372,7 @@ static void setup_vulkan(GLFWwindow* window) break; } } - if( !presentModeAvailable ) + if (!presentModeAvailable) g_PresentMode = VK_PRESENT_MODE_FIFO_KHR; // always available } @@ -383,10 +380,10 @@ static void setup_vulkan(GLFWwindow* window) // Create Logical Device { int device_extension_count = 1; - const char* device_extensions[] = {"VK_KHR_swapchain"}; + const char* device_extensions[] = { "VK_KHR_swapchain" }; const uint32_t queue_index = 0; const uint32_t queue_count = 1; - const float queue_priority[] = {1.0f}; + const float queue_priority[] = { 1.0f }; VkDeviceQueueCreateInfo queue_info[1] = {}; queue_info[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; queue_info[0].queueFamilyIndex = g_QueueFamily; @@ -394,7 +391,7 @@ static void setup_vulkan(GLFWwindow* window) queue_info[0].pQueuePriorities = queue_priority; VkDeviceCreateInfo create_info = {}; create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; - create_info.queueCreateInfoCount = sizeof(queue_info)/sizeof(queue_info[0]); + create_info.queueCreateInfoCount = sizeof(queue_info) / sizeof(queue_info[0]); create_info.pQueueCreateInfos = queue_info; create_info.enabledExtensionCount = device_extension_count; create_info.ppEnabledExtensionNames = device_extensions; @@ -576,8 +573,8 @@ static void frame_present() uint32_t PresentIndex = g_FrameIndex; #endif // IMGUI_UNLIMITED_FRAME_RATE - VkSwapchainKHR swapchains[1] = {g_Swapchain}; - uint32_t indices[1] = {g_BackbufferIndices[PresentIndex]}; + VkSwapchainKHR swapchains[1] = { g_Swapchain }; + uint32_t indices[1] = { g_BackbufferIndices[PresentIndex] }; VkPresentInfoKHR info = {}; info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; info.waitSemaphoreCount = 1; @@ -612,7 +609,9 @@ int main(int, char**) printf("GLFW: Vulkan Not Supported\n"); return 1; } - setup_vulkan(window); + uint32_t extensions_count = 0; + const char** glfw_extensions = glfwGetRequiredInstanceExtensions(&extensions_count); + setup_vulkan(window, glfw_extensions, extensions_count); // Setup ImGui binding ImGui::CreateContext(); @@ -739,6 +738,7 @@ int main(int, char**) ImGui::ShowDemoWindow(&show_demo_window); } + // Rendering memcpy(&g_ClearValue.color.float32[0], &clear_color, 4 * sizeof(float)); frame_begin(); ImGui_ImplVulkan_Render(g_CommandBuffer[g_FrameIndex]); From f67699456cdca62d0752e2dca92ca0d7b51473cc Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 21 Feb 2018 22:41:04 +0100 Subject: [PATCH 019/828] Examples: Added SDL+Vulkan example, the abstraction worked here :) (ref #1367) --- examples/.gitignore | 4 + examples/imgui_examples.sln | 10 + examples/sdl_vulkan_example/main.cpp | 765 ++++++++++++++++++ .../sdl_vulkan_example.vcxproj | 174 ++++ .../sdl_vulkan_example.vcxproj.filters | 55 ++ 5 files changed, 1008 insertions(+) create mode 100644 examples/sdl_vulkan_example/main.cpp create mode 100644 examples/sdl_vulkan_example/sdl_vulkan_example.vcxproj create mode 100644 examples/sdl_vulkan_example/sdl_vulkan_example.vcxproj.filters diff --git a/examples/.gitignore b/examples/.gitignore index 7a6e9f4f35aa..cf756ff6f2a4 100644 --- a/examples/.gitignore +++ b/examples/.gitignore @@ -33,6 +33,10 @@ sdl_opengl3_example/Debug/* sdl_opengl3_example/Release/* sdl_opengl3_example/ipch/* sdl_opengl3_example/x64/* +sdl_vulkan_example/Debug/* +sdl_vulkan_example/Release/* +sdl_vulkan_example/ipch/* +sdl_vulkan_example/x64/* vulkan_example/Debug/* vulkan_example/Release/* vulkan_example/ipch/* diff --git a/examples/imgui_examples.sln b/examples/imgui_examples.sln index f0aca4326bcc..0d6bbd6c8e97 100644 --- a/examples/imgui_examples.sln +++ b/examples/imgui_examples.sln @@ -19,6 +19,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sdl_opengl3_example", "sdl_ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vulkan_example", "vulkan_example\vulkan_example.vcxproj", "{57E2DF5A-6FC8-45BB-99DD-91A18C646E80}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sdl_vulkan_example", "sdl_vulkan_example\sdl_vulkan_example.vcxproj", "{BAE3D0B5-9695-4EB1-AD0F-75890EB4A3B3}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -91,6 +93,14 @@ Global {57E2DF5A-6FC8-45BB-99DD-91A18C646E80}.Release|Win32.Build.0 = Release|Win32 {57E2DF5A-6FC8-45BB-99DD-91A18C646E80}.Release|x64.ActiveCfg = Release|x64 {57E2DF5A-6FC8-45BB-99DD-91A18C646E80}.Release|x64.Build.0 = Release|x64 + {BAE3D0B5-9695-4EB1-AD0F-75890EB4A3B3}.Debug|Win32.ActiveCfg = Debug|Win32 + {BAE3D0B5-9695-4EB1-AD0F-75890EB4A3B3}.Debug|Win32.Build.0 = Debug|Win32 + {BAE3D0B5-9695-4EB1-AD0F-75890EB4A3B3}.Debug|x64.ActiveCfg = Debug|x64 + {BAE3D0B5-9695-4EB1-AD0F-75890EB4A3B3}.Debug|x64.Build.0 = Debug|x64 + {BAE3D0B5-9695-4EB1-AD0F-75890EB4A3B3}.Release|Win32.ActiveCfg = Release|Win32 + {BAE3D0B5-9695-4EB1-AD0F-75890EB4A3B3}.Release|Win32.Build.0 = Release|Win32 + {BAE3D0B5-9695-4EB1-AD0F-75890EB4A3B3}.Release|x64.ActiveCfg = Release|x64 + {BAE3D0B5-9695-4EB1-AD0F-75890EB4A3B3}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/examples/sdl_vulkan_example/main.cpp b/examples/sdl_vulkan_example/main.cpp new file mode 100644 index 000000000000..158a1b3e97c3 --- /dev/null +++ b/examples/sdl_vulkan_example/main.cpp @@ -0,0 +1,765 @@ +// ImGui - standalone example application for SDL2 + Vulkan +// If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp. + +#include "imgui.h" +#include "../imgui_impl_sdl2.h" +#include "../imgui_impl_vulkan.h" +#include +#include +#include +#include + +#define IMGUI_MAX_POSSIBLE_BACK_BUFFERS 16 +#define IMGUI_UNLIMITED_FRAME_RATE +//#ifdef _DEBUG +//#define IMGUI_VULKAN_DEBUG_REPORT +//#endif + +static VkAllocationCallbacks* g_Allocator = NULL; +static VkInstance g_Instance = VK_NULL_HANDLE; +static VkSurfaceKHR g_Surface = VK_NULL_HANDLE; +static VkPhysicalDevice g_Gpu = VK_NULL_HANDLE; +static VkDevice g_Device = VK_NULL_HANDLE; +static VkSwapchainKHR g_Swapchain = VK_NULL_HANDLE; +static VkRenderPass g_RenderPass = VK_NULL_HANDLE; +static uint32_t g_QueueFamily = 0; +static VkQueue g_Queue = VK_NULL_HANDLE; +static VkDebugReportCallbackEXT g_Debug_Report = VK_NULL_HANDLE; + +static VkSurfaceFormatKHR g_SurfaceFormat; +static VkImageSubresourceRange g_ImageRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; +static VkPresentModeKHR g_PresentMode; + +static VkPipelineCache g_PipelineCache = VK_NULL_HANDLE; +static VkDescriptorPool g_DescriptorPool = VK_NULL_HANDLE; + +static int fb_width, fb_height; +static uint32_t g_BackbufferIndices[IMGUI_VK_QUEUED_FRAMES]; // keep track of recently rendered swapchain frame indices +static uint32_t g_BackBufferCount = 0; +static VkImage g_BackBuffer[IMGUI_MAX_POSSIBLE_BACK_BUFFERS] = {}; +static VkImageView g_BackBufferView[IMGUI_MAX_POSSIBLE_BACK_BUFFERS] = {}; +static VkFramebuffer g_Framebuffer[IMGUI_MAX_POSSIBLE_BACK_BUFFERS] = {}; + +static uint32_t g_FrameIndex = 0; +static VkCommandPool g_CommandPool[IMGUI_VK_QUEUED_FRAMES]; +static VkCommandBuffer g_CommandBuffer[IMGUI_VK_QUEUED_FRAMES]; +static VkFence g_Fence[IMGUI_VK_QUEUED_FRAMES]; +static VkSemaphore g_PresentCompleteSemaphore[IMGUI_VK_QUEUED_FRAMES]; +static VkSemaphore g_RenderCompleteSemaphore[IMGUI_VK_QUEUED_FRAMES]; + +static VkClearValue g_ClearValue = {}; + +static void check_vk_result(VkResult err) +{ + if (err == 0) return; + printf("VkResult %d\n", err); + if (err < 0) + abort(); +} + +static void resize_vulkan(SDL_Window*, int w, int h) +{ + VkResult err; + VkSwapchainKHR old_swapchain = g_Swapchain; + err = vkDeviceWaitIdle(g_Device); + check_vk_result(err); + + // Destroy old Framebuffer: + for (uint32_t i = 0; i < g_BackBufferCount; i++) + if (g_BackBufferView[i]) + vkDestroyImageView(g_Device, g_BackBufferView[i], g_Allocator); + for (uint32_t i = 0; i < g_BackBufferCount; i++) + if (g_Framebuffer[i]) + vkDestroyFramebuffer(g_Device, g_Framebuffer[i], g_Allocator); + if (g_RenderPass) + vkDestroyRenderPass(g_Device, g_RenderPass, g_Allocator); + + // Create Swapchain: + { + VkSwapchainCreateInfoKHR info = {}; + info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + info.surface = g_Surface; + info.imageFormat = g_SurfaceFormat.format; + info.imageColorSpace = g_SurfaceFormat.colorSpace; + info.imageArrayLayers = 1; + info.imageUsage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + info.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; + info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + info.presentMode = g_PresentMode; + info.clipped = VK_TRUE; + info.oldSwapchain = old_swapchain; + VkSurfaceCapabilitiesKHR cap; + err = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(g_Gpu, g_Surface, &cap); + check_vk_result(err); + if (cap.maxImageCount > 0) + info.minImageCount = (cap.minImageCount + 2 < cap.maxImageCount) ? (cap.minImageCount + 2) : cap.maxImageCount; + else + info.minImageCount = cap.minImageCount + 2; + + if (cap.currentExtent.width == 0xffffffff) + { + fb_width = w; + fb_height = h; + info.imageExtent.width = fb_width; + info.imageExtent.height = fb_height; + } + else + { + fb_width = cap.currentExtent.width; + fb_height = cap.currentExtent.height; + info.imageExtent.width = fb_width; + info.imageExtent.height = fb_height; + } + err = vkCreateSwapchainKHR(g_Device, &info, g_Allocator, &g_Swapchain); + check_vk_result(err); + err = vkGetSwapchainImagesKHR(g_Device, g_Swapchain, &g_BackBufferCount, NULL); + check_vk_result(err); + err = vkGetSwapchainImagesKHR(g_Device, g_Swapchain, &g_BackBufferCount, g_BackBuffer); + check_vk_result(err); + } + if (old_swapchain) + vkDestroySwapchainKHR(g_Device, old_swapchain, g_Allocator); + + // Create the Render Pass: + { + VkAttachmentDescription attachment = {}; + attachment.format = g_SurfaceFormat.format; + attachment.samples = VK_SAMPLE_COUNT_1_BIT; + attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + VkAttachmentReference color_attachment = {}; + color_attachment.attachment = 0; + color_attachment.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + VkSubpassDescription subpass = {}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &color_attachment; + VkRenderPassCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + info.attachmentCount = 1; + info.pAttachments = &attachment; + info.subpassCount = 1; + info.pSubpasses = &subpass; + err = vkCreateRenderPass(g_Device, &info, g_Allocator, &g_RenderPass); + check_vk_result(err); + } + + // Create The Image Views + { + VkImageViewCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + info.viewType = VK_IMAGE_VIEW_TYPE_2D; + info.format = g_SurfaceFormat.format; + info.components.r = VK_COMPONENT_SWIZZLE_R; + info.components.g = VK_COMPONENT_SWIZZLE_G; + info.components.b = VK_COMPONENT_SWIZZLE_B; + info.components.a = VK_COMPONENT_SWIZZLE_A; + info.subresourceRange = g_ImageRange; + for (uint32_t i = 0; i < g_BackBufferCount; i++) + { + info.image = g_BackBuffer[i]; + err = vkCreateImageView(g_Device, &info, g_Allocator, &g_BackBufferView[i]); + check_vk_result(err); + } + } + + // Create Framebuffer: + { + VkImageView attachment[1]; + VkFramebufferCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + info.renderPass = g_RenderPass; + info.attachmentCount = 1; + info.pAttachments = attachment; + info.width = fb_width; + info.height = fb_height; + info.layers = 1; + for (uint32_t i = 0; i < g_BackBufferCount; i++) + { + attachment[0] = g_BackBufferView[i]; + err = vkCreateFramebuffer(g_Device, &info, g_Allocator, &g_Framebuffer[i]); + check_vk_result(err); + } + } +} + +#ifdef IMGUI_VULKAN_DEBUG_REPORT +static VKAPI_ATTR VkBool32 VKAPI_CALL debug_report( + VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char* pLayerPrefix, const char* pMessage, void* pUserData) +{ + printf("[vulkan] ObjectType: %i\nMessage: %s\n\n", objectType, pMessage); + return VK_FALSE; +} +#endif // IMGUI_VULKAN_DEBUG_REPORT + +static void setup_vulkan(SDL_Window* window, const char** extensions, uint32_t extensions_count) +{ + VkResult err; + + // Create Vulkan Instance + { + VkInstanceCreateInfo create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + create_info.enabledExtensionCount = extensions_count; + create_info.ppEnabledExtensionNames = extensions; + +#ifdef IMGUI_VULKAN_DEBUG_REPORT + // enabling multiple validation layers grouped as lunarg standard validation + const char* layers[] = { "VK_LAYER_LUNARG_standard_validation" }; + create_info.enabledLayerCount = 1; + create_info.ppEnabledLayerNames = layers; + + // need additional storage for char pointer to debug report extension + const char** extensions = (const char**)malloc(sizeof(const char*) * (extensions_count + 1)); + for (size_t i = 0; i < extensions_count; i++) + extensions[i] = extensions[i]; + extensions[extensions_count] = "VK_EXT_debug_report"; + create_info.enabledExtensionCount = extensions_count + 1; + create_info.ppEnabledExtensionNames = extensions; +#endif // IMGUI_VULKAN_DEBUG_REPORT + + err = vkCreateInstance(&create_info, g_Allocator, &g_Instance); + check_vk_result(err); + +#ifdef IMGUI_VULKAN_DEBUG_REPORT + free(extensions); + + // create the debug report callback + VkDebugReportCallbackCreateInfoEXT debug_report_ci = {}; + debug_report_ci.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT; + debug_report_ci.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT; + debug_report_ci.pfnCallback = debug_report; + debug_report_ci.pUserData = NULL; + + // get the proc address of the function pointer, required for used extensions + PFN_vkCreateDebugReportCallbackEXT vkCreateDebugReportCallbackEXT = + (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(g_Instance, "vkCreateDebugReportCallbackEXT"); + + err = vkCreateDebugReportCallbackEXT(g_Instance, &debug_report_ci, g_Allocator, &g_Debug_Report); + check_vk_result(err); +#endif // IMGUI_VULKAN_DEBUG_REPORT + } + + // Create Window Surface (with SDL) + { + SDL_bool result = SDL_Vulkan_CreateSurface(window, g_Instance, &g_Surface); + if (result == 0) + { + printf("failed to create Vulkan surface\n"); + abort(); + } + } + + // Get GPU + { + uint32_t gpu_count; + err = vkEnumeratePhysicalDevices(g_Instance, &gpu_count, NULL); + check_vk_result(err); + + VkPhysicalDevice* gpus = (VkPhysicalDevice*)malloc(sizeof(VkPhysicalDevice) * gpu_count); + err = vkEnumeratePhysicalDevices(g_Instance, &gpu_count, gpus); + check_vk_result(err); + + // If a number >1 of GPUs got reported, you should find the best fit GPU for your purpose + // e.g. VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU if available, or with the greatest memory available, etc. + // for sake of simplicity we'll just take the first one, assuming it has a graphics queue family. + g_Gpu = gpus[0]; + free(gpus); + } + + // Get queue + { + uint32_t count; + vkGetPhysicalDeviceQueueFamilyProperties(g_Gpu, &count, NULL); + VkQueueFamilyProperties* queues = (VkQueueFamilyProperties*)malloc(sizeof(VkQueueFamilyProperties) * count); + vkGetPhysicalDeviceQueueFamilyProperties(g_Gpu, &count, queues); + for (uint32_t i = 0; i < count; i++) + { + if (queues[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) + { + g_QueueFamily = i; + break; + } + } + free(queues); + } + + // Check for WSI support + { + VkBool32 res; + vkGetPhysicalDeviceSurfaceSupportKHR(g_Gpu, g_QueueFamily, g_Surface, &res); + if (res != VK_TRUE) + { + fprintf(stderr, "Error no WSI support on physical device 0\n"); + exit(-1); + } + } + + // Get Surface Format + { + // Per Spec Format and View Format are expected to be the same unless VK_IMAGE_CREATE_MUTABLE_BIT was set at image creation + // Assuming that the default behavior is without setting this bit, there is no need for separate Spawchain image and image view format + // additionally several new color spaces were introduced with Vulkan Spec v1.0.40 + // hence we must make sure that a format with the mostly available color space, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, is found and used + uint32_t count; + vkGetPhysicalDeviceSurfaceFormatsKHR(g_Gpu, g_Surface, &count, NULL); + VkSurfaceFormatKHR *formats = (VkSurfaceFormatKHR*)malloc(sizeof(VkSurfaceFormatKHR) * count); + vkGetPhysicalDeviceSurfaceFormatsKHR(g_Gpu, g_Surface, &count, formats); + + // first check if only one format, VK_FORMAT_UNDEFINED, is available, which would imply that any format is available + if (count == 1) + { + if (formats[0].format == VK_FORMAT_UNDEFINED) + { + g_SurfaceFormat.format = VK_FORMAT_B8G8R8A8_UNORM; + g_SurfaceFormat.colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; + } + else + { // no point in searching another format + g_SurfaceFormat = formats[0]; + } + } + else + { + // request several formats, the first found will be used + VkFormat requestSurfaceImageFormat[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM }; + VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; + bool requestedFound = false; + for (size_t i = 0; i < sizeof(requestSurfaceImageFormat) / sizeof(requestSurfaceImageFormat[0]); i++) + { + if (requestedFound) + break; + for (uint32_t j = 0; j < count; j++) + { + if (formats[j].format == requestSurfaceImageFormat[i] && formats[j].colorSpace == requestSurfaceColorSpace) + { + g_SurfaceFormat = formats[j]; + requestedFound = true; + } + } + } + + // if none of the requested image formats could be found, use the first available + if (!requestedFound) + g_SurfaceFormat = formats[0]; + } + free(formats); + } + + + // Get Present Mode + { + // Requst a certain mode and confirm that it is available. If not use VK_PRESENT_MODE_FIFO_KHR which is mandatory +#ifdef IMGUI_UNLIMITED_FRAME_RATE + g_PresentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; +#else + g_PresentMode = VK_PRESENT_MODE_FIFO_KHR; +#endif + uint32_t count = 0; + vkGetPhysicalDeviceSurfacePresentModesKHR(g_Gpu, g_Surface, &count, nullptr); + VkPresentModeKHR* presentModes = (VkPresentModeKHR*)malloc(sizeof(VkQueueFamilyProperties) * count); + vkGetPhysicalDeviceSurfacePresentModesKHR(g_Gpu, g_Surface, &count, presentModes); + bool presentModeAvailable = false; + for (size_t i = 0; i < count; i++) + { + if (presentModes[i] == g_PresentMode) + { + presentModeAvailable = true; + break; + } + } + if (!presentModeAvailable) + g_PresentMode = VK_PRESENT_MODE_FIFO_KHR; // always available + } + + + // Create Logical Device + { + int device_extension_count = 1; + const char* device_extensions[] = { "VK_KHR_swapchain" }; + const uint32_t queue_index = 0; + const uint32_t queue_count = 1; + const float queue_priority[] = { 1.0f }; + VkDeviceQueueCreateInfo queue_info[1] = {}; + queue_info[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queue_info[0].queueFamilyIndex = g_QueueFamily; + queue_info[0].queueCount = queue_count; + queue_info[0].pQueuePriorities = queue_priority; + VkDeviceCreateInfo create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + create_info.queueCreateInfoCount = sizeof(queue_info) / sizeof(queue_info[0]); + create_info.pQueueCreateInfos = queue_info; + create_info.enabledExtensionCount = device_extension_count; + create_info.ppEnabledExtensionNames = device_extensions; + err = vkCreateDevice(g_Gpu, &create_info, g_Allocator, &g_Device); + check_vk_result(err); + vkGetDeviceQueue(g_Device, g_QueueFamily, queue_index, &g_Queue); + } + + // Create Framebuffers + { + int w, h; + SDL_GetWindowSize(window, &w, &h); + resize_vulkan(window, w, h); + } + + // Create Command Buffers + for (int i = 0; i < IMGUI_VK_QUEUED_FRAMES; i++) + { + { + VkCommandPoolCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + info.queueFamilyIndex = g_QueueFamily; + err = vkCreateCommandPool(g_Device, &info, g_Allocator, &g_CommandPool[i]); + check_vk_result(err); + } + { + VkCommandBufferAllocateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + info.commandPool = g_CommandPool[i]; + info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + info.commandBufferCount = 1; + err = vkAllocateCommandBuffers(g_Device, &info, &g_CommandBuffer[i]); + check_vk_result(err); + } + { + VkFenceCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + info.flags = VK_FENCE_CREATE_SIGNALED_BIT; + err = vkCreateFence(g_Device, &info, g_Allocator, &g_Fence[i]); + check_vk_result(err); + } + { + VkSemaphoreCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + err = vkCreateSemaphore(g_Device, &info, g_Allocator, &g_PresentCompleteSemaphore[i]); + check_vk_result(err); + err = vkCreateSemaphore(g_Device, &info, g_Allocator, &g_RenderCompleteSemaphore[i]); + check_vk_result(err); + } + } + + // Create Descriptor Pool + { + VkDescriptorPoolSize pool_size[11] = + { + { VK_DESCRIPTOR_TYPE_SAMPLER, 1000 }, + { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000 }, + { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1000 }, + { VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1000 }, + { VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1000 }, + { VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1000 }, + { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1000 }, + { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1000 }, + { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1000 }, + { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1000 }, + { VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1000 } + }; + VkDescriptorPoolCreateInfo pool_info = {}; + pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT; + pool_info.maxSets = 1000 * 11; + pool_info.poolSizeCount = 11; + pool_info.pPoolSizes = pool_size; + err = vkCreateDescriptorPool(g_Device, &pool_info, g_Allocator, &g_DescriptorPool); + check_vk_result(err); + } +} + +static void cleanup_vulkan() +{ + vkDestroyDescriptorPool(g_Device, g_DescriptorPool, g_Allocator); + for (int i = 0; i < IMGUI_VK_QUEUED_FRAMES; i++) + { + vkDestroyFence(g_Device, g_Fence[i], g_Allocator); + vkFreeCommandBuffers(g_Device, g_CommandPool[i], 1, &g_CommandBuffer[i]); + vkDestroyCommandPool(g_Device, g_CommandPool[i], g_Allocator); + vkDestroySemaphore(g_Device, g_PresentCompleteSemaphore[i], g_Allocator); + vkDestroySemaphore(g_Device, g_RenderCompleteSemaphore[i], g_Allocator); + } + for (uint32_t i = 0; i < g_BackBufferCount; i++) + { + vkDestroyImageView(g_Device, g_BackBufferView[i], g_Allocator); + vkDestroyFramebuffer(g_Device, g_Framebuffer[i], g_Allocator); + } + vkDestroyRenderPass(g_Device, g_RenderPass, g_Allocator); + vkDestroySwapchainKHR(g_Device, g_Swapchain, g_Allocator); + vkDestroySurfaceKHR(g_Instance, g_Surface, g_Allocator); + +#ifdef IMGUI_VULKAN_DEBUG_REPORT + // get the proc address of the function pointer, required for used extensions + auto vkDestroyDebugReportCallbackEXT = (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr(g_Instance, "vkDestroyDebugReportCallbackEXT"); + vkDestroyDebugReportCallbackEXT(g_Instance, g_Debug_Report, g_Allocator); +#endif // IMGUI_VULKAN_DEBUG_REPORT + + vkDestroyDevice(g_Device, g_Allocator); + vkDestroyInstance(g_Instance, g_Allocator); +} + +static void frame_begin() +{ + VkResult err; + for (;;) + { + err = vkWaitForFences(g_Device, 1, &g_Fence[g_FrameIndex], VK_TRUE, 100); + if (err == VK_SUCCESS) break; + if (err == VK_TIMEOUT) continue; + check_vk_result(err); + } + { + err = vkAcquireNextImageKHR(g_Device, g_Swapchain, UINT64_MAX, g_PresentCompleteSemaphore[g_FrameIndex], VK_NULL_HANDLE, &g_BackbufferIndices[g_FrameIndex]); + check_vk_result(err); + } + { + err = vkResetCommandPool(g_Device, g_CommandPool[g_FrameIndex], 0); + check_vk_result(err); + VkCommandBufferBeginInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + err = vkBeginCommandBuffer(g_CommandBuffer[g_FrameIndex], &info); + check_vk_result(err); + } + { + VkRenderPassBeginInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + info.renderPass = g_RenderPass; + info.framebuffer = g_Framebuffer[g_BackbufferIndices[g_FrameIndex]]; + info.renderArea.extent.width = fb_width; + info.renderArea.extent.height = fb_height; + info.clearValueCount = 1; + info.pClearValues = &g_ClearValue; + vkCmdBeginRenderPass(g_CommandBuffer[g_FrameIndex], &info, VK_SUBPASS_CONTENTS_INLINE); + } +} + +static void frame_end() +{ + VkResult err; + vkCmdEndRenderPass(g_CommandBuffer[g_FrameIndex]); + { + VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + VkSubmitInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + info.waitSemaphoreCount = 1; + info.pWaitSemaphores = &g_PresentCompleteSemaphore[g_FrameIndex]; + info.pWaitDstStageMask = &wait_stage; + info.commandBufferCount = 1; + info.pCommandBuffers = &g_CommandBuffer[g_FrameIndex]; + info.signalSemaphoreCount = 1; + info.pSignalSemaphores = &g_RenderCompleteSemaphore[g_FrameIndex]; + + err = vkEndCommandBuffer(g_CommandBuffer[g_FrameIndex]); + check_vk_result(err); + err = vkResetFences(g_Device, 1, &g_Fence[g_FrameIndex]); + check_vk_result(err); + err = vkQueueSubmit(g_Queue, 1, &info, g_Fence[g_FrameIndex]); + check_vk_result(err); + } +} + +static void frame_present() +{ + VkResult err; + // If IMGUI_UNLIMITED_FRAME_RATE is defined we present the latest but one frame. Otherwise we present the latest rendered frame +#ifdef IMGUI_UNLIMITED_FRAME_RATE + uint32_t PresentIndex = (g_FrameIndex + IMGUI_VK_QUEUED_FRAMES - 1) % IMGUI_VK_QUEUED_FRAMES; +#else + uint32_t PresentIndex = g_FrameIndex; +#endif // IMGUI_UNLIMITED_FRAME_RATE + + VkSwapchainKHR swapchains[1] = { g_Swapchain }; + uint32_t indices[1] = { g_BackbufferIndices[PresentIndex] }; + VkPresentInfoKHR info = {}; + info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + info.waitSemaphoreCount = 1; + info.pWaitSemaphores = &g_RenderCompleteSemaphore[PresentIndex]; + info.swapchainCount = 1; + info.pSwapchains = swapchains; + info.pImageIndices = indices; + err = vkQueuePresentKHR(g_Queue, &info); + check_vk_result(err); + + g_FrameIndex = (g_FrameIndex + 1) % IMGUI_VK_QUEUED_FRAMES; +} + +int main(int, char**) +{ + // Setup SDL + if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER) != 0) + { + printf("Error: %s\n", SDL_GetError()); + return 1; + } + + // Setup window + SDL_DisplayMode current; + SDL_GetCurrentDisplayMode(0, ¤t); + SDL_Window* window = SDL_CreateWindow("ImGui SDL2+Vulkan example", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, SDL_WINDOW_VULKAN|SDL_WINDOW_RESIZABLE); + + // Setup Vulkan + uint32_t extensions_count; + SDL_Vulkan_GetInstanceExtensions(window, &extensions_count, nullptr); + const char** sdl_extensions = new const char*[extensions_count]; + SDL_Vulkan_GetInstanceExtensions(window, &extensions_count, sdl_extensions); + setup_vulkan(window, sdl_extensions, extensions_count); + delete[] sdl_extensions; + + // Setup ImGui binding + ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); (void)io; + //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; + + ImGui_ImplVulkan_InitData init_data = {}; + init_data.allocator = g_Allocator; + init_data.gpu = g_Gpu; + init_data.device = g_Device; + init_data.render_pass = g_RenderPass; + init_data.pipeline_cache = g_PipelineCache; + init_data.descriptor_pool = g_DescriptorPool; + init_data.check_vk_result = check_vk_result; + ImGui_ImplVulkan_Init(&init_data); + ImGui_ImplSDL2_Init(window); + + // Setup style + ImGui::StyleColorsDark(); + //ImGui::StyleColorsClassic(); + + // Load Fonts + // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. + // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. + // - If the file cannot be loaded, the function will return NULL. Please handle those errors in your application (e.g. use an assertion, or display an error and quit). + // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. + // - Read 'misc/fonts/README.txt' for more instructions and details. + // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! + //io.Fonts->AddFontDefault(); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/ProggyTiny.ttf", 10.0f); + //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, NULL, io.Fonts->GetGlyphRangesJapanese()); + //IM_ASSERT(font != NULL); + + // Upload Fonts + { + VkResult err; + err = vkResetCommandPool(g_Device, g_CommandPool[g_FrameIndex], 0); + check_vk_result(err); + VkCommandBufferBeginInfo begin_info = {}; + begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + begin_info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + err = vkBeginCommandBuffer(g_CommandBuffer[g_FrameIndex], &begin_info); + check_vk_result(err); + + ImGui_ImplVulkan_CreateFontsTexture(g_CommandBuffer[g_FrameIndex]); + + VkSubmitInfo end_info = {}; + end_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + end_info.commandBufferCount = 1; + end_info.pCommandBuffers = &g_CommandBuffer[g_FrameIndex]; + err = vkEndCommandBuffer(g_CommandBuffer[g_FrameIndex]); + check_vk_result(err); + err = vkQueueSubmit(g_Queue, 1, &end_info, VK_NULL_HANDLE); + check_vk_result(err); + + err = vkDeviceWaitIdle(g_Device); + check_vk_result(err); + ImGui_ImplVulkan_InvalidateFontUploadObjects(); + } + + bool show_demo_window = true; + bool show_another_window = false; + ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); + + // When IMGUI_UNLIMITED_FRAME_RATE is defined we render into latest image acquired from the swapchain but we display the image which was rendered before. + // Hence we must render once and increase the g_FrameIndex without presenting, which we do before entering the render loop. + // This is also the reason why frame_end() is split into frame_end() and frame_present(), the later one not being called here. +#ifdef IMGUI_UNLIMITED_FRAME_RATE + ImGui_ImplVulkan_NewFrame(); + ImGui_ImplSDL2_NewFrame(window); + frame_begin(); + ImGui_ImplVulkan_Render(g_CommandBuffer[g_FrameIndex]); + frame_end(); + g_FrameIndex = (g_FrameIndex + 1) % IMGUI_VK_QUEUED_FRAMES; +#endif // IMGUI_UNLIMITED_FRAME_RATE + + // Main loop + bool done = false; + while (!done) + { + // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. + // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application. + // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. + // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. + SDL_Event event; + while (SDL_PollEvent(&event)) + { + ImGui_ImplSDL2_ProcessEvent(&event); + if (event.type == SDL_QUIT) + done = true; + } + ImGui_ImplVulkan_NewFrame(); + ImGui_ImplSDL2_NewFrame(window); + + // 1. Show a simple window. + // Tip: if we don't call ImGui::Begin()/ImGui::End() the widgets automatically appears in a window called "Debug". + { + static float f = 0.0f; + static int counter = 0; + ImGui::Text("Hello, world!"); // Display some text (you can use a format string too) + ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f + ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color + + ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our windows open/close state + ImGui::Checkbox("Another Window", &show_another_window); + + if (ImGui::Button("Button")) // Buttons return true when clicked (NB: most widgets return true when edited/activated) + counter++; + ImGui::SameLine(); + ImGui::Text("counter = %d", counter); + + ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate); + } + + // 2. Show another simple window. In most cases you will use an explicit Begin/End pair to name your windows. + if (show_another_window) + { + ImGui::Begin("Another Window", &show_another_window); + ImGui::Text("Hello from another window!"); + if (ImGui::Button("Close Me")) + show_another_window = false; + ImGui::End(); + } + + // 3. Show the ImGui demo window. Most of the sample code is in ImGui::ShowDemoWindow(). Read its code to learn more about Dear ImGui! + if (show_demo_window) + { + ImGui::SetNextWindowPos(ImVec2(650, 20), ImGuiCond_FirstUseEver); // Normally user code doesn't need/want to call this because positions are saved in .ini file anyway. Here we just want to make the demo initial state a bit more friendly! + ImGui::ShowDemoWindow(&show_demo_window); + } + + // Rendering + memcpy(&g_ClearValue.color.float32[0], &clear_color, 4 * sizeof(float)); + frame_begin(); + ImGui_ImplVulkan_Render(g_CommandBuffer[g_FrameIndex]); + frame_end(); + frame_present(); + } + + // Cleanup + VkResult err = vkDeviceWaitIdle(g_Device); + check_vk_result(err); + ImGui_ImplVulkan_Shutdown(); + ImGui_ImplSDL2_Shutdown(); + ImGui::DestroyContext(); + SDL_DestroyWindow(window); + cleanup_vulkan(); + SDL_Quit(); + + return 0; +} diff --git a/examples/sdl_vulkan_example/sdl_vulkan_example.vcxproj b/examples/sdl_vulkan_example/sdl_vulkan_example.vcxproj new file mode 100644 index 000000000000..f848f1ffc133 --- /dev/null +++ b/examples/sdl_vulkan_example/sdl_vulkan_example.vcxproj @@ -0,0 +1,174 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {BAE3D0B5-9695-4EB1-AD0F-75890EB4A3B3} + opengl3_example + + + + Application + true + MultiByte + + + Application + true + MultiByte + + + Application + false + true + MultiByte + + + Application + false + true + MultiByte + + + + + + + + + + + + + + + + + + + $(ProjectDir)$(Configuration)\ + $(ProjectDir)$(Configuration)\ + $(IncludePath) + + + $(ProjectDir)$(Configuration)\ + $(ProjectDir)$(Configuration)\ + $(IncludePath) + + + $(ProjectDir)$(Configuration)\ + $(ProjectDir)$(Configuration)\ + $(IncludePath) + + + $(ProjectDir)$(Configuration)\ + $(ProjectDir)$(Configuration)\ + $(IncludePath) + + + + Level4 + Disabled + %VULKAN_SDK%\include;%SDL2_DIR%\include;..\..;%(AdditionalIncludeDirectories) + + + true + %VULKAN_SDK%\lib32;%SDL2_DIR%\lib\x86;%(AdditionalLibraryDirectories) + vulkan-1.lib;SDL2.lib;SDL2main.lib;%(AdditionalDependencies) + Console + msvcrt.lib + + + + + Level4 + Disabled + %VULKAN_SDK%\include;%SDL2_DIR%\include;..\..;%(AdditionalIncludeDirectories) + + + true + %VULKAN_SDK%\lib;%SDL2_DIR%\lib\x64;%(AdditionalLibraryDirectories) + vulkan-1.lib;SDL2.lib;SDL2main.lib;%(AdditionalDependencies) + Console + msvcrt.lib + + + + + Level4 + MaxSpeed + true + true + %VULKAN_SDK%\include;%SDL2_DIR%\include;..\..;%(AdditionalIncludeDirectories) + false + + + true + true + true + %VULKAN_SDK%\lib32;%SDL2_DIR%\lib\x86;%(AdditionalLibraryDirectories) + vulkan-1.lib;SDL2.lib;SDL2main.lib;%(AdditionalDependencies) + Console + + + + + + + Level4 + MaxSpeed + true + true + %VULKAN_SDK%\include;%SDL2_DIR%\include;..\..;%(AdditionalIncludeDirectories) + false + + + true + true + true + %VULKAN_SDK%\lib;%SDL2_DIR%\lib\x64;%(AdditionalLibraryDirectories) + vulkan-1.lib;SDL2.lib;SDL2main.lib;%(AdditionalDependencies) + Console + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/sdl_vulkan_example/sdl_vulkan_example.vcxproj.filters b/examples/sdl_vulkan_example/sdl_vulkan_example.vcxproj.filters new file mode 100644 index 000000000000..35e8aa1f2adb --- /dev/null +++ b/examples/sdl_vulkan_example/sdl_vulkan_example.vcxproj.filters @@ -0,0 +1,55 @@ + + + + + {20b90ce4-7fcb-4731-b9a0-075f875de82d} + + + {f18ab499-84e1-499f-8eff-9754361e0e52} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + + + imgui + + + imgui + + + imgui + + + sources + + + sources + + + sources + + + + + imgui + + + imgui + + + imgui + + + sources + + + sources + + + + + + sources + + + \ No newline at end of file From 47d13601248726392cf81d34f5c18a42cd7c3f83 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 21 Feb 2018 22:41:46 +0100 Subject: [PATCH 020/828] Examples: Using SDL2_DIR instead of SDL_DIR (more standard). --- .../sdl_opengl2_example.vcxproj | 16 ++++++++-------- .../sdl_opengl3_example.vcxproj | 16 ++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/examples/sdl_opengl2_example/sdl_opengl2_example.vcxproj b/examples/sdl_opengl2_example/sdl_opengl2_example.vcxproj index 776cde6cba71..3e3ea79051fd 100644 --- a/examples/sdl_opengl2_example/sdl_opengl2_example.vcxproj +++ b/examples/sdl_opengl2_example/sdl_opengl2_example.vcxproj @@ -85,11 +85,11 @@ Level4 Disabled - %SDL_DIR%\include;$(SolutionDir)\libs\gl3w;..\..;%(AdditionalIncludeDirectories) + %SDL2_DIR%\include;$(SolutionDir)\libs\gl3w;..\..;%(AdditionalIncludeDirectories) true - %SDL_DIR%\lib\x86;%(AdditionalLibraryDirectories) + %SDL2_DIR%\lib\x86;%(AdditionalLibraryDirectories) opengl32.lib;SDL2.lib;SDL2main.lib;%(AdditionalDependencies) Console msvcrt.lib @@ -99,11 +99,11 @@ Level4 Disabled - %SDL_DIR%\include;$(SolutionDir)\libs\gl3w;..\..;%(AdditionalIncludeDirectories) + %SDL2_DIR%\include;$(SolutionDir)\libs\gl3w;..\..;%(AdditionalIncludeDirectories) true - %SDL_DIR%\lib\x64;%(AdditionalLibraryDirectories) + %SDL2_DIR%\lib\x64;%(AdditionalLibraryDirectories) opengl32.lib;SDL2.lib;SDL2main.lib;%(AdditionalDependencies) Console msvcrt.lib @@ -115,14 +115,14 @@ MaxSpeed true true - %SDL_DIR%\include;$(SolutionDir)\libs\gl3w;..\..;%(AdditionalIncludeDirectories) + %SDL2_DIR%\include;$(SolutionDir)\libs\gl3w;..\..;%(AdditionalIncludeDirectories) false true true true - %SDL_DIR%\lib\x86;%(AdditionalLibraryDirectories) + %SDL2_DIR%\lib\x86;%(AdditionalLibraryDirectories) opengl32.lib;SDL2.lib;SDL2main.lib;%(AdditionalDependencies) Console @@ -135,14 +135,14 @@ MaxSpeed true true - %SDL_DIR%\include;$(SolutionDir)\libs\gl3w;..\..;%(AdditionalIncludeDirectories) + %SDL2_DIR%\include;$(SolutionDir)\libs\gl3w;..\..;%(AdditionalIncludeDirectories) false true true true - %SDL_DIR%\lib\x64;%(AdditionalLibraryDirectories) + %SDL2_DIR%\lib\x64;%(AdditionalLibraryDirectories) opengl32.lib;SDL2.lib;SDL2main.lib;%(AdditionalDependencies) Console diff --git a/examples/sdl_opengl3_example/sdl_opengl3_example.vcxproj b/examples/sdl_opengl3_example/sdl_opengl3_example.vcxproj index 8d17b2255987..7ef3fd96e586 100644 --- a/examples/sdl_opengl3_example/sdl_opengl3_example.vcxproj +++ b/examples/sdl_opengl3_example/sdl_opengl3_example.vcxproj @@ -85,11 +85,11 @@ Level4 Disabled - %SDL_DIR%\include;$(SolutionDir)\libs\gl3w;..\..;%(AdditionalIncludeDirectories) + %SDL2_DIR%\include;$(SolutionDir)\libs\gl3w;..\..;%(AdditionalIncludeDirectories) true - %SDL_DIR%\lib\x86;%(AdditionalLibraryDirectories) + %SDL2_DIR%\lib\x86;%(AdditionalLibraryDirectories) opengl32.lib;SDL2.lib;SDL2main.lib;%(AdditionalDependencies) Console msvcrt.lib @@ -99,11 +99,11 @@ Level4 Disabled - %SDL_DIR%\include;$(SolutionDir)\libs\gl3w;..\..;%(AdditionalIncludeDirectories) + %SDL2_DIR%\include;$(SolutionDir)\libs\gl3w;..\..;%(AdditionalIncludeDirectories) true - %SDL_DIR%\lib\x64;%(AdditionalLibraryDirectories) + %SDL2_DIR%\lib\x64;%(AdditionalLibraryDirectories) opengl32.lib;SDL2.lib;SDL2main.lib;%(AdditionalDependencies) Console msvcrt.lib @@ -115,14 +115,14 @@ MaxSpeed true true - %SDL_DIR%\include;$(SolutionDir)\libs\gl3w;..\..;%(AdditionalIncludeDirectories) + %SDL2_DIR%\include;$(SolutionDir)\libs\gl3w;..\..;%(AdditionalIncludeDirectories) false true true true - %SDL_DIR%\lib\x86;%(AdditionalLibraryDirectories) + %SDL2_DIR%\lib\x86;%(AdditionalLibraryDirectories) opengl32.lib;SDL2.lib;SDL2main.lib;%(AdditionalDependencies) Console @@ -135,14 +135,14 @@ MaxSpeed true true - %SDL_DIR%\include;$(SolutionDir)\libs\gl3w;..\..;%(AdditionalIncludeDirectories) + %SDL2_DIR%\include;$(SolutionDir)\libs\gl3w;..\..;%(AdditionalIncludeDirectories) false true true true - %SDL_DIR%\lib\x64;%(AdditionalLibraryDirectories) + %SDL2_DIR%\lib\x64;%(AdditionalLibraryDirectories) opengl32.lib;SDL2.lib;SDL2main.lib;%(AdditionalDependencies) Console From f7ef10e5478321ec5246c805aa2f4c265aa68f3f Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 21 Feb 2018 23:05:17 +0100 Subject: [PATCH 021/828] Examples: Titles/comments in headers --- examples/imgui_impl_allegro5.cpp | 3 ++- examples/imgui_impl_allegro5.h | 3 ++- examples/imgui_impl_dx10.cpp | 3 ++- examples/imgui_impl_dx10.h | 3 ++- examples/imgui_impl_dx11.cpp | 3 ++- examples/imgui_impl_dx11.h | 3 ++- examples/imgui_impl_dx9.cpp | 3 ++- examples/imgui_impl_dx9.h | 3 ++- examples/imgui_impl_glfw.cpp | 12 +++--------- examples/imgui_impl_glfw.h | 7 +++---- examples/imgui_impl_marmalade.cpp | 2 +- examples/imgui_impl_marmalade.h | 2 +- examples/imgui_impl_opengl2.cpp | 5 ++++- examples/imgui_impl_opengl2.h | 10 ++++++++++ examples/imgui_impl_opengl3.cpp | 4 ++++ examples/imgui_impl_opengl3.h | 3 +++ examples/imgui_impl_sdl2.cpp | 6 +++--- examples/imgui_impl_sdl2.h | 9 +++------ examples/imgui_impl_vulkan.cpp | 10 +++------- examples/imgui_impl_vulkan.h | 3 ++- examples/imgui_impl_win32.cpp | 3 +++ examples/imgui_impl_win32.h | 2 ++ 22 files changed, 61 insertions(+), 41 deletions(-) diff --git a/examples/imgui_impl_allegro5.cpp b/examples/imgui_impl_allegro5.cpp index 228ae0cb4f70..c82b7cc8c3c3 100644 --- a/examples/imgui_impl_allegro5.cpp +++ b/examples/imgui_impl_allegro5.cpp @@ -1,4 +1,5 @@ -// ImGui Allegro 5 bindings +// ImGui Renderer + Platform Binding for: Allegro 5 +// (Info: Allegro 5 is a cross-platform general purpose library for handling windows, inputs, graphics, etc.) // Implemented features: // [X] User texture binding. Use 'ALLEGRO_BITMAP*' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. diff --git a/examples/imgui_impl_allegro5.h b/examples/imgui_impl_allegro5.h index 4b75dffa6003..9fc259201d8e 100644 --- a/examples/imgui_impl_allegro5.h +++ b/examples/imgui_impl_allegro5.h @@ -1,4 +1,5 @@ -// ImGui Allegro 5 bindings +// ImGui Renderer + Platform Binding for: Allegro 5 +// (Info: Allegro 5 is a cross-platform general purpose library for handling windows, inputs, graphics, etc.) // Implemented features: // [X] User texture binding. Use 'ALLEGRO_BITMAP*' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. diff --git a/examples/imgui_impl_dx10.cpp b/examples/imgui_impl_dx10.cpp index 112bace85e17..6b165118b1b0 100644 --- a/examples/imgui_impl_dx10.cpp +++ b/examples/imgui_impl_dx10.cpp @@ -1,4 +1,5 @@ -// ImGui Win32 + DirectX10 binding +// ImGui Renderer for: DirectX10 +// This needs to be used along with a Platform Binding (e.g. Win32) // Implemented features: // [X] User texture binding. Use 'ID3D10ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. diff --git a/examples/imgui_impl_dx10.h b/examples/imgui_impl_dx10.h index c375351bf9d9..5d972013a90e 100644 --- a/examples/imgui_impl_dx10.h +++ b/examples/imgui_impl_dx10.h @@ -1,4 +1,5 @@ -// ImGui Win32 + DirectX10 binding +// ImGui Renderer for: DirectX10 +// This needs to be used along with a Platform Binding (e.g. Win32) // Implemented features: // [X] User texture binding. Use 'ID3D10ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. diff --git a/examples/imgui_impl_dx11.cpp b/examples/imgui_impl_dx11.cpp index 12536a5358a8..44e5737a27d4 100644 --- a/examples/imgui_impl_dx11.cpp +++ b/examples/imgui_impl_dx11.cpp @@ -1,4 +1,5 @@ -// ImGui Win32 + DirectX11 binding +// ImGui Renderer for: DirectX11 +// This needs to be used along with a Platform Binding (e.g. Win32) // Implemented features: // [X] User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. diff --git a/examples/imgui_impl_dx11.h b/examples/imgui_impl_dx11.h index 10e0d9637b7d..62fb627eff97 100644 --- a/examples/imgui_impl_dx11.h +++ b/examples/imgui_impl_dx11.h @@ -1,4 +1,5 @@ -// ImGui Win32 + DirectX11 binding +// ImGui Renderer for: DirectX11 +// This needs to be used along with a Platform Binding (e.g. Win32) // Implemented features: // [X] User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. diff --git a/examples/imgui_impl_dx9.cpp b/examples/imgui_impl_dx9.cpp index 17e4bcdaadc9..8be032d51cdb 100644 --- a/examples/imgui_impl_dx9.cpp +++ b/examples/imgui_impl_dx9.cpp @@ -1,4 +1,5 @@ -// ImGui Win32 + DirectX9 binding +// ImGui Renderer for: DirectX9 +// This needs to be used along with a Platform Binding (e.g. Win32) // Implemented features: // [X] User texture binding. Use 'LPDIRECT3DTEXTURE9' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. diff --git a/examples/imgui_impl_dx9.h b/examples/imgui_impl_dx9.h index eb52eabefd99..32e78241a21f 100644 --- a/examples/imgui_impl_dx9.h +++ b/examples/imgui_impl_dx9.h @@ -1,4 +1,5 @@ -// ImGui Win32 + DirectX9 binding +// ImGui Renderer for: DirectX9 +// This needs to be used along with a Platform Binding (e.g. Win32) // Implemented features: // [X] User texture binding. Use 'LPDIRECT3DTEXTURE9' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index 578752c6729a..187331b64f33 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -1,9 +1,8 @@ -// ImGui GLFW binding with OpenGL3 + shaders -// (GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) -// (GL3W is a helper library to access OpenGL functions since there is no standard header to access modern OpenGL functions easily. Alternatives are GLEW, Glad, etc.) +// ImGui Platform Binding for: GLFW +// This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan..) +// (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) // Implemented features: -// [X] User texture binding. Cast 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. // [X] Gamepad navigation mapping. Enable with 'io.NavFlags |= ImGuiNavFlags_EnableGamepad'. // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. @@ -21,13 +20,8 @@ // 2018-01-25: Inputs: Honoring the io.WantMoveMouse by repositioning the mouse by using navigation and ImGuiNavFlags_MoveMouse is set. // 2018-01-20: Inputs: Added Horizontal Mouse Wheel support. // 2018-01-18: Inputs: Added mapping for ImGuiKey_Insert. -// 2018-01-07: OpenGL: Changed GLSL shader version from 330 to 150. (Also changed GL context from 3.3 to 3.2 in example's main.cpp) -// 2017-09-01: OpenGL: Save and restore current bound sampler. Save and restore current polygon mode. // 2017-08-25: Inputs: MousePos set to -FLT_MAX,-FLT_MAX when mouse is unavailable/missing (instead of -1,-1). -// 2017-05-01: OpenGL: Fixed save and restore of current blend function state. // 2016-10-15: Misc: Added a void* user_data parameter to Clipboard function handlers. -// 2016-09-05: OpenGL: Fixed save and restore of current scissor rectangle. -// 2016-04-30: OpenGL: Fixed save and restore of current GL_ACTIVE_TEXTURE. #include "imgui.h" #include "imgui_impl_glfw.h" diff --git a/examples/imgui_impl_glfw.h b/examples/imgui_impl_glfw.h index d1b42eed05d6..c6cb4c8a3c47 100644 --- a/examples/imgui_impl_glfw.h +++ b/examples/imgui_impl_glfw.h @@ -1,9 +1,8 @@ -// ImGui GLFW binding with OpenGL3 + shaders -// (GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) -// (GL3W is a helper library to access OpenGL functions since there is no standard header to access modern OpenGL functions easily. Alternatives are GLEW, Glad, etc.) +// ImGui Platform Binding for: GLFW +// This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan..) +// (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) // Implemented features: -// [X] User texture binding. Cast 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. // [X] Gamepad navigation mapping. Enable with 'io.NavFlags |= ImGuiNavFlags_EnableGamepad'. // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. diff --git a/examples/imgui_impl_marmalade.cpp b/examples/imgui_impl_marmalade.cpp index 947bcbd6fb5a..1b6fd6ef5365 100644 --- a/examples/imgui_impl_marmalade.cpp +++ b/examples/imgui_impl_marmalade.cpp @@ -1,4 +1,4 @@ -// ImGui Marmalade binding with IwGx +// ImGui Renderer + Platform Binding for: Marmalade + IwGx // Implemented features: // [X] User texture binding. Use 'CIwTexture*' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. diff --git a/examples/imgui_impl_marmalade.h b/examples/imgui_impl_marmalade.h index c41a5e8f264f..884ea9e4cb6a 100644 --- a/examples/imgui_impl_marmalade.h +++ b/examples/imgui_impl_marmalade.h @@ -1,4 +1,4 @@ -// ImGui Marmalade binding with IwGx +// ImGui Renderer + Platform Binding for: Marmalade + IwGx // Implemented features: // [X] User texture binding. Use 'CIwTexture*' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. diff --git a/examples/imgui_impl_opengl2.cpp b/examples/imgui_impl_opengl2.cpp index f77695d65655..3d47702bd231 100644 --- a/examples/imgui_impl_opengl2.cpp +++ b/examples/imgui_impl_opengl2.cpp @@ -1,5 +1,8 @@ +// ImGui Renderer for: OpenGL2 (legacy OpenGL, fixed pipeline) +// This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..) + // **DO NOT USE THIS CODE IF YOUR CODE/ENGINE IS USING MODERN OPENGL (SHADERS, VBO, VAO, etc.)** -// **Prefer using the code in the opengl3_example/ folder** +// **Prefer using the code in imgui_impl_opengl3.cpp** // This code is mostly provided as a reference to learn how ImGui integration works, because it is shorter to read. // If your code is using GL3+ context or any semi modern OpenGL calls, using this is likely to make everything more // complicated, will require your code to reset every single OpenGL attributes to their initial state, and might diff --git a/examples/imgui_impl_opengl2.h b/examples/imgui_impl_opengl2.h index c1ef2f5e70d3..a80dfac4b6ab 100644 --- a/examples/imgui_impl_opengl2.h +++ b/examples/imgui_impl_opengl2.h @@ -1,3 +1,13 @@ +// ImGui Renderer for: OpenGL2 (legacy OpenGL, fixed pipeline) +// This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..) + +// **DO NOT USE THIS CODE IF YOUR CODE/ENGINE IS USING MODERN OPENGL (SHADERS, VBO, VAO, etc.)** +// **Prefer using the code in imgui_impl_opengl3.cpp** +// This code is mostly provided as a reference to learn how ImGui integration works, because it is shorter to read. +// If your code is using GL3+ context or any semi modern OpenGL calls, using this is likely to make everything more +// complicated, will require your code to reset every single OpenGL attributes to their initial state, and might +// confuse your GPU driver. +// The GL2 code is unable to reset attributes or even call e.g. "glUseProgram(0)" because they don't exist in that API. IMGUI_API bool ImGui_ImplOpenGL2_Init(); IMGUI_API void ImGui_ImplOpenGL2_Shutdown(); diff --git a/examples/imgui_impl_opengl3.cpp b/examples/imgui_impl_opengl3.cpp index fcb8ca5f50a9..af5ccd029ebb 100644 --- a/examples/imgui_impl_opengl3.cpp +++ b/examples/imgui_impl_opengl3.cpp @@ -1,3 +1,7 @@ +// ImGui Renderer for: OpenGL3 (modern OpenGL with shaders / programmatic pipeline) +// This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..) +// (Note: We are using GL3W as a helper library to access OpenGL functions since there is no standard header to access modern OpenGL functions easily. Alternatives are GLEW, Glad, etc..) + // CHANGELOG // (minor and older changes stripped away, please see git history for details) // 2018-XX-XX: OpenGL: Offset projection matrix and clipping rectangle by io.DisplayPos (which will be non-zero for multi-viewport applications). diff --git a/examples/imgui_impl_opengl3.h b/examples/imgui_impl_opengl3.h index eaf8cf8a3caf..9a9e9971738a 100644 --- a/examples/imgui_impl_opengl3.h +++ b/examples/imgui_impl_opengl3.h @@ -1,3 +1,6 @@ +// ImGui Renderer for: OpenGL3 (modern OpenGL with shaders / programmatic pipeline) +// This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..) +// (Note: We are using GL3W as a helper library to access OpenGL functions since there is no standard header to access modern OpenGL functions easily. Alternatives are GLEW, Glad, etc..) IMGUI_API bool ImGui_ImplOpenGL3_Init(); IMGUI_API void ImGui_ImplOpenGL3_Shutdown(); diff --git a/examples/imgui_impl_sdl2.cpp b/examples/imgui_impl_sdl2.cpp index 75ea617d7e64..21b26aaba830 100644 --- a/examples/imgui_impl_sdl2.cpp +++ b/examples/imgui_impl_sdl2.cpp @@ -1,6 +1,6 @@ -// ImGui SDL2 binding with OpenGL3 -// (SDL is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) -// (GL3W is a helper library to access OpenGL functions since there is no standard header to access modern OpenGL functions easily. Alternatives are GLEW, Glad, etc.) +// ImGui Platform Binding for: SDL2 +// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..) +// (Info: SDL2 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.) // Implemented features: // [X] User texture binding. Cast 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. diff --git a/examples/imgui_impl_sdl2.h b/examples/imgui_impl_sdl2.h index b19c0f5e2bf4..4a181283a143 100644 --- a/examples/imgui_impl_sdl2.h +++ b/examples/imgui_impl_sdl2.h @@ -1,9 +1,6 @@ -// ImGui SDL2 binding with OpenGL3 -// (SDL is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) -// (GL3W is a helper library to access OpenGL functions since there is no standard header to access modern OpenGL functions easily. Alternatives are GLEW, Glad, etc.) - -// Implemented features: -// [X] User texture binding. Cast 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. +// ImGui Platform Binding for: SDL2 +// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..) +// (Info: SDL2 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.) // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). diff --git a/examples/imgui_impl_vulkan.cpp b/examples/imgui_impl_vulkan.cpp index 42ca412ff7d2..312bb486f732 100644 --- a/examples/imgui_impl_vulkan.cpp +++ b/examples/imgui_impl_vulkan.cpp @@ -1,4 +1,5 @@ -// ImGui GLFW binding with Vulkan + shaders +// ImGui Renderer for: Vulkan +// This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..) // Missing features: // [ ] User texture binding. Changes of ImTextureID aren't supported by this binding! See https://github.com/ocornut/imgui/pull/914 @@ -10,17 +11,12 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2018-XX-XX: Draw: Offset projection matrix and clipping rectangle by io.DisplayPos (which will be non-zero for multi-viewport applications). +// 2018-XX-XX: Vulkan: Offset projection matrix and clipping rectangle by io.DisplayPos (which will be non-zero for multi-viewport applications). // 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback, ImGui_ImplVulkan_Render() calls ImGui_ImplVulkan_RenderDrawData() itself. // 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. -// 2018-02-06: Inputs: Added mapping for ImGuiKey_Space. -// 2018-01-20: Inputs: Added Horizontal Mouse Wheel support. -// 2018-01-18: Inputs: Added mapping for ImGuiKey_Insert. -// 2017-08-25: Inputs: MousePos set to -FLT_MAX,-FLT_MAX when mouse is unavailable/missing (instead of -1,-1). // 2017-05-15: Vulkan: Fix scissor offset being negative. Fix new Vulkan validation warnings. Set required depth member for buffer image copy. // 2016-11-13: Vulkan: Fix validation layer warnings and errors and redeclare gl_PerVertex. // 2016-10-18: Vulkan: Add location decorators & change to use structs as in/out in glsl, update embedded spv (produced with glslangValidator -x). Null the released resources. -// 2016-10-15: Misc: Added a void* user_data parameter to Clipboard function handlers. // 2016-08-27: Vulkan: Fix Vulkan example for use when a depth buffer is active. #include "imgui.h" diff --git a/examples/imgui_impl_vulkan.h b/examples/imgui_impl_vulkan.h index 0a0e9be0eb0e..c27c11eebb74 100644 --- a/examples/imgui_impl_vulkan.h +++ b/examples/imgui_impl_vulkan.h @@ -1,4 +1,5 @@ -// ImGui GLFW binding with Vulkan + shaders +// ImGui Renderer for: Vulkan +// This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..) // Missing features: // [ ] User texture binding. Changes of ImTextureID aren't supported by this binding! See https://github.com/ocornut/imgui/pull/914 diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index 1df7b9f218c5..f538590a7418 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -1,3 +1,6 @@ +// ImGui Platform Binding for: Windows (standard windows API for 32 and 64 bits applications) +// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..) + #include "imgui.h" #include "imgui_impl_win32.h" #define WIN32_LEAN_AND_MEAN diff --git a/examples/imgui_impl_win32.h b/examples/imgui_impl_win32.h index 086cf032a0d7..4f5d65e67708 100644 --- a/examples/imgui_impl_win32.h +++ b/examples/imgui_impl_win32.h @@ -1,3 +1,5 @@ +// ImGui Platform Binding for: Windows (standard windows API for 32 and 64 bits applications) +// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..) IMGUI_API bool ImGui_ImplWin32_Init(void* hwnd); IMGUI_API void ImGui_ImplWin32_Shutdown(); From 0b26387a2b7322026f7a94458dd129d2b82a6caa Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 21 Feb 2018 23:13:53 +0100 Subject: [PATCH 022/828] Simplified .gitignore list --- examples/.gitignore | 54 +++++++++------------------------------------ 1 file changed, 11 insertions(+), 43 deletions(-) diff --git a/examples/.gitignore b/examples/.gitignore index cf756ff6f2a4..ff44bccb746f 100644 --- a/examples/.gitignore +++ b/examples/.gitignore @@ -1,46 +1,8 @@ build/* -Debug/* -Release/* -ipch/* -x64/* -directx9_example/Debug/* -directx9_example/Release/* -directx9_example/ipch/* -directx9_example/x64/* -directx10_example/Debug/* -directx10_example/Release/* -directx10_example/ipch/* -directx10_example/x64/* -directx11_example/Debug/* -directx11_example/Release/* -directx11_example/ipch/* -directx11_example/x64/* -opengl2_example/Debug/* -opengl2_example/Release/* -opengl2_example/ipch/* -opengl2_example/x64/* -opengl2_example/opengl_example -opengl3_example/Debug/* -opengl3_example/Release/* -opengl3_example/ipch/* -opengl3_example/x64/* -opengl3_example/opengl3_example -sdl_opengl2_example/Debug/* -sdl_opengl2_example/Release/* -sdl_opengl2_example/ipch/* -sdl_opengl2_example/x64/* -sdl_opengl3_example/Debug/* -sdl_opengl3_example/Release/* -sdl_opengl3_example/ipch/* -sdl_opengl3_example/x64/* -sdl_vulkan_example/Debug/* -sdl_vulkan_example/Release/* -sdl_vulkan_example/ipch/* -sdl_vulkan_example/x64/* -vulkan_example/Debug/* -vulkan_example/Release/* -vulkan_example/ipch/* -vulkan_example/x64/* +*/Debug/* +*/Release/* +*/ipch/* +*/x64/* *.opensdf *.sdf *.suo @@ -53,5 +15,11 @@ vulkan_example/x64/* *.VC.db *.VC.VC.opendb -## Ini files +## Unix executables +opengl2_example/opengl2_example +opengl3_example/opengl3_example +sdl_opengl2_example/sdl_opengl2_example +sdl_opengl3_example/sdl_opengl3_example + +## Dear ImGui Ini files imgui.ini From ffda84cfae66b3654cf379008036a04ce798ddd4 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 23 Feb 2018 10:56:06 +0100 Subject: [PATCH 023/828] Examples: DirectX12: Merged to new example format, imgui_impl_dx12.cpp contains the DX12 stuff, couple with imgui_impl_win32.cpp --- .../directx12_example.vcxproj | 6 +- .../directx12_example.vcxproj.filters | 18 +- examples/directx12_example/main.cpp | 12 +- .../imgui_impl_dx12.cpp | 191 +----------------- .../{directx12_example => }/imgui_impl_dx12.h | 24 +-- 5 files changed, 32 insertions(+), 219 deletions(-) rename examples/{directx12_example => }/imgui_impl_dx12.cpp (77%) rename examples/{directx12_example => }/imgui_impl_dx12.h (54%) diff --git a/examples/directx12_example/directx12_example.vcxproj b/examples/directx12_example/directx12_example.vcxproj index d3962a31734a..1ee7e343555d 100644 --- a/examples/directx12_example/directx12_example.vcxproj +++ b/examples/directx12_example/directx12_example.vcxproj @@ -146,13 +146,15 @@ - + + - + + diff --git a/examples/directx12_example/directx12_example.vcxproj.filters b/examples/directx12_example/directx12_example.vcxproj.filters index 6baac5986ca7..43fdebab4413 100644 --- a/examples/directx12_example/directx12_example.vcxproj.filters +++ b/examples/directx12_example/directx12_example.vcxproj.filters @@ -15,12 +15,15 @@ imgui - - sources - imgui + + sources + + + sources + @@ -29,15 +32,18 @@ sources - - sources - imgui imgui + + sources + + + sources + diff --git a/examples/directx12_example/main.cpp b/examples/directx12_example/main.cpp index 6a4b78d85e84..e6d19b8f6176 100644 --- a/examples/directx12_example/main.cpp +++ b/examples/directx12_example/main.cpp @@ -3,7 +3,8 @@ // FIXME: 64-bit only for now! (Because sizeof(ImTextureId) == sizeof(void*)) #include "imgui.h" -#include "imgui_impl_dx12.h" +#include "../imgui_impl_win32.h" +#include "../imgui_impl_dx12.h" #include #include #include @@ -289,10 +290,9 @@ int main(int, char**) ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; // Enable Keyboard Controls - ImGui_ImplDX12_Init(hwnd, NUM_FRAMES_IN_FLIGHT, g_pd3dDevice, - DXGI_FORMAT_R8G8B8A8_UNORM, - g_pd3dSrvDescHeap->GetCPUDescriptorHandleForHeapStart(), - g_pd3dSrvDescHeap->GetGPUDescriptorHandleForHeapStart()); + ImGui_ImplWin32_Init(hwnd); + ImGui_ImplDX12_Init(g_pd3dDevice, NUM_FRAMES_IN_FLIGHT, DXGI_FORMAT_R8G8B8A8_UNORM, + g_pd3dSrvDescHeap->GetCPUDescriptorHandleForHeapStart(), g_pd3dSrvDescHeap->GetGPUDescriptorHandleForHeapStart()); // Setup style ImGui::StyleColorsDark(); @@ -333,6 +333,7 @@ int main(int, char**) continue; } ImGui_ImplDX12_NewFrame(g_pd3dCommandList); + ImGui_ImplWin32_NewFrame(); // 1. Show a simple window. // Tip: if we don't call ImGui::Begin()/ImGui::End() the widgets automatically appears in a window called "Debug". @@ -409,6 +410,7 @@ int main(int, char**) WaitForLastSubmittedFrame(); ImGui_ImplDX12_Shutdown(); + ImGui_ImplWin32_Shutdown(); ImGui::DestroyContext(); CleanupDeviceD3D(); UnregisterClass(_T("ImGui Example"), wc.hInstance); diff --git a/examples/directx12_example/imgui_impl_dx12.cpp b/examples/imgui_impl_dx12.cpp similarity index 77% rename from examples/directx12_example/imgui_impl_dx12.cpp rename to examples/imgui_impl_dx12.cpp index beb48b9cb167..65692a3897c5 100644 --- a/examples/directx12_example/imgui_impl_dx12.cpp +++ b/examples/imgui_impl_dx12.cpp @@ -20,12 +20,6 @@ #include #include -// Win32 data -static HWND g_hWnd = 0; -static INT64 g_Time = 0; -static INT64 g_TicksPerSecond = 0; -static ImGuiMouseCursor g_LastMouseCursor = ImGuiMouseCursor_Count_; - // DirectX data static ID3D12Device* g_pd3dDevice = NULL; static ID3D12GraphicsCommandList* g_pd3dCommandList = NULL; @@ -221,110 +215,6 @@ void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data) } } -static void ImGui_ImplWin32_UpdateMouseCursor() -{ - ImGuiIO& io = ImGui::GetIO(); - ImGuiMouseCursor imgui_cursor = io.MouseDrawCursor ? ImGuiMouseCursor_None : ImGui::GetMouseCursor(); - if (imgui_cursor == ImGuiMouseCursor_None) - { - // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor - ::SetCursor(NULL); - } - else - { - // Hardware cursor type - LPTSTR win32_cursor = IDC_ARROW; - switch (imgui_cursor) - { - case ImGuiMouseCursor_Arrow: win32_cursor = IDC_ARROW; break; - case ImGuiMouseCursor_TextInput: win32_cursor = IDC_IBEAM; break; - case ImGuiMouseCursor_ResizeAll: win32_cursor = IDC_SIZEALL; break; - case ImGuiMouseCursor_ResizeEW: win32_cursor = IDC_SIZEWE; break; - case ImGuiMouseCursor_ResizeNS: win32_cursor = IDC_SIZENS; break; - case ImGuiMouseCursor_ResizeNESW: win32_cursor = IDC_SIZENESW; break; - case ImGuiMouseCursor_ResizeNWSE: win32_cursor = IDC_SIZENWSE; break; - } - ::SetCursor(::LoadCursor(NULL, win32_cursor)); - } -} - -// Process Win32 mouse/keyboard inputs. -// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. -// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application. -// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. -// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. -// PS: In this Win32 handler, we use the capture API (GetCapture/SetCapture/ReleaseCapture) to be able to read mouse coordinations when dragging mouse outside of our window bounds. -// PS: We treat DBLCLK messages as regular mouse down messages, so this code will work on windows classes that have the CS_DBLCLKS flag set. Our own example app code doesn't set this flag. -IMGUI_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - if (ImGui::GetCurrentContext() == NULL) - return 0; - - ImGuiIO& io = ImGui::GetIO(); - switch (msg) - { - case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK: - case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK: - case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK: - { - int button = 0; - if (msg == WM_LBUTTONDOWN || msg == WM_LBUTTONDBLCLK) button = 0; - if (msg == WM_RBUTTONDOWN || msg == WM_RBUTTONDBLCLK) button = 1; - if (msg == WM_MBUTTONDOWN || msg == WM_MBUTTONDBLCLK) button = 2; - if (!ImGui::IsAnyMouseDown() && ::GetCapture() == NULL) - ::SetCapture(hwnd); - io.MouseDown[button] = true; - return 0; - } - case WM_LBUTTONUP: - case WM_RBUTTONUP: - case WM_MBUTTONUP: - { - int button = 0; - if (msg == WM_LBUTTONUP) button = 0; - if (msg == WM_RBUTTONUP) button = 1; - if (msg == WM_MBUTTONUP) button = 2; - io.MouseDown[button] = false; - if (!ImGui::IsAnyMouseDown() && ::GetCapture() == hwnd) - ::ReleaseCapture(); - return 0; - } - case WM_MOUSEWHEEL: - io.MouseWheel += GET_WHEEL_DELTA_WPARAM(wParam) > 0 ? +1.0f : -1.0f; - return 0; - case WM_MOUSEHWHEEL: - io.MouseWheelH += GET_WHEEL_DELTA_WPARAM(wParam) > 0 ? +1.0f : -1.0f; - return 0; - case WM_MOUSEMOVE: - io.MousePos.x = (signed short)(lParam); - io.MousePos.y = (signed short)(lParam >> 16); - return 0; - case WM_KEYDOWN: - case WM_SYSKEYDOWN: - if (wParam < 256) - io.KeysDown[wParam] = 1; - return 0; - case WM_KEYUP: - case WM_SYSKEYUP: - if (wParam < 256) - io.KeysDown[wParam] = 0; - return 0; - case WM_CHAR: - // You can also use ToAscii()+GetKeyboardState() to retrieve characters. - if (wParam > 0 && wParam < 0x10000) - io.AddInputCharacter((unsigned short)wParam); - return 0; - case WM_SETCURSOR: - if (LOWORD(lParam) == HTCLIENT) - { - ImGui_ImplWin32_UpdateMouseCursor(); - return 1; - } - return 0; - } - return 0; -} - static void ImGui_ImplDX12_CreateFontsTexture() { // Build texture atlas @@ -690,13 +580,9 @@ void ImGui_ImplDX12_InvalidateDeviceObjects() } } -bool ImGui_ImplDX12_Init(void* hwnd, int num_frames_in_flight, - ID3D12Device* device, - DXGI_FORMAT rtv_format, - D3D12_CPU_DESCRIPTOR_HANDLE font_srv_cpu_desc_handle, - D3D12_GPU_DESCRIPTOR_HANDLE font_srv_gpu_desc_handle) +bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FORMAT rtv_format, + D3D12_CPU_DESCRIPTOR_HANDLE font_srv_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE font_srv_gpu_desc_handle) { - g_hWnd = (HWND)hwnd; g_pd3dDevice = device; g_RTVFormat = rtv_format; g_hFontSrvCpuDescHandle = font_srv_cpu_desc_handle; @@ -713,36 +599,6 @@ bool ImGui_ImplDX12_Init(void* hwnd, int num_frames_in_flight, g_pFrameResources[i].IndexBufferSize = 10000; } - if (!QueryPerformanceFrequency((LARGE_INTEGER *)&g_TicksPerSecond)) - return false; - if (!QueryPerformanceCounter((LARGE_INTEGER *)&g_Time)) - return false; - - ImGuiIO& io = ImGui::GetIO(); - io.KeyMap[ImGuiKey_Tab] = VK_TAB; // Keyboard mapping. ImGui will use those indices to peek into the io.KeyDown[] array that we will update during the application lifetime. - io.KeyMap[ImGuiKey_LeftArrow] = VK_LEFT; - io.KeyMap[ImGuiKey_RightArrow] = VK_RIGHT; - io.KeyMap[ImGuiKey_UpArrow] = VK_UP; - io.KeyMap[ImGuiKey_DownArrow] = VK_DOWN; - io.KeyMap[ImGuiKey_PageUp] = VK_PRIOR; - io.KeyMap[ImGuiKey_PageDown] = VK_NEXT; - io.KeyMap[ImGuiKey_Home] = VK_HOME; - io.KeyMap[ImGuiKey_End] = VK_END; - io.KeyMap[ImGuiKey_Insert] = VK_INSERT; - io.KeyMap[ImGuiKey_Delete] = VK_DELETE; - io.KeyMap[ImGuiKey_Backspace] = VK_BACK; - io.KeyMap[ImGuiKey_Space] = VK_SPACE; - io.KeyMap[ImGuiKey_Enter] = VK_RETURN; - io.KeyMap[ImGuiKey_Escape] = VK_ESCAPE; - io.KeyMap[ImGuiKey_A] = 'A'; - io.KeyMap[ImGuiKey_C] = 'C'; - io.KeyMap[ImGuiKey_V] = 'V'; - io.KeyMap[ImGuiKey_X] = 'X'; - io.KeyMap[ImGuiKey_Y] = 'Y'; - io.KeyMap[ImGuiKey_Z] = 'Z'; - - io.ImeWindowHandle = g_hWnd; - return true; } @@ -751,7 +607,6 @@ void ImGui_ImplDX12_Shutdown() ImGui_ImplDX12_InvalidateDeviceObjects(); delete[] g_pFrameResources; g_pd3dDevice = NULL; - g_hWnd = (HWND)0; g_pd3dCommandList = NULL; g_hFontSrvCpuDescHandle.ptr = 0; g_hFontSrvGpuDescHandle.ptr = 0; @@ -766,46 +621,4 @@ void ImGui_ImplDX12_NewFrame(ID3D12GraphicsCommandList* command_list) ImGui_ImplDX12_CreateDeviceObjects(); g_pd3dCommandList = command_list; - - ImGuiIO& io = ImGui::GetIO(); - - // Setup display size (every frame to accommodate for window resizing) - RECT rect; - GetClientRect(g_hWnd, &rect); - io.DisplaySize = ImVec2((float)(rect.right - rect.left), (float)(rect.bottom - rect.top)); - - // Setup time step - INT64 current_time; - QueryPerformanceCounter((LARGE_INTEGER *)¤t_time); - io.DeltaTime = (float)(current_time - g_Time) / g_TicksPerSecond; - g_Time = current_time; - - // Read keyboard modifiers inputs - io.KeyCtrl = (GetKeyState(VK_CONTROL) & 0x8000) != 0; - io.KeyShift = (GetKeyState(VK_SHIFT) & 0x8000) != 0; - io.KeyAlt = (GetKeyState(VK_MENU) & 0x8000) != 0; - io.KeySuper = false; - // io.KeysDown : filled by WM_KEYDOWN/WM_KEYUP events - // io.MousePos : filled by WM_MOUSEMOVE events - // io.MouseDown : filled by WM_*BUTTON* events - // io.MouseWheel : filled by WM_MOUSEWHEEL events - - // Set OS mouse position if requested last frame by io.WantMoveMouse flag (used when io.NavMovesTrue is enabled by user and using directional navigation) - if (io.WantMoveMouse) - { - POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y }; - ClientToScreen(g_hWnd, &pos); - SetCursorPos(pos.x, pos.y); - } - - // Update OS mouse cursor with the cursor requested by imgui - ImGuiMouseCursor mouse_cursor = io.MouseDrawCursor ? ImGuiMouseCursor_None : ImGui::GetMouseCursor(); - if (g_LastMouseCursor != mouse_cursor) - { - g_LastMouseCursor = mouse_cursor; - ImGui_ImplWin32_UpdateMouseCursor(); - } - - // Start the frame. This call will update the io.WantCaptureMouse, io.WantCaptureKeyboard flag that you can use to dispatch inputs (or not) to your application. - ImGui::NewFrame(); } diff --git a/examples/directx12_example/imgui_impl_dx12.h b/examples/imgui_impl_dx12.h similarity index 54% rename from examples/directx12_example/imgui_impl_dx12.h rename to examples/imgui_impl_dx12.h index 628bbd497a1f..0f8eaebbfaea 100644 --- a/examples/directx12_example/imgui_impl_dx12.h +++ b/examples/imgui_impl_dx12.h @@ -19,22 +19,12 @@ struct D3D12_GPU_DESCRIPTOR_HANDLE; // Before calling the render function, caller must prepare cmd_list by resetting it and setting the appropriate // render target and descriptor heap that contains font_srv_cpu_desc_handle/font_srv_gpu_desc_handle. // font_srv_cpu_desc_handle and font_srv_gpu_desc_handle are handles to a single SRV descriptor to use for the internal font texture. -IMGUI_API bool ImGui_ImplDX12_Init(void* hwnd, int num_frames_in_flight, - ID3D12Device* device, - DXGI_FORMAT rtv_format, - D3D12_CPU_DESCRIPTOR_HANDLE font_srv_cpu_desc_handle, - D3D12_GPU_DESCRIPTOR_HANDLE font_srv_gpu_desc_handle); -IMGUI_API void ImGui_ImplDX12_Shutdown(); -IMGUI_API void ImGui_ImplDX12_NewFrame(ID3D12GraphicsCommandList* cmd_list); -IMGUI_API void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data); +IMGUI_API bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FORMAT rtv_format, + D3D12_CPU_DESCRIPTOR_HANDLE font_srv_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE font_srv_gpu_desc_handle); +IMGUI_API void ImGui_ImplDX12_Shutdown(); +IMGUI_API void ImGui_ImplDX12_NewFrame(ID3D12GraphicsCommandList* cmd_list); +IMGUI_API void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data); // Use if you want to reset your rendering device without losing ImGui state. -IMGUI_API void ImGui_ImplDX12_InvalidateDeviceObjects(); -IMGUI_API bool ImGui_ImplDX12_CreateDeviceObjects(); - -// Handler for Win32 messages, update mouse/keyboard data. -// You may or not need this for your implementation, but it can serve as reference for handling inputs. -// Commented out to avoid dragging dependencies on types. You can copy the extern declaration in your code. -/* -IMGUI_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); -*/ +IMGUI_API void ImGui_ImplDX12_InvalidateDeviceObjects(); +IMGUI_API bool ImGui_ImplDX12_CreateDeviceObjects(); From 29510fcb8360647a7e127f80a4d5a95ff554808a Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 27 Feb 2018 10:29:37 +0100 Subject: [PATCH 024/828] Examples: Misc tweaks/fixes. --- examples/imgui_impl_glfw.cpp | 88 ++++++++++++++++++++---------------- examples/imgui_impl_sdl2.cpp | 2 +- examples/imgui_impl_win32.h | 2 +- 3 files changed, 50 insertions(+), 42 deletions(-) diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index 187331b64f33..1342a9b1e5fd 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -13,7 +13,6 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) // 2018-XX-XX: Inputs: Added support for mouse cursors, honoring ImGui::GetMouseCursor() value. -// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplGlfwGL3_RenderDrawData() in the .h file so you can call it yourself. // 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. // 2018-02-06: Inputs: Added mapping for ImGuiKey_Space. // 2018-01-25: Inputs: Added gamepad support if ImGuiNavFlags_EnableGamepad is set. @@ -26,7 +25,7 @@ #include "imgui.h" #include "imgui_impl_glfw.h" -// GL3W/GLFW +// GLFW #include #ifdef _WIN32 #undef APIENTRY @@ -37,22 +36,22 @@ // Data static GLFWwindow* g_Window = NULL; static double g_Time = 0.0f; -static bool g_MouseJustPressed[3] = { false, false, false }; +static bool g_MouseJustPressed[5] = { false, false, false, false, false }; static GLFWcursor* g_MouseCursors[ImGuiMouseCursor_Count_] = { 0 }; -static const char* ImGui_ImplGlfwGL3_GetClipboardText(void* user_data) +static const char* ImGui_ImplGlfw_GetClipboardText(void* user_data) { return glfwGetClipboardString((GLFWwindow*)user_data); } -static void ImGui_ImplGlfwGL3_SetClipboardText(void* user_data, const char* text) +static void ImGui_ImplGlfw_SetClipboardText(void* user_data, const char* text) { glfwSetClipboardString((GLFWwindow*)user_data, text); } void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow*, int button, int action, int /*mods*/) { - if (action == GLFW_PRESS && button >= 0 && button < 3) + if (action == GLFW_PRESS && button >= 0 && button < IM_ARRAYSIZE(g_MouseJustPressed)) g_MouseJustPressed[button] = true; } @@ -85,6 +84,14 @@ void ImGui_ImplGlfw_CharCallback(GLFWwindow*, unsigned int c) io.AddInputCharacter((unsigned short)c); } +void ImGui_ImplGlfw_InstallCallbacks(GLFWwindow* window) +{ + glfwSetMouseButtonCallback(window, ImGui_ImplGlfw_MouseButtonCallback); + glfwSetScrollCallback(window, ImGui_ImplGlfw_ScrollCallback); + glfwSetKeyCallback(window, ImGui_ImplGlfw_KeyCallback); + glfwSetCharCallback(window, ImGui_ImplGlfw_CharCallback); +} + bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks) { g_Window = window; @@ -112,8 +119,8 @@ bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks) io.KeyMap[ImGuiKey_Y] = GLFW_KEY_Y; io.KeyMap[ImGuiKey_Z] = GLFW_KEY_Z; - io.SetClipboardTextFn = ImGui_ImplGlfwGL3_SetClipboardText; - io.GetClipboardTextFn = ImGui_ImplGlfwGL3_GetClipboardText; + io.SetClipboardTextFn = ImGui_ImplGlfw_SetClipboardText; + io.GetClipboardTextFn = ImGui_ImplGlfw_GetClipboardText; io.ClipboardUserData = g_Window; #ifdef _WIN32 io.ImeWindowHandle = glfwGetWin32Window(g_Window); @@ -128,12 +135,7 @@ bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks) g_MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); // FIXME: GLFW doesn't have this. if (install_callbacks) - { - glfwSetMouseButtonCallback(window, ImGui_ImplGlfw_MouseButtonCallback); - glfwSetScrollCallback(window, ImGui_ImplGlfw_ScrollCallback); - glfwSetKeyCallback(window, ImGui_ImplGlfw_KeyCallback); - glfwSetCharCallback(window, ImGui_ImplGlfw_CharCallback); - } + ImGui_ImplGlfw_InstallCallbacks(window); return true; } @@ -147,26 +149,17 @@ void ImGui_ImplGlfw_Shutdown() } } -void ImGui_ImplGlfw_NewFrame() +static void ImGui_ImplGlfw_UpdateMousePosButtons() { - ImGuiIO& io = ImGui::GetIO(); - IM_ASSERT(io.Fonts->IsBuilt()); // Font atlas needs to be built, call renderer _NewFrame() function e.g. ImGui_ImplOpenGL3_NewFrame() - - // Setup display size - int w, h; - int display_w, display_h; - glfwGetWindowSize(g_Window, &w, &h); - glfwGetFramebufferSize(g_Window, &display_w, &display_h); - io.DisplaySize = ImVec2((float)w, (float)h); - io.DisplayFramebufferScale = ImVec2(w > 0 ? ((float)display_w / w) : 0, h > 0 ? ((float)display_h / h) : 0); - - // Setup time step - double current_time = glfwGetTime(); - io.DeltaTime = g_Time > 0.0 ? (float)(current_time - g_Time) : (float)(1.0f/60.0f); - g_Time = current_time; + // Update buttons + for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) + { + // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame. + io.MouseDown[i] = g_MouseJustPressed[i] || glfwGetMouseButton(g_Window, i) != 0; + g_MouseJustPressed[i] = false; + } - // Setup inputs - // (we already got mouse wheel, keyboard keys & characters from glfw callbacks polled in glfwPollEvents()) + // Update mouse position if (glfwGetWindowAttrib(g_Window, GLFW_FOCUSED)) { if (io.WantMoveMouse) @@ -185,14 +178,29 @@ void ImGui_ImplGlfw_NewFrame() io.MousePos = ImVec2(-FLT_MAX,-FLT_MAX); } - for (int i = 0; i < 3; i++) - { - // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame. - io.MouseDown[i] = g_MouseJustPressed[i] || glfwGetMouseButton(g_Window, i) != 0; - g_MouseJustPressed[i] = false; - } +} + +void ImGui_ImplGlfw_NewFrame() +{ + ImGuiIO& io = ImGui::GetIO(); + IM_ASSERT(io.Fonts->IsBuilt()); // Font atlas needs to be built, call renderer _NewFrame() function e.g. ImGui_ImplOpenGL3_NewFrame() + + // Setup display size + int w, h; + int display_w, display_h; + glfwGetWindowSize(g_Window, &w, &h); + glfwGetFramebufferSize(g_Window, &display_w, &display_h); + io.DisplaySize = ImVec2((float)w, (float)h); + io.DisplayFramebufferScale = ImVec2(w > 0 ? ((float)display_w / w) : 0, h > 0 ? ((float)display_h / h) : 0); + + // Setup time step + double current_time = glfwGetTime(); + io.DeltaTime = g_Time > 0.0 ? (float)(current_time - g_Time) : (float)(1.0f/60.0f); + g_Time = current_time; + + ImGui_ImplGlfw_UpdateMousePosButtons(); - // Hide OS mouse cursor if ImGui is drawing it + // Update OS/hardware mouse cursor if imgui isn't drawing a software cursor ImGuiMouseCursor cursor = ImGui::GetMouseCursor(); if (io.MouseDrawCursor || cursor == ImGuiMouseCursor_None) { @@ -200,8 +208,8 @@ void ImGui_ImplGlfw_NewFrame() } else { + glfwSetCursor(g_Window, g_MouseCursors[cursor] ? g_MouseCursors[cursor] : g_MouseCursors[ImGuiMouseCursor_Arrow]); glfwSetInputMode(g_Window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); - glfwSetCursor(g_Window, g_MouseCursors[cursor]); } // Gamepad navigation mapping [BETA] diff --git a/examples/imgui_impl_sdl2.cpp b/examples/imgui_impl_sdl2.cpp index 21b26aaba830..2dcb978f653a 100644 --- a/examples/imgui_impl_sdl2.cpp +++ b/examples/imgui_impl_sdl2.cpp @@ -26,7 +26,7 @@ #include "imgui.h" #include "imgui_impl_sdl2.h" -// SDL,GL3W +// SDL #include #include diff --git a/examples/imgui_impl_win32.h b/examples/imgui_impl_win32.h index 4f5d65e67708..a43f067d1cd5 100644 --- a/examples/imgui_impl_win32.h +++ b/examples/imgui_impl_win32.h @@ -7,7 +7,7 @@ IMGUI_API void ImGui_ImplWin32_NewFrame(); // Handler for Win32 messages, update mouse/keyboard data. // You may or not need this for your implementation, but it can serve as reference for handling inputs. -// Commented out to avoid dragging dependencies on types. You can copy the extern declaration in your code. +// Intentionally commented out to avoid dragging dependencies on types. You can copy the extern declaration in your code. /* IMGUI_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); */ From a761779b128f7cfe20b71239aaa3fdfbccad8d02 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 27 Feb 2018 22:32:45 +0100 Subject: [PATCH 025/828] Internals: Settings: Added ReadCloseFn to allow handlers to sanitize data on a per-entry basis. --- imgui.cpp | 9 +++++++++ imgui_internal.h | 7 ++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 8a577564bd24..5285bc746eae 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3732,6 +3732,10 @@ static void LoadIniSettingsFromMemory(const char* buf_readonly) if (line[0] == '[' && line_end > line && line_end[-1] == ']') { + // Close last entry + if (entry_data && entry_handler && entry_handler->ReadCloseFn) + entry_handler->ReadCloseFn(&g, entry_handler, entry_data); + // Parse "[Type][Name]". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code. line_end[-1] = 0; const char* name_end = line_end - 1; @@ -3757,6 +3761,11 @@ static void LoadIniSettingsFromMemory(const char* buf_readonly) entry_handler->ReadLineFn(&g, entry_handler, entry_data, line); } } + + // Close last entry + if (entry_data && entry_handler && entry_handler->ReadCloseFn) + entry_handler->ReadCloseFn(&g, entry_handler, entry_data); + ImGui::MemFree(buf); g.SettingsLoaded = true; } diff --git a/imgui_internal.h b/imgui_internal.h index ddbbf0d77e84..e13b558010e8 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -428,9 +428,10 @@ struct ImGuiSettingsHandler { const char* TypeName; // Short description stored in .ini file. Disallowed characters: '[' ']' ImGuiID TypeHash; // == ImHash(TypeName, 0, 0) - void* (*ReadOpenFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, const char* name); - void (*ReadLineFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, void* entry, const char* line); - void (*WriteAllFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* out_buf); + void* (*ReadOpenFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, const char* name); // Read: Called when entering into a new ini entry e.g. "[Window][Name]" + void (*ReadCloseFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, void* entry); // Read: Called when closing an existing entry, so code can validate overall data. [Optional] + void (*ReadLineFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, void* entry, const char* line); // Read: Called for every line of text within an ini entry + void (*WriteAllFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* out_buf); // Write: Output every entries into 'out_buf' void* UserData; ImGuiSettingsHandler() { memset(this, 0, sizeof(*this)); } From 0e62b7d68acb898e5bcdf37d2fddd48523a3b9da Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 27 Feb 2018 22:38:10 +0100 Subject: [PATCH 026/828] Clearing next window data at the end of Begin() more reliably. Misc comments. --- imgui.cpp | 18 +++--------------- imgui_internal.h | 9 +++++---- 2 files changed, 8 insertions(+), 19 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 5285bc746eae..c0accaefdbbf 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5657,14 +5657,12 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) { SetWindowPos(window, g.NextWindowData.PosVal, g.NextWindowData.PosCond); } - g.NextWindowData.PosCond = 0; } if (g.NextWindowData.SizeCond) { window_size_x_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.x > 0.0f); window_size_y_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.y > 0.0f); SetWindowSize(window, g.NextWindowData.SizeVal, g.NextWindowData.SizeCond); - g.NextWindowData.SizeCond = 0; } if (g.NextWindowData.ContentSizeCond) { @@ -5672,22 +5670,15 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->SizeContentsExplicit = g.NextWindowData.ContentSizeVal; if (window->SizeContentsExplicit.y != 0.0f) window->SizeContentsExplicit.y += window->TitleBarHeight() + window->MenuBarHeight(); - g.NextWindowData.ContentSizeCond = 0; } else if (first_begin_of_the_frame) { window->SizeContentsExplicit = ImVec2(0.0f, 0.0f); } if (g.NextWindowData.CollapsedCond) - { SetWindowCollapsed(window, g.NextWindowData.CollapsedVal, g.NextWindowData.CollapsedCond); - g.NextWindowData.CollapsedCond = 0; - } if (g.NextWindowData.FocusCond) - { SetWindowFocus(); - g.NextWindowData.FocusCond = 0; - } if (window->Appearing) SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, false); @@ -5714,7 +5705,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->LastFrameActive = current_frame; window->IDStack.resize(1); - // Lock window rounding, border size and rounding so that altering the border sizes for children doesn't have side-effects. + // Lock window rounding, border size and padding for the frame (so that altering them doesn't cause inconsistencies) window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding; window->WindowBorderSize = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildBorderSize : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize; window->WindowPadding = style.WindowPadding; @@ -5948,10 +5939,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Window background ImU32 bg_col = GetColorU32(GetWindowBgColorIdxFromFlags(flags)); if (g.NextWindowData.BgAlphaCond != 0) - { bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(g.NextWindowData.BgAlphaVal) << IM_COL32_A_SHIFT); - g.NextWindowData.BgAlphaCond = 0; - } window->DrawList->AddRectFilled(window->Pos+ImVec2(0,window->TitleBarHeight()), window->Pos+window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Bot); // Title bar @@ -6090,7 +6078,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) { ImGuiID id = window->GetID("#COLLAPSE"); ImRect bb(window->Pos + style.FramePadding + ImVec2(1,1), window->Pos + style.FramePadding + ImVec2(g.FontSize,g.FontSize) - ImVec2(1,1)); - ItemAdd(bb, id); // To allow navigation + ItemAdd(bb, id); if (ButtonBehavior(bb, id, NULL, NULL)) window->CollapseToggleWanted = true; // Defer collapsing to next frame as we are too far in the Begin() function RenderNavHighlight(bb, id); @@ -6166,7 +6154,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->WriteAccessed = false; window->BeginCount++; - g.NextWindowData.SizeConstraintCond = 0; + g.NextWindowData.Clear(); // Child window can be out of sight and have "negative" clip windows. // Mark them as collapsed so commands are skipped earlier (we can't manually collapse because they have no title bar). diff --git a/imgui_internal.h b/imgui_internal.h index e13b558010e8..e723f3e25d2b 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -543,7 +543,7 @@ struct ImGuiNextWindowData ImVec2 SizeVal; ImVec2 ContentSizeVal; bool CollapsedVal; - ImRect SizeConstraintRect; // Valid if 'SetNextWindowSizeConstraint' is true + ImRect SizeConstraintRect; ImGuiSizeCallback SizeCallback; void* SizeCallbackUserData; float BgAlphaVal; @@ -940,9 +940,10 @@ struct IMGUI_API ImGuiWindow int AutoFitChildAxises; ImGuiDir AutoPosLastDirection; int HiddenFrames; - ImGuiCond SetWindowPosAllowFlags; // store condition flags for next SetWindowPos() call. - ImGuiCond SetWindowSizeAllowFlags; // store condition flags for next SetWindowSize() call. - ImGuiCond SetWindowCollapsedAllowFlags; // store condition flags for next SetWindowCollapsed() call. + ImGuiCond SetWindowPosAllowFlags; // store accepted condition flags for SetNextWindowPos() use. + ImGuiCond SetWindowSizeAllowFlags; // store accepted condition flags for SetNextWindowSize() use. + ImGuiCond SetWindowCollapsedAllowFlags; // store accepted condition flags for SetNextWindowCollapsed() use. + ImVec2 SetWindowPosVal; // store window position when using a non-zero Pivot (position set needs to be processed when we know the window size) ImVec2 SetWindowPosPivot; // store window pivot for positioning. ImVec2(0,0) when positioning from top-left corner; ImVec2(0.5f,0.5f) for centering; ImVec2(1,1) for bottom right. From b32bb4e6e368669f52e7e4623a3bca3a5b0332bf Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 27 Feb 2018 22:54:11 +0100 Subject: [PATCH 027/828] Removed DisplayVisibleMin/DisplayVisibleMax facility (as part of # 1542). Misc comments. --- imgui.cpp | 4 +--- imgui.h | 8 +++----- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index c0accaefdbbf..e05811a7ad03 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -250,6 +250,7 @@ Here is a change-log of API breaking changes, if you are using one of the functions listed, expect to have to fix some code. Also read releases logs https://github.com/ocornut/imgui/releases for more details. + - 2018/02/27 (1.XX) - removed io.DisplayVisibleMin, io.DisplayVisibleMax settings (it was used to clip within the DisplayPos..DisplayPos+Size range, I don't know of anyone using it) - 2018/02/18 (1.60) - BeginDragDropSource(): temporarily removed the optional mouse_button=0 parameter because it is not really usable in many situations at the moment. - 2018/02/16 (1.60) - obsoleted the io.RenderDrawListsFn callback, you can call your graphics engine render function after ImGui::Render(). Use ImGui::GetDrawData() to retrieve the ImDrawData* to display. - 2018/02/07 (1.60) - reorganized context handling to be more explicit, @@ -864,7 +865,6 @@ ImGuiIO::ImGuiIO() FontDefault = NULL; FontAllowUserScaling = false; DisplayFramebufferScale = ImVec2(1.0f, 1.0f); - DisplayVisibleMin = DisplayVisibleMax = ImVec2(0.0f, 0.0f); // Advanced/subtle behaviors #ifdef __APPLE__ @@ -4747,8 +4747,6 @@ ImVec2 ImGui::GetItemRectSize() static ImRect GetViewportRect() { ImGuiContext& g = *GImGui; - if (g.IO.DisplayVisibleMin.x != g.IO.DisplayVisibleMax.x && g.IO.DisplayVisibleMin.y != g.IO.DisplayVisibleMax.y) - return ImRect(g.IO.DisplayVisibleMin, g.IO.DisplayVisibleMax); return ImRect(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y); } diff --git a/imgui.h b/imgui.h index 608ce1b289ea..02095f3f044b 100644 --- a/imgui.h +++ b/imgui.h @@ -970,8 +970,6 @@ struct ImGuiIO bool FontAllowUserScaling; // = false // Allow user scaling text of individual window with CTRL+Wheel. ImFont* FontDefault; // = NULL // Font to use on NewFrame(). Use NULL to uses Fonts->Fonts[0]. ImVec2 DisplayFramebufferScale; // = (1.0f,1.0f) // For retina display or other situations where window coordinates are different from framebuffer coordinates. User storage only, presently not used by ImGui. - ImVec2 DisplayVisibleMin; // (0.0f,0.0f) // If you use DisplaySize as a virtual space larger than your screen, set DisplayVisibleMin/Max to the visible area. - ImVec2 DisplayVisibleMax; // (0.0f,0.0f) // If the values are the same, we defaults to Min=(0.0f) and Max=DisplaySize // Advanced/subtle behaviors bool OptMacOSXBehaviors; // = defined(__APPLE__) // OS X style: Text editing cursor movement using Alt instead of Ctrl, Shortcuts using Cmd/Super instead of Ctrl, Line/Text Start and End using Cmd+Arrows instead of Home/End, Double click selects by word instead of selecting whole text, Multi-selection in lists uses Cmd/Super instead of Ctrl @@ -1004,8 +1002,8 @@ struct ImGuiIO ImVec2 MousePos; // Mouse position, in pixels. Set to ImVec2(-FLT_MAX,-FLT_MAX) if mouse is unavailable (on another screen, etc.) bool MouseDown[5]; // Mouse buttons: left, right, middle + extras. ImGui itself mostly only uses left button (BeginPopupContext** are using right button). Others buttons allows us to track if the mouse is being used by your application + available to user as a convenience via IsMouse** API. - float MouseWheel; // Mouse wheel: 1 unit scrolls about 5 lines text. - float MouseWheelH; // Mouse wheel (Horizontal). Most users don't have a mouse with an horizontal wheel, may not be filled by all back-ends. + float MouseWheel; // Mouse wheel Vertical: 1 unit scrolls about 5 lines text. + float MouseWheelH; // Mouse wheel Horizontal. Most users don't have a mouse with an horizontal wheel, may not be filled by all back-ends. bool MouseDrawCursor; // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). bool KeyCtrl; // Keyboard modifier pressed: Control bool KeyShift; // Keyboard modifier pressed: Shift @@ -1477,7 +1475,7 @@ enum ImDrawListFlags_ // This is the low-level list of polygons that ImGui functions are filling. At the end of the frame, all command lists are passed to your ImGuiIO::RenderDrawListFn function for rendering. // Each ImGui window contains its own ImDrawList. You can use ImGui::GetWindowDrawList() to access the current window draw list and draw custom primitives. // You can interleave normal ImGui:: calls and adding primitives to the current draw list. -// All positions are generally in pixel coordinates (top-left at (0,0), bottom-right at io.DisplaySize), however you are totally free to apply whatever transformation matrix to want to the data (if you apply such transformation you'll want to apply it to ClipRect as well) +// All positions are generally in pixel coordinates (generally top-left at 0,0, bottom-right at io.DisplaySize, unless multiple viewports are used), however you are totally free to apply whatever transformation matrix to want to the data (if you apply such transformation you'll want to apply it to ClipRect as well) // Important: Primitives are always added to the list and not culled (culling is done at higher-level by ImGui:: functions), if you use this API a lot consider coarse culling your drawn objects. struct ImDrawList { From 184a6f11985212f442934d9bbed9b7ae8203e5eb Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 27 Feb 2018 22:56:22 +0100 Subject: [PATCH 028/828] Internals: Moving window: test mouse validity while translating window + using SetWindowPos(). --- imgui.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index e05811a7ad03..6555f6d97496 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3234,13 +3234,13 @@ static void ImGui::UpdateMovingWindow() KeepAliveID(g.ActiveId); IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindow); ImGuiWindow* moving_window = g.MovingWindow->RootWindow; - if (g.IO.MouseDown[0]) + if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos)) { ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset; if (moving_window->PosFloat.x != pos.x || moving_window->PosFloat.y != pos.y) { MarkIniSettingsDirty(moving_window); - moving_window->PosFloat = pos; + SetWindowPos(moving_window, pos, ImGuiCond_Always); } FocusWindow(g.MovingWindow); } From 67319a71e5991d02dae9b9a86240119fb47d5d6a Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 27 Feb 2018 22:58:27 +0100 Subject: [PATCH 029/828] Internal: Minor renaming. --- imgui.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 6555f6d97496..2bb007c96438 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3847,7 +3847,7 @@ static void AddWindowToSortedBuffer(ImVector* out_sorted_windows, } } -static void AddDrawListToDrawData(ImVector* out_render_list, ImDrawList* draw_list) +static void AddDrawListToDrawData(ImVector* out_list, ImDrawList* draw_list) { if (draw_list->CmdBuffer.empty()) return; @@ -3877,17 +3877,17 @@ static void AddDrawListToDrawData(ImVector* out_render_list, ImDraw if (sizeof(ImDrawIdx) == 2) IM_ASSERT(draw_list->_VtxCurrentIdx < (1 << 16) && "Too many vertices in ImDrawList using 16-bit indices. Read comment above"); - out_render_list->push_back(draw_list); + out_list->push_back(draw_list); } -static void AddWindowToDrawData(ImVector* out_render_list, ImGuiWindow* window) +static void AddWindowToDrawData(ImVector* out_list, ImGuiWindow* window) { - AddDrawListToDrawData(out_render_list, window->DrawList); + AddDrawListToDrawData(out_list, window->DrawList); for (int i = 0; i < window->DC.ChildWindows.Size; i++) { ImGuiWindow* child = window->DC.ChildWindows[i]; if (child->Active && child->HiddenFrames <= 0) // clipped children may have been marked not active - AddWindowToDrawData(out_render_list, child); + AddWindowToDrawData(out_list, child); } } From b5ced477f4dc1e392214b022ef53bf148b9f23b6 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 27 Feb 2018 23:02:50 +0100 Subject: [PATCH 030/828] Metrics: Added option to show begin order, useful for debugging. --- imgui.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 2bb007c96438..f7976f72c21d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -13100,7 +13100,9 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::Text("%d vertices, %d indices (%d triangles)", ImGui::GetIO().MetricsRenderVertices, ImGui::GetIO().MetricsRenderIndices, ImGui::GetIO().MetricsRenderIndices / 3); ImGui::Text("%d allocations", (int)GImAllocatorActiveAllocationsCount); static bool show_clip_rects = true; + static bool show_window_begin_order = false; ImGui::Checkbox("Show clipping rectangles when hovering draw commands", &show_clip_rects); + ImGui::Checkbox("Show window begin order", &show_window_begin_order); ImGui::Separator(); struct Funcs @@ -13194,7 +13196,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) (flags & ImGuiWindowFlags_ChildWindow) ? "Child " : "", (flags & ImGuiWindowFlags_Tooltip) ? "Tooltip " : "", (flags & ImGuiWindowFlags_Popup) ? "Popup " : "", (flags & ImGuiWindowFlags_Modal) ? "Modal " : "", (flags & ImGuiWindowFlags_ChildMenu) ? "ChildMenu " : "", (flags & ImGuiWindowFlags_NoSavedSettings) ? "NoSavedSettings " : ""); ImGui::BulletText("Scroll: (%.2f/%.2f,%.2f/%.2f)", window->Scroll.x, GetScrollMaxX(window), window->Scroll.y, GetScrollMaxY(window)); - ImGui::BulletText("Active: %d, WriteAccessed: %d", window->Active, window->WriteAccessed); + ImGui::BulletText("Active: %d, WriteAccessed: %d, BeginOrderWithinContext: %d", window->Active, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1); ImGui::BulletText("NavLastIds: 0x%08X,0x%08X, NavLayerActiveMask: %X", window->NavLastIds[0], window->NavLastIds[1], window->DC.NavLayerActiveMask); ImGui::BulletText("NavLastChildNavWindow: %s", window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL"); if (window->NavRectRel[0].IsInverted()) @@ -13242,6 +13244,20 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize); ImGui::TreePop(); } + if (show_window_begin_order) + { + for (int n = 0; n < g.Windows.Size; n++) + { + ImGuiWindow* window = g.Windows[n]; + if ((window->Flags & ImGuiWindowFlags_ChildWindow) || !window->WasActive) + continue; + char buf[32]; + ImFormatString(buf, IM_ARRAYSIZE(buf), "%d", window->BeginOrderWithinContext); + float font_size = ImGui::GetFontSize() * 2; + g.OverlayDrawList.AddRectFilled(window->Pos, window->Pos + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255)); + g.OverlayDrawList.AddText(NULL, font_size, window->Pos, IM_COL32(255, 255, 255, 255), buf); + } + } } ImGui::End(); } From 735267d27c4079306eac46c4e5fbdfdfc2681149 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 27 Feb 2018 23:07:45 +0100 Subject: [PATCH 031/828] Viewport: Initial viewport branch+ platform api commit (squashed from previous commits, still very WIP) (#1542) --- imgui.cpp | 763 ++++++++++++++++++++++++++++++++++++++++++++--- imgui.h | 68 ++++- imgui_demo.cpp | 2 + imgui_internal.h | 76 ++++- 4 files changed, 847 insertions(+), 62 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index f7976f72c21d..891c85d0179b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -713,8 +713,6 @@ static void SaveIniSettingsToDisk(const char* ini_filename); static void SaveIniSettingsToMemory(ImVector& out_buf); static void MarkIniSettingsDirty(ImGuiWindow* window); -static ImRect GetViewportRect(); - static void ClosePopupToLevel(int remaining); static ImGuiWindow* GetFrontMostModalRootWindow(); @@ -734,8 +732,20 @@ static void NavUpdateWindowing(); static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id); static void UpdateMovingWindow(); +static void UpdateMovingWindowDropViewport(ImGuiWindow* window); static void UpdateManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4]); static void FocusFrontMostActiveWindow(ImGuiWindow* ignore_window); + +// Viewport +const ImGuiID IMGUI_VIEWPORT_DEFAULT_ID = 0x11111111; // Using a constant instead of e.g. ImHash("ViewportDefault", 0); so it's easier to spot in the debugger. The exact value doesn't matter. +static inline ImRect GetViewportRect(ImGuiWindow* window) { return window->Viewport->GetRect(); } +static inline ImVec2 ConvertViewportPosToOsDesktopPos(const ImVec2& imgui_pos, ImGuiViewport* viewport) { return imgui_pos - viewport->Pos + viewport->PlatformOsDesktopPos; } +static inline ImVec2 ConvertOsDesktopPosToViewportPos(const ImVec2& os_pos, ImGuiViewport* viewport) { return os_pos - viewport->PlatformOsDesktopPos + viewport->Pos; } +static void UpdateViewports(); +static void UpdateWindowViewport(ImGuiWindow* window, bool window_pos_set_by_api); +static void SetWindowViewportTranslateToPreservePlatformPos(ImGuiWindow* window, ImGuiViewport* old_viewport, ImGuiViewport* new_viewport); +static void ResizeViewport(ImGuiViewport* viewport, const ImVec2& size); +static void ResizeViewportTranslateWindows(int viewport_idx_min, int viewport_idx_max, float pos_x_delta, int idx_delta, ImGuiViewport* viewport_to_erase); } //----------------------------------------------------------------------------- @@ -848,6 +858,7 @@ ImGuiIO::ImGuiIO() // Settings DisplaySize = ImVec2(-1.0f, -1.0f); DeltaTime = 1.0f/60.0f; + ConfigFlags = 0x00; NavFlags = 0x00; IniSavingRate = 5.0f; IniFilename = "imgui.ini"; @@ -1900,7 +1911,10 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) Name = ImStrdup(name); ID = ImHash(name, 0); IDStack.push_back(ID); - Flags = 0; + Flags = FlagsPreviousFrame = 0; + Viewport = NULL; + ViewportId = 0; + ViewportOsDesktopPos = ImVec2(FLT_MAX, FLT_MAX); PosFloat = Pos = ImVec2(0.0f, 0.0f); Size = SizeFull = ImVec2(0.0f, 0.0f); SizeContents = SizeContentsExplicit = ImVec2(0.0f, 0.0f); @@ -2113,6 +2127,10 @@ static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFla return false; } + // Filter by viewport + if (window->Viewport != g.MouseViewport) + return false; + return true; } @@ -2655,7 +2673,13 @@ ImGuiStyle& ImGui::GetStyle() ImDrawData* ImGui::GetDrawData() { ImGuiContext& g = *GImGui; - return g.DrawData.Valid ? &g.DrawData : NULL; + return g.Viewports[0]->DrawData.Valid ? &g.Viewports[0]->DrawData : NULL; +} + +ImDrawData* ImGui::GetDrawDataForViewport(ImGuiID viewport_id) +{ + ImGuiViewport* viewport = FindViewportByID(viewport_id); + return viewport && viewport->DrawData.Valid ? &viewport->DrawData : NULL; } float ImGui::GetTime() @@ -2710,7 +2734,7 @@ static ImVec2 NavCalcPreferredMousePos() return g.IO.MousePos; const ImRect& rect_rel = window->NavRectRel[g.NavLayer]; ImVec2 pos = g.NavWindow->Pos + ImVec2(rect_rel.Min.x + ImMin(g.Style.FramePadding.x*4, rect_rel.GetWidth()), rect_rel.Max.y - ImMin(g.Style.FramePadding.y, rect_rel.GetHeight())); - ImRect visible_rect = GetViewportRect(); + ImRect visible_rect = ImGui::GetViewportRect(window); return ImFloor(ImClamp(pos, visible_rect.Min, visible_rect.Max)); // ImFloor() is important because non-integer mouse position application in back-end might be lossy and result in undesirable non-zero delta. } @@ -3212,7 +3236,7 @@ static void ImGui::NavUpdate() // For scoring we use a single segment on the left side our current item bounding box (not touching the edge to avoid box overlap with zero-spaced items) ImRect nav_rect_rel = (g.NavWindow && !g.NavWindow->NavRectRel[g.NavLayer].IsInverted()) ? g.NavWindow->NavRectRel[g.NavLayer] : ImRect(0,0,0,0); - g.NavScoringRectScreen = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : GetViewportRect(); + g.NavScoringRectScreen = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : ImRect(0,0,0,0); g.NavScoringRectScreen.Min.x = ImMin(g.NavScoringRectScreen.Min.x + 1.0f, g.NavScoringRectScreen.Max.x); g.NavScoringRectScreen.Max.x = g.NavScoringRectScreen.Min.x; IM_ASSERT(!g.NavScoringRectScreen.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows us to remove extraneous fabsf() calls in NavScoreItem(). @@ -3224,6 +3248,39 @@ static void ImGui::NavUpdate() #endif } +static void ImGui::UpdateMovingWindowDropViewport(ImGuiWindow* window) +{ + // On release we either drop window over an existing viewport or create a new one + // (We convert position from one viewport space to another, which is unnecessary at the moment but allows us to have viewport overlapping in term of imgui position) + ImGuiContext& g = *GImGui; + if (!(g.IO.ConfigFlags & ImGuiConfigFlags_MultiViewports)) + return; + + ImRect mouse_viewport_rect = g.MouseViewport->GetRect(); + ImVec2 window_pos_in_mouse_viewport = ConvertOsDesktopPosToViewportPos(ConvertViewportPosToOsDesktopPos(window->Pos, window->Viewport), g.MouseViewport); + ImRect window_rect_in_mouse_viewport = ImRect(window_pos_in_mouse_viewport, window_pos_in_mouse_viewport + window->Size); + if (mouse_viewport_rect.Contains(window_rect_in_mouse_viewport)) + { + // Drop on an existing viewport + ImGuiViewport* old_viewport = window->Viewport; + SetWindowViewportTranslateToPreservePlatformPos(window, window->Viewport, g.MouseViewport); + + // Our current scheme allow any window to land on a viewport, so when that viewport merges, move other windows as well + // FIXME-OPT + if (window->Flags & ImGuiWindowFlags_FullViewport) + for (int n = 0; n < g.Windows.Size; n++) + if (g.Windows[n]->Viewport == old_viewport) + SetWindowViewportTranslateToPreservePlatformPos(g.Windows[n], old_viewport, g.MouseViewport); + } + else + { + // Create new viewport + ImVec2 os_pos = ConvertViewportPosToOsDesktopPos(window->Pos, window->Viewport); + ImGuiViewport* viewport = Viewport(window->ID, 0, os_pos, window->Size); + SetWindowViewportTranslateToPreservePlatformPos(window, window->Viewport, viewport); + } +} + static void ImGui::UpdateMovingWindow() { ImGuiContext& g = *GImGui; @@ -3246,6 +3303,11 @@ static void ImGui::UpdateMovingWindow() } else { + UpdateMovingWindowDropViewport(moving_window); + + // Clear the NoInput flag set by the Viewport system + moving_window->Viewport->Flags &= ~ImGuiViewportFlags_NoInputs; + ClearActiveID(); g.MovingWindow = NULL; } @@ -3259,10 +3321,210 @@ static void ImGui::UpdateMovingWindow() if (!g.IO.MouseDown[0]) ClearActiveID(); } + + if (g.MovingWindow != NULL) + g.MovingWindow->Viewport->Flags &= ~ImGuiViewportFlags_NoInputs; g.MovingWindow = NULL; } } +// If the back-end doesn't set MouseLastHoveredViewport or doesn't honor ImGuiViewportFlags_NoInputs, we do a search ourselves. +// This search won't take account of the possibility that non-imgui windows may be in-between our dragged window and our target window. +static ImGuiViewport* FindViewportHoveredFromOsWindowStack(const ImVec2 mouse_os_pos) +{ + ImGuiContext& g = *GImGui; + ImGuiViewport* best_candidate = NULL; + for (int n = 0; n < g.Viewports.Size; n++) + { + ImGuiViewport* viewport = g.Viewports[n]; + ImRect os_rect = ImRect(viewport->PlatformOsDesktopPos, viewport->PlatformOsDesktopPos + viewport->Size); + if (!(viewport->Flags & ImGuiViewportFlags_NoInputs) && os_rect.Contains(mouse_os_pos)) + if (best_candidate == NULL || best_candidate->LastFrameAsRefViewport < viewport->LastFrameAsRefViewport) + best_candidate = viewport; + } + return best_candidate; +} + +static void ImGui::UpdateViewports() +{ + ImGuiContext& g = *GImGui; + + // Mouse handling: latch the expected mouse OS position (if any) before processing viewport erasure + ImGuiViewport* viewport_ref = g.IO.MousePosViewport ? FindViewportByID(g.IO.MousePosViewport) : g.Viewports[0]; + const ImVec2 mouse_os_pos = ConvertViewportPosToOsDesktopPos(g.IO.MousePos, viewport_ref); + + for (int n = 1; n < g.Viewports.Size; n++) + { + // Erase unused viewports + ImGuiViewport* viewport = g.Viewports[n]; + IM_ASSERT(viewport->Idx == n); + IM_ASSERT(!(viewport->Flags & ImGuiViewportFlags_MainViewport)); + if (viewport->LastFrameActive < g.FrameCount - 2) + { + // Translate windows like if we were resizing the viewport to be zero-width + ResizeViewportTranslateWindows(n + 1, g.Viewports.Size, viewport->Pos.x - viewport->GetNextX(), -1, viewport); + if (g.IO.RendererInterface.DestroyViewport) + g.IO.RendererInterface.DestroyViewport(viewport); + if (g.IO.PlatformInterface.DestroyViewport) + g.IO.PlatformInterface.DestroyViewport(viewport); + viewport->PlatformUserData = viewport->PlatformHandle = viewport->RendererUserData = NULL; + g.Viewports.erase(g.Viewports.Data + n); + + // Destroy + if (viewport == viewport_ref) viewport_ref = NULL; + if (viewport == g.MouseViewport) g.MouseViewport = NULL; + if (viewport == g.MouseLastHoveredViewport) g.MouseLastHoveredViewport = NULL; + IM_DELETE(viewport); + n--; + continue; + } + + // Translate resized viewports + if (n + 1 < g.Viewports.Size) + { + float dx = viewport->GetNextX() - g.Viewports[viewport->Idx + 1]->Pos.x; + if (dx != 0.0f) + ResizeViewportTranslateWindows(viewport->Idx + 1, g.Viewports.Size, dx, 0, NULL); + } + } + + // Update main viewport with current size (and OS window position, if known) + ImGuiViewport* main_viewport = g.Viewports[0]; + IM_ASSERT(main_viewport->ID == IMGUI_VIEWPORT_DEFAULT_ID); + ImVec2 main_viewport_os_desktop_pos = ImVec2(0.0f, 0.0f); + if ((g.IO.ConfigFlags & ImGuiConfigFlags_MultiViewports)) + main_viewport_os_desktop_pos = g.IO.PlatformInterface.GetWindowPos(main_viewport); + Viewport(IMGUI_VIEWPORT_DEFAULT_ID, ImGuiViewportFlags_MainViewport, main_viewport_os_desktop_pos, g.IO.DisplaySize); + + if (!(g.IO.ConfigFlags & ImGuiConfigFlags_MultiViewports)) + { + g.MouseViewport = g.MouseLastViewport = main_viewport; + return; + } + + // Mouse handling: decide on the actual mouse viewport for this frame between the active/focused viewport and the hovered viewport. + ImGuiViewport* viewport_hovered = NULL; + if (g.IO.ConfigFlags & ImGuiConfigFlags_PlatformHasMouseHoveredViewport) + { + viewport_hovered = g.IO.MouseHoveredViewport ? FindViewportByID(g.IO.MouseHoveredViewport) : NULL; + if (viewport_hovered && (viewport_hovered->Flags & ImGuiViewportFlags_NoInputs)) + { + // Back-end failed at honoring its contract + IM_ASSERT(0); + viewport_hovered = FindViewportHoveredFromOsWindowStack(mouse_os_pos); + } + } + else + { + // If the back-end doesn't set MouseLastHoveredViewport or doesn't honor ImGuiViewportFlags_NoInputs, we do a search ourselves. + // This search won't take account of the possibility that non-imgui windows may be in-between our dragged window and our target window. + viewport_hovered = FindViewportHoveredFromOsWindowStack(mouse_os_pos); + } + if (viewport_hovered != NULL) + g.MouseLastHoveredViewport = viewport_hovered; + + // If reference viewport just has been deleted, use a position relative to the main viewport + if (viewport_ref == NULL) + { + viewport_ref = main_viewport; + g.IO.MousePos = ConvertOsDesktopPosToViewportPos(mouse_os_pos, viewport_ref); + } + + g.MouseLastViewport = g.MouseViewport; + g.MouseViewport = viewport_ref; + g.MouseViewport->LastFrameAsRefViewport = g.FrameCount; + + // When dragging something, always refer to the last hovered viewport (so when we are between viewport, our dragged preview will tend to show in the last viewport) + const bool is_mouse_dragging_with_an_expected_destination = g.DragDropActive || (g.MovingWindow != NULL); + const bool is_mouse_all_released = !ImGui::IsAnyMouseDown(); + if (is_mouse_dragging_with_an_expected_destination || is_mouse_all_released) + { + if (is_mouse_dragging_with_an_expected_destination) + viewport_hovered = g.MouseLastHoveredViewport; + if (viewport_hovered != NULL && viewport_hovered != g.MouseViewport && !(viewport_hovered->Flags & ImGuiViewportFlags_NoInputs)) + { + g.IO.MousePos = ConvertOsDesktopPosToViewportPos(ConvertViewportPosToOsDesktopPos(g.IO.MousePos, g.MouseViewport), viewport_hovered); + g.MouseViewport = viewport_hovered; + } + } + + IM_ASSERT(g.MouseViewport != NULL); +} + +void ImGui::UpdatePlatformWindows() +{ + // Create/resize windows + ImGuiContext& g = *GImGui; + if (!(g.IO.ConfigFlags & ImGuiConfigFlags_MultiViewports)) + return; + for (int i = 0; i < g.Viewports.Size; i++) + { + ImGuiViewport* viewport = g.Viewports[i]; + if ((viewport->Flags & ImGuiViewportFlags_MainViewport) || (viewport->LastFrameActive < g.FrameCount)) + continue; + viewport->PlatformRequestClose = false; + + // FIXME-PLATFORM + bool is_new_window = viewport->PlatformHandle == NULL && viewport->PlatformUserData == NULL && viewport->RendererUserData == NULL; + if (is_new_window && viewport->PlatformHandle == NULL && viewport->PlatformUserData == NULL) + g.IO.PlatformInterface.CreateViewport(viewport); + if (is_new_window && viewport->RendererUserData == NULL && g.IO.RendererInterface.CreateViewport != NULL) + g.IO.RendererInterface.CreateViewport(viewport); + + g.IO.PlatformInterface.SetWindowPos(viewport, viewport->PlatformOsDesktopPos); + g.IO.PlatformInterface.SetWindowSize(viewport, viewport->Size); + + char name[20]; + sprintf(name, "Viewport_%08X", viewport->ID); + if (viewport->Name == NULL || strcmp(viewport->Name, name) != 0) + { + g.IO.PlatformInterface.SetWindowTitle(viewport, name); + ImGui::MemFree(viewport->Name); + viewport->Name = ImStrdup(name); + } + + if (is_new_window) + { + // On startup ensure platform window don't get focus. + if (g.FrameCount < 2) + viewport->Flags |= ImGuiViewportFlags_NoFocusOnAppearing; + g.IO.PlatformInterface.ShowWindow(viewport); + } + } +} + +void ImGui::RenderPlatformWindows() +{ + // Render + ImGuiContext& g = *GImGui; + if (!(g.IO.ConfigFlags & ImGuiConfigFlags_MultiViewports)) + return; + for (int i = 0; i < g.Viewports.Size; i++) + { + ImGuiViewport* viewport = g.Viewports[i]; + if ((viewport->Flags & ImGuiViewportFlags_MainViewport) || (viewport->LastFrameActive < g.FrameCount)) + continue; + g.IO.DisplayPos = viewport->Pos; // For legacy reason our render functions are viewport agnostic so we pass our coordinates via the IO structure + g.IO.DisplaySize = viewport->Size; + if (g.IO.PlatformInterface.RenderViewport) + g.IO.PlatformInterface.RenderViewport(viewport); + if (g.IO.RendererInterface.RenderViewport) + g.IO.RendererInterface.RenderViewport(viewport); + } + + // Swap + for (int i = 0; i < g.Viewports.Size; i++) + { + ImGuiViewport* viewport = g.Viewports[i]; + if ((viewport->Flags & ImGuiViewportFlags_MainViewport) || (viewport->LastFrameActive < g.FrameCount)) + continue; + if (g.IO.PlatformInterface.SwapBuffers) + g.IO.PlatformInterface.SwapBuffers(viewport); + if (g.IO.RendererInterface.SwapBuffers) + g.IO.RendererInterface.SwapBuffers(viewport); + } +} + void ImGui::NewFrame() { IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() or ImGui::SetCurrentContext()?"); @@ -3285,6 +3547,14 @@ void ImGui::NewFrame() if (g.IO.NavFlags & ImGuiNavFlags_EnableKeyboard) IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation."); + if (g.IO.ConfigFlags & ImGuiConfigFlags_MultiViewports) + { + IM_ASSERT(g.IO.PlatformInterface.CreateViewport != NULL); + IM_ASSERT(g.IO.PlatformInterface.DestroyViewport != NULL); + //IM_ASSERT(g.IO.PlatformInterface.RenderViewport != NULL || g.IO.RendererInterface.RenderViewport != NULL); // Missing rendering function + IM_ASSERT(g.Viewports[0]->PlatformUserData != NULL); // Platform init function didn't setup main viewport + } + // Load settings on first frame if (!g.SettingsLoaded) { @@ -3298,9 +3568,16 @@ void ImGui::NewFrame() g.TooltipOverrideCount = 0; g.WindowsActiveCount = 0; + UpdateViewports(); + + // Setup font, draw list shared data + // FIXME-VIEWPORT: the concept of a single ClipRectFullscreen is not ideal! SetCurrentFont(GetDefaultFont()); IM_ASSERT(g.Font->IsLoaded()); - g.DrawListSharedData.ClipRectFullscreen = ImVec4(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y); + ImVec2 virtual_space_max(0,0); + for (int n = 0; n < g.Viewports.Size; n++) + virtual_space_max = ImMax(virtual_space_max, g.Viewports[n]->Pos + g.Viewports[n]->Size); + g.DrawListSharedData.ClipRectFullscreen = ImVec4(0.0f, 0.0f, virtual_space_max.x, virtual_space_max.y); g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol; g.OverlayDrawList.Clear(); @@ -3309,7 +3586,8 @@ void ImGui::NewFrame() g.OverlayDrawList.Flags = (g.Style.AntiAliasedLines ? ImDrawListFlags_AntiAliasedLines : 0) | (g.Style.AntiAliasedFill ? ImDrawListFlags_AntiAliasedFill : 0); // Mark rendering data as invalid to prevent user who may have a handle on it to use it - g.DrawData.Clear(); + for (int n = 0; n < g.Viewports.Size; n++) + g.Viewports[n]->DrawData.Clear(); // Clear reference to active widget if the widget isn't alive anymore if (!g.HoveredIdPreviousFrame) @@ -3348,7 +3626,7 @@ void ImGui::NewFrame() // Update mouse input state // If mouse just appeared or disappeared (usually denoted by -FLT_MAX component, but in reality we test for -256000.0f) we cancel out movement in MouseDelta - if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MousePosPrev)) + if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MousePosPrev) && g.MouseViewport == g.MouseLastViewport) g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev; else g.IO.MouseDelta = ImVec2(0.0f, 0.0f); @@ -3413,6 +3691,7 @@ void ImGui::NewFrame() // - We also support the moved window toggling the NoInputs flag after moving has started in order to be able to detect windows below it, which is useful for e.g. docking mechanisms. g.HoveredWindow = (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoInputs)) ? g.MovingWindow : FindHoveredWindow(); g.HoveredRootWindow = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL; + IM_ASSERT(g.HoveredWindow == NULL || g.HoveredWindow == g.MovingWindow || g.HoveredWindow->Viewport == g.MouseViewport); ImGuiWindow* modal_window = GetFrontMostModalRootWindow(); if (modal_window != NULL) @@ -3520,6 +3799,7 @@ void ImGui::NewFrame() { ImGuiWindow* window = g.Windows[i]; window->WasActive = window->Active; + window->BeginCount = 0; window->Active = false; window->WriteAccessed = false; } @@ -3553,9 +3833,12 @@ static void SettingsHandlerWindow_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry; float x, y; int i; - if (sscanf(line, "Pos=%f,%f", &x, &y) == 2) settings->Pos = ImVec2(x, y); - else if (sscanf(line, "Size=%f,%f", &x, &y) == 2) settings->Size = ImMax(ImVec2(x, y), GImGui->Style.WindowMinSize); - else if (sscanf(line, "Collapsed=%d", &i) == 1) settings->Collapsed = (i != 0); + ImU32 u1; + if (sscanf(line, "Pos=%f,%f", &x, &y) == 2) { settings->Pos = ImVec2(x, y); } + else if (sscanf(line, "Size=%f,%f", &x, &y) == 2) { settings->Size = ImMax(ImVec2(x, y), GImGui->Style.WindowMinSize); } + else if (sscanf(line, "ViewportId=0x%08X", &u1) == 1) { settings->ViewportId = u1; } + else if (sscanf(line, "ViewportOsDesktopPos=%f,%f", &x, &y)==2) { settings->ViewportOsDesktopPos = ImVec2(x, y); } + else if (sscanf(line, "Collapsed=%d", &i) == 1) { settings->Collapsed = (i != 0); } } static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf) @@ -3570,8 +3853,10 @@ static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSetting ImGuiWindowSettings* settings = ImGui::FindWindowSettings(window->ID); if (!settings) settings = AddWindowSettings(window->Name); - settings->Pos = window->Pos; + settings->Pos = window->Pos;// - window->Viewport->Pos; settings->Size = window->SizeFull; + settings->ViewportId = window->ViewportId; + settings->ViewportOsDesktopPos = window->ViewportOsDesktopPos; settings->Collapsed = window->Collapsed; } @@ -3589,6 +3874,12 @@ static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSetting buf->appendf("[%s][%s]\n", handler->TypeName, name); buf->appendf("Pos=%d,%d\n", (int)settings->Pos.x, (int)settings->Pos.y); buf->appendf("Size=%d,%d\n", (int)settings->Size.x, (int)settings->Size.y); + if (settings->ViewportId != 0 && settings->ViewportId != ImGui::IMGUI_VIEWPORT_DEFAULT_ID) + { + buf->appendf("ViewportId=0x%08X\n", settings->ViewportId); + if (settings->ViewportOsDesktopPos.x != FLT_MAX && settings->ViewportOsDesktopPos.y != FLT_MAX) + buf->appendf("ViewportOsDesktopPos=%d,%d\n", (int)settings->ViewportOsDesktopPos.x, (int)settings->ViewportOsDesktopPos.y); + } buf->appendf("Collapsed=%d\n", settings->Collapsed); buf->appendf("\n"); } @@ -3609,6 +3900,10 @@ void ImGui::Initialize(ImGuiContext* context) ini_handler.WriteAllFn = SettingsHandlerWindow_WriteAll; g.SettingsHandlers.push_front(ini_handler); + // Create default viewport + ImGuiViewport* viewport = IM_NEW(ImGuiViewport)(IMGUI_VIEWPORT_DEFAULT_ID, 0); + g.Viewports.push_back(viewport); + g.Initialized = true; } @@ -3647,7 +3942,21 @@ void ImGui::Shutdown(ImGuiContext* context) g.FontStack.clear(); g.OpenPopupStack.clear(); g.CurrentPopupStack.clear(); - g.DrawDataBuilder.ClearFreeMemory(); + g.MouseViewport = g.MouseLastHoveredViewport = NULL; + for (int i = 0; i < g.Viewports.Size; i++) + { + ImGuiViewport* viewport = g.Viewports[i]; + if (!(viewport->Flags & ImGuiViewportFlags_MainViewport)) // FIXME-VIEWPORT + { + if (g.IO.RendererInterface.DestroyViewport) + g.IO.RendererInterface.DestroyViewport(viewport); + if (g.IO.PlatformInterface.DestroyViewport) + g.IO.PlatformInterface.DestroyViewport(viewport); + } + viewport->PlatformUserData = viewport->PlatformHandle = viewport->RendererUserData = NULL; + IM_DELETE(viewport); + } + g.Viewports.clear(); g.OverlayDrawList.ClearFreeMemory(); g.PrivateClipboard.clear(); g.InputTextState.Text.clear(); @@ -3896,9 +4205,9 @@ static void AddWindowToDrawDataSelectLayer(ImGuiWindow* window) ImGuiContext& g = *GImGui; g.IO.MetricsActiveWindows++; if (window->Flags & ImGuiWindowFlags_Tooltip) - AddWindowToDrawData(&g.DrawDataBuilder.Layers[1], window); + AddWindowToDrawData(&window->Viewport->DrawDataBuilder.Layers[1], window); else - AddWindowToDrawData(&g.DrawDataBuilder.Layers[0], window); + AddWindowToDrawData(&window->Viewport->DrawDataBuilder.Layers[0], window); } void ImDrawDataBuilder::FlattenIntoSingleLayer() @@ -4053,7 +4362,8 @@ void ImGui::Render() { // Gather windows to render g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = g.IO.MetricsActiveWindows = 0; - g.DrawDataBuilder.Clear(); + for (int n = 0; n != g.Viewports.Size; n++) + g.Viewports[n]->DrawDataBuilder.Clear(); ImGuiWindow* window_to_render_front_most = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget : NULL; for (int n = 0; n != g.Windows.Size; n++) { @@ -4063,7 +4373,6 @@ void ImGui::Render() } if (window_to_render_front_most && window_to_render_front_most->Active && window_to_render_front_most->HiddenFrames <= 0) // NavWindowingTarget is always temporarily displayed as the front-most window AddWindowToDrawDataSelectLayer(window_to_render_front_most); - g.DrawDataBuilder.FlattenIntoSingleLayer(); // Draw software mouse cursor if requested ImVec2 offset, size, uv[4]; @@ -4079,22 +4388,125 @@ void ImGui::Render() g.OverlayDrawList.AddImage(tex_id, pos, pos + size*sc, uv[0], uv[1], IM_COL32(255,255,255,255)); // White fill g.OverlayDrawList.PopTextureID(); } - if (!g.OverlayDrawList.VtxBuffer.empty()) - AddDrawListToDrawData(&g.DrawDataBuilder.Layers[0], &g.OverlayDrawList); - // Setup ImDrawData structure for end-user - SetupDrawData(&g.DrawDataBuilder.Layers[0], &g.DrawData); - g.IO.MetricsRenderVertices = g.DrawData.TotalVtxCount; - g.IO.MetricsRenderIndices = g.DrawData.TotalIdxCount; + // Setup ImDrawData structures for end-user + g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = 0; + for (int n = 0; n < g.Viewports.Size; n++) + { + ImGuiViewport* viewport = g.Viewports[n]; + viewport->DrawDataBuilder.FlattenIntoSingleLayer(); + AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[0], &g.OverlayDrawList); + SetupDrawData(&viewport->DrawDataBuilder.Layers[0], &viewport->DrawData); + g.IO.MetricsRenderVertices += viewport->DrawData.TotalVtxCount; + g.IO.MetricsRenderIndices += viewport->DrawData.TotalIdxCount; + } // Render. If user hasn't set a callback then they may retrieve the draw data via GetDrawData() #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - if (g.DrawData.CmdListsCount > 0 && g.IO.RenderDrawListsFn != NULL) - g.IO.RenderDrawListsFn(&g.DrawData); + if (g.Viewports[0]->DrawData.CmdListsCount > 0 && g.IO.RenderDrawListsFn != NULL) + g.IO.RenderDrawListsFn(&g.Viewports[0]->DrawData); #endif } } +ImGuiViewport* ImGui::FindViewportByID(ImGuiID id) +{ + ImGuiContext& g = *GImGui; + for (int n = 0; n < g.Viewports.Size; n++) + if (g.Viewports[n]->ID == id) + return g.Viewports[n]; + return NULL; +} + +ImGuiViewport* ImGui::FindViewportByPlatformHandle(void* platform_handle) +{ + ImGuiContext& g = *GImGui; + for (int i = 0; i != g.Viewports.Size; i++) + if (g.Viewports[i]->PlatformHandle == platform_handle) + return g.Viewports[i]; + return NULL; +} + +static void TranslateWindowX(ImGuiWindow* window, float dx) +{ + window->Pos.x += dx; + window->PosFloat.x += dx; + window->ClipRect.Translate(dx, 0.0f); + window->WindowRectClipped.Translate(dx, 0.0f); + window->InnerRect.Translate(dx, 0.0f); + window->DC.CursorPos.x += dx; + window->DC.CursorStartPos.x += dx; + window->DC.CursorMaxPos.x += dx; + window->DC.LastItemRect.Translate(dx, 0.0f); + window->DC.LastItemDisplayRect.Translate(dx, 0.0f); +} + +static void ImGui::ResizeViewportTranslateWindows(int viewport_idx_min, int viewport_idx_max, float pos_x_delta, int idx_delta, ImGuiViewport* viewport_to_erase) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(pos_x_delta != 0.0f || idx_delta != 0); + for (int n = 0; n < g.Windows.Size; n++) + { + ImGuiWindow* window = g.Windows[n]; + if (window->Viewport == viewport_to_erase) + window->Viewport = NULL; // Set to NULL so window->ViewportId becomes the master data + if (window->Viewport == NULL) + continue; + if (window->Viewport->Idx < viewport_idx_min || window->Viewport->Idx > viewport_idx_max) + continue; + TranslateWindowX(window, pos_x_delta); + } + for (int n = viewport_idx_min; n < viewport_idx_max; n++) + { + g.Viewports[n]->Pos.x += pos_x_delta; + g.Viewports[n]->Idx += idx_delta; + } +} + +static void ImGui::ResizeViewport(ImGuiViewport* viewport, const ImVec2& size) +{ + ImGuiContext& g = *GImGui; + if (viewport->Size.x != size.x || viewport->Size.y != size.y) + { + // We defer translating windows to the beginning of the frame. + // Our viewport system already works with fully overlapped viewports, it's only certain user interactions that don't and they can't be performed while resizing. + viewport->Size = size; + //if (viewport->Idx + 1 < g.Viewports.Size) // If this isn't the last viewport, translate following viewports + // ResizeViewportTranslateWindows(viewport->Idx + 1, g.Viewports.Size, viewport->GetNextX() - g.Viewports[viewport->Idx + 1]->Pos.x, 0, NULL); + } + if (viewport == g.Viewports[0]) + { + g.IO.DisplayPos = viewport->Pos; + g.IO.DisplaySize = viewport->Size; + } +} + +ImGuiViewport* ImGui::Viewport(ImGuiID id, ImGuiViewportFlags flags, const ImVec2& os_desktop_pos, const ImVec2& size) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(id != 0); + + ImGuiViewport* viewport = FindViewportByID(id); + if (viewport) + { + ResizeViewport(viewport, size); + } + else + { + // New viewport + viewport = IM_NEW(ImGuiViewport)(id, g.Viewports.Size); + viewport->Pos = ImVec2(g.Viewports.back()->GetNextX(), 0.0f); + viewport->Size = size; + g.Viewports.push_back(viewport); + } + + IM_ASSERT(viewport->Pos.y == 0.0f); + viewport->Flags = flags; + viewport->PlatformOsDesktopPos = os_desktop_pos; + viewport->LastFrameActive = g.FrameCount; + return viewport; +} + const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end) { const char* text_display_end = text; @@ -4460,6 +4872,9 @@ static ImGuiWindow* FindHoveredWindow() continue; if (window->Flags & ImGuiWindowFlags_NoInputs) continue; + IM_ASSERT(window->Viewport); + if (window->Viewport != g.MouseViewport) + continue; // Using the clipped AABB, a child window will typically be clipped by its parent (not always) ImRect bb(window->WindowRectClipped.Min - g.Style.TouchExtraPadding, window->WindowRectClipped.Max + g.Style.TouchExtraPadding); @@ -4484,7 +4899,11 @@ bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool c // Expand for touch input const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding); - return rect_for_touch.Contains(g.IO.MousePos); + if (!rect_for_touch.Contains(g.IO.MousePos)) + return false; + if (!g.MouseViewport->GetRect().Overlaps(rect_clipped)) + return false; + return true; } static bool IsKeyPressedMap(ImGuiKey key, bool repeat) @@ -4975,7 +5394,7 @@ bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags fla // Center modal windows by default // FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window. if (g.NextWindowData.PosCond == 0) - SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); + SetNextWindowPos(GetViewportRect(window).GetCenter(), ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); bool is_open = Begin(name, p_open, flags | ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings); if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display) @@ -5197,14 +5616,14 @@ enum ImGuiPopupPositionPolicy ImGuiPopupPositionPolicy_ComboBox }; -static ImVec2 FindBestWindowPosForPopup(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy = ImGuiPopupPositionPolicy_Default) +static ImVec2 FindBestWindowPosForPopup(ImGuiWindow* window, const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy = ImGuiPopupPositionPolicy_Default) { const ImGuiStyle& style = GImGui->Style; // r_avoid = the rectangle to avoid (e.g. for tooltip it is a rectangle around the mouse cursor which we want to avoid. for popups it's a small point around the cursor.) // r_outer = the visible area rectangle, minus safe area padding. If our popup size won't fit because of safe area padding we ignore it. ImVec2 safe_padding = style.DisplaySafeAreaPadding; - ImRect r_outer(GetViewportRect()); + ImRect r_outer(ImGui::GetViewportRect(window)); r_outer.Expand(ImVec2((size.x - r_outer.GetWidth() > safe_padding.x*2) ? -safe_padding.x : 0.0f, (size.y - r_outer.GetHeight() > safe_padding.y*2) ? -safe_padding.y : 0.0f)); ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size); //GImGui->OverlayDrawList.AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255)); @@ -5293,6 +5712,10 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFl window->PosFloat = settings->Pos; window->Pos = ImFloor(window->PosFloat); window->Collapsed = settings->Collapsed; + if (settings->ViewportId) + window->ViewportId = settings->ViewportId; + if (settings->ViewportOsDesktopPos.x != FLT_MAX && settings->ViewportOsDesktopPos.y != FLT_MAX) + window->ViewportOsDesktopPos = settings->ViewportOsDesktopPos; if (ImLengthSqr(settings->Size) > 0.00001f) size = settings->Size; } @@ -5371,8 +5794,9 @@ static ImVec2 CalcSizeAutoFit(ImGuiWindow* window, const ImVec2& size_contents) } else { - // When the window cannot fit all contents (either because of constraints, either because screen is too small): we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than DisplaySize-WindowPadding. - size_auto_fit = ImClamp(size_contents, style.WindowMinSize, ImMax(style.WindowMinSize, g.IO.DisplaySize - g.Style.DisplaySafeAreaPadding)); + // When the window cannot fit all contents (either because of constraints, either because screen is too small): we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than ViewportSize-WindowPadding. + ImVec2 viewport_size = (window->Flags & ImGuiWindowFlags_FullViewport) ? ImVec2(FLT_MAX, FLT_MAX) : window->Viewport->Size; + size_auto_fit = ImClamp(size_contents, style.WindowMinSize, ImMax(style.WindowMinSize, viewport_size - g.Style.DisplaySafeAreaPadding)); ImVec2 size_auto_fit_after_constraint = CalcSizeAfterConstraint(window, size_auto_fit); if (size_auto_fit_after_constraint.x < size_contents.x && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar)) size_auto_fit.y += style.ScrollbarSize; @@ -5433,6 +5857,144 @@ static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& co *out_size = size_constrained; } +static void ImGui::SetWindowViewportTranslateToPreservePlatformPos(ImGuiWindow* window, ImGuiViewport* prev_viewport, ImGuiViewport* curr_viewport) +{ + if (prev_viewport == curr_viewport) + return; + ImVec2 new_pos = ConvertOsDesktopPosToViewportPos(ConvertViewportPosToOsDesktopPos(window->PosFloat, prev_viewport), curr_viewport); + if ((window->FlagsPreviousFrame ^ window->Flags) & ImGuiWindowFlags_NoTitleBar) + { + // As a convenience, automatically adjust for client rect difference for the common use case of toggling the imgui title-bar when we move our tools to a separate OS window + float title_bar_height = GetFrameHeight(); + if (!(window->Flags & ImGuiWindowFlags_NoTitleBar)) + title_bar_height = -title_bar_height; + new_pos.y += title_bar_height; + window->SizeFull.y -= title_bar_height; + } + SetWindowPos(window, new_pos, ImGuiCond_Always); + window->Viewport = curr_viewport; + window->ViewportId = curr_viewport->ID; +} + +// FIXME-VIEWPORT: This is all super messy and ought to be clarified or rewritten. +static void ImGui::UpdateWindowViewport(ImGuiWindow* window, bool window_pos_set_by_api) +{ + ImGuiContext& g = *GImGui; + ImGuiWindowFlags flags = window->Flags; + + // Restore main viewport if multi viewports are not supported by the back-end + ImGuiViewport* main_viewport = g.Viewports[0]; + if (!(g.IO.ConfigFlags & ImGuiConfigFlags_MultiViewports)) + { + window->Viewport = main_viewport; + window->ViewportId = main_viewport->ID; + return; + } + + const bool window_is_mouse_tooltip = (flags & ImGuiWindowFlags_Tooltip) && g.NavDisableHighlight && !g.NavDisableMouseHover; + const bool window_follow_mouse_viewport = window_is_mouse_tooltip || (g.MovingWindow && g.MovingWindow->RootWindow == window); + + bool created_viewport = false; + + ImGuiViewport* prev_viewport = window->Viewport; + if (g.NextWindowData.ViewportCond) + { + window->Viewport = prev_viewport = FindViewportByID(g.NextWindowData.ViewportId); + window->ViewportId = g.NextWindowData.ViewportId; + } + else if (flags & ImGuiWindowFlags_ChildWindow)// || (flags & ImGuiWindowFlags_Popup)) + { + IM_ASSERT(window->ParentWindow); + window->Viewport = prev_viewport = window->ParentWindow->Viewport; + } + else if (window_follow_mouse_viewport) + { + IM_ASSERT(IsMousePosValid()); + + // Calculate mouse position in OS/platform coordinates + if (!window_is_mouse_tooltip && !GetViewportRect(window).Contains(window->Rect())) + { + // Create an undecorated, temporary OS/platform window + ImVec2 os_desktop_pos = ConvertViewportPosToOsDesktopPos(g.IO.MousePos - g.ActiveIdClickOffset, g.MouseViewport); + ImGuiViewportFlags viewport_flags = ImGuiViewportFlags_NoDecoration | ImGuiViewportFlags_NoFocusOnAppearing | ImGuiViewportFlags_NoInputs; + ImGuiViewport* viewport = Viewport(window->ID, viewport_flags, os_desktop_pos, window->Size); + window->Flags |= ImGuiWindowFlags_FullViewport | ImGuiWindowFlags_NoBringToFrontOnFocus; + window->Viewport = prev_viewport = viewport; + created_viewport = true; + + // Preserve relative mouse position so docking title bar test stays valid mid-frame (since it isn't latched) + g.IO.MousePos = g.IO.MousePosPrev = window->Viewport->Pos + g.ActiveIdClickOffset; + } + else + { + // When dragging a window back into another, only change viewport on mouse release (in UpdateMovingWindow()). + // This is so we don't require of the multi-viewport windowing back-end to preserve mouse buttons after a window closure, making it easier to implement them. + bool preserve_temporary_viewport = g.MovingWindow && g.MovingWindow->RootWindow == window && (window->Viewport->Flags & ImGuiWindowFlags_FullViewport); + if (!preserve_temporary_viewport) + window->Viewport = prev_viewport = g.MouseViewport; + } + } + else if (g.NavWindow != NULL && g.NavWindow != window && (flags & ImGuiWindowFlags_Tooltip)) + { + window->Viewport = prev_viewport = g.NavWindow->Viewport; + } + + // Appearing popups grabs the viewport from their parent window + const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFrames == 1); + if ((flags & ImGuiWindowFlags_Popup) && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize) + window->Viewport = window->ParentWindow->Viewport; + + // If we stored a viewport id (= window that hasn't been activated yet), try to restore the viewport based on saved 'window->ViewportOsDesktopPos' + if (window->Viewport == NULL && window->ViewportId != 0) + { + window->Viewport = FindViewportByID(window->ViewportId); + if (window->Viewport == NULL) + { + if (window->ViewportOsDesktopPos.x != FLT_MAX && window->ViewportOsDesktopPos.y != FLT_MAX) + { + ImGuiViewport* viewport = Viewport(window->ID, ImGuiViewportFlags_NoDecoration, window->ViewportOsDesktopPos, window->Size); + window->Flags |= ImGuiWindowFlags_FullViewport | ImGuiWindowFlags_NoBringToFrontOnFocus; + window->Viewport = prev_viewport = viewport; + created_viewport = true; + } + else + { + window->ViewportId = 0; + } + } + } + + if (window->Viewport == NULL && window->ParentWindow) + window->Viewport = window->ParentWindow->Viewport; + + // Fallback to default viewport + if (window->Viewport == NULL) + window->Viewport = g.Viewports[0]; + + if (window->ID == window->Viewport->ID && !created_viewport) + { + window->Viewport->Flags |= ImGuiViewportFlags_NoDecoration; + window->Viewport->Size = window->Size; + window->Viewport->PlatformOsDesktopPos = ConvertViewportPosToOsDesktopPos(window->Pos, window->Viewport); + window->Flags |= ImGuiWindowFlags_FullViewport | ImGuiWindowFlags_NoBringToFrontOnFocus; + } + + // Disable rounding for the window + if (window->Viewport != g.Viewports[0]) + window->WindowRounding = 0.0f; + + if (window->Flags & ImGuiWindowFlags_FullViewport) + SetWindowPos(window, window->Viewport->Pos, ImGuiCond_Always); + + window->ViewportId = window->Viewport->ID; + window->Viewport->LastFrameActive = g.FrameCount; + + // On unexpected viewport changes, we try to preserve the same position in OS space. + if (prev_viewport != window->Viewport && prev_viewport != NULL && window->WasActive) + if (!(flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip) && !window_pos_set_by_api) + SetWindowViewportTranslateToPreservePlatformPos(window, prev_viewport, window->Viewport); +} + struct ImGuiResizeGripDef { ImVec2 CornerPos; @@ -5559,7 +6121,10 @@ static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au } if (pos_target.x != FLT_MAX) { - window->Pos = window->PosFloat = ImFloor(pos_target); + if (window->Flags & ImGuiWindowFlags_FullViewport) + window->Viewport->PlatformOsDesktopPos = ConvertViewportPosToOsDesktopPos(ImFloor(pos_target), window->Viewport); + else + window->Pos = window->PosFloat = ImFloor(pos_target); MarkIniSettingsDirty(window); } @@ -5599,9 +6164,14 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) const int current_frame = g.FrameCount; const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame); if (first_begin_of_the_frame) + { + window->FlagsPreviousFrame = window->Flags; window->Flags = (ImGuiWindowFlags)flags; + } else + { flags = window->Flags; + } // Update the Appearing flag bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on @@ -5698,7 +6268,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->Active = true; window->BeginOrderWithinParent = 0; window->BeginOrderWithinContext = g.WindowsActiveCount++; - window->BeginCount = 0; window->ClipRect = ImVec4(-FLT_MAX,-FLT_MAX,+FLT_MAX,+FLT_MAX); window->LastFrameActive = current_frame; window->IDStack.resize(1); @@ -5728,6 +6297,16 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) } window->CollapseToggleWanted = false; + // VIEWPORT + + UpdateWindowViewport(window, window_pos_set_by_api); + flags = window->Flags; + if (p_open != NULL && window->Viewport->PlatformRequestClose && !(window->Viewport->Flags & ImGuiViewportFlags_MainViewport)) + { + window->Viewport->PlatformRequestClose = false; + *p_open = false; + } + // SIZE // Update contents size from last frame for auto-fitting (unless explicitly specified) @@ -5828,12 +6407,12 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) rect_to_avoid = ImRect(-FLT_MAX, parent_menu->Pos.y + parent_menu->TitleBarHeight(), FLT_MAX, parent_menu->Pos.y + parent_menu->TitleBarHeight() + parent_menu->MenuBarHeight()); else rect_to_avoid = ImRect(parent_menu->Pos.x + horizontal_overlap, -FLT_MAX, parent_menu->Pos.x + parent_menu->Size.x - horizontal_overlap - parent_menu->ScrollbarSizes.x, FLT_MAX); - window->PosFloat = FindBestWindowPosForPopup(window->PosFloat, window->Size, &window->AutoPosLastDirection, rect_to_avoid); + window->PosFloat = FindBestWindowPosForPopup(window, window->PosFloat, window->Size, &window->AutoPosLastDirection, rect_to_avoid); } else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize) { ImRect rect_to_avoid(window->PosFloat.x - 1, window->PosFloat.y - 1, window->PosFloat.x + 1, window->PosFloat.y + 1); - window->PosFloat = FindBestWindowPosForPopup(window->PosFloat, window->Size, &window->AutoPosLastDirection, rect_to_avoid); + window->PosFloat = FindBestWindowPosForPopup(window, window->PosFloat, window->Size, &window->AutoPosLastDirection, rect_to_avoid); } // Position tooltip (always follows mouse) @@ -5846,19 +6425,21 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) rect_to_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8); else rect_to_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24 * sc, ref_pos.y + 24 * sc); // FIXME: Hard-coded based on mouse cursor shape expectation. Exact dimension not very important. - window->PosFloat = FindBestWindowPosForPopup(ref_pos, window->Size, &window->AutoPosLastDirection, rect_to_avoid); + window->PosFloat = FindBestWindowPosForPopup(window, ref_pos, window->Size, &window->AutoPosLastDirection, rect_to_avoid); if (window->AutoPosLastDirection == ImGuiDir_None) window->PosFloat = ref_pos + ImVec2(2,2); // If there's not enough room, for tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible. } - // Clamp position so it stays visible - if (!(flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip)) + // Clamp position so window stays visible within its viewport + ImRect viewport_rect(GetViewportRect(window)); + if (!window_pos_set_by_api && !(flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_FullViewport)) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) { - if (!window_pos_set_by_api && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && g.IO.DisplaySize.x > 0.0f && g.IO.DisplaySize.y > 0.0f) // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing. + // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing. + if (viewport_rect.GetWidth() > 0 && viewport_rect.GetHeight() > 0.0f) { ImVec2 padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding); - window->PosFloat = ImMax(window->PosFloat + window->Size, padding) - window->Size; - window->PosFloat = ImMin(window->PosFloat, g.IO.DisplaySize - padding); + window->PosFloat = ImMax(window->PosFloat + window->Size, viewport_rect.Min + padding) - window->Size; + window->PosFloat = ImMin(window->PosFloat, viewport_rect.Max - padding); } } window->Pos = ImFloor(window->PosFloat); @@ -5893,13 +6474,21 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if (!window->Collapsed) UpdateManualResize(window, size_auto_fit, &border_held, resize_grip_count, &resize_grip_col[0]); + // When a window is marked as owning its viewport, we immediately update the viewport after a resize + window->ViewportOsDesktopPos = window->Viewport->PlatformOsDesktopPos; + if (flags & ImGuiWindowFlags_FullViewport) + if (window->Size.x != window->Viewport->Size.x || window->Size.y != window->Viewport->Size.y) + { + ResizeViewport(window->Viewport, window->SizeFull); + viewport_rect = GetViewportRect(window); + } + // DRAWING // Setup draw list and outer clipping rectangle window->DrawList->Clear(); window->DrawList->Flags = (g.Style.AntiAliasedLines ? ImDrawListFlags_AntiAliasedLines : 0) | (g.Style.AntiAliasedFill ? ImDrawListFlags_AntiAliasedFill : 0); window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID); - ImRect viewport_rect(GetViewportRect()); if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip) PushClipRect(parent_window->ClipRect.Min, parent_window->ClipRect.Max, true); else @@ -5977,7 +6566,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Borders if (window_border_size > 0.0f) - window->DrawList->AddRect(window->Pos, window->Pos+window->Size, GetColorU32(ImGuiCol_Border), window_rounding, ImDrawCornerFlags_All, window_border_size); + window->DrawList->AddRect(window->Pos, window->Pos + window->Size, GetColorU32(ImGuiCol_Border), window_rounding, ImDrawCornerFlags_All, window_border_size); if (border_held != -1) { ImRect border = GetBorderRect(window, border_held, grip_draw_size, 0.0f); @@ -6994,6 +7583,13 @@ void ImGui::SetNextWindowBgAlpha(float alpha) g.NextWindowData.BgAlphaCond = ImGuiCond_Always; // Using a Cond member for consistency (may transition all of them to single flag set for fast Clear() op) } +void ImGui::SetNextWindowViewport(ImGuiID id) +{ + ImGuiContext& g = *GImGui; + g.NextWindowData.ViewportCond = ImGuiCond_Always; + g.NextWindowData.ViewportId = id; +} + // In window space (not screen space!) ImVec2 ImGui::GetContentRegionMax() { @@ -10644,7 +11240,7 @@ bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboF ImVec2 size_expected = CalcSizeAfterConstraint(popup_window, CalcSizeAutoFit(popup_window, size_contents)); if (flags & ImGuiComboFlags_PopupAlignLeft) popup_window->AutoPosLastDirection = ImGuiDir_Left; - ImVec2 pos = FindBestWindowPosForPopup(frame_bb.GetBL(), size_expected, &popup_window->AutoPosLastDirection, frame_bb, ImGuiPopupPositionPolicy_ComboBox); + ImVec2 pos = FindBestWindowPosForPopup(popup_window, frame_bb.GetBL(), size_expected, &popup_window->AutoPosLastDirection, frame_bb, ImGuiPopupPositionPolicy_ComboBox); SetNextWindowPos(pos); } @@ -12954,7 +13550,7 @@ const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDrop // FIXME-DRAG: Settle on a proper default visuals for drop target. r.Expand(3.5f); bool push_clip_rect = !window->ClipRect.Contains(r); - if (push_clip_rect) window->DrawList->PushClipRectFullScreen(); + if (push_clip_rect) window->DrawList->PushClipRect(r.Min, r.Max); window->DrawList->AddRect(r.Min, r.Max, GetColorU32(ImGuiCol_DragDropTarget), 0.0f, ~0, 2.0f); if (push_clip_rect) window->DrawList->PopClipRect(); } @@ -13091,6 +13687,60 @@ static void ImeSetInputScreenPosFn_DefaultImpl(int, int) {} // HELP //----------------------------------------------------------------------------- +static void RenderViewportThumbnail(ImDrawList* draw_list, const ImRect& bb, const ImVec2& viewport_pos, const ImVec2& viewport_size) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + + ImRect viewport_r(viewport_pos, viewport_pos + viewport_size); + ImVec2 scale = bb.GetSize() / viewport_size; + for (int i = 0; i != g.Windows.Size; i++) + { + ImGuiWindow* thumb_window = g.Windows[i]; + if (!thumb_window->WasActive || ((thumb_window->Flags & ImGuiWindowFlags_ChildWindow))) + continue; + if (thumb_window->SkipItems && (thumb_window->Flags & ImGuiWindowFlags_ChildWindow)) // FIXME-DOCK: Skip hidden docked windows. Identify those betters. + continue; + if (!viewport_r.Overlaps(thumb_window->WindowRectClipped)) + continue; + + ImRect thumb_r = thumb_window->Rect(); + ImRect title_r = thumb_window->TitleBarRect(); + ImRect thumb_r_scaled = ImRect(ImFloor(bb.Min + (thumb_r.Min - viewport_pos) * scale), ImFloor(bb.Min + (thumb_r.Max - viewport_pos) * scale)); + ImRect title_r_scaled = ImRect(ImFloor(bb.Min + (title_r.Min - viewport_pos) * scale), ImFloor(bb.Min + (title_r.Max - viewport_pos) * scale)); + thumb_r_scaled.ClipWithFull(bb); + title_r_scaled.ClipWithFull(bb); + const bool window_is_focused = (g.NavWindow && thumb_window->RootWindowForTitleBarHighlight == g.NavWindow->RootWindowForTitleBarHighlight); + window->DrawList->AddRectFilled(thumb_r_scaled.Min, thumb_r_scaled.Max, ImGui::GetColorU32(ImGuiCol_WindowBg)); + window->DrawList->AddRectFilled(title_r_scaled.Min, title_r_scaled.Max, ImGui::GetColorU32(window_is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg)); + window->DrawList->AddRect(thumb_r_scaled.Min, thumb_r_scaled.Max, ImGui::GetColorU32(ImGuiCol_Border)); + } + draw_list->AddRect(bb.Min, bb.Max, ImGui::GetColorU32(ImGuiCol_Border)); +} + +void ImGui::ShowViewportThumbnails() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + + float SCALE = 1.0f / 7.0f; + ImGui::NewLine(); // For labels + ImVec2 p = window->DC.CursorPos; + for (int n = 0; n < g.Viewports.Size; n++) + { + ImGuiViewport* viewport = g.Viewports[n]; + if (n > 0) + ImGui::SameLine(); + ImRect bb(p + (viewport->Pos) * SCALE, p + (viewport->Pos + viewport->Size) * SCALE); + window->DrawList->AddRect(bb.Min, bb.Max, ImGui::GetColorU32(ImGuiCol_Border)); + RenderViewportThumbnail(window->DrawList, bb, viewport->Pos, viewport->Size); + char buf[64]; + ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f", viewport->Pos.x); + ImGui::RenderText(bb.Min + ImVec2(1, -g.FontSize), buf, NULL, false); + ImGui::Dummy(bb.GetSize()); + } +} + void ImGui::ShowMetricsWindow(bool* p_open) { if (ImGui::Begin("ImGui Metrics", p_open)) @@ -13203,6 +13853,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::BulletText("NavRectRel[0]: (%.1f,%.1f)(%.1f,%.1f)", window->NavRectRel[0].Min.x, window->NavRectRel[0].Min.y, window->NavRectRel[0].Max.x, window->NavRectRel[0].Max.y); else ImGui::BulletText("NavRectRel[0]: "); + ImGui::BulletText("Viewport: %d, ViewportId: 0x%08X", window->Viewport ? window->Viewport->Idx : -1, window->ViewportId); if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow"); if (window->DC.ChildWindows.Size > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows"); ImGui::BulletText("Storage: %d bytes", window->StateStorage.Data.Size * (int)sizeof(ImGuiStorage::Pair)); @@ -13213,10 +13864,24 @@ void ImGui::ShowMetricsWindow(bool* p_open) // Access private state, we are going to display the draw lists from last frame ImGuiContext& g = *GImGui; Funcs::NodeWindows(g.Windows, "Windows"); - if (ImGui::TreeNode("DrawList", "Active DrawLists (%d)", g.DrawDataBuilder.Layers[0].Size)) + if (ImGui::TreeNode("Viewport", "Viewports (%d)", g.Viewports.Size)) { - for (int i = 0; i < g.DrawDataBuilder.Layers[0].Size; i++) - Funcs::NodeDrawList(NULL, g.DrawDataBuilder.Layers[0][i], "DrawList"); + ImGui::Indent(ImGui::GetTreeNodeToLabelSpacing()); + ImGui::ShowViewportThumbnails(); + ImGui::Unindent(ImGui::GetTreeNodeToLabelSpacing()); + for (int i = 0; i < g.Viewports.Size; i++) + { + ImGuiViewport* viewport = g.Viewports[i]; + ImDrawDataBuilder* draw_data = &viewport->DrawDataBuilder; // Because we avoid create an extra list when there's only one viewport + ImGui::SetNextTreeNodeOpen(true, ImGuiCond_Once); + if (ImGui::TreeNode((void*)(intptr_t)viewport->ID, "Viewport #%d, ID: 0x%08X, DrawLists: %d, Size: (%.0f,%.0f)", i, viewport->ID, draw_data->Layers[0].Size, viewport->Size.x, viewport->Size.y)) + { + ImGui::BulletText("Pos: (%.0f,%.0f)", viewport->Pos.x, viewport->Pos.y); + for (int draw_list_i = 0; draw_list_i < viewport->DrawDataBuilder.Layers[0].Size; draw_list_i++) + Funcs::NodeDrawList(NULL, viewport->DrawDataBuilder.Layers[0][draw_list_i], "DrawList"); + ImGui::TreePop(); + } + } ImGui::TreePop(); } if (ImGui::TreeNode("Popups", "Open Popups Stack (%d)", g.OpenPopupStack.Size)) diff --git a/imgui.h b/imgui.h index 02095f3f044b..8d3a4efcdd06 100644 --- a/imgui.h +++ b/imgui.h @@ -22,6 +22,7 @@ #include // memset, memmove, memcpy, strlen, strchr, strcpy, strcmp #define IMGUI_VERSION "1.60 WIP" +#define IMGUI_HAS_VIEWPORT 1 // Viewport WIP branch // Define attributes of all API symbols declarations, e.g. for DLL under Windows. #ifndef IMGUI_API @@ -72,6 +73,7 @@ struct ImGuiTextEditCallbackData; // Shared state of ImGui::InputText() when u struct ImGuiSizeCallbackData; // Structure used to constraint window size in custom ways when using custom ImGuiSizeCallback (rare/advanced use) struct ImGuiListClipper; // Helper to manually clip large list of items struct ImGuiPayload; // User data payload for drag and drop operations +struct ImGuiViewport; // Viewport (generally ~1 per window to output to at the OS level. Need per-platform support to use multiple viewports) struct ImGuiContext; // ImGui context (opaque) // Typedefs and Enumerations (declared as int for compatibility and to not pollute the top of this file) @@ -90,6 +92,7 @@ typedef int ImDrawListFlags; // flags: for ImDrawList typedef int ImFontAtlasFlags; // flags: for ImFontAtlas // enum ImFontAtlasFlags_ typedef int ImGuiColorEditFlags; // flags: for ColorEdit*(), ColorPicker*() // enum ImGuiColorEditFlags_ typedef int ImGuiColumnsFlags; // flags: for *Columns*() // enum ImGuiColumnsFlags_ +typedef int ImGuiConfigFlags; // flags: for io.ConfigFlags // enum ImGuiConfigFlags_ typedef int ImGuiDragDropFlags; // flags: for *DragDrop*() // enum ImGuiDragDropFlags_ typedef int ImGuiComboFlags; // flags: for BeginCombo() // enum ImGuiComboFlags_ typedef int ImGuiFocusedFlags; // flags: for IsWindowFocused() // enum ImGuiFocusedFlags_ @@ -150,6 +153,7 @@ namespace ImGui IMGUI_API void NewFrame(); // start a new ImGui frame, you can submit any command from this point until Render()/EndFrame(). IMGUI_API void Render(); // ends the ImGui frame, finalize the draw data. (Obsolete: optionally call io.RenderDrawListsFn if set. Nowadays, prefer calling your render function yourself.) IMGUI_API ImDrawData* GetDrawData(); // valid after Render() and until the next call to NewFrame(). this is what you have to render. (Obsolete: this used to be passed to your io.RenderDrawListsFn() function.) + IMGUI_API ImDrawData* GetDrawDataForViewport(ImGuiID viewport_id);// ImDrawData filtered to hold only the ImDrawList covering a given viewport. valid after Render() and until the next call to NewFrame() IMGUI_API void EndFrame(); // ends the ImGui frame. automatically called by Render(), so most likely don't need to ever call that yourself directly. If you don't need to render you may call EndFrame() but you'll have wasted CPU already. If you don't need to render, better to not create any imgui windows instead! // Demo, Debug, Informations @@ -259,8 +263,8 @@ namespace ImGui IMGUI_API void SetCursorPosX(float x); // " IMGUI_API void SetCursorPosY(float y); // " IMGUI_API ImVec2 GetCursorStartPos(); // initial cursor position - IMGUI_API ImVec2 GetCursorScreenPos(); // cursor position in absolute screen coordinates [0..io.DisplaySize] (useful to work with ImDrawList API) - IMGUI_API void SetCursorScreenPos(const ImVec2& pos); // cursor position in absolute screen coordinates [0..io.DisplaySize] + IMGUI_API ImVec2 GetCursorScreenPos(); // cursor position in screen coordinates [0..io.DisplaySize] (or [io.ViewportPos..io.ViewportPos + io.ViewportSize] when using multiple viewport). useful to work with ImDrawList API. + IMGUI_API void SetCursorScreenPos(const ImVec2& pos); // cursor position in screen coordinates [0..io.DisplaySize] (or [io.ViewportPos..io.ViewportPos + io.ViewportSize] when using multiple viewport) IMGUI_API void AlignTextToFramePadding(); // vertically align/lower upcoming text to FramePadding.y so that it will aligns to upcoming widgets (call if you have text on a line before regular widgets) IMGUI_API float GetTextLineHeight(); // ~ FontSize IMGUI_API float GetTextLineHeightWithSpacing(); // ~ FontSize + style.ItemSpacing.y (distance in pixels between 2 consecutive lines of text) @@ -527,6 +531,10 @@ namespace ImGui // Clipboard Utilities (also see the LogToClipboard() function to capture or output text data to the clipboard) IMGUI_API const char* GetClipboardText(); IMGUI_API void SetClipboardText(const char* text); + + // Additional OS/Platform Windows (when ImGuiConfigFlags_MultiViewports is set) + IMGUI_API void UpdatePlatformWindows(); // FIXME-PLATFORM + IMGUI_API void RenderPlatformWindows(); // FIXME-PLATFORM // Memory Utilities // All those functions are not reliant on the current context. @@ -568,7 +576,8 @@ enum ImGuiWindowFlags_ ImGuiWindowFlags_Tooltip = 1 << 25, // Don't use! For internal use by BeginTooltip() ImGuiWindowFlags_Popup = 1 << 26, // Don't use! For internal use by BeginPopup() ImGuiWindowFlags_Modal = 1 << 27, // Don't use! For internal use by BeginPopupModal() - ImGuiWindowFlags_ChildMenu = 1 << 28 // Don't use! For internal use by BeginMenu() + ImGuiWindowFlags_ChildMenu = 1 << 28, // Don't use! For internal use by BeginMenu() + ImGuiWindowFlags_FullViewport = 1 << 29 // Don't use! For internal use by Begin() and viewports. }; // Flags for ImGui::InputText() @@ -904,6 +913,46 @@ enum ImGuiCond_ #endif }; +// [BETA] Configuration flags, mostly setup by imgui back-end, stored in io.ConfigFlags +enum ImGuiConfigFlags_ +{ + ImGuiConfigFlags_MultiViewports = 1 << 0, // User enable multiple viewports (require io.PlatformInterface + io.RendererInterface) + ImGuiConfigFlags_PlatformHasMouseHoveredViewport = 1 << 1, // Back-end knows how to set io.MouseHoveredViewport to the viewport directly under the mouse _IGNORING_ viewports with the ImGuiViewportFlags_NoInputs flag and _REGARDLESS_ of whether another viewport is focused and may have mouse capture. This info is not easy to provide correctly with most high-level engines. + ImGuiConfigFlags_PlatformHasWantMoveMouseSupport = 1 << 2, // Back-end honors io.WantMoveMouse request by updating the OS mouse cursor position (currently only used by ImGuiNavFlags_MoveMouse feature, will be useful for widgets teleporting/wrapping the cursor) + ImGuiConfigFlags_PlatformHasWindowAlpha = 1 << 3, // Back-end can have transparent windows + + ImGuiConfigFlags_IsSRGB = 1 << 10, // Back-end is SRGB-aware (Storage flag to allow your back-end to communicate to shared widgets. Not used by core ImGui) + ImGuiConfigFlags_IsTouchScreen = 1 << 11, // Back-end is using a touch screen instead of a mouse (Storage flag to allow your back-end to communicate to shared widgets. Not used by core ImGui) + ImGuiConfigFlags_IsOnScreenKeyboard = 1 << 12 // Back-end uses an on-screen keyboard when io.WantTextInput is set. +}; + +// (Optional) Setup required only if (io.ConfigFlags & ImGuiConfigFlags_EnableMultiViewport) is enabled +struct ImGuiPlatformInterface +{ + void (*CreateViewport)(ImGuiViewport* viewport); + void (*DestroyViewport)(ImGuiViewport* viewport); + void (*ShowWindow)(ImGuiViewport* viewport); + void (*SetWindowPos)(ImGuiViewport* viewport, ImVec2 pos); + ImVec2 (*GetWindowPos)(ImGuiViewport* viewport); + void (*SetWindowSize)(ImGuiViewport* viewport, ImVec2 size); + ImVec2 (*GetWindowSize)(ImGuiViewport* viewport); + void (*SetWindowTitle)(ImGuiViewport* viewport, const char* name); + void (*SetWindowAlpha)(ImGuiViewport* viewport, float alpha); + void (*RenderViewport)(ImGuiViewport* viewport); + void (*SwapBuffers)(ImGuiViewport* viewport); +}; + +// (Optional) Setup required only if (io.ConfigFlags & ImGuiConfigFlags_EnableMultiViewport) is enabled +struct ImGuiRendererInterface +{ + void (*CreateViewport)(ImGuiViewport* viewport); + void (*DestroyViewport)(ImGuiViewport* viewport); + void (*ResizeViewport)(ImGuiViewport* viewport, int w, int h); + void (*RenderViewport)(ImGuiViewport* viewport); // Setup render output, clear targets, call Renderer_RenderDrawData + void (*RenderDrawData)(ImDrawData* draw_data); // Render a ImDrawList (collection of ImDrawList) for the area covering (io.DisplayPos) to (io.DisplayPos + io.DisplaySize) + void (*SwapBuffers)(ImGuiViewport* viewport); // Call Present/SwapBuffers +}; + // You may modify the ImGui::GetStyle() main instance during initialization and before NewFrame(). // During the frame, prefer using ImGui::PushStyleVar(ImGuiStyleVar_XXXX)/PopStyleVar() to alter the main style values, and ImGui::PushStyleColor(ImGuiCol_XXX)/PopStyleColor() for colors. struct ImGuiStyle @@ -951,8 +1000,9 @@ struct ImGuiIO // Settings (fill once) // Default value: //------------------------------------------------------------------ - ImVec2 DisplaySize; // // Display size, in pixels. For clamping windows positions. + ImVec2 DisplaySize; // // Main display size. Used e.g. to clamp windows positions. This is the default viewport. Use BeginViewport() for other viewports. float DeltaTime; // = 1.0f/60.0f // Time elapsed since last frame, in seconds. + ImGuiConfigFlags ConfigFlags; // = 0 // Set ImGuiConfigFlags_. Features/options flags for ImGui back-ends. ImGuiNavFlags NavFlags; // = 0x00 // See ImGuiNavFlags_. Gamepad/keyboard navigation options. float IniSavingRate; // = 5.0f // Maximum time between saving positions/sizes to .ini file, in seconds. const char* IniFilename; // = "imgui.ini" // Path to .ini file. NULL to disable .ini saving. @@ -985,6 +1035,10 @@ struct ImGuiIO void (*SetClipboardTextFn)(void* user_data, const char* text); void* ClipboardUserData; + // Optional: platform interface to use multiple viewports + ImGuiPlatformInterface PlatformInterface; + ImGuiRendererInterface RendererInterface; + // Optional: notify OS Input Method Editor of the screen position of your cursor for text input position (e.g. when using Japanese/Chinese IME in Windows) // (default to use native imm32 api on Windows) void (*ImeSetInputScreenPosFn)(int x, int y); @@ -1004,6 +1058,8 @@ struct ImGuiIO bool MouseDown[5]; // Mouse buttons: left, right, middle + extras. ImGui itself mostly only uses left button (BeginPopupContext** are using right button). Others buttons allows us to track if the mouse is being used by your application + available to user as a convenience via IsMouse** API. float MouseWheel; // Mouse wheel Vertical: 1 unit scrolls about 5 lines text. float MouseWheelH; // Mouse wheel Horizontal. Most users don't have a mouse with an horizontal wheel, may not be filled by all back-ends. + ImGuiID MousePosViewport; // (Optional) When using multiple viewports: viewport from which io.MousePos is based from (when dragging this is generally the captured/focused viewport, even though we can drag outside of it and then it's not hovered anymore). (0 == default viewport) + ImGuiID MouseHoveredViewport; // (Optional) When using multiple viewports: viewport the OS mouse cursor is hovering. (0 == default viewport) bool MouseDrawCursor; // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). bool KeyCtrl; // Keyboard modifier pressed: Control bool KeyShift; // Keyboard modifier pressed: Shift @@ -1022,7 +1078,7 @@ struct ImGuiIO // Output - Retrieve after calling NewFrame() //------------------------------------------------------------------ - ImVec2 DisplayPos; // Always ImVec2(0,0) for now. (In upcoming multiple viewports branch, this will be repositioned by API on a per-viewport basis). The display area goes from DisplayPos to DisplayPos+DisplaySize. + ImVec2 DisplayPos; // Generally ImVec2(0,0). When using multiple viewports this can be repositioned by imgui. The display area goes from DisplayPos to DisplayPos+DisplaySize. bool WantCaptureMouse; // When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application. This is set by ImGui when it wants to use your mouse (e.g. unclicked mouse is hovering a window, or a widget is active). bool WantCaptureKeyboard; // When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. This is set by ImGui when it wants to use your keyboard inputs. bool WantTextInput; // Mobile/console: when io.WantTextInput is true, you may display an on-screen keyboard. This is set by ImGui when it wants textual keyboard input to happen (e.g. when a InputText widget is active). @@ -1079,7 +1135,7 @@ namespace ImGui bool Begin(const char* name, bool* p_open, const ImVec2& size_on_first_use, float bg_alpha_override = -1.0f, ImGuiWindowFlags flags = 0); // Use SetNextWindowSize(size, ImGuiCond_FirstUseEver) + SetNextWindowBgAlpha() instead. static inline bool IsRootWindowOrAnyChildHovered() { return IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows); } static inline void AlignFirstTextHeightToWidgets() { AlignTextToFramePadding(); } - static inline void SetNextWindowPosCenter(ImGuiCond c=0) { ImGuiIO& io = GetIO(); SetNextWindowPos(ImVec2(io.DisplayPos.x + io.DisplaySize.x * 0.5f, io.DisplayPos.y + io.DisplaySize.y * 0.5f), c, ImVec2(0.5f, 0.5f)); } + static inline void SetNextWindowPosCenter(ImGuiCond c=0) { ImGuiIO& io = GetIO(); SetNextWindowPos(ImVec2(io.DisplayPos.x + io.DisplaySize.x * 0.5f, io.DisplayPos.y + io.DisplaySize.y * 0.5f), c, ImVec2(0.5f, 0.5f)); } // FIXME-VIEWPORT: Select viewport based on mouse position // OBSOLETED in 1.51 (between Jun 2017 and Aug 2017) static inline bool IsItemHoveredRect() { return IsItemHovered(ImGuiHoveredFlags_RectOnly); } static inline bool IsPosHoveringAnyWindow(const ImVec2&) { IM_ASSERT(0); return false; } // This was misleading and partly broken. You probably want to use the ImGui::GetIO().WantCaptureMouse flag instead. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 00cd299dcaac..6efa4e019000 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1822,6 +1822,7 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::Text("Mouse dbl-clicked:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseDoubleClicked(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } ImGui::Text("Mouse released:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseReleased(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } ImGui::Text("Mouse wheel: %.1f", io.MouseWheel); + ImGui::Text("Mouse viewport: Ref 0x%08X Hovered 0x%08X", io.MousePosViewport, io.MouseHoveredViewport); ImGui::Text("Keys down:"); for (int i = 0; i < IM_ARRAYSIZE(io.KeysDown); i++) if (io.KeysDownDuration[i] >= 0.0f) { ImGui::SameLine(); ImGui::Text("%d (%.02f secs)", i, io.KeysDownDuration[i]); } ImGui::Text("Keys pressed:"); for (int i = 0; i < IM_ARRAYSIZE(io.KeysDown); i++) if (ImGui::IsKeyPressed(i)) { ImGui::SameLine(); ImGui::Text("%d", i); } @@ -2427,6 +2428,7 @@ static void ShowExampleAppConstrainedResize(bool* p_open) // Demonstrate creating a simple static window with no decoration + a context-menu to choose which corner of the screen to use. static void ShowExampleAppFixedOverlay(bool* p_open) { + // FIXME-VIEWPORT: Select a default viewport const float DISTANCE = 10.0f; static int corner = 0; ImGuiIO& io = ImGui::GetIO(); diff --git a/imgui_internal.h b/imgui_internal.h index e723f3e25d2b..6a12f0442f7e 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -40,6 +40,7 @@ struct ImGuiMenuColumns; struct ImGuiDrawContext; struct ImGuiTextEditState; struct ImGuiPopupRef; +struct ImGuiViewport; struct ImGuiWindow; struct ImGuiWindowSettings; @@ -51,6 +52,7 @@ typedef int ImGuiNavHighlightFlags; // flags: for RenderNavHighlight() typedef int ImGuiNavDirSourceFlags; // flags: for GetNavInputAmount2d() // enum ImGuiNavDirSourceFlags_ typedef int ImGuiSeparatorFlags; // flags: for Separator() - internal // enum ImGuiSeparatorFlags_ typedef int ImGuiSliderFlags; // flags: for SliderBehavior() // enum ImGuiSliderFlags_ +typedef int ImGuiViewportFlags; // flags: for Viewport() // enum ImGuiViewportFlags_ //------------------------------------------------------------------------- // STB libraries @@ -335,7 +337,8 @@ struct IMGUI_API ImRect void Add(const ImRect& r) { if (Min.x > r.Min.x) Min.x = r.Min.x; if (Min.y > r.Min.y) Min.y = r.Min.y; if (Max.x < r.Max.x) Max.x = r.Max.x; if (Max.y < r.Max.y) Max.y = r.Max.y; } void Expand(const float amount) { Min.x -= amount; Min.y -= amount; Max.x += amount; Max.y += amount; } void Expand(const ImVec2& amount) { Min.x -= amount.x; Min.y -= amount.y; Max.x += amount.x; Max.y += amount.y; } - void Translate(const ImVec2& v) { Min.x += v.x; Min.y += v.y; Max.x += v.x; Max.y += v.y; } + void Translate(const ImVec2& d) { Min.x += d.x; Min.y += d.y; Max.x += d.x; Max.y += d.y; } + void Translate(float dx, float dy) { Min.x += dx; Min.y += dy; Max.x += dx; Max.y += dy; } void ClipWith(const ImRect& r) { Min = ImMax(Min, r.Min); Max = ImMin(Max, r.Max); } // Simple version, may lead to an inverted rectangle, which is fine for Contains/Overlaps test but not for display. void ClipWithFull(const ImRect& r) { Min = ImClamp(Min, r.Min, r.Max); Max = ImClamp(Max, r.Min, r.Max); } // Full version, ensure both points are fully clipped. void Floor() { Min.x = (float)(int)Min.x; Min.y = (float)(int)Min.y; Max.x = (float)(int)Max.x; Max.y = (float)(int)Max.y; } @@ -419,9 +422,11 @@ struct ImGuiWindowSettings ImGuiID Id; ImVec2 Pos; ImVec2 Size; + ImVec2 ViewportOsDesktopPos; + ImGuiID ViewportId; bool Collapsed; - ImGuiWindowSettings() { Name = NULL; Id = 0; Pos = Size = ImVec2(0,0); Collapsed = false; } + ImGuiWindowSettings() { Name = NULL; Id = ViewportId = 0; Pos = Size = ImVec2(0,0); ViewportOsDesktopPos = ImVec2(FLT_MAX, FLT_MAX); Collapsed = false; } }; struct ImGuiSettingsHandler @@ -514,6 +519,41 @@ struct ImDrawDataBuilder IMGUI_API void FlattenIntoSingleLayer(); }; +enum ImGuiViewportFlags_ +{ + ImGuiViewportFlags_MainViewport = 1 << 0, + ImGuiViewportFlags_NoDecoration = 1 << 1, // Platform Window: Disable platform title bar, borders, etc. + ImGuiViewportFlags_NoFocusOnAppearing = 1 << 2, // Platform Window: Don't take focus when created. + ImGuiViewportFlags_NoInputs = 1 << 3 // Platform Window: Make mouse pass through so we can drag this window while peaking behind it. +}; + +struct ImGuiViewport +{ + ImGuiID ID; + int Idx; + ImGuiViewportFlags Flags; + int LastFrameActive; + int LastFrameAsRefViewport; // Last frame number this viewport was io.MouseViewportRef + char* Name; // Name (OPTIONAL) + ImVec2 Pos; // Position in imgui virtual space (Pos.y == 0.0) + ImVec2 Size; + ImDrawData DrawData; + ImDrawDataBuilder DrawDataBuilder; + + // [Optional] OS/Platform Layer data. This is to allow the creation/manipulate of multiple OS/Platform windows. Not all back-ends will allow this. + ImVec2 PlatformOsDesktopPos; // Position in OS desktop/native space + void* PlatformUserData; // void* to hold custom data structure for the platform (e.g. windowing info, render context) + void* PlatformHandle; // void* for FindViewportByPlatformHandle(). (e.g. HWND, GlfwWindow*) + bool PlatformRequestClose; // Platform window requested closure + bool PlatformRequestResize; // Platform window requested resize + void* RendererUserData; // void* to hold custom data structure for the renderer (e.g. framebuffer) + + ImGuiViewport(ImGuiID id, int idx) { ID = id; Idx = idx; Flags = 0; LastFrameActive = LastFrameAsRefViewport = -1; Name = NULL; PlatformUserData = PlatformHandle = NULL; PlatformRequestClose = PlatformRequestResize = false; RendererUserData = NULL; } + ~ImGuiViewport() { IM_ASSERT(PlatformUserData == NULL && RendererUserData == NULL); if (Name) ImGui::MemFree(Name); } + ImRect GetRect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } + float GetNextX() const { const float SPACING = 4.0f; return Pos.x + Size.x + SPACING; } +}; + struct ImGuiNavMoveResult { ImGuiID ID; // Best candidate @@ -538,6 +578,7 @@ struct ImGuiNextWindowData ImGuiCond SizeConstraintCond; ImGuiCond FocusCond; ImGuiCond BgAlphaCond; + ImGuiCond ViewportCond; ImVec2 PosVal; ImVec2 PosPivotVal; ImVec2 SizeVal; @@ -547,10 +588,11 @@ struct ImGuiNextWindowData ImGuiSizeCallback SizeCallback; void* SizeCallbackUserData; float BgAlphaVal; + ImGuiID ViewportId; ImGuiNextWindowData() { - PosCond = SizeCond = ContentSizeCond = CollapsedCond = SizeConstraintCond = FocusCond = BgAlphaCond = 0; + PosCond = SizeCond = ContentSizeCond = CollapsedCond = SizeConstraintCond = FocusCond = BgAlphaCond = ViewportCond = 0; PosVal = PosPivotVal = SizeVal = ImVec2(0.0f, 0.0f); ContentSizeVal = ImVec2(0.0f, 0.0f); CollapsedVal = false; @@ -558,11 +600,12 @@ struct ImGuiNextWindowData SizeCallback = NULL; SizeCallbackUserData = NULL; BgAlphaVal = FLT_MAX; + ViewportId = 0; } void Clear() { - PosCond = SizeCond = ContentSizeCond = CollapsedCond = SizeConstraintCond = FocusCond = BgAlphaCond = 0; + PosCond = SizeCond = ContentSizeCond = CollapsedCond = SizeConstraintCond = FocusCond = BgAlphaCond = ViewportCond = 0; } }; @@ -614,6 +657,12 @@ struct ImGuiContext bool NextTreeNodeOpenVal; // Storage for SetNextTreeNode** functions ImGuiCond NextTreeNodeOpenCond; + // Viewports + ImVectorViewports; + ImGuiViewport* MouseViewport; + ImGuiViewport* MouseLastViewport; + ImGuiViewport* MouseLastHoveredViewport; + // Navigation data (for gamepad/keyboard) ImGuiWindow* NavWindow; // Focused window for navigation. Could be called 'FocusWindow' ImGuiID NavId; // Focused item for navigation @@ -650,8 +699,6 @@ struct ImGuiContext ImGuiNavMoveResult NavMoveResultOther; // Best move request candidate within NavWindow's flattened hierarchy (when using the NavFlattened flag) // Render - ImDrawData DrawData; // Main ImDrawData instance to pass render information to the user - ImDrawDataBuilder DrawDataBuilder; float ModalWindowDarkeningRatio; ImDrawList OverlayDrawList; // Optional software render of mouse cursors, if io.MouseDrawCursor is set + a few debug overlays ImGuiMouseCursor MouseCursor; @@ -741,6 +788,9 @@ struct ImGuiContext NextTreeNodeOpenVal = false; NextTreeNodeOpenCond = 0; + MouseViewport = NULL; + MouseLastViewport = MouseLastHoveredViewport = NULL; + NavWindow = NULL; NavId = NavActivateId = NavActivateDownId = NavActivatePressedId = NavInputId = 0; NavJustTabbedId = NavJustMovedToId = NavNextActivateId = 0; @@ -904,7 +954,10 @@ struct IMGUI_API ImGuiWindow { char* Name; ImGuiID ID; // == ImHash(Name) - ImGuiWindowFlags Flags; // See enum ImGuiWindowFlags_ + ImGuiWindowFlags Flags, FlagsPreviousFrame; // See enum ImGuiWindowFlags_ + ImGuiViewport* Viewport; // Always set in Begin(), only inactive windows may have a NULL value here + ImGuiID ViewportId; // Inactive windows preserve their last viewport id (since the viewport may disappear with the window inactivity) + ImVec2 ViewportOsDesktopPos; ImVec2 PosFloat; ImVec2 Pos; // Position rounded-up to nearest pixel ImVec2 Size; // Current size (==SizeFull or collapsed title bar size) @@ -1032,6 +1085,15 @@ namespace ImGui IMGUI_API void Initialize(ImGuiContext* context); IMGUI_API void Shutdown(ImGuiContext* context); // Since 1.60 this is a _private_ function. You can call DestroyContext() to destroy the context created by CreateContext(). + // Viewports + IMGUI_API ImGuiViewport* Viewport(ImGuiID id, ImGuiViewportFlags flags, const ImVec2& os_desktop_pos, const ImVec2& size); // os_desktop_pos allows imgui to reposition windows relative to each order when moving from one viewport to the other. + inline ImVector&GetViewports() { return GImGui->Viewports; } + inline ImGuiViewport* GetMainViewport() { return GImGui->Viewports[0]; } + IMGUI_API ImGuiViewport* FindViewportByID(ImGuiID id); + IMGUI_API ImGuiViewport* FindViewportByPlatformHandle(void* platform_handle); + IMGUI_API void SetNextWindowViewport(ImGuiID id); + IMGUI_API void ShowViewportThumbnails(); + IMGUI_API void MarkIniSettingsDirty(); IMGUI_API ImGuiSettingsHandler* FindSettingsHandler(const char* type_name); IMGUI_API ImGuiWindowSettings* FindWindowSettings(ImGuiID id); From 25349b31d7ba93c6c1cab121696b71e1efc16e7c Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 27 Feb 2018 23:26:51 +0100 Subject: [PATCH 032/828] Examples: DX11 + Win32: Initial attempt at implementing the viewport/platform api. (WIP/test API) (#1542) --- examples/directx11_example/main.cpp | 9 +- examples/imgui_impl_dx11.cpp | 140 +++++++++++++++ examples/imgui_impl_win32.cpp | 254 +++++++++++++++++++++++++++- 3 files changed, 400 insertions(+), 3 deletions(-) diff --git a/examples/directx11_example/main.cpp b/examples/directx11_example/main.cpp index c52fe00a8807..356798e59794 100644 --- a/examples/directx11_example/main.cpp +++ b/examples/directx11_example/main.cpp @@ -117,10 +117,12 @@ int main(int, char**) // Setup ImGui binding ImGui::CreateContext(); - ImGuiIO& io = ImGui::GetIO(); (void)io; + ImGuiIO& io = ImGui::GetIO(); + io.NavFlags |= ImGuiNavFlags_EnableKeyboard; + io.ConfigFlags |= ImGuiConfigFlags_MultiViewports; + ImGui_ImplWin32_Init(hwnd); ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext); - //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; // Enable Keyboard Controls // Setup style ImGui::StyleColorsDark(); @@ -206,6 +208,9 @@ int main(int, char**) ImGui::Render(); ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData()); + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindows(); + g_pSwapChain->Present(1, 0); // Present with vsync //g_pSwapChain->Present(0, 0); // Present without vsync } diff --git a/examples/imgui_impl_dx11.cpp b/examples/imgui_impl_dx11.cpp index 44e5737a27d4..ed4736d02aaa 100644 --- a/examples/imgui_impl_dx11.cpp +++ b/examples/imgui_impl_dx11.cpp @@ -11,6 +11,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2018-XX-XX: Platform: Added support for multiple windows via the ImGuiRendererInterface // 2018-XX-XX: DirectX11: Offset projection matrix and clipping rectangle by io.DisplayPos (which will be non-zero for multi-viewport applications). // 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplDX11_RenderDrawData() in the .h file so you can call it yourself. // 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. @@ -26,6 +27,7 @@ // DirectX data static ID3D11Device* g_pd3dDevice = NULL; static ID3D11DeviceContext* g_pd3dDeviceContext = NULL; +static IDXGIFactory1* g_pFactory = NULL; static ID3D11Buffer* g_pVB = NULL; static ID3D11Buffer* g_pIB = NULL; static ID3D10Blob * g_pVertexShaderBlob = NULL; @@ -46,6 +48,10 @@ struct VERTEX_CONSTANT_BUFFER float mvp[4][4]; }; +// Forward Declarations +static void ImGui_ImplDX11_InitPlatformInterface(); +static void ImGui_ImplDX11_ShutdownPlatformInterface(); + // Render function // (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop) void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) @@ -466,13 +472,29 @@ void ImGui_ImplDX11_InvalidateDeviceObjects() bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_context) { + // Get factory from device + IDXGIDevice* pDXGIDevice = NULL; + IDXGIAdapter* pDXGIAdapter = NULL; + IDXGIFactory1* pFactory = NULL; + if (device->QueryInterface(IID_PPV_ARGS(&pDXGIDevice)) != S_OK) + return false; + if (pDXGIDevice->GetParent(IID_PPV_ARGS(&pDXGIAdapter)) != S_OK) + return false; + if (pDXGIAdapter->GetParent(IID_PPV_ARGS(&pFactory)) != S_OK) + return false; + + ImGuiIO& io = ImGui::GetIO(); g_pd3dDevice = device; g_pd3dDeviceContext = device_context; + g_pFactory = pFactory; + if (io.ConfigFlags & ImGuiConfigFlags_MultiViewports) + ImGui_ImplDX11_InitPlatformInterface(); return true; } void ImGui_ImplDX11_Shutdown() { + ImGui_ImplDX11_ShutdownPlatformInterface(); ImGui_ImplDX11_InvalidateDeviceObjects(); g_pd3dDevice = NULL; g_pd3dDeviceContext = NULL; @@ -483,3 +505,121 @@ void ImGui_ImplDX11_NewFrame() if (!g_pFontSampler) ImGui_ImplDX11_CreateDeviceObjects(); } + +// -------------------------------------------------------------------------------------------------------- +// Platform Windows +// -------------------------------------------------------------------------------------------------------- + +#include "imgui_internal.h" // ImGuiViewport + +struct ImGuiPlatformDataDx11 +{ + IDXGISwapChain* SwapChain; + ID3D11RenderTargetView* RTView; + + ImGuiPlatformDataDx11() { SwapChain = NULL; RTView = NULL; } + ~ImGuiPlatformDataDx11() { IM_ASSERT(SwapChain == NULL && RTView == NULL); } +}; + +static void ImGui_ImplDX11_CreateViewport(ImGuiViewport* viewport) +{ + ImGuiPlatformDataDx11* data = IM_NEW(ImGuiPlatformDataDx11)(); + viewport->RendererUserData = data; + + // FIXME-PLATFORM + HWND hwnd = (HWND)viewport->PlatformHandle; + IM_ASSERT(hwnd != 0); + + // Create swap chain + DXGI_SWAP_CHAIN_DESC sd; + ZeroMemory(&sd, sizeof(sd)); + sd.BufferDesc.Width = (UINT)viewport->Size.x; + sd.BufferDesc.Height = (UINT)viewport->Size.y; + sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + sd.SampleDesc.Count = 1; + sd.SampleDesc.Quality = 0; + sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + sd.BufferCount = 1; + sd.OutputWindow = hwnd; + sd.Windowed = TRUE; + sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; + sd.Flags = 0; + + IM_ASSERT(data->SwapChain == NULL && data->RTView == NULL); + g_pFactory->CreateSwapChain(g_pd3dDevice, &sd, &data->SwapChain); + + // Create the render target + if (data->SwapChain) + { + ID3D11Texture2D* pBackBuffer; + data->SwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer)); + g_pd3dDevice->CreateRenderTargetView(pBackBuffer, NULL, &data->RTView); + pBackBuffer->Release(); + } +} + +static void ImGui_ImplDX11_DestroyViewport(ImGuiViewport* viewport) +{ + if (ImGuiPlatformDataDx11* data = (ImGuiPlatformDataDx11*)viewport->RendererUserData) + { + if (data->SwapChain) + data->SwapChain->Release(); + data->SwapChain = NULL; + if (data->RTView) + data->RTView->Release(); + data->RTView = NULL; + IM_DELETE(data); + } + viewport->RendererUserData = NULL; +} + +static void ImGui_ImplDX11_ResizeViewport(ImGuiViewport* viewport, int w, int h) +{ + ImGuiPlatformDataDx11* data = (ImGuiPlatformDataDx11*)viewport->RendererUserData; + if (data->RTView) + { + data->RTView->Release(); + data->RTView = NULL; + } + if (data->SwapChain) + { + ID3D11Texture2D* pBackBuffer = NULL; + data->SwapChain->ResizeBuffers(0, w, h, DXGI_FORMAT_UNKNOWN, 0); + data->SwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer)); + g_pd3dDevice->CreateRenderTargetView(pBackBuffer, NULL, &data->RTView); + pBackBuffer->Release(); + } +} + +static void ImGui_ImplDX11_RenderViewport(ImGuiViewport* viewport) +{ + ImGuiPlatformDataDx11* data = (ImGuiPlatformDataDx11*)viewport->RendererUserData; + ImVec4 clear_color = ImGui::GetStyle().Colors[ImGuiCol_WindowBg]; // FIXME-PLATFORM + clear_color.w = 1.0f; + g_pd3dDeviceContext->OMSetRenderTargets(1, &data->RTView, NULL); + g_pd3dDeviceContext->ClearRenderTargetView(data->RTView, (float*)&clear_color); + ImGui_ImplDX11_RenderDrawData(&viewport->DrawData); +} + +static void ImGui_ImplDX11_SwapBuffers(ImGuiViewport* viewport) +{ + ImGuiPlatformDataDx11* data = (ImGuiPlatformDataDx11*)viewport->RendererUserData; + data->SwapChain->Present(0, 0); // Present without vsync +} + +void ImGui_ImplDX11_InitPlatformInterface() +{ + ImGuiIO& io = ImGui::GetIO(); + io.RendererInterface.CreateViewport = ImGui_ImplDX11_CreateViewport; + io.RendererInterface.DestroyViewport = ImGui_ImplDX11_DestroyViewport; + io.RendererInterface.ResizeViewport = ImGui_ImplDX11_ResizeViewport; + io.RendererInterface.RenderViewport = ImGui_ImplDX11_RenderViewport; + io.RendererInterface.SwapBuffers = ImGui_ImplDX11_SwapBuffers; +} + +void ImGui_ImplDX11_ShutdownPlatformInterface() +{ + ImGuiIO& io = ImGui::GetIO(); + memset(&io.RendererInterface, 0, sizeof(io.RendererInterface)); +} + diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index f538590a7418..331b67ae0176 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -5,8 +5,12 @@ #include "imgui_impl_win32.h" #define WIN32_LEAN_AND_MEAN #include +#include + +#include "imgui_internal.h" // FIXME-PLATFORM // CHANGELOG +// 2018-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformInterface // 2018-02-20: Inputs: Added support for mouse cursors (ImGui::GetMouseCursor() value and WM_SETCURSOR message handling). // 2018-02-06: Inputs: Added mapping for ImGuiKey_Space. // 2018-02-06: Inputs: Honoring the io.WantMoveMouse by repositioning the mouse by using navigation and ImGuiNavFlags_MoveMouse is set. @@ -24,6 +28,10 @@ static INT64 g_Time = 0; static INT64 g_TicksPerSecond = 0; static ImGuiMouseCursor g_LastMouseCursor = ImGuiMouseCursor_Count_; +// Forward Declarations +static void ImGui_ImplWin32_InitPlatformInterface(); +static void ImGui_ImplWin32_ShutdownPlatformInterface(); + // Functions bool ImGui_ImplWin32_Init(void* hwnd) { @@ -57,11 +65,21 @@ bool ImGui_ImplWin32_Init(void* hwnd) io.KeyMap[ImGuiKey_Y] = 'Y'; io.KeyMap[ImGuiKey_Z] = 'Z'; - io.ImeWindowHandle = g_hWnd; return true; + io.ImeWindowHandle = g_hWnd; + + // Our mouse update function expect PlatformHandle to be filled for the main viewport + ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + main_viewport->PlatformHandle = (void*)g_hWnd; + + if (io.ConfigFlags & ImGuiConfigFlags_MultiViewports) + ImGui_ImplWin32_InitPlatformInterface(); + + return true; } void ImGui_ImplWin32_Shutdown() { + ImGui_ImplWin32_ShutdownPlatformInterface(); g_hWnd = (HWND)0; } @@ -92,6 +110,42 @@ static void ImGui_ImplWin32_UpdateMouseCursor() } } +// This code supports multiple OS Windows mapped into different ImGui viewports, +// So it is a little more complicated than your typical binding code (which only needs to set io.MousePos in your WM_MOUSEMOVE handler) +// This is what imgui needs from the back-end to support multiple windows: +// - io.MousePos = mouse position (e.g. io.MousePos == viewport->Pos when we are on the upper-left of our viewport) +// - io.MousePosViewport = viewport which mouse position is based from (generally the focused/active/capturing viewport) +// - io.MouseHoveredWindow = viewport which mouse is hovering, **regardless of it being the active/focused window**, **regardless of another window holding mouse captured**. [Optional] +// This function overwrite the value of io.MousePos normally updated by the WM_MOUSEMOVE handler. +// We keep the WM_MOUSEMOVE handling code so that WndProc function can be copied as-in in applications which do not need multiple OS windows support. +static void ImGui_ImplWin32_UpdateMousePos() +{ + ImGuiIO& io = ImGui::GetIO(); + io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); + io.MousePosViewport = 0; + io.MouseHoveredViewport = 0; + + POINT pos; + if (!::GetCursorPos(&pos)) + return; + + // Our back-end can tell which window is under the mouse cursor (not every back-end can), so pass that info to imgui + io.ConfigFlags |= ImGuiConfigFlags_PlatformHasMouseHoveredViewport; + HWND hovered_hwnd = ::WindowFromPoint(pos); + if (hovered_hwnd) + if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle((void*)hovered_hwnd)) + io.MouseHoveredViewport = viewport->ID; + + // Convert mouse from screen position to window client position + HWND focused_hwnd = ::GetActiveWindow(); + if (focused_hwnd != 0 && ::ScreenToClient(focused_hwnd, &pos)) + if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle((void*)focused_hwnd)) + { + io.MousePos = ImVec2(viewport->Pos.x + (float)pos.x, viewport->Pos.y + (float)pos.y); + io.MousePosViewport = viewport->ID; + } +} + void ImGui_ImplWin32_NewFrame() { ImGuiIO& io = ImGui::GetIO(); @@ -133,6 +187,8 @@ void ImGui_ImplWin32_NewFrame() ImGui_ImplWin32_UpdateMouseCursor(); } + ImGui_ImplWin32_UpdateMousePos(); + // Start the frame. This call will update the io.WantCaptureMouse, io.WantCaptureKeyboard flag that you can use to dispatch inputs (or not) to your application. ImGui::NewFrame(); } @@ -213,3 +269,199 @@ IMGUI_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARAM wPa } return 0; } + +// -------------------------------------------------------------------------------------------------------- +// Platform Windows +// -------------------------------------------------------------------------------------------------------- + +struct ImGuiPlatformDataWin32 +{ + HWND Hwnd; + bool ExternalResize; + DWORD DwStyle; + DWORD DwExStyle; + + ImGuiPlatformDataWin32() { Hwnd = NULL; ExternalResize = false; DwStyle = DwExStyle = 0; } + ~ImGuiPlatformDataWin32() { IM_ASSERT(Hwnd == NULL); } +}; + +static void ImGui_ImplWin32_CreateViewport(ImGuiViewport* viewport) +{ + ImGuiPlatformDataWin32* data = IM_NEW(ImGuiPlatformDataWin32)(); + viewport->PlatformUserData = data; + + if (viewport->Flags & ImGuiViewportFlags_NoDecoration) + { + data->DwStyle = WS_POPUP; + data->DwExStyle = 0; + } + else + { + data->DwStyle = WS_OVERLAPPEDWINDOW; + data->DwExStyle = WS_EX_TOOLWINDOW; + } + + // Create window + RECT rect = { (LONG)viewport->PlatformOsDesktopPos.x, (LONG)viewport->PlatformOsDesktopPos.y, (LONG)(viewport->PlatformOsDesktopPos.x + viewport->Size.x), (LONG)(viewport->PlatformOsDesktopPos.y + viewport->Size.y) }; + ::AdjustWindowRectEx(&rect, data->DwStyle, FALSE, data->DwExStyle); + data->ExternalResize = true; + data->Hwnd = ::CreateWindowExA( + data->DwExStyle, "ImGui Platform", "No Title Yet", data->DwStyle, // Style, class name, window name + rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, // Window area + g_hWnd, NULL, ::GetModuleHandle(NULL), NULL); // Parent window, Menu, Instance, Param + data->ExternalResize = false; + viewport->PlatformHandle = data->Hwnd; +} + +static void ImGui_ImplWin32_DestroyViewport(ImGuiViewport* viewport) +{ + if (ImGuiPlatformDataWin32* data = (ImGuiPlatformDataWin32*)viewport->PlatformUserData) + { + if (::GetCapture() == data->Hwnd) + { + // Transfer capture so if we started dragging from a window that later disappears, we'll still release the MOUSEUP event. + ::ReleaseCapture(); + ::SetCapture(g_hWnd); + } + if (data->Hwnd) + ::DestroyWindow(data->Hwnd); + data->Hwnd = NULL; + IM_DELETE(data); + } + viewport->PlatformUserData = viewport->PlatformHandle = NULL; +} + +static void ImGui_ImplWin32_ShowWindow(ImGuiViewport* viewport) +{ + ImGuiPlatformDataWin32* data = (ImGuiPlatformDataWin32*)viewport->PlatformUserData; + IM_ASSERT(data->Hwnd != 0); + data->ExternalResize = true; + if (viewport->Flags & ImGuiViewportFlags_NoFocusOnAppearing) + ::ShowWindow(data->Hwnd, SW_SHOWNA); + else + ::ShowWindow(data->Hwnd, SW_SHOW); + data->ExternalResize = false; +} + +static ImVec2 ImGui_ImplWin32_GetWindowPos(ImGuiViewport* viewport) +{ + ImGuiPlatformDataWin32* data = (ImGuiPlatformDataWin32*)viewport->PlatformUserData; + IM_ASSERT(data->Hwnd != 0); + POINT pos = { 0, 0 }; + ::ClientToScreen(data->Hwnd, &pos); + return ImVec2((float)pos.x, (float)pos.y); +} + +static void ImGui_ImplWin32_SetWindowPos(ImGuiViewport* viewport, ImVec2 pos) +{ + ImGuiPlatformDataWin32* data = (ImGuiPlatformDataWin32*)viewport->PlatformUserData; + IM_ASSERT(data->Hwnd != 0); + RECT rect = { (LONG)pos.x, (LONG)pos.y, (LONG)pos.x, (LONG)pos.y }; + ::AdjustWindowRectEx(&rect, data->DwStyle, FALSE, data->DwExStyle); + ::SetWindowPos(data->Hwnd, NULL, rect.left, rect.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE); +} + +static ImVec2 ImGui_ImplWin32_GetWindowSize(ImGuiViewport* viewport) +{ + ImGuiPlatformDataWin32* data = (ImGuiPlatformDataWin32*)viewport->PlatformUserData; + IM_ASSERT(data->Hwnd != 0); + RECT rect; + ::GetClientRect(data->Hwnd, &rect); + return ImVec2(float(rect.right - rect.left), float(rect.bottom - rect.top)); +} + +static void ImGui_ImplWin32_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) +{ + ImGuiPlatformDataWin32* data = (ImGuiPlatformDataWin32*)viewport->PlatformUserData; + IM_ASSERT(data->Hwnd != 0); + data->ExternalResize = true; + RECT rect = { 0, 0, (LONG)size.x, (LONG)size.y }; + ::AdjustWindowRectEx(&rect, data->DwStyle, FALSE, data->DwExStyle); // Client to Screen + ::SetWindowPos(data->Hwnd, NULL, 0, 0, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE); + data->ExternalResize = false; +} + +static void ImGui_ImplWin32_SetWindowTitle(ImGuiViewport* viewport, const char* title) +{ + ImGuiPlatformDataWin32* data = (ImGuiPlatformDataWin32*)viewport->PlatformUserData; + IM_ASSERT(data->Hwnd != 0); + ::SetWindowTextA(data->Hwnd, title); +} + +static LRESULT CALLBACK ImGui_ImplWin32_WndProcHandler_PlatformWindow(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam)) + return true; + + if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle((void*)hWnd)) + { + ImGuiIO& io = ImGui::GetIO(); + ImGuiPlatformDataWin32* data = (ImGuiPlatformDataWin32*)viewport->PlatformUserData; + switch (msg) + { + case WM_CLOSE: + viewport->PlatformRequestClose = true; + return 0; + case WM_MOVE: + viewport->PlatformOsDesktopPos = ImVec2((float)(short)LOWORD(lParam), (float)(short)HIWORD(lParam)); + break; + case WM_NCHITTEST: + // Let mouse pass-through the window, this is used while e.g. dragging a window, we creates a temporary overlay but want the cursor to aim behind our overlay. + if (viewport->Flags & ImGuiViewportFlags_NoInputs) + return HTTRANSPARENT; + break; + case WM_SIZE: + if (!data->ExternalResize) + viewport->PlatformRequestResize = true; + if (io.RendererInterface.ResizeViewport) + io.RendererInterface.ResizeViewport(viewport, (int)LOWORD(lParam), (int)HIWORD(lParam)); + break; + } + } + + return DefWindowProc(hWnd, msg, wParam, lParam); +} + +static void ImGui_ImplWin32_InitPlatformInterface() +{ + WNDCLASSEX wcex; + wcex.cbSize = sizeof(WNDCLASSEX); + wcex.style = CS_HREDRAW | CS_VREDRAW; + wcex.lpfnWndProc = ImGui_ImplWin32_WndProcHandler_PlatformWindow; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = ::GetModuleHandle(NULL); + wcex.hIcon = NULL; + wcex.hCursor = NULL; + wcex.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1); + wcex.lpszMenuName = NULL; + wcex.lpszClassName = _T("ImGui Platform"); + wcex.hIconSm = NULL; + ::RegisterClassEx(&wcex); + + // Register platform interface (will be coupled with a renderer interface) + ImGuiIO& io = ImGui::GetIO(); + io.PlatformInterface.CreateViewport = ImGui_ImplWin32_CreateViewport; + io.PlatformInterface.DestroyViewport = ImGui_ImplWin32_DestroyViewport; + io.PlatformInterface.ShowWindow = ImGui_ImplWin32_ShowWindow; + io.PlatformInterface.SetWindowPos = ImGui_ImplWin32_SetWindowPos; + io.PlatformInterface.GetWindowPos = ImGui_ImplWin32_GetWindowPos; + io.PlatformInterface.SetWindowSize = ImGui_ImplWin32_SetWindowSize; + io.PlatformInterface.GetWindowSize = ImGui_ImplWin32_GetWindowSize; + io.PlatformInterface.SetWindowTitle = ImGui_ImplWin32_SetWindowTitle; + + // Register main window handle + ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + ImGuiPlatformDataWin32* data = IM_NEW(ImGuiPlatformDataWin32)(); + data->Hwnd = g_hWnd; + main_viewport->PlatformUserData = data; + main_viewport->PlatformHandle = (void*)data->Hwnd; +} + +static void ImGui_ImplWin32_ShutdownPlatformInterface() +{ + ImGuiIO& io = ImGui::GetIO(); + memset(&io.PlatformInterface, 0, sizeof(io.PlatformInterface)); + + ::UnregisterClass(_T("ImGui Platform"), ::GetModuleHandle(NULL)); +} From 4cee46f909afb9d242907555093bfc3b77f6009f Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 27 Feb 2018 23:27:41 +0100 Subject: [PATCH 033/828] Examples: GLFW + OpenGL3: Initial attempt at implementing the viewport/platform api. (WIP/test API) (#1542) --- examples/imgui_impl_glfw.cpp | 253 +++++++++++++++++++++++++++--- examples/imgui_impl_opengl3.cpp | 32 ++++ examples/opengl3_example/main.cpp | 14 +- 3 files changed, 270 insertions(+), 29 deletions(-) diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index 1342a9b1e5fd..ab7bebc256e1 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -12,6 +12,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2018-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformInterface // 2018-XX-XX: Inputs: Added support for mouse cursors, honoring ImGui::GetMouseCursor() value. // 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. // 2018-02-06: Inputs: Added mapping for ImGuiKey_Space. @@ -24,6 +25,7 @@ #include "imgui.h" #include "imgui_impl_glfw.h" +#include "imgui_internal.h" // FIXME-PLATFORM // GLFW #include @@ -39,6 +41,10 @@ static double g_Time = 0.0f; static bool g_MouseJustPressed[5] = { false, false, false, false, false }; static GLFWcursor* g_MouseCursors[ImGuiMouseCursor_Count_] = { 0 }; +// Forward Declarations +static void ImGui_ImplGlfw_InitPlatformInterface(); +static void ImGui_ImplGlfw_ShutdownPlatformInterface(); + static const char* ImGui_ImplGlfw_GetClipboardText(void* user_data) { return glfwGetClipboardString((GLFWwindow*)user_data); @@ -137,11 +143,19 @@ bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks) if (install_callbacks) ImGui_ImplGlfw_InstallCallbacks(window); + // Our mouse update function expect PlatformHandle to be filled for the main viewport + ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + main_viewport->PlatformHandle = (void*)g_Window; + if (io.ConfigFlags & ImGuiConfigFlags_MultiViewports) + ImGui_ImplGlfw_InitPlatformInterface(); + return true; } void ImGui_ImplGlfw_Shutdown() { + ImGui_ImplGlfw_ShutdownPlatformInterface(); + for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_Count_; cursor_n++) { glfwDestroyCursor(g_MouseCursors[cursor_n]); @@ -149,8 +163,18 @@ void ImGui_ImplGlfw_Shutdown() } } -static void ImGui_ImplGlfw_UpdateMousePosButtons() +static void ImGui_ImplGlfw_UpdateMouse() { +#if 0 + if (io.WantMoveMouse) + glfwSetCursorPos(g_Window, (double)io.MousePos.x, (double)io.MousePos.y); // Set mouse position if requested by io.WantMoveMouse flag (used when io.NavMovesTrue is enabled by user and using directional navigation) +#endif + + ImGuiIO& io = ImGui::GetIO(); + io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); + io.MousePosViewport = 0; + io.MouseHoveredViewport = 0; + // Update buttons for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) { @@ -159,25 +183,40 @@ static void ImGui_ImplGlfw_UpdateMousePosButtons() g_MouseJustPressed[i] = false; } - // Update mouse position - if (glfwGetWindowAttrib(g_Window, GLFW_FOCUSED)) + const ImVector& viewports = ImGui::GetViewports(); + for (int n = 0; n < viewports.Size; n++) { - if (io.WantMoveMouse) - { - glfwSetCursorPos(g_Window, (double)io.MousePos.x, (double)io.MousePos.y); // Set mouse position if requested by io.WantMoveMouse flag (used when io.NavMovesTrue is enabled by user and using directional navigation) - } - else + ImGuiViewport* viewport = viewports[n]; + GLFWwindow* window = (GLFWwindow*)viewport->PlatformHandle; + IM_ASSERT(window != NULL); + if (glfwGetWindowAttrib(window, GLFW_FOCUSED)) { double mouse_x, mouse_y; - glfwGetCursorPos(g_Window, &mouse_x, &mouse_y); - io.MousePos = ImVec2((float)mouse_x, (float)mouse_y); + glfwGetCursorPos(window, &mouse_x, &mouse_y); + io.MousePos = ImVec2((float)mouse_x + viewport->Pos.x, (float)mouse_y + viewport->Pos.y); + io.MousePosViewport = viewport->ID; + for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) + io.MouseDown[i] |= glfwGetMouseButton(window, i) != 0; } + +#if GLFW_HAS_GLFW_HOVERED + io.ConfigFlags |= ImGuiConfigFlags_PlatformHasMouseHoveredViewport; + if (glfwGetWindowAttrib(data->Window, GLFW_HOVERED) && !(viewport->Flags & ImGuiViewportFlags_NoInputs)) + io.MouseHoveredViewport = viewport->ID; +#endif + } + + // Update OS/hardware mouse cursor if imgui isn't drawing a software cursor + ImGuiMouseCursor cursor = ImGui::GetMouseCursor(); + if (io.MouseDrawCursor || cursor == ImGuiMouseCursor_None) + { + glfwSetInputMode(g_Window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); } else { - io.MousePos = ImVec2(-FLT_MAX,-FLT_MAX); + glfwSetCursor(g_Window, g_MouseCursors[cursor] ? g_MouseCursors[cursor] : g_MouseCursors[ImGuiMouseCursor_Arrow]); + glfwSetInputMode(g_Window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); } - } void ImGui_ImplGlfw_NewFrame() @@ -194,23 +233,11 @@ void ImGui_ImplGlfw_NewFrame() io.DisplayFramebufferScale = ImVec2(w > 0 ? ((float)display_w / w) : 0, h > 0 ? ((float)display_h / h) : 0); // Setup time step - double current_time = glfwGetTime(); + double current_time = glfwGetTime(); io.DeltaTime = g_Time > 0.0 ? (float)(current_time - g_Time) : (float)(1.0f/60.0f); g_Time = current_time; - ImGui_ImplGlfw_UpdateMousePosButtons(); - - // Update OS/hardware mouse cursor if imgui isn't drawing a software cursor - ImGuiMouseCursor cursor = ImGui::GetMouseCursor(); - if (io.MouseDrawCursor || cursor == ImGuiMouseCursor_None) - { - glfwSetInputMode(g_Window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); - } - else - { - glfwSetCursor(g_Window, g_MouseCursors[cursor] ? g_MouseCursors[cursor] : g_MouseCursors[ImGuiMouseCursor_Arrow]); - glfwSetInputMode(g_Window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); - } + ImGui_ImplGlfw_UpdateMouse(); // Gamepad navigation mapping [BETA] memset(io.NavInputs, 0, sizeof(io.NavInputs)); @@ -245,3 +272,177 @@ void ImGui_ImplGlfw_NewFrame() // Start the frame. This call will update the io.WantCaptureMouse, io.WantCaptureKeyboard flag that you can use to dispatch inputs (or not) to your application. ImGui::NewFrame(); } + +// -------------------------------------------------------------------------------------------------------- +// Platform Windows +// -------------------------------------------------------------------------------------------------------- + +struct ImGuiPlatformDataGlfw +{ + GLFWwindow* Window; + bool WindowOwned; + + ImGuiPlatformDataGlfw() { Window = NULL; WindowOwned = false; } + ~ImGuiPlatformDataGlfw() { IM_ASSERT(Window == NULL); } +}; + +static void ImGui_ImplGlfw_CreateViewport(ImGuiViewport* viewport) +{ + ImGuiPlatformDataGlfw* data = IM_NEW(ImGuiPlatformDataGlfw)(); + viewport->PlatformUserData = data; + + // GLFW 3.2 unfortunately always set focus on glfwCreateWindow() if GLFW_VISIBLE is set, regardless of GLFW_FOCUSED + glfwWindowHint(GLFW_VISIBLE, false); + glfwWindowHint(GLFW_FOCUSED, false); + glfwWindowHint(GLFW_DECORATED, (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? false : true); + data->Window = glfwCreateWindow((int)viewport->Size.x, (int)viewport->Size.y, "No Title Yet", NULL, g_Window); + data->WindowOwned = true; + viewport->PlatformHandle = (void*)data->Window; + viewport->Name = NULL; + ImGui_ImplGlfw_InstallCallbacks(data->Window); +} + +static void ImGui_ImplGlfw_DestroyViewport(ImGuiViewport* viewport) +{ + if (ImGuiPlatformDataGlfw* data = (ImGuiPlatformDataGlfw*)viewport->PlatformUserData) + { +#if GLFW_HAS_GLFW_HOVERED + HWND hwnd = glfwGetWin32Window(data->Window); + ::RemovePropA(hwnd, "IMGUI_VIEWPORT"); + #endif + if (data->Window && data->WindowOwned) + glfwDestroyWindow(data->Window); + data->Window = NULL; + IM_DELETE(data); + } + viewport->PlatformUserData = viewport->PlatformHandle = NULL; +} + +#if defined(_WIN32) && GLFW_HAS_GLFW_HOVERED +static WNDPROC g_GlfwWndProc = NULL; +static LRESULT CALLBACK WndProcNoInputs(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + if (msg == WM_NCHITTEST) + { + ImGuiViewport* viewport = (ImGuiViewport*)::GetPropA(hWnd, "IMGUI_VIEWPORT"); + if (viewport->Flags & ImGuiViewportFlags_NoInputs) + return HTTRANSPARENT; + } + return ::CallWindowProc(g_GlfwWndProc, hWnd, msg, wParam, lParam); +} +#endif + +static void ImGui_ImplGlfw_ShowWindow(ImGuiViewport* viewport) +{ + ImGuiPlatformDataGlfw* data = (ImGuiPlatformDataGlfw*)viewport->PlatformUserData; + +#if defined(_WIN32) + // GLFW hack: Hide icon from task bar + HWND hwnd = glfwGetWin32Window(data->Window); + if (viewport->Flags & ImGuiViewportFlags_NoDecoration) + { + LONG ex_style = ::GetWindowLong(hwnd, GWL_EXSTYLE); + ex_style &= ~WS_EX_APPWINDOW; + ex_style |= WS_EX_TOOLWINDOW; + ::SetWindowLong(hwnd, GWL_EXSTYLE, ex_style); + } + + // GLFW hack: install hook for WM_NCHITTEST message handler +#if GLFW_HAS_GLFW_HOVERED + ::SetPropA(hwnd, "IMGUI_VIEWPORT", viewport); + if (g_GlfwWndProc == NULL) + g_GlfwWndProc = (WNDPROC)::GetWindowLongPtr(hwnd, GWLP_WNDPROC); + ::SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)WndProcNoInputs); +#endif + + // GLFW hack: GLFW 3.2 has a bug where glfwShowWindow() also activates/focus the window. + // The fix was pushed to GLFW repository on 2018/01/09 and should be included in GLFW 3.3. See https://github.com/glfw/glfw/issues/1179 + if (viewport->Flags & ImGuiViewportFlags_NoFocusOnAppearing) + { + ::ShowWindow(hwnd, SW_SHOWNA); + return; + } +#endif + + glfwShowWindow(data->Window); +} + +static ImVec2 ImGui_ImplGlfw_GetWindowPos(ImGuiViewport* viewport) +{ + ImGuiPlatformDataGlfw* data = (ImGuiPlatformDataGlfw*)viewport->PlatformUserData; + int x = 0, y = 0; + glfwGetWindowPos(data->Window, &x, &y); + return ImVec2((float)x, (float)y); +} + +static void ImGui_ImplGlfw_SetWindowPos(ImGuiViewport* viewport, ImVec2 pos) +{ + ImGuiPlatformDataGlfw* data = (ImGuiPlatformDataGlfw*)viewport->PlatformUserData; + glfwSetWindowPos(data->Window, (int)pos.x, (int)pos.y); +} + +static ImVec2 ImGui_ImplGlfw_GetWindowSize(ImGuiViewport* viewport) +{ + ImGuiPlatformDataGlfw* data = (ImGuiPlatformDataGlfw*)viewport->PlatformUserData; + int w = 0, h = 0; + glfwGetWindowSize(data->Window, &w, &h); + return ImVec2((float)w, (float)h); +} + +static void ImGui_ImplGlfw_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) +{ + ImGuiPlatformDataGlfw* data = (ImGuiPlatformDataGlfw*)viewport->PlatformUserData; + glfwSetWindowSize(data->Window, (int)size.x, (int)size.y); +} + +static void ImGui_ImplGlfw_SetWindowTitle(ImGuiViewport* viewport, const char* title) +{ + ImGuiPlatformDataGlfw* data = (ImGuiPlatformDataGlfw*)viewport->PlatformUserData; + glfwSetWindowTitle(data->Window, title); +} + +static void ImGui_ImplGlfw_RenderViewport(ImGuiViewport* viewport) +{ + ImGuiPlatformDataGlfw* data = (ImGuiPlatformDataGlfw*)viewport->PlatformUserData; + glfwMakeContextCurrent(data->Window); + + if (glfwWindowShouldClose(data->Window)) + viewport->PlatformRequestClose = true; +} + +static void ImGui_ImplGlfw_SwapBuffers(ImGuiViewport* viewport) +{ + ImGuiPlatformDataGlfw* data = (ImGuiPlatformDataGlfw*)viewport->PlatformUserData; + glfwSwapBuffers(data->Window); +} + +static void ImGui_ImplGlfw_InitPlatformInterface() +{ + // Register platform interface (will be coupled with a renderer interface) + ImGuiIO& io = ImGui::GetIO(); + io.PlatformInterface.CreateViewport = ImGui_ImplGlfw_CreateViewport; + io.PlatformInterface.DestroyViewport = ImGui_ImplGlfw_DestroyViewport; + io.PlatformInterface.ShowWindow = ImGui_ImplGlfw_ShowWindow; + io.PlatformInterface.SetWindowPos = ImGui_ImplGlfw_SetWindowPos; + io.PlatformInterface.GetWindowPos = ImGui_ImplGlfw_GetWindowPos; + io.PlatformInterface.SetWindowSize = ImGui_ImplGlfw_SetWindowSize; + io.PlatformInterface.GetWindowSize = ImGui_ImplGlfw_GetWindowSize; + io.PlatformInterface.SetWindowTitle = ImGui_ImplGlfw_SetWindowTitle; + io.PlatformInterface.RenderViewport = ImGui_ImplGlfw_RenderViewport; + io.PlatformInterface.SwapBuffers = ImGui_ImplGlfw_SwapBuffers; + + // Register main window handle + ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + ImGuiPlatformDataGlfw* data = IM_NEW(ImGuiPlatformDataGlfw)(); + data->Window = g_Window; + data->WindowOwned = false; + main_viewport->PlatformUserData = data; +} + +static void ImGui_ImplGlfw_ShutdownPlatformInterface() +{ + ImGuiIO& io = ImGui::GetIO(); + ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + main_viewport->PlatformHandle = NULL; + memset(&io.PlatformInterface, 0, sizeof(io.PlatformInterface)); +} diff --git a/examples/imgui_impl_opengl3.cpp b/examples/imgui_impl_opengl3.cpp index 70a53af50de8..d2a459ddd634 100644 --- a/examples/imgui_impl_opengl3.cpp +++ b/examples/imgui_impl_opengl3.cpp @@ -25,14 +25,20 @@ static int g_AttribLocationTex = 0, g_AttribLocationProjMtx = 0; static int g_AttribLocationPosition = 0, g_AttribLocationUV = 0, g_AttribLocationColor = 0; static unsigned int g_VboHandle = 0, g_ElementsHandle = 0; +// Forward Declarations +static void ImGui_ImplOpenGL3_InitPlatformInterface(); +static void ImGui_ImplOpenGL3_ShutdownPlatformInterface(); + // Functions bool ImGui_ImplOpenGL3_Init() { + ImGui_ImplOpenGL3_InitPlatformInterface(); return true; } void ImGui_ImplOpenGL3_Shutdown() { + ImGui_ImplOpenGL3_ShutdownPlatformInterface(); ImGui_ImplOpenGL3_DestroyDeviceObjects(); } @@ -298,3 +304,29 @@ void ImGui_ImplOpenGL3_DestroyDeviceObjects() ImGui_ImplOpenGL3_DestroyFontsTexture(); } + +// -------------------------------------------------------------------------------------------------------- +// Platform Windows +// -------------------------------------------------------------------------------------------------------- + +#include "imgui_internal.h" // ImGuiViewport + +static void ImGui_ImplOpenGL3_RenderViewport(ImGuiViewport* viewport) +{ + ImVec4 clear_color = ImGui::GetStyle().Colors[ImGuiCol_WindowBg]; + glClearColor(clear_color.x, clear_color.y, clear_color.z, 1.0f); // FIXME-PLATFORM + glClear(GL_COLOR_BUFFER_BIT); + ImGui_ImplOpenGL3_RenderDrawData(&viewport->DrawData); +} + +void ImGui_ImplOpenGL3_InitPlatformInterface() +{ + ImGuiIO& io = ImGui::GetIO(); + io.RendererInterface.RenderViewport = ImGui_ImplOpenGL3_RenderViewport; +} + +void ImGui_ImplOpenGL3_ShutdownPlatformInterface() +{ + ImGuiIO& io = ImGui::GetIO(); + memset(&io.RendererInterface, 0, sizeof(io.RendererInterface)); +} diff --git a/examples/opengl3_example/main.cpp b/examples/opengl3_example/main.cpp index 3a49f8ee5074..e2d626e77a30 100644 --- a/examples/opengl3_example/main.cpp +++ b/examples/opengl3_example/main.cpp @@ -34,12 +34,14 @@ int main(int, char**) // Setup ImGui binding ImGui::CreateContext(); - ImGuiIO& io = ImGui::GetIO(); (void)io; - ImGui_ImplGlfw_Init(window, true); - ImGui_ImplOpenGL3_Init(); + ImGuiIO& io = ImGui::GetIO(); + io.ConfigFlags |= ImGuiConfigFlags_MultiViewports; //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; // Enable Keyboard Controls //io.NavFlags |= ImGuiNavFlags_EnableGamepad; // Enable Gamepad Controls + ImGui_ImplGlfw_Init(window, true); + ImGui_ImplOpenGL3_Init(); + // Setup style ImGui::StyleColorsDark(); //ImGui::StyleColorsClassic(); @@ -113,12 +115,18 @@ int main(int, char**) // Rendering int display_w, display_h; + glfwMakeContextCurrent(window); glfwGetFramebufferSize(window, &display_w, &display_h); glViewport(0, 0, display_w, display_h); glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w); glClear(GL_COLOR_BUFFER_BIT); ImGui::Render(); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindows(); + + glfwMakeContextCurrent(window); glfwSwapBuffers(window); } From cb601d79e834182e2c8fd752a60b170507b0c753 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 27 Feb 2018 23:30:39 +0100 Subject: [PATCH 034/828] Examples: SDL: Initial attempt at implementing the viewport/platform api. (WIP/test API) (#1542) ImGui_ImplSDL2_Init() now takes a SDL GL context. --- examples/imgui_impl_sdl2.cpp | 267 ++++++++++++++++++++++---- examples/imgui_impl_sdl2.h | 8 +- examples/sdl_opengl2_example/main.cpp | 6 +- examples/sdl_opengl3_example/main.cpp | 20 +- examples/sdl_vulkan_example/main.cpp | 2 +- 5 files changed, 257 insertions(+), 46 deletions(-) diff --git a/examples/imgui_impl_sdl2.cpp b/examples/imgui_impl_sdl2.cpp index 2dcb978f653a..0d6f8490f697 100644 --- a/examples/imgui_impl_sdl2.cpp +++ b/examples/imgui_impl_sdl2.cpp @@ -12,6 +12,8 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2018-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformInterface +// 2018-XX-XX: Misc: ImGui_ImplSDL2_Init() now takes a SDL_GLContext parameter. // 2018-02-16: Inputs: Added support for mouse cursors, honoring ImGui::GetMouseCursor() value. // 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. // 2018-02-06: Inputs: Added mapping for ImGuiKey_Space. @@ -26,6 +28,8 @@ #include "imgui.h" #include "imgui_impl_sdl2.h" +#include "imgui_internal.h" // FIXME-PLATFORM + // SDL #include #include @@ -35,6 +39,10 @@ static Uint64 g_Time = 0; static bool g_MousePressed[3] = { false, false, false }; static SDL_Cursor* g_MouseCursors[ImGuiMouseCursor_Count_] = { 0 }; +// Forward Declarations +static void ImGui_ImplSDL2_InitPlatformInterface(SDL_Window* window, void* sdl_gl_context); +static void ImGui_ImplSDL2_ShutdownPlatformInterface(); + static const char* ImGui_ImplSDL2_GetClipboardText(void*) { return SDL_GetClipboardText(); @@ -90,7 +98,7 @@ bool ImGui_ImplSDL2_ProcessEvent(SDL_Event* event) return false; } -bool ImGui_ImplSDL2_Init(SDL_Window* window) +bool ImGui_ImplSDL2_Init(SDL_Window* window, void* sdl_gl_context) { // Keyboard mapping. ImGui will use those indices to peek into the io.KeyDown[] array. ImGuiIO& io = ImGui::GetIO(); @@ -137,17 +145,77 @@ bool ImGui_ImplSDL2_Init(SDL_Window* window) (void)window; #endif + // Our mouse update function expect PlatformHandle to be filled for the main viewport + ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + main_viewport->PlatformHandle = (void*)window; + if (io.ConfigFlags & ImGuiConfigFlags_MultiViewports) + ImGui_ImplSDL2_InitPlatformInterface(window, sdl_gl_context); + return true; } void ImGui_ImplSDL2_Shutdown() { + ImGui_ImplSDL2_ShutdownPlatformInterface(); + // Destroy SDL mouse cursors for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_Count_; cursor_n++) SDL_FreeCursor(g_MouseCursors[cursor_n]); memset(g_MouseCursors, 0, sizeof(g_MouseCursors)); } +static void ImGui_ImplSDL2_UpdateMouse() +{ + ImGuiIO& io = ImGui::GetIO(); + io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); + io.MousePosViewport = 0; + io.MouseHoveredViewport = 0; + io.ConfigFlags &= ~ImGuiConfigFlags_PlatformHasMouseHoveredViewport; // FIXME-VIEWPORT: We can't get this info properly with SDL, capture is messing up with SDL_WINDOW_MOUSE_FOCUS report and we'd need to handle _NoInputs + + int mx, my; + Uint32 mouse_buttons = SDL_GetMouseState(&mx, &my); + io.MouseDown[0] = g_MousePressed[0] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_LEFT)) != 0; // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame. + io.MouseDown[1] = g_MousePressed[1] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_RIGHT)) != 0; + io.MouseDown[2] = g_MousePressed[2] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_MIDDLE)) != 0; + g_MousePressed[0] = g_MousePressed[1] = g_MousePressed[2] = false; + + SDL_Window* focused_window = SDL_GetKeyboardFocus(); + if (focused_window) + { + // SDL_GetMouseState() gives me mouse position seemingly based on the last window entered/focused(?) + // The creation of new window and SDL_CaptureMouse both seems to severely mess up with that, so we retrieve that position globally. + int wx, wy; + SDL_GetWindowPosition(focused_window, &wx, &wy); + SDL_GetGlobalMouseState(&mx, &my); + mx -= wx; + my -= wy; + } + if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle((void*)focused_window)) + { + io.MousePos = ImVec2(viewport->Pos.x + (float)mx, viewport->Pos.y + (float)my); + io.MousePosViewport = viewport->ID; + } + + // We already retrieve global mouse position, SDL_CaptureMouse() also let the OS know our drag outside boundaries shouldn't trigger, e.g.: OS window resize cursor + // The function is only supported from SDL 2.0.4 (released Jan 2016) +#if (SDL_MAJOR_VERSION >= 2) && (SDL_MINOR_VERSION >= 0) && (SDL_PATCHLEVEL >= 4) + bool any_mouse_button_down = ImGui::IsAnyMouseDown(); + SDL_CaptureMouse(any_mouse_button_down ? SDL_TRUE : SDL_FALSE); +#endif + + // Update OS/hardware mouse cursor if imgui isn't drawing a software cursor + ImGuiMouseCursor cursor = ImGui::GetMouseCursor(); + if (io.MouseDrawCursor || cursor == ImGuiMouseCursor_None) + { + SDL_ShowCursor(SDL_FALSE); + } + else + { + SDL_SetCursor(g_MouseCursors[cursor] ? g_MouseCursors[cursor] : g_MouseCursors[ImGuiMouseCursor_Arrow]); + SDL_ShowCursor(SDL_TRUE); + } +} + void ImGui_ImplSDL2_NewFrame(SDL_Window* window) { ImGuiIO& io = ImGui::GetIO(); @@ -167,43 +235,176 @@ void ImGui_ImplSDL2_NewFrame(SDL_Window* window) io.DeltaTime = g_Time > 0 ? (float)((double)(current_time - g_Time) / frequency) : (float)(1.0f / 60.0f); g_Time = current_time; - // Setup mouse inputs (we already got mouse wheel, keyboard keys & characters from our event handler) - int mx, my; - Uint32 mouse_buttons = SDL_GetMouseState(&mx, &my); - io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); - io.MouseDown[0] = g_MousePressed[0] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_LEFT)) != 0; // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame. - io.MouseDown[1] = g_MousePressed[1] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_RIGHT)) != 0; - io.MouseDown[2] = g_MousePressed[2] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_MIDDLE)) != 0; - g_MousePressed[0] = g_MousePressed[1] = g_MousePressed[2] = false; + ImGui_ImplSDL2_UpdateMouse(); - // We need to use SDL_CaptureMouse() to easily retrieve mouse coordinates outside of the client area. This is only supported from SDL 2.0.4 (released Jan 2016) -#if (SDL_MAJOR_VERSION >= 2) && (SDL_MINOR_VERSION >= 0) && (SDL_PATCHLEVEL >= 4) - if ((SDL_GetWindowFlags(window) & (SDL_WINDOW_MOUSE_FOCUS | SDL_WINDOW_MOUSE_CAPTURE)) != 0) - io.MousePos = ImVec2((float)mx, (float)my); - bool any_mouse_button_down = false; - for (int n = 0; n < IM_ARRAYSIZE(io.MouseDown); n++) - any_mouse_button_down |= io.MouseDown[n]; - if (any_mouse_button_down && (SDL_GetWindowFlags(window) & SDL_WINDOW_MOUSE_CAPTURE) == 0) - SDL_CaptureMouse(SDL_TRUE); - if (!any_mouse_button_down && (SDL_GetWindowFlags(window) & SDL_WINDOW_MOUSE_CAPTURE) != 0) - SDL_CaptureMouse(SDL_FALSE); -#else - if ((SDL_GetWindowFlags(window) & SDL_WINDOW_INPUT_FOCUS) != 0) - io.MousePos = ImVec2((float)mx, (float)my); -#endif + // Start the frame. This call will update the io.WantCaptureMouse, io.WantCaptureKeyboard flag that you can use to dispatch inputs (or not) to your application. + ImGui::NewFrame(); +} - // Update OS/hardware mouse cursor if imgui isn't drawing a software cursor - ImGuiMouseCursor cursor = ImGui::GetMouseCursor(); - if (io.MouseDrawCursor || cursor == ImGuiMouseCursor_None) +// -------------------------------------------------------------------------------------------------------- +// Platform Windows +// -------------------------------------------------------------------------------------------------------- + +#include "imgui_internal.h" + +#define SDL_HAS_WINDOW_OPACITY SDL_VERSION_ATLEAST(2,0,5) + +struct ImGuiPlatformDataSDL2 +{ + SDL_Window* Window; + Uint32 WindowID; + SDL_GLContext GLContext; + + ImGuiPlatformDataSDL2() { Window = NULL; WindowID = 0; GLContext = NULL; } + ~ImGuiPlatformDataSDL2() { IM_ASSERT(Window == NULL && GLContext == NULL); } +}; + +static void ImGui_ImplSDL2_CreateViewport(ImGuiViewport* viewport) +{ + ImGuiPlatformDataSDL2* data = IM_NEW(ImGuiPlatformDataSDL2)(); + viewport->PlatformUserData = data; + + // Share GL resources with main context + // FIXME-PLATFORM + ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + ImGuiPlatformDataSDL2* main_viewport_data = (ImGuiPlatformDataSDL2*)main_viewport->PlatformUserData; + SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1); + SDL_GL_MakeCurrent(main_viewport_data->Window, main_viewport_data->GLContext); + + // We don't enable SDL_WINDOW_RESIZABLE because it enforce windows decorations + Uint32 sdl_flags = 0; + sdl_flags |= SDL_WINDOW_OPENGL; // FIXME-PLATFORM + sdl_flags |= SDL_WINDOW_HIDDEN; + sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? SDL_WINDOW_BORDERLESS : 0; + sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? 0 : SDL_WINDOW_RESIZABLE; + data->Window = SDL_CreateWindow("No Title Yet", 0, 0, (int)viewport->Size.x, (int)viewport->Size.y, sdl_flags); + data->GLContext = SDL_GL_CreateContext(data->Window); + viewport->PlatformHandle = (void*)data->Window; +} + +static void ImGui_ImplSDL2_DestroyViewport(ImGuiViewport* viewport) +{ + if (ImGuiPlatformDataSDL2* data = (ImGuiPlatformDataSDL2*)viewport->PlatformUserData) { - SDL_ShowCursor(0); + if (data->GLContext) + SDL_GL_DeleteContext(data->GLContext); + data->GLContext = NULL; + if (data->Window) + SDL_DestroyWindow(data->Window); + data->Window = NULL; + IM_DELETE(data); } - else + viewport->PlatformUserData = viewport->PlatformHandle = NULL; +} + +static void ImGui_ImplSDL2_ShowWindow(ImGuiViewport* viewport) +{ + ImGuiPlatformDataSDL2* data = (ImGuiPlatformDataSDL2*)viewport->PlatformUserData; +#if defined(_WIN32) + SDL_SysWMinfo info; + SDL_VERSION(&info.version); + if (SDL_GetWindowWMInfo(data->Window, &info)) { - SDL_SetCursor(g_MouseCursors[cursor] ? g_MouseCursors[cursor] : g_MouseCursors[ImGuiMouseCursor_Arrow]); - SDL_ShowCursor(1); + HWND hwnd = info.info.win.window; + + // SDL hack: Hide icon from task bar + // Note: SDL 2.0.6+ has a SDL_WINDOW_SKIP_TASKBAR flag which is supported under Windows but the way it create the window breaks our seamless transition. + if (viewport->Flags & ImGuiViewportFlags_NoDecoration) + { + LONG ex_style = ::GetWindowLong(hwnd, GWL_EXSTYLE); + ex_style &= ~WS_EX_APPWINDOW; + ex_style |= WS_EX_TOOLWINDOW; + ::SetWindowLong(hwnd, GWL_EXSTYLE, ex_style); } - // Start the frame. This call will update the io.WantCaptureMouse, io.WantCaptureKeyboard flag that you can use to dispatch inputs (or not) to your application. - ImGui::NewFrame(); + // SDL hack: SDL always activate/focus windows :/ + if (viewport->Flags & ImGuiViewportFlags_NoFocusOnAppearing) + { + ::ShowWindow(hwnd, SW_SHOWNA); + return; + } +} + +#endif + SDL_ShowWindow(data->Window); +} + +static ImVec2 ImGui_ImplSDL2_GetWindowPos(ImGuiViewport* viewport) +{ + ImGuiPlatformDataSDL2* data = (ImGuiPlatformDataSDL2*)viewport->PlatformUserData; + int x = 0, y = 0; + SDL_GetWindowPosition(data->Window, &x, &y); + return ImVec2((float)x, (float)y); +} + +static void ImGui_ImplSDL2_SetWindowPos(ImGuiViewport* viewport, ImVec2 pos) +{ + ImGuiPlatformDataSDL2* data = (ImGuiPlatformDataSDL2*)viewport->PlatformUserData; + SDL_SetWindowPosition(data->Window, (int)pos.x, (int)pos.y); +} + +static ImVec2 ImGui_ImplSDL2_GetWindowSize(ImGuiViewport* viewport) +{ + ImGuiPlatformDataSDL2* data = (ImGuiPlatformDataSDL2*)viewport->PlatformUserData; + int w = 0, h = 0; + SDL_GetWindowSize(data->Window, &w, &h); + return ImVec2((float)w, (float)h); +} + +static void ImGui_ImplSDL2_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) +{ + ImGuiPlatformDataSDL2* data = (ImGuiPlatformDataSDL2*)viewport->PlatformUserData; + SDL_SetWindowSize(data->Window, (int)size.x, (int)size.y); +} + +static void ImGui_ImplSDL2_SetWindowTitle(ImGuiViewport* viewport, const char* title) +{ + ImGuiPlatformDataSDL2* data = (ImGuiPlatformDataSDL2*)viewport->PlatformUserData; + SDL_SetWindowTitle(data->Window, title); +} + +static void ImGui_ImplSDL2_RenderViewport(ImGuiViewport* viewport) +{ + ImGuiPlatformDataSDL2* data = (ImGuiPlatformDataSDL2*)viewport->PlatformUserData; + SDL_GL_MakeCurrent(data->Window, data->GLContext); +} + +static void ImGui_ImplSDL2_SwapBuffers(ImGuiViewport* viewport) +{ + ImGuiPlatformDataSDL2* data = (ImGuiPlatformDataSDL2*)viewport->PlatformUserData; + SDL_GL_MakeCurrent(data->Window, data->GLContext); // FIXME-PLATFORM2 + SDL_GL_SwapWindow(data->Window); +} + +static void ImGui_ImplSDL2_InitPlatformInterface(SDL_Window* window, void* sdl_gl_context) +{ + // Register platform interface (will be coupled with a renderer interface) + ImGuiIO& io = ImGui::GetIO(); + io.PlatformInterface.CreateViewport = ImGui_ImplSDL2_CreateViewport; + io.PlatformInterface.DestroyViewport = ImGui_ImplSDL2_DestroyViewport; + io.PlatformInterface.ShowWindow = ImGui_ImplSDL2_ShowWindow; + io.PlatformInterface.SetWindowPos = ImGui_ImplSDL2_SetWindowPos; + io.PlatformInterface.GetWindowPos = ImGui_ImplSDL2_GetWindowPos; + io.PlatformInterface.SetWindowSize = ImGui_ImplSDL2_SetWindowSize; + io.PlatformInterface.GetWindowSize = ImGui_ImplSDL2_GetWindowSize; + io.PlatformInterface.SetWindowTitle = ImGui_ImplSDL2_SetWindowTitle; + io.PlatformInterface.RenderViewport = ImGui_ImplSDL2_RenderViewport; + io.PlatformInterface.SwapBuffers = ImGui_ImplSDL2_SwapBuffers; + + io.ConfigFlags |= SDL_HAS_WINDOW_OPACITY ? ImGuiConfigFlags_PlatformHasWindowAlpha : 0; + + // Register main window handle + ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + ImGuiPlatformDataSDL2* data = IM_NEW(ImGuiPlatformDataSDL2)(); + data->Window = window; + data->WindowID = SDL_GetWindowID(window); + data->GLContext = sdl_gl_context; + main_viewport->PlatformUserData = data; + main_viewport->PlatformHandle = data->Window; +} + +static void ImGui_ImplSDL2_ShutdownPlatformInterface() +{ + ImGuiIO& io = ImGui::GetIO(); + memset(&io.PlatformInterface, 0, sizeof(io.PlatformInterface)); } diff --git a/examples/imgui_impl_sdl2.h b/examples/imgui_impl_sdl2.h index 4a181283a143..051411d74908 100644 --- a/examples/imgui_impl_sdl2.h +++ b/examples/imgui_impl_sdl2.h @@ -10,7 +10,7 @@ struct SDL_Window; typedef union SDL_Event SDL_Event; -IMGUI_API bool ImGui_ImplSDL2_Init(SDL_Window* window); -IMGUI_API void ImGui_ImplSDL2_Shutdown(); -IMGUI_API void ImGui_ImplSDL2_NewFrame(SDL_Window* window); -IMGUI_API bool ImGui_ImplSDL2_ProcessEvent(SDL_Event* event); +IMGUI_API bool ImGui_ImplSDL2_Init(SDL_Window* window, void* sdl_gl_context); +IMGUI_API void ImGui_ImplSDL2_Shutdown(); +IMGUI_API void ImGui_ImplSDL2_NewFrame(SDL_Window* window); +IMGUI_API bool ImGui_ImplSDL2_ProcessEvent(SDL_Event* event); diff --git a/examples/sdl_opengl2_example/main.cpp b/examples/sdl_opengl2_example/main.cpp index 6c8dd1d3b3f2..4e497c405b17 100644 --- a/examples/sdl_opengl2_example/main.cpp +++ b/examples/sdl_opengl2_example/main.cpp @@ -31,13 +31,13 @@ int main(int, char**) SDL_DisplayMode current; SDL_GetCurrentDisplayMode(0, ¤t); SDL_Window *window = SDL_CreateWindow("ImGui SDL2+OpenGL example", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE); - SDL_GLContext glcontext = SDL_GL_CreateContext(window); + SDL_GLContext gl_context = SDL_GL_CreateContext(window); SDL_GL_SetSwapInterval(1); // Enable vsync // Setup ImGui binding ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - ImGui_ImplSDL2_Init(window); + ImGui_ImplSDL2_Init(window, gl_context); ImGui_ImplOpenGL2_Init(); //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; // Enable Keyboard Controls @@ -134,7 +134,7 @@ int main(int, char**) ImGui_ImplSDL2_Shutdown(); ImGui::DestroyContext(); - SDL_GL_DeleteContext(glcontext); + SDL_GL_DeleteContext(gl_context); SDL_DestroyWindow(window); SDL_Quit(); diff --git a/examples/sdl_opengl3_example/main.cpp b/examples/sdl_opengl3_example/main.cpp index 24f2f8dc51d2..756fe53e3b21 100644 --- a/examples/sdl_opengl3_example/main.cpp +++ b/examples/sdl_opengl3_example/main.cpp @@ -30,17 +30,19 @@ int main(int, char**) SDL_DisplayMode current; SDL_GetCurrentDisplayMode(0, ¤t); SDL_Window *window = SDL_CreateWindow("ImGui SDL2+OpenGL3 example", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE); - SDL_GLContext glcontext = SDL_GL_CreateContext(window); + SDL_GLContext gl_context = SDL_GL_CreateContext(window); SDL_GL_SetSwapInterval(1); // Enable vsync gl3wInit(); // Setup ImGui binding ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - ImGui_ImplSDL2_Init(window); - ImGui_ImplOpenGL3_Init(); + io.ConfigFlags |= ImGuiConfigFlags_MultiViewports; //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; + ImGui_ImplSDL2_Init(window, gl_context); + ImGui_ImplOpenGL3_Init(); + // Setup style ImGui::StyleColorsDark(); //ImGui::StyleColorsClassic(); @@ -78,6 +80,8 @@ int main(int, char**) ImGui_ImplSDL2_ProcessEvent(&event); if (event.type == SDL_QUIT) done = true; + if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window)) + done = true; } ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplSDL2_NewFrame(window); @@ -120,11 +124,17 @@ int main(int, char**) } // Rendering - glViewport(0, 0, (int)ImGui::GetIO().DisplaySize.x, (int)ImGui::GetIO().DisplaySize.y); + SDL_GL_MakeCurrent(window, gl_context); + glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y); glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w); glClear(GL_COLOR_BUFFER_BIT); ImGui::Render(); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindows(); + + SDL_GL_MakeCurrent(window, gl_context); SDL_GL_SwapWindow(window); } @@ -133,7 +143,7 @@ int main(int, char**) ImGui_ImplSDL2_Shutdown(); ImGui::DestroyContext(); - SDL_GL_DeleteContext(glcontext); + SDL_GL_DeleteContext(gl_context); SDL_DestroyWindow(window); SDL_Quit(); diff --git a/examples/sdl_vulkan_example/main.cpp b/examples/sdl_vulkan_example/main.cpp index 158a1b3e97c3..b81784df42d4 100644 --- a/examples/sdl_vulkan_example/main.cpp +++ b/examples/sdl_vulkan_example/main.cpp @@ -624,7 +624,7 @@ int main(int, char**) init_data.descriptor_pool = g_DescriptorPool; init_data.check_vk_result = check_vk_result; ImGui_ImplVulkan_Init(&init_data); - ImGui_ImplSDL2_Init(window); + ImGui_ImplSDL2_Init(window, NULL); // Setup style ImGui::StyleColorsDark(); From baef79415b59b4ac459a7c0d3fafa3746aef7bcf Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 27 Feb 2018 23:32:30 +0100 Subject: [PATCH 035/828] Examples: DX12: Empty skeleton toward implementing the multi-viewport api for DirectX12. (WIP/test API) (#1542, #301) --- examples/directx12_example/main.cpp | 7 +- examples/imgui_impl_dx12.cpp | 144 +++++++++++++++++++++++++++- 2 files changed, 149 insertions(+), 2 deletions(-) diff --git a/examples/directx12_example/main.cpp b/examples/directx12_example/main.cpp index e6d19b8f6176..beb9aa2445c3 100644 --- a/examples/directx12_example/main.cpp +++ b/examples/directx12_example/main.cpp @@ -93,7 +93,7 @@ void ResizeSwapChain(HWND hWnd, int width, int height) sd.Width = width; sd.Height = height; - IDXGIFactory4* dxgiFactory = nullptr; + IDXGIFactory4* dxgiFactory = NULL; g_pSwapChain->GetParent(IID_PPV_ARGS(&dxgiFactory)); g_pSwapChain->Release(); @@ -289,7 +289,9 @@ int main(int, char**) // Setup ImGui binding ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; + io.ConfigFlags |= ImGuiConfigFlags_MultiViewports; //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; // Enable Keyboard Controls + ImGui_ImplWin32_Init(hwnd); ImGui_ImplDX12_Init(g_pd3dDevice, NUM_FRAMES_IN_FLIGHT, DXGI_FORMAT_R8G8B8A8_UNORM, g_pd3dSrvDescHeap->GetCPUDescriptorHandleForHeapStart(), g_pd3dSrvDescHeap->GetGPUDescriptorHandleForHeapStart()); @@ -399,6 +401,9 @@ int main(int, char**) g_pd3dCommandQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*)&g_pd3dCommandList); + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindows(); + g_pSwapChain->Present(1, 0); // Present with vsync //g_pSwapChain->Present(0, 0); // Present without vsync diff --git a/examples/imgui_impl_dx12.cpp b/examples/imgui_impl_dx12.cpp index 65692a3897c5..9c1de56baa7e 100644 --- a/examples/imgui_impl_dx12.cpp +++ b/examples/imgui_impl_dx12.cpp @@ -18,6 +18,7 @@ // DirectX #include +#include #include // DirectX data @@ -48,6 +49,10 @@ struct VERTEX_CONSTANT_BUFFER float mvp[4][4]; }; +// Forward Declarations +static void ImGui_ImplDX12_InitPlatformInterface(); +static void ImGui_ImplDX12_ShutdownPlatformInterface(); + // Render function // (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop) void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data) @@ -587,7 +592,7 @@ bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FO g_RTVFormat = rtv_format; g_hFontSrvCpuDescHandle = font_srv_cpu_desc_handle; g_hFontSrvGpuDescHandle = font_srv_gpu_desc_handle; - g_pFrameResources = new FrameResources [num_frames_in_flight]; + g_pFrameResources = new FrameResources[num_frames_in_flight]; g_numFramesInFlight = num_frames_in_flight; g_frameIndex = UINT_MAX; @@ -599,11 +604,16 @@ bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FO g_pFrameResources[i].IndexBufferSize = 10000; } + ImGuiIO& io = ImGui::GetIO(); + if (io.ConfigFlags & ImGuiConfigFlags_MultiViewports) + ImGui_ImplDX12_InitPlatformInterface(); + return true; } void ImGui_ImplDX12_Shutdown() { + ImGui_ImplDX12_ShutdownPlatformInterface(); ImGui_ImplDX12_InvalidateDeviceObjects(); delete[] g_pFrameResources; g_pd3dDevice = NULL; @@ -622,3 +632,135 @@ void ImGui_ImplDX12_NewFrame(ID3D12GraphicsCommandList* command_list) g_pd3dCommandList = command_list; } + +// -------------------------------------------------------------------------------------------------------- +// Platform Windows +// -------------------------------------------------------------------------------------------------------- + +#include "imgui_internal.h" // ImGuiViewport + +struct ImGuiPlatformDataDx12 +{ + IDXGISwapChain3* SwapChain; + + ImGuiPlatformDataDx12() { SwapChain = NULL; } + ~ImGuiPlatformDataDx12() { IM_ASSERT(SwapChain == NULL); } +}; + +static void ImGui_ImplDX12_CreateViewport(ImGuiViewport* viewport) +{ + ImGuiPlatformDataDx12* data = IM_NEW(ImGuiPlatformDataDx12)(); + viewport->RendererUserData = data; + IM_ASSERT(0); + + /* + // FIXME-PLATFORM + HWND hwnd = (HWND)viewport->PlatformHandle; + IM_ASSERT(hwnd != 0); + + // Create swap chain + DXGI_SWAP_CHAIN_DESC sd; + ZeroMemory(&sd, sizeof(sd)); + sd.BufferDesc.Width = (UINT)viewport->Size.x; + sd.BufferDesc.Height = (UINT)viewport->Size.y; + sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + sd.SampleDesc.Count = 1; + sd.SampleDesc.Quality = 0; + sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + sd.BufferCount = 1; + sd.OutputWindow = hwnd; + sd.Windowed = TRUE; + sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; + sd.Flags = 0; + + IM_ASSERT(data->SwapChain == NULL && data->RTView == NULL); + g_pFactory->CreateSwapChain(g_pd3dDevice, &sd, &data->SwapChain); + + // Create the render target + if (data->SwapChain) + { + ID3D11Texture2D* pBackBuffer; + data->SwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer)); + g_pd3dDevice->CreateRenderTargetView(pBackBuffer, NULL, &data->RTView); + pBackBuffer->Release(); + } + */ +} + +static void ImGui_ImplDX12_DestroyViewport(ImGuiViewport* viewport) +{ + if (ImGuiPlatformDataDx12* data = (ImGuiPlatformDataDx12*)viewport->RendererUserData) + { + IM_ASSERT(0); + /* + if (data->SwapChain) + data->SwapChain->Release(); + data->SwapChain = NULL; + if (data->RTView) + data->RTView->Release(); + data->RTView = NULL; + IM_DELETE(data); + */ + } + viewport->RendererUserData = NULL; +} + +static void ImGui_ImplDX12_ResizeViewport(ImGuiViewport* viewport, int w, int h) +{ + ImGuiPlatformDataDx12* data = (ImGuiPlatformDataDx12*)viewport->RendererUserData; + IM_ASSERT(0); + /* + if (data->RTView) + { + data->RTView->Release(); + data->RTView = NULL; + } + if (data->SwapChain) + { + ID3D11Texture2D* pBackBuffer = NULL; + data->SwapChain->ResizeBuffers(0, w, h, DXGI_FORMAT_UNKNOWN, 0); + data->SwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer)); + g_pd3dDevice->CreateRenderTargetView(pBackBuffer, NULL, &data->RTView); + pBackBuffer->Release(); + } + */ +} + +static void ImGui_ImplDX12_RenderViewport(ImGuiViewport* viewport) +{ + ImGuiPlatformDataDx12* data = (ImGuiPlatformDataDx12*)viewport->RendererUserData; + IM_ASSERT(0); + /* + ImVec4 clear_color = ImGui::GetStyle().Colors[ImGuiCol_WindowBg]; // FIXME-PLATFORM + clear_color.w = 1.0f; + g_pd3dDeviceContext->OMSetRenderTargets(1, &data->RTView, NULL); + g_pd3dDeviceContext->ClearRenderTargetView(data->RTView, (float*)&clear_color); + */ + ImGui_ImplDX12_RenderDrawData(&viewport->DrawData); +} + +static void ImGui_ImplDX12_SwapBuffers(ImGuiViewport* viewport) +{ + ImGuiPlatformDataDx12* data = (ImGuiPlatformDataDx12*)viewport->RendererUserData; + IM_ASSERT(0); + /* + data->SwapChain->Present(0, 0); // Present without vsync + */ +} + +void ImGui_ImplDX12_InitPlatformInterface() +{ + ImGuiIO& io = ImGui::GetIO(); + io.RendererInterface.CreateViewport = ImGui_ImplDX12_CreateViewport; + io.RendererInterface.DestroyViewport = ImGui_ImplDX12_DestroyViewport; + io.RendererInterface.ResizeViewport = ImGui_ImplDX12_ResizeViewport; + io.RendererInterface.RenderViewport = ImGui_ImplDX12_RenderViewport; + io.RendererInterface.SwapBuffers = ImGui_ImplDX12_SwapBuffers; +} + +void ImGui_ImplDX12_ShutdownPlatformInterface() +{ + ImGuiIO& io = ImGui::GetIO(); + memset(&io.RendererInterface, 0, sizeof(io.RendererInterface)); +} + From 997d1bd0eb49e55af38e65c0c5f6a7aec0aee54f Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 27 Feb 2018 23:33:33 +0100 Subject: [PATCH 036/828] Examples: NULL pointers for consistency. --- examples/sdl_vulkan_example/main.cpp | 4 ++-- examples/vulkan_example/main.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/sdl_vulkan_example/main.cpp b/examples/sdl_vulkan_example/main.cpp index b81784df42d4..7772f8572f86 100644 --- a/examples/sdl_vulkan_example/main.cpp +++ b/examples/sdl_vulkan_example/main.cpp @@ -361,7 +361,7 @@ static void setup_vulkan(SDL_Window* window, const char** extensions, uint32_t e g_PresentMode = VK_PRESENT_MODE_FIFO_KHR; #endif uint32_t count = 0; - vkGetPhysicalDeviceSurfacePresentModesKHR(g_Gpu, g_Surface, &count, nullptr); + vkGetPhysicalDeviceSurfacePresentModesKHR(g_Gpu, g_Surface, &count, NULL); VkPresentModeKHR* presentModes = (VkPresentModeKHR*)malloc(sizeof(VkQueueFamilyProperties) * count); vkGetPhysicalDeviceSurfacePresentModesKHR(g_Gpu, g_Surface, &count, presentModes); bool presentModeAvailable = false; @@ -604,7 +604,7 @@ int main(int, char**) // Setup Vulkan uint32_t extensions_count; - SDL_Vulkan_GetInstanceExtensions(window, &extensions_count, nullptr); + SDL_Vulkan_GetInstanceExtensions(window, &extensions_count, NULL); const char** sdl_extensions = new const char*[extensions_count]; SDL_Vulkan_GetInstanceExtensions(window, &extensions_count, sdl_extensions); setup_vulkan(window, sdl_extensions, extensions_count); diff --git a/examples/vulkan_example/main.cpp b/examples/vulkan_example/main.cpp index 24135e2699cb..015d763f10ab 100644 --- a/examples/vulkan_example/main.cpp +++ b/examples/vulkan_example/main.cpp @@ -360,7 +360,7 @@ static void setup_vulkan(GLFWwindow* window, const char** extensions, uint32_t e g_PresentMode = VK_PRESENT_MODE_FIFO_KHR; #endif uint32_t count = 0; - vkGetPhysicalDeviceSurfacePresentModesKHR(g_Gpu, g_Surface, &count, nullptr); + vkGetPhysicalDeviceSurfacePresentModesKHR(g_Gpu, g_Surface, &count, NULL); VkPresentModeKHR* presentModes = (VkPresentModeKHR*)malloc(sizeof(VkQueueFamilyProperties) * count); vkGetPhysicalDeviceSurfacePresentModesKHR(g_Gpu, g_Surface, &count, presentModes); bool presentModeAvailable = false; From fc3c3de70dd9c3a41d14b13640503c8d866e60c4 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 28 Feb 2018 18:35:16 +0100 Subject: [PATCH 037/828] Examples: DX10: Multi viewport/platform support (based on DX11 version). (#1542) --- examples/directx10_example/main.cpp | 7 +- examples/imgui_impl_dx10.cpp | 143 +++++++++++++++++++++++++++- examples/imgui_impl_dx11.cpp | 2 +- 3 files changed, 149 insertions(+), 3 deletions(-) diff --git a/examples/directx10_example/main.cpp b/examples/directx10_example/main.cpp index 4dc5939e09ce..4a55f253e09f 100644 --- a/examples/directx10_example/main.cpp +++ b/examples/directx10_example/main.cpp @@ -115,9 +115,11 @@ int main(int, char**) // Setup ImGui binding ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; + io.ConfigFlags |= ImGuiConfigFlags_MultiViewports; + //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; // Enable Keyboard Controls + ImGui_ImplWin32_Init(hwnd); ImGui_ImplDX10_Init(g_pd3dDevice); - //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; // Enable Keyboard Controls // Setup style ImGui::StyleColorsDark(); @@ -203,6 +205,9 @@ int main(int, char**) ImGui::Render(); ImGui_ImplDX10_RenderDrawData(ImGui::GetDrawData()); + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindows(); + g_pSwapChain->Present(1, 0); // Present with vsync //g_pSwapChain->Present(0, 0); // Present without vsync } diff --git a/examples/imgui_impl_dx10.cpp b/examples/imgui_impl_dx10.cpp index 6b165118b1b0..d88e2cc8b49f 100644 --- a/examples/imgui_impl_dx10.cpp +++ b/examples/imgui_impl_dx10.cpp @@ -11,6 +11,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2018-XX-XX: Platform: Added support for multiple windows via the ImGuiRendererInterface. // 2018-XX-XX: DirectX10: Offset projection matrix and clipping rectangle by io.DisplayPos (which will be non-zero for multi-viewport applications). // 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplDX10_RenderDrawData() in the .h file so you can call it yourself. // 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. @@ -26,6 +27,7 @@ // DirectX data static ID3D10Device* g_pd3dDevice = NULL; +static IDXGIFactory* g_pFactory = NULL; static ID3D10Buffer* g_pVB = NULL; static ID3D10Buffer* g_pIB = NULL; static ID3D10Blob * g_pVertexShaderBlob = NULL; @@ -46,6 +48,10 @@ struct VERTEX_CONSTANT_BUFFER float mvp[4][4]; }; +// Forward Declarations +static void ImGui_ImplDX10_InitPlatformInterface(); +static void ImGui_ImplDX10_ShutdownPlatformInterface(); + // Render function // (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop) void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data) @@ -302,7 +308,7 @@ bool ImGui_ImplDX10_CreateDeviceObjects() ImGui_ImplDX10_InvalidateDeviceObjects(); // By using D3DCompile() from / d3dcompiler.lib, we introduce a dependency to a given version of d3dcompiler_XX.dll (see D3DCOMPILER_DLL_A) - // If you would like to use this DX11 sample code but remove this dependency you can: + // If you would like to use this DX10 sample code but remove this dependency you can: // 1) compile once, save the compiled shader blobs into a file or source code and pass them to CreateVertexShader()/CreatePixelShader() [preferred solution] // 2) use code to detect any version of the DLL and grab a pointer to D3DCompile from the DLL. // See https://github.com/ocornut/imgui/pull/638 for sources and details. @@ -459,12 +465,28 @@ void ImGui_ImplDX10_InvalidateDeviceObjects() bool ImGui_ImplDX10_Init(ID3D10Device* device) { + // Get factory from device + IDXGIDevice* pDXGIDevice = NULL; + IDXGIAdapter* pDXGIAdapter = NULL; + IDXGIFactory* pFactory = NULL; + if (device->QueryInterface(IID_PPV_ARGS(&pDXGIDevice)) != S_OK) + return false; + if (pDXGIDevice->GetParent(IID_PPV_ARGS(&pDXGIAdapter)) != S_OK) + return false; + if (pDXGIAdapter->GetParent(IID_PPV_ARGS(&pFactory)) != S_OK) + return false; + + ImGuiIO& io = ImGui::GetIO(); g_pd3dDevice = device; + g_pFactory = pFactory; + if (io.ConfigFlags & ImGuiConfigFlags_MultiViewports) + ImGui_ImplDX10_InitPlatformInterface(); return true; } void ImGui_ImplDX10_Shutdown() { + ImGui_ImplDX10_ShutdownPlatformInterface(); ImGui_ImplDX10_InvalidateDeviceObjects(); g_pd3dDevice = NULL; } @@ -474,3 +496,122 @@ void ImGui_ImplDX10_NewFrame() if (!g_pFontSampler) ImGui_ImplDX10_CreateDeviceObjects(); } + + +// -------------------------------------------------------------------------------------------------------- +// Platform Windows +// -------------------------------------------------------------------------------------------------------- + +#include "imgui_internal.h" // ImGuiViewport + +struct ImGuiPlatformDataDx10 +{ + IDXGISwapChain* SwapChain; + ID3D10RenderTargetView* RTView; + + ImGuiPlatformDataDx10() { SwapChain = NULL; RTView = NULL; } + ~ImGuiPlatformDataDx10() { IM_ASSERT(SwapChain == NULL && RTView == NULL); } +}; + +static void ImGui_ImplDX10_CreateViewport(ImGuiViewport* viewport) +{ + ImGuiPlatformDataDx10* data = IM_NEW(ImGuiPlatformDataDx10)(); + viewport->RendererUserData = data; + + // FIXME-PLATFORM + HWND hwnd = (HWND)viewport->PlatformHandle; + IM_ASSERT(hwnd != 0); + + // Create swap chain + DXGI_SWAP_CHAIN_DESC sd; + ZeroMemory(&sd, sizeof(sd)); + sd.BufferDesc.Width = (UINT)viewport->Size.x; + sd.BufferDesc.Height = (UINT)viewport->Size.y; + sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + sd.SampleDesc.Count = 1; + sd.SampleDesc.Quality = 0; + sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + sd.BufferCount = 1; + sd.OutputWindow = hwnd; + sd.Windowed = TRUE; + sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; + sd.Flags = 0; + + IM_ASSERT(data->SwapChain == NULL && data->RTView == NULL); + g_pFactory->CreateSwapChain(g_pd3dDevice, &sd, &data->SwapChain); + + // Create the render target + if (data->SwapChain) + { + ID3D10Texture2D* pBackBuffer; + data->SwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer)); + g_pd3dDevice->CreateRenderTargetView(pBackBuffer, NULL, &data->RTView); + pBackBuffer->Release(); + } +} + +static void ImGui_ImplDX10_DestroyViewport(ImGuiViewport* viewport) +{ + if (ImGuiPlatformDataDx10* data = (ImGuiPlatformDataDx10*)viewport->RendererUserData) + { + if (data->SwapChain) + data->SwapChain->Release(); + data->SwapChain = NULL; + if (data->RTView) + data->RTView->Release(); + data->RTView = NULL; + IM_DELETE(data); + } + viewport->RendererUserData = NULL; +} + +static void ImGui_ImplDX10_ResizeViewport(ImGuiViewport* viewport, int w, int h) +{ + ImGuiPlatformDataDx10* data = (ImGuiPlatformDataDx10*)viewport->RendererUserData; + if (data->RTView) + { + data->RTView->Release(); + data->RTView = NULL; + } + if (data->SwapChain) + { + ID3D10Texture2D* pBackBuffer = NULL; + data->SwapChain->ResizeBuffers(0, w, h, DXGI_FORMAT_UNKNOWN, 0); + data->SwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer)); + g_pd3dDevice->CreateRenderTargetView(pBackBuffer, NULL, &data->RTView); + pBackBuffer->Release(); + } +} + +static void ImGui_ImplDX10_RenderViewport(ImGuiViewport* viewport) +{ + ImGuiPlatformDataDx10* data = (ImGuiPlatformDataDx10*)viewport->RendererUserData; + ImVec4 clear_color = ImGui::GetStyle().Colors[ImGuiCol_WindowBg]; // FIXME-PLATFORM + clear_color.w = 1.0f; + g_pd3dDevice->OMSetRenderTargets(1, &data->RTView, NULL); + g_pd3dDevice->ClearRenderTargetView(data->RTView, (float*)&clear_color); + ImGui_ImplDX10_RenderDrawData(&viewport->DrawData); +} + +static void ImGui_ImplDX10_SwapBuffers(ImGuiViewport* viewport) +{ + ImGuiPlatformDataDx10* data = (ImGuiPlatformDataDx10*)viewport->RendererUserData; + data->SwapChain->Present(0, 0); // Present without vsync +} + +void ImGui_ImplDX10_InitPlatformInterface() +{ + ImGuiIO& io = ImGui::GetIO(); + io.RendererInterface.CreateViewport = ImGui_ImplDX10_CreateViewport; + io.RendererInterface.DestroyViewport = ImGui_ImplDX10_DestroyViewport; + io.RendererInterface.ResizeViewport = ImGui_ImplDX10_ResizeViewport; + io.RendererInterface.RenderViewport = ImGui_ImplDX10_RenderViewport; + io.RendererInterface.SwapBuffers = ImGui_ImplDX10_SwapBuffers; +} + +void ImGui_ImplDX10_ShutdownPlatformInterface() +{ + ImGuiIO& io = ImGui::GetIO(); + memset(&io.RendererInterface, 0, sizeof(io.RendererInterface)); +} + diff --git a/examples/imgui_impl_dx11.cpp b/examples/imgui_impl_dx11.cpp index ed4736d02aaa..b7c26cd38a37 100644 --- a/examples/imgui_impl_dx11.cpp +++ b/examples/imgui_impl_dx11.cpp @@ -11,7 +11,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2018-XX-XX: Platform: Added support for multiple windows via the ImGuiRendererInterface +// 2018-XX-XX: Platform: Added support for multiple windows via the ImGuiRendererInterface. // 2018-XX-XX: DirectX11: Offset projection matrix and clipping rectangle by io.DisplayPos (which will be non-zero for multi-viewport applications). // 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplDX11_RenderDrawData() in the .h file so you can call it yourself. // 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. From 8074d56bdd9117483cdc7f2811f9d59db3a6ea15 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 28 Feb 2018 18:51:40 +0100 Subject: [PATCH 038/828] Renamed ImGuiNavFlags io.NavFlags to ImGuiConfigFlags io.ConfigFlags. (#787) --- CHANGELOG.txt | 8 ++++---- README.md | 2 +- examples/allegro5_example/main.cpp | 2 +- examples/directx10_example/main.cpp | 2 +- examples/directx11_example/main.cpp | 2 +- examples/directx12_example/main.cpp | 2 +- examples/directx9_example/main.cpp | 2 +- examples/imgui_impl_glfw.cpp | 9 +++++---- examples/imgui_impl_glfw.h | 2 +- examples/imgui_impl_win32.cpp | 2 +- examples/marmalade_example/main.cpp | 2 +- examples/opengl2_example/main.cpp | 2 +- examples/opengl3_example/main.cpp | 4 ++-- examples/sdl_opengl2_example/main.cpp | 2 +- examples/sdl_opengl3_example/main.cpp | 2 +- examples/sdl_vulkan_example/main.cpp | 5 +++-- examples/vulkan_example/main.cpp | 6 ++++-- imgui.cpp | 28 +++++++++++++-------------- imgui.h | 22 ++++++++++----------- imgui_demo.cpp | 6 +++--- imgui_internal.h | 4 ++-- 21 files changed, 60 insertions(+), 56 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index a0f0e2a7ffb9..06ee3c3ba087 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -66,11 +66,11 @@ Other Changes: - Navigation: merged in the gamepad/keyboard navigation (about one million changes!). (#787, #323) The initial focus was to support game controllers, but keyboard is becoming increasingly and decently usable. - To use Keyboard Navigation: - - Set io.NavFlags |= ImGuiNavFlags_EnableKeyboard to enable. NewFrame() will automatically fill io.NavInputs[] based on your io.KeyDown[] + io.KeyMap[] arrays. - - When keyboard navigation is active (io.NavActive + NavFlags_EnableKeyboard), the io.WantCaptureKeyboard flag will be set. + - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable. NewFrame() will automatically fill io.NavInputs[] based on your io.KeyDown[] + io.KeyMap[] arrays. + - When keyboard navigation is active (io.NavActive + ImGuiConfigFlags_NavEnableKeyboard), the io.WantCaptureKeyboard flag will be set. For more advanced uses, you may want to read from io.NavActive or io.NavVisible. Read imgui.cpp for more details. - To use Gamepad Navigation: - - Set io.NavFlags |= ImGuiNavFlags_EnableGamepad to enable. Fill the io.NavInputs[] fields before calling NewFrame(). Note that io.NavInputs[] is cleared by EndFrame(). + - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable. Fill the io.NavInputs[] fields before calling NewFrame(). Note that io.NavInputs[] is cleared by EndFrame(). - See https://github.com/ocornut/imgui/issues/1599 for recommended gamepad mapping. - See 'enum ImGuiNavInput_' in imgui.h for a description of inputs. Read imgui.cpp for more details. - Navigation: SetItemDefaultFocus() sets the navigation position in addition to scrolling. (#787) @@ -78,7 +78,7 @@ Other Changes: - Navigation: Added window flags: ImGuiWindowFlags_NoNav (ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus). - Navigation: Style: Added ImGuiCol_NavHighlight, ImGuiCol_NavWindowingHighlight colors. (#787) - Navigation: TreeNode: Added ImGuiTreeNodeFlags_NavLeftJumpsBackHere flag to allow Nav Left direction to jump back to parent tree node from any of its child. (#1079) -- Navigation: IO: Added io.NavFlags (input), io.NavActive (output), io.NavVisible (output). (#787) +- Navigation: IO: Added io.ConfigFlags (input), io.NavActive (output), io.NavVisible (output). (#787) - Context: Removed the default global context and font atlas instances, which caused various problems to users of multiple contexts and DLL users. (#1565) YOU NOW NEED TO CALL ImGui::CreateContext() AT THE BEGINNING OF YOUR APP, AND CALL ImGui::DestroyContext() AT THE END. Existing apps will assert/crash without it. - Context: Removed allocator parameters from CreateContext(), they are now setup with SetAllocatorFunctions() and shared by all contexts. (#1565, #586, #992, #1007, #1558) diff --git a/README.md b/README.md index 1190405ac4fa..da7979635baf 100644 --- a/README.md +++ b/README.md @@ -248,7 +248,7 @@ See the FAQ in imgui.cpp for answers. How do you use Dear ImGui on a platform that may not have a mouse or keyboard? -You can control Dear ImGui with a gamepad, see the explanation in imgui.cpp about how to use the navigation feature (short version: map your gamepad inputs into the `io.NavInputs[]` array and set `io.NavFlags |= ImGuiNavFlags_EnableGamepad`). +You can control Dear ImGui with a gamepad, see the explanation in imgui.cpp about how to use the navigation feature (short version: map your gamepad inputs into the `io.NavInputs[]` array and set `io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad`). You can share your computer mouse seamlessy with your console/tablet/phone using [Synergy](http://synergy-project.org). This is the prefered solution for developer productivity. In particular, their [micro-synergy-client](https://github.com/symless/micro-synergy-client) repo there is _uSynergy.c_ sources for a small embeddable that you can use on any platform to connect to your host PC. You may also use a third party solution such as [Remote ImGui](https://github.com/JordiRos/remoteimgui). diff --git a/examples/allegro5_example/main.cpp b/examples/allegro5_example/main.cpp index 4a53461027e6..80326bca9d21 100644 --- a/examples/allegro5_example/main.cpp +++ b/examples/allegro5_example/main.cpp @@ -25,8 +25,8 @@ int main(int, char**) // Setup ImGui binding ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls ImGui_ImplAllegro5_Init(display); - //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; // Enable Keyboard Controls // Setup style ImGui::StyleColorsDark(); diff --git a/examples/directx10_example/main.cpp b/examples/directx10_example/main.cpp index 4dc5939e09ce..db7c01a7734b 100644 --- a/examples/directx10_example/main.cpp +++ b/examples/directx10_example/main.cpp @@ -115,9 +115,9 @@ int main(int, char**) // Setup ImGui binding ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls ImGui_ImplWin32_Init(hwnd); ImGui_ImplDX10_Init(g_pd3dDevice); - //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; // Enable Keyboard Controls // Setup style ImGui::StyleColorsDark(); diff --git a/examples/directx11_example/main.cpp b/examples/directx11_example/main.cpp index c52fe00a8807..51f42793e00a 100644 --- a/examples/directx11_example/main.cpp +++ b/examples/directx11_example/main.cpp @@ -118,9 +118,9 @@ int main(int, char**) // Setup ImGui binding ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls ImGui_ImplWin32_Init(hwnd); ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext); - //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; // Enable Keyboard Controls // Setup style ImGui::StyleColorsDark(); diff --git a/examples/directx12_example/main.cpp b/examples/directx12_example/main.cpp index e6d19b8f6176..b8be4bfe0be0 100644 --- a/examples/directx12_example/main.cpp +++ b/examples/directx12_example/main.cpp @@ -289,7 +289,7 @@ int main(int, char**) // Setup ImGui binding ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; // Enable Keyboard Controls + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls ImGui_ImplWin32_Init(hwnd); ImGui_ImplDX12_Init(g_pd3dDevice, NUM_FRAMES_IN_FLIGHT, DXGI_FORMAT_R8G8B8A8_UNORM, g_pd3dSrvDescHeap->GetCPUDescriptorHandleForHeapStart(), g_pd3dSrvDescHeap->GetGPUDescriptorHandleForHeapStart()); diff --git a/examples/directx9_example/main.cpp b/examples/directx9_example/main.cpp index c7f54255ab6f..1035cee29e7b 100644 --- a/examples/directx9_example/main.cpp +++ b/examples/directx9_example/main.cpp @@ -78,9 +78,9 @@ int main(int, char**) // Setup ImGui binding ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls ImGui_ImplWin32_Init(hwnd); ImGui_ImplDX9_Init(g_pd3dDevice); - //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; // Enable Keyboard Controls // Setup style ImGui::StyleColorsDark(); diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index 1342a9b1e5fd..f0c06e80ea91 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -3,7 +3,7 @@ // (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) // Implemented features: -// [X] Gamepad navigation mapping. Enable with 'io.NavFlags |= ImGuiNavFlags_EnableGamepad'. +// [X] Gamepad navigation mapping. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). @@ -15,8 +15,8 @@ // 2018-XX-XX: Inputs: Added support for mouse cursors, honoring ImGui::GetMouseCursor() value. // 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. // 2018-02-06: Inputs: Added mapping for ImGuiKey_Space. -// 2018-01-25: Inputs: Added gamepad support if ImGuiNavFlags_EnableGamepad is set. -// 2018-01-25: Inputs: Honoring the io.WantMoveMouse by repositioning the mouse by using navigation and ImGuiNavFlags_MoveMouse is set. +// 2018-01-25: Inputs: Added gamepad support if ImGuiConfigFlags_NavEnableGamepad is set. +// 2018-01-25: Inputs: Honoring the io.WantMoveMouse by repositioning the mouse (when using navigation and ImGuiConfigFlags_NavMoveMouse is set). // 2018-01-20: Inputs: Added Horizontal Mouse Wheel support. // 2018-01-18: Inputs: Added mapping for ImGuiKey_Insert. // 2017-08-25: Inputs: MousePos set to -FLT_MAX,-FLT_MAX when mouse is unavailable/missing (instead of -1,-1). @@ -152,6 +152,7 @@ void ImGui_ImplGlfw_Shutdown() static void ImGui_ImplGlfw_UpdateMousePosButtons() { // Update buttons + ImGuiIO& io = ImGui::GetIO(); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) { // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame. @@ -214,7 +215,7 @@ void ImGui_ImplGlfw_NewFrame() // Gamepad navigation mapping [BETA] memset(io.NavInputs, 0, sizeof(io.NavInputs)); - if (io.NavFlags & ImGuiNavFlags_EnableGamepad) + if (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) { // Update gamepad inputs #define MAP_BUTTON(NAV_NO, BUTTON_NO) { if (buttons_count > BUTTON_NO && buttons[BUTTON_NO] == GLFW_PRESS) io.NavInputs[NAV_NO] = 1.0f; } diff --git a/examples/imgui_impl_glfw.h b/examples/imgui_impl_glfw.h index c6cb4c8a3c47..3e8fd6c7ecdc 100644 --- a/examples/imgui_impl_glfw.h +++ b/examples/imgui_impl_glfw.h @@ -3,7 +3,7 @@ // (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) // Implemented features: -// [X] Gamepad navigation mapping. Enable with 'io.NavFlags |= ImGuiNavFlags_EnableGamepad'. +// [X] Gamepad navigation mapping. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index f538590a7418..ffe8d90f9cc5 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -9,7 +9,7 @@ // CHANGELOG // 2018-02-20: Inputs: Added support for mouse cursors (ImGui::GetMouseCursor() value and WM_SETCURSOR message handling). // 2018-02-06: Inputs: Added mapping for ImGuiKey_Space. -// 2018-02-06: Inputs: Honoring the io.WantMoveMouse by repositioning the mouse by using navigation and ImGuiNavFlags_MoveMouse is set. +// 2018-02-06: Inputs: Honoring the io.WantMoveMouse by repositioning the mouse (when using navigation and ImGuiConfigFlags_NavMoveMouse is set). // 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. // 2018-01-20: Inputs: Added Horizontal Mouse Wheel support. // 2018-01-08: Inputs: Added mapping for ImGuiKey_Insert. diff --git a/examples/marmalade_example/main.cpp b/examples/marmalade_example/main.cpp index 08f8b1d5ce16..817a28727e51 100644 --- a/examples/marmalade_example/main.cpp +++ b/examples/marmalade_example/main.cpp @@ -19,8 +19,8 @@ int main(int, char**) // Setup ImGui binding ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls ImGui_Marmalade_Init(true); - //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; // Enable Keyboard Controls // Setup style ImGui::StyleColorsDark(); diff --git a/examples/opengl2_example/main.cpp b/examples/opengl2_example/main.cpp index 6eefca8fffa6..0b2885c42d85 100644 --- a/examples/opengl2_example/main.cpp +++ b/examples/opengl2_example/main.cpp @@ -30,9 +30,9 @@ int main(int, char**) // Setup ImGui binding ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls ImGui_ImplGlfw_Init(window, true); ImGui_ImplOpenGL2_Init(); - //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; // Enable Keyboard Controls // Setup style ImGui::StyleColorsDark(); diff --git a/examples/opengl3_example/main.cpp b/examples/opengl3_example/main.cpp index 3a49f8ee5074..37667235d047 100644 --- a/examples/opengl3_example/main.cpp +++ b/examples/opengl3_example/main.cpp @@ -35,10 +35,10 @@ int main(int, char**) // Setup ImGui binding ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls ImGui_ImplGlfw_Init(window, true); ImGui_ImplOpenGL3_Init(); - //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; // Enable Keyboard Controls - //io.NavFlags |= ImGuiNavFlags_EnableGamepad; // Enable Gamepad Controls // Setup style ImGui::StyleColorsDark(); diff --git a/examples/sdl_opengl2_example/main.cpp b/examples/sdl_opengl2_example/main.cpp index 6c8dd1d3b3f2..eb12c539f7f4 100644 --- a/examples/sdl_opengl2_example/main.cpp +++ b/examples/sdl_opengl2_example/main.cpp @@ -37,9 +37,9 @@ int main(int, char**) // Setup ImGui binding ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls ImGui_ImplSDL2_Init(window); ImGui_ImplOpenGL2_Init(); - //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; // Enable Keyboard Controls // Setup style ImGui::StyleColorsDark(); diff --git a/examples/sdl_opengl3_example/main.cpp b/examples/sdl_opengl3_example/main.cpp index 24f2f8dc51d2..eb4249cc4016 100644 --- a/examples/sdl_opengl3_example/main.cpp +++ b/examples/sdl_opengl3_example/main.cpp @@ -37,9 +37,9 @@ int main(int, char**) // Setup ImGui binding ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls ImGui_ImplSDL2_Init(window); ImGui_ImplOpenGL3_Init(); - //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; // Setup style ImGui::StyleColorsDark(); diff --git a/examples/sdl_vulkan_example/main.cpp b/examples/sdl_vulkan_example/main.cpp index 158a1b3e97c3..973d75b7b101 100644 --- a/examples/sdl_vulkan_example/main.cpp +++ b/examples/sdl_vulkan_example/main.cpp @@ -612,8 +612,6 @@ int main(int, char**) // Setup ImGui binding ImGui::CreateContext(); - ImGuiIO& io = ImGui::GetIO(); (void)io; - //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; ImGui_ImplVulkan_InitData init_data = {}; init_data.allocator = g_Allocator; @@ -623,6 +621,9 @@ int main(int, char**) init_data.pipeline_cache = g_PipelineCache; init_data.descriptor_pool = g_DescriptorPool; init_data.check_vk_result = check_vk_result; + + ImGuiIO& io = ImGui::GetIO(); (void)io; + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls ImGui_ImplVulkan_Init(&init_data); ImGui_ImplSDL2_Init(window); diff --git a/examples/vulkan_example/main.cpp b/examples/vulkan_example/main.cpp index 24135e2699cb..b979fc004df3 100644 --- a/examples/vulkan_example/main.cpp +++ b/examples/vulkan_example/main.cpp @@ -615,7 +615,7 @@ int main(int, char**) // Setup ImGui binding ImGui::CreateContext(); - ImGuiIO& io = ImGui::GetIO(); (void)io; + ImGui_ImplVulkan_InitData init_data = {}; init_data.allocator = g_Allocator; init_data.gpu = g_Gpu; @@ -624,9 +624,11 @@ int main(int, char**) init_data.pipeline_cache = g_PipelineCache; init_data.descriptor_pool = g_DescriptorPool; init_data.check_vk_result = check_vk_result; + + ImGuiIO& io = ImGui::GetIO(); (void)io; + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls ImGui_ImplVulkan_Init(&init_data); ImGui_ImplGlfw_Init(window, true); - //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; // Enable Keyboard Controls // Setup style ImGui::StyleColorsDark(); diff --git a/imgui.cpp b/imgui.cpp index 8a577564bd24..3e9f690d1235 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -216,15 +216,15 @@ - Ask questions and report issues at https://github.com/ocornut/imgui/issues/787 - The initial focus was to support game controllers, but keyboard is becoming increasingly and decently usable. - Keyboard: - - Set io.NavFlags |= ImGuiNavFlags_EnableKeyboard to enable. NewFrame() will automatically fill io.NavInputs[] based on your io.KeyDown[] + io.KeyMap[] arrays. - - When keyboard navigation is active (io.NavActive + NavFlags_EnableKeyboard), the io.WantCaptureKeyboard flag will be set. + - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable. NewFrame() will automatically fill io.NavInputs[] based on your io.KeyDown[] + io.KeyMap[] arrays. + - When keyboard navigation is active (io.NavActive + ImGuiConfigFlags_NavEnableKeyboard), the io.WantCaptureKeyboard flag will be set. For more advanced uses, you may want to read from: - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set. - io.NavVisible: true when the navigation cursor is visible (and usually goes false when mouse is used). - or query focus information with e.g. IsWindowFocused(), IsItemFocused() etc. functions. Please reach out if you think the game vs navigation input sharing could be improved. - Gamepad: - - Set io.NavFlags |= ImGuiNavFlags_EnableGamepad to enable. Fill the io.NavInputs[] fields before calling NewFrame(). Note that io.NavInputs[] is cleared by EndFrame(). + - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable. Fill the io.NavInputs[] fields before calling NewFrame(). Note that io.NavInputs[] is cleared by EndFrame(). - See 'enum ImGuiNavInput_' in imgui.h for a description of inputs. For each entry of io.NavInputs[], set the following values: 0.0f= not held. 1.0f= fully held. Pass intermediate 0.0f..1.0f values for analog triggers/sticks. - We uses a simple >0.0f test for activation testing, and won't attempt to test for a dead-zone. @@ -234,11 +234,11 @@ - Mouse: - PS4 users: Consider emulating a mouse cursor with DualShock4 touch pad or a spare analog stick as a mouse-emulation fallback. - Consoles/Tablet/Phone users: Consider using Synergy host (on your computer) + uSynergy.c (in your console/tablet/phone app) to use your PC mouse/keyboard. - - On a TV/console system where readability may be lower or mouse inputs may be awkward, you may want to set the ImGuiNavFlags_MoveMouse flag in io.NavFlags. - Enabling ImGuiNavFlags_MoveMouse instructs dear imgui to move your mouse cursor along with navigation movements. + - On a TV/console system where readability may be lower or mouse inputs may be awkward, you may want to set the ImGuiConfigFlags_NavMoveMouse flag. + Enabling ImGuiConfigFlags_NavMoveMouse instructs dear imgui to move your mouse cursor along with navigation movements. When enabled, the NewFrame() function may alter 'io.MousePos' and set 'io.WantMoveMouse' to notify you that it wants the mouse cursor to be moved. When that happens your back-end NEEDS to move the OS or underlying mouse cursor on the next frame. Some of the binding in examples/ do that. - (If you set the ImGuiNavFlags_MoveMouse flag but don't honor 'io.WantMoveMouse' properly, imgui will misbehave as it will see your mouse as moving back and forth.) + (If you set the NavMoveMouse flag but don't honor 'io.WantMoveMouse' properly, imgui will misbehave as it will see your mouse as moving back and forth!) (In a setup when you may not have easy control over the mouse cursor, e.g. uSynergy.c doesn't expose moving remote mouse cursor, you may want to set a boolean to ignore your other external mouse positions until the external source is moved again.) @@ -847,7 +847,7 @@ ImGuiIO::ImGuiIO() // Settings DisplaySize = ImVec2(-1.0f, -1.0f); DeltaTime = 1.0f/60.0f; - NavFlags = 0x00; + ConfigFlags = 0x00; IniSavingRate = 5.0f; IniFilename = "imgui.ini"; LogFilename = "imgui_log.txt"; @@ -2810,7 +2810,7 @@ static void ImGui::NavUpdateWindowing() bool apply_toggle_layer = false; bool start_windowing_with_gamepad = !g.NavWindowingTarget && IsNavInputPressed(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed); - bool start_windowing_with_keyboard = !g.NavWindowingTarget && g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (g.IO.NavFlags & ImGuiNavFlags_EnableKeyboard); + bool start_windowing_with_keyboard = !g.NavWindowingTarget && g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard); if (start_windowing_with_gamepad || start_windowing_with_keyboard) if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavigable(g.Windows.Size - 1, -INT_MAX, -1)) { @@ -2965,7 +2965,7 @@ static void ImGui::NavUpdate() // Update Keyboard->Nav inputs mapping memset(g.IO.NavInputs + ImGuiNavInput_InternalStart_, 0, (ImGuiNavInput_COUNT - ImGuiNavInput_InternalStart_) * sizeof(g.IO.NavInputs[0])); - if (g.IO.NavFlags & ImGuiNavFlags_EnableKeyboard) + if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) { #define NAV_MAP_KEY(_KEY, _NAV_INPUT) if (g.IO.KeyMap[_KEY] != -1 && IsKeyDown(g.IO.KeyMap[_KEY])) g.IO.NavInputs[_NAV_INPUT] = 1.0f; NAV_MAP_KEY(ImGuiKey_Space, ImGuiNavInput_Activate ); @@ -3037,7 +3037,7 @@ static void ImGui::NavUpdate() if (g.NavMousePosDirty && g.NavIdIsAlive) { // Set mouse position given our knowledge of the nav widget position from last frame - if (g.IO.NavFlags & ImGuiNavFlags_MoveMouse) + if (g.IO.ConfigFlags & ImGuiConfigFlags_NavMoveMouse) { g.IO.MousePos = g.IO.MousePosPrev = NavCalcPreferredMousePos(); g.IO.WantMoveMouse = true; @@ -3057,7 +3057,7 @@ static void ImGui::NavUpdate() NavUpdateWindowing(); // Set output flags for user application - g.IO.NavActive = (g.IO.NavFlags & (ImGuiNavFlags_EnableGamepad | ImGuiNavFlags_EnableKeyboard)) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs); + g.IO.NavActive = (g.IO.ConfigFlags & (ImGuiConfigFlags_NavEnableGamepad | ImGuiConfigFlags_NavEnableKeyboard)) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs); g.IO.NavVisible = (g.IO.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL) || g.NavInitRequest; // Process NavCancel input (to close a popup, get back to parent, clear focus) @@ -3282,7 +3282,7 @@ void ImGui::NewFrame() IM_ASSERT(g.IO.KeyMap[n] >= -1 && g.IO.KeyMap[n] < IM_ARRAYSIZE(g.IO.KeysDown) && "io.KeyMap[] contains an out of bound value (need to be 0..512, or -1 for unmapped key)"); // Do a simple check for required key mapping (we intentionally do NOT check all keys to not pressure user into setting up everything, but Space is required and was super recently added in 1.60 WIP) - if (g.IO.NavFlags & ImGuiNavFlags_EnableKeyboard) + if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation."); // Load settings on first frame @@ -3449,7 +3449,7 @@ void ImGui::NewFrame() g.IO.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0); else g.IO.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL); - if (g.IO.NavActive && (g.IO.NavFlags & ImGuiNavFlags_EnableKeyboard) && !(g.IO.NavFlags & ImGuiNavFlags_NoCaptureKeyboard)) + if (g.IO.NavActive && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard)) g.IO.WantCaptureKeyboard = true; g.IO.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : 0; @@ -5844,7 +5844,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) float sc = g.Style.MouseCursorScale; ImVec2 ref_pos = (!g.NavDisableHighlight && g.NavDisableMouseHover) ? NavCalcPreferredMousePos() : g.IO.MousePos; ImRect rect_to_avoid; - if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.NavFlags & ImGuiNavFlags_MoveMouse)) + if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavMoveMouse)) rect_to_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8); else rect_to_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24 * sc, ref_pos.y + 24 * sc); // FIXME: Hard-coded based on mouse cursor shape expectation. Exact dimension not very important. diff --git a/imgui.h b/imgui.h index 608ce1b289ea..66144d6d5ef2 100644 --- a/imgui.h +++ b/imgui.h @@ -95,7 +95,7 @@ typedef int ImGuiComboFlags; // flags: for BeginCombo() typedef int ImGuiFocusedFlags; // flags: for IsWindowFocused() // enum ImGuiFocusedFlags_ typedef int ImGuiHoveredFlags; // flags: for IsItemHovered() etc. // enum ImGuiHoveredFlags_ typedef int ImGuiInputTextFlags; // flags: for InputText*() // enum ImGuiInputTextFlags_ -typedef int ImGuiNavFlags; // flags: for io.NavFlags // enum ImGuiNavFlags_ +typedef int ImGuiConfigFlags; // flags: for io.ConfigFlags // enum ImGuiConfigFlags_ typedef int ImGuiSelectableFlags; // flags: for Selectable() // enum ImGuiSelectableFlags_ typedef int ImGuiTreeNodeFlags; // flags: for TreeNode*(),CollapsingHeader()// enum ImGuiTreeNodeFlags_ typedef int ImGuiWindowFlags; // flags: for Begin*() // enum ImGuiWindowFlags_ @@ -710,8 +710,8 @@ enum ImGuiKey_ }; // [BETA] Gamepad/Keyboard directional navigation -// Keyboard: Set io.NavFlags |= ImGuiNavFlags_EnableKeyboard to enable. NewFrame() will automatically fill io.NavInputs[] based on your io.KeyDown[] + io.KeyMap[] arrays. -// Gamepad: Set io.NavFlags |= ImGuiNavFlags_EnableGamepad to enable. Fill the io.NavInputs[] fields before calling NewFrame(). Note that io.NavInputs[] is cleared by EndFrame(). +// Keyboard: Set io.ConfigFlags |= ImGuiConfigFlags_EnableKeyboard to enable. NewFrame() will automatically fill io.NavInputs[] based on your io.KeyDown[] + io.KeyMap[] arrays. +// Gamepad: Set io.ConfigFlags |= ImGuiConfigFlags_EnableGamepad to enable. Fill the io.NavInputs[] fields before calling NewFrame(). Note that io.NavInputs[] is cleared by EndFrame(). // Read instructions in imgui.cpp for more details. enum ImGuiNavInput_ { @@ -744,13 +744,13 @@ enum ImGuiNavInput_ ImGuiNavInput_InternalStart_ = ImGuiNavInput_KeyMenu_ }; -// [BETA] Gamepad/Keyboard directional navigation flags, stored in io.NavFlags -enum ImGuiNavFlags_ +// Configuration flags stored in io.ConfigFlags +enum ImGuiConfigFlags_ { - ImGuiNavFlags_EnableKeyboard = 1 << 0, // Master keyboard navigation enable flag. NewFrame() will automatically fill io.NavInputs[] based on io.KeyDown[]. - ImGuiNavFlags_EnableGamepad = 1 << 1, // Master gamepad navigation enable flag. This is mostly to instruct your imgui back-end to fill io.NavInputs[]. - ImGuiNavFlags_MoveMouse = 1 << 2, // Request navigation to allow moving the mouse cursor. May be useful on TV/console systems where moving a virtual mouse is awkward. Will update io.MousePos and set io.WantMoveMouse=true. If enabled you MUST honor io.WantMoveMouse requests in your binding, otherwise ImGui will react as if the mouse is jumping around back and forth. - ImGuiNavFlags_NoCaptureKeyboard = 1 << 3 // Do not set the io.WantCaptureKeyboard flag with io.NavActive is set. + ImGuiConfigFlags_NavEnableKeyboard = 1 << 0, // Master keyboard navigation enable flag. NewFrame() will automatically fill io.NavInputs[] based on io.KeyDown[]. + ImGuiConfigFlags_NavEnableGamepad = 1 << 1, // Master gamepad navigation enable flag. This is mostly to instruct your imgui back-end to fill io.NavInputs[]. + ImGuiConfigFlags_NavMoveMouse = 1 << 2, // Request navigation to allow moving the mouse cursor. May be useful on TV/console systems where moving a virtual mouse is awkward. Will update io.MousePos and set io.WantMoveMouse=true. If enabled you MUST honor io.WantMoveMouse requests in your binding, otherwise ImGui will react as if the mouse is jumping around back and forth. + ImGuiConfigFlags_NavNoCaptureKeyboard = 1 << 3 // Do not set the io.WantCaptureKeyboard flag with io.NavActive is set. }; // Enumeration for PushStyleColor() / PopStyleColor() @@ -953,7 +953,7 @@ struct ImGuiIO ImVec2 DisplaySize; // // Display size, in pixels. For clamping windows positions. float DeltaTime; // = 1.0f/60.0f // Time elapsed since last frame, in seconds. - ImGuiNavFlags NavFlags; // = 0x00 // See ImGuiNavFlags_. Gamepad/keyboard navigation options. + ImGuiConfigFlags ConfigFlags; // = 0 // See ImGuiConfigFlags_ enum. Gamepad/keyboard navigation options, etc. float IniSavingRate; // = 5.0f // Maximum time between saving positions/sizes to .ini file, in seconds. const char* IniFilename; // = "imgui.ini" // Path to .ini file. NULL to disable .ini saving. const char* LogFilename; // = "imgui_log.txt" // Path to .log file (default parameter to ImGui::LogToFile when no file is specified). @@ -1028,7 +1028,7 @@ struct ImGuiIO bool WantCaptureMouse; // When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application. This is set by ImGui when it wants to use your mouse (e.g. unclicked mouse is hovering a window, or a widget is active). bool WantCaptureKeyboard; // When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. This is set by ImGui when it wants to use your keyboard inputs. bool WantTextInput; // Mobile/console: when io.WantTextInput is true, you may display an on-screen keyboard. This is set by ImGui when it wants textual keyboard input to happen (e.g. when a InputText widget is active). - bool WantMoveMouse; // MousePos has been altered, back-end should reposition mouse on next frame. Set only when ImGuiNavFlags_MoveMouse flag is enabled in io.NavFlags. + bool WantMoveMouse; // MousePos has been altered, back-end should reposition mouse on next frame. Set only when ImGuiConfigFlags_NavMoveMouse flag is enabled. bool NavActive; // Directional navigation is currently allowed (will handle ImGuiKey_NavXXX events) = a window is focused and it doesn't use the ImGuiWindowFlags_NoNavInputs flag. bool NavVisible; // Directional navigation is visible and allowed (will handle ImGuiKey_NavXXX events). float Framerate; // Application framerate estimation, in frame per second. Solely for convenience. Rolling average estimation based on IO.DeltaTime over 120 frames diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 00cd299dcaac..abc1d5ba2254 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1806,9 +1806,9 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::Checkbox("io.MouseDrawCursor", &io.MouseDrawCursor); ImGui::SameLine(); ShowHelpMarker("Request ImGui to render a mouse cursor for you in software. Note that a mouse cursor rendered via your application GPU rendering path will feel more laggy than hardware cursor, but will be more in sync with your other visuals.\n\nSome desktop applications may use both kinds of cursors (e.g. enable software cursor only when resizing/dragging something)."); - ImGui::CheckboxFlags("io.NavFlags: EnableGamepad", (unsigned int *)&io.NavFlags, ImGuiNavFlags_EnableGamepad); - ImGui::CheckboxFlags("io.NavFlags: EnableKeyboard", (unsigned int *)&io.NavFlags, ImGuiNavFlags_EnableKeyboard); - ImGui::CheckboxFlags("io.NavFlags: MoveMouse", (unsigned int *)&io.NavFlags, ImGuiNavFlags_MoveMouse); + ImGui::CheckboxFlags("io.ConfigFlags: NavEnableGamepad", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_NavEnableGamepad); + ImGui::CheckboxFlags("io.ConfigFlags: NavEnableKeyboard", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_NavEnableKeyboard); + ImGui::CheckboxFlags("io.ConfigFlags: NavMoveMouse", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_NavMoveMouse); ImGui::SameLine(); ShowHelpMarker("Request ImGui to move your move cursor when using gamepad/keyboard navigation. NewFrame() will change io.MousePos and set the io.WantMoveMouse flag, your backend will need to apply the new mouse position."); if (ImGui::TreeNode("Keyboard, Mouse & Navigation State")) diff --git a/imgui_internal.h b/imgui_internal.h index ddbbf0d77e84..9b82043692c2 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -633,8 +633,8 @@ struct ImGuiContext int NavLayer; // Layer we are navigating on. For now the system is hard-coded for 0=main contents and 1=menu/title bar, may expose layers later. int NavIdTabCounter; // == NavWindow->DC.FocusIdxTabCounter at time of NavId processing bool NavIdIsAlive; // Nav widget has been seen this frame ~~ NavRefRectRel is valid - bool NavMousePosDirty; // When set we will update mouse position if (NavFlags & ImGuiNavFlags_MoveMouse) if set (NB: this not enabled by default) - bool NavDisableHighlight; // When user starts using mouse, we hide gamepad/keyboard highlight (nb: but they are still available, which is why NavDisableHighlight isn't always != NavDisableMouseHover) + bool NavMousePosDirty; // When set we will update mouse position if (io.ConfigFlags & ImGuiConfigFlags_NavMoveMouse) if set (NB: this not enabled by default) + bool NavDisableHighlight; // When user starts using mouse, we hide gamepad/keyboard highlight (NB: but they are still available, which is why NavDisableHighlight isn't always != NavDisableMouseHover) bool NavDisableMouseHover; // When user starts using gamepad/keyboard, we hide mouse hovering highlight until mouse is touched again. bool NavAnyRequest; // ~~ NavMoveRequest || NavInitRequest bool NavInitRequest; // Init request for appearing window to select first item From d98a5d54aabb9fd24bbcc14d446ab6fc6a2b5d3f Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 28 Feb 2018 18:51:40 +0100 Subject: [PATCH 039/828] Renamed ImGuiNavFlags io.NavFlags to ImGuiConfigFlags io.ConfigFlags. (#787) --- CHANGELOG.txt | 8 ++--- README.md | 2 +- examples/allegro5_example/main.cpp | 2 +- examples/directx10_example/main.cpp | 2 +- examples/directx11_example/main.cpp | 4 +-- examples/directx12_example/main.cpp | 2 +- examples/directx9_example/main.cpp | 2 +- examples/imgui_impl_glfw.cpp | 8 ++--- examples/imgui_impl_glfw.h | 2 +- examples/imgui_impl_win32.cpp | 2 +- examples/marmalade_example/main.cpp | 2 +- examples/opengl2_example/main.cpp | 2 +- examples/opengl3_example/main.cpp | 7 ++-- examples/sdl_opengl2_example/main.cpp | 3 +- examples/sdl_opengl3_example/main.cpp | 2 +- examples/sdl_vulkan_example/main.cpp | 5 +-- examples/vulkan_example/main.cpp | 6 ++-- imgui.cpp | 27 ++++++++------- imgui.h | 47 +++++++++++++-------------- imgui_demo.cpp | 6 ++-- imgui_internal.h | 4 +-- 21 files changed, 72 insertions(+), 73 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index a0f0e2a7ffb9..06ee3c3ba087 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -66,11 +66,11 @@ Other Changes: - Navigation: merged in the gamepad/keyboard navigation (about one million changes!). (#787, #323) The initial focus was to support game controllers, but keyboard is becoming increasingly and decently usable. - To use Keyboard Navigation: - - Set io.NavFlags |= ImGuiNavFlags_EnableKeyboard to enable. NewFrame() will automatically fill io.NavInputs[] based on your io.KeyDown[] + io.KeyMap[] arrays. - - When keyboard navigation is active (io.NavActive + NavFlags_EnableKeyboard), the io.WantCaptureKeyboard flag will be set. + - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable. NewFrame() will automatically fill io.NavInputs[] based on your io.KeyDown[] + io.KeyMap[] arrays. + - When keyboard navigation is active (io.NavActive + ImGuiConfigFlags_NavEnableKeyboard), the io.WantCaptureKeyboard flag will be set. For more advanced uses, you may want to read from io.NavActive or io.NavVisible. Read imgui.cpp for more details. - To use Gamepad Navigation: - - Set io.NavFlags |= ImGuiNavFlags_EnableGamepad to enable. Fill the io.NavInputs[] fields before calling NewFrame(). Note that io.NavInputs[] is cleared by EndFrame(). + - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable. Fill the io.NavInputs[] fields before calling NewFrame(). Note that io.NavInputs[] is cleared by EndFrame(). - See https://github.com/ocornut/imgui/issues/1599 for recommended gamepad mapping. - See 'enum ImGuiNavInput_' in imgui.h for a description of inputs. Read imgui.cpp for more details. - Navigation: SetItemDefaultFocus() sets the navigation position in addition to scrolling. (#787) @@ -78,7 +78,7 @@ Other Changes: - Navigation: Added window flags: ImGuiWindowFlags_NoNav (ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus). - Navigation: Style: Added ImGuiCol_NavHighlight, ImGuiCol_NavWindowingHighlight colors. (#787) - Navigation: TreeNode: Added ImGuiTreeNodeFlags_NavLeftJumpsBackHere flag to allow Nav Left direction to jump back to parent tree node from any of its child. (#1079) -- Navigation: IO: Added io.NavFlags (input), io.NavActive (output), io.NavVisible (output). (#787) +- Navigation: IO: Added io.ConfigFlags (input), io.NavActive (output), io.NavVisible (output). (#787) - Context: Removed the default global context and font atlas instances, which caused various problems to users of multiple contexts and DLL users. (#1565) YOU NOW NEED TO CALL ImGui::CreateContext() AT THE BEGINNING OF YOUR APP, AND CALL ImGui::DestroyContext() AT THE END. Existing apps will assert/crash without it. - Context: Removed allocator parameters from CreateContext(), they are now setup with SetAllocatorFunctions() and shared by all contexts. (#1565, #586, #992, #1007, #1558) diff --git a/README.md b/README.md index 1190405ac4fa..da7979635baf 100644 --- a/README.md +++ b/README.md @@ -248,7 +248,7 @@ See the FAQ in imgui.cpp for answers. How do you use Dear ImGui on a platform that may not have a mouse or keyboard? -You can control Dear ImGui with a gamepad, see the explanation in imgui.cpp about how to use the navigation feature (short version: map your gamepad inputs into the `io.NavInputs[]` array and set `io.NavFlags |= ImGuiNavFlags_EnableGamepad`). +You can control Dear ImGui with a gamepad, see the explanation in imgui.cpp about how to use the navigation feature (short version: map your gamepad inputs into the `io.NavInputs[]` array and set `io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad`). You can share your computer mouse seamlessy with your console/tablet/phone using [Synergy](http://synergy-project.org). This is the prefered solution for developer productivity. In particular, their [micro-synergy-client](https://github.com/symless/micro-synergy-client) repo there is _uSynergy.c_ sources for a small embeddable that you can use on any platform to connect to your host PC. You may also use a third party solution such as [Remote ImGui](https://github.com/JordiRos/remoteimgui). diff --git a/examples/allegro5_example/main.cpp b/examples/allegro5_example/main.cpp index 4a53461027e6..80326bca9d21 100644 --- a/examples/allegro5_example/main.cpp +++ b/examples/allegro5_example/main.cpp @@ -25,8 +25,8 @@ int main(int, char**) // Setup ImGui binding ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls ImGui_ImplAllegro5_Init(display); - //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; // Enable Keyboard Controls // Setup style ImGui::StyleColorsDark(); diff --git a/examples/directx10_example/main.cpp b/examples/directx10_example/main.cpp index 4a55f253e09f..eb6e5a66161d 100644 --- a/examples/directx10_example/main.cpp +++ b/examples/directx10_example/main.cpp @@ -116,7 +116,7 @@ int main(int, char**) ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_MultiViewports; - //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; // Enable Keyboard Controls + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls ImGui_ImplWin32_Init(hwnd); ImGui_ImplDX10_Init(g_pd3dDevice); diff --git a/examples/directx11_example/main.cpp b/examples/directx11_example/main.cpp index 356798e59794..f949037b4d77 100644 --- a/examples/directx11_example/main.cpp +++ b/examples/directx11_example/main.cpp @@ -117,9 +117,9 @@ int main(int, char**) // Setup ImGui binding ImGui::CreateContext(); - ImGuiIO& io = ImGui::GetIO(); - io.NavFlags |= ImGuiNavFlags_EnableKeyboard; + ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_MultiViewports; + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls ImGui_ImplWin32_Init(hwnd); ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext); diff --git a/examples/directx12_example/main.cpp b/examples/directx12_example/main.cpp index beb9aa2445c3..61d65b028f96 100644 --- a/examples/directx12_example/main.cpp +++ b/examples/directx12_example/main.cpp @@ -290,7 +290,7 @@ int main(int, char**) ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_MultiViewports; - //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; // Enable Keyboard Controls + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls ImGui_ImplWin32_Init(hwnd); ImGui_ImplDX12_Init(g_pd3dDevice, NUM_FRAMES_IN_FLIGHT, DXGI_FORMAT_R8G8B8A8_UNORM, diff --git a/examples/directx9_example/main.cpp b/examples/directx9_example/main.cpp index c7f54255ab6f..1035cee29e7b 100644 --- a/examples/directx9_example/main.cpp +++ b/examples/directx9_example/main.cpp @@ -78,9 +78,9 @@ int main(int, char**) // Setup ImGui binding ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls ImGui_ImplWin32_Init(hwnd); ImGui_ImplDX9_Init(g_pd3dDevice); - //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; // Enable Keyboard Controls // Setup style ImGui::StyleColorsDark(); diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index ab7bebc256e1..aebdf26f5255 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -3,7 +3,7 @@ // (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) // Implemented features: -// [X] Gamepad navigation mapping. Enable with 'io.NavFlags |= ImGuiNavFlags_EnableGamepad'. +// [X] Gamepad navigation mapping. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). @@ -16,8 +16,8 @@ // 2018-XX-XX: Inputs: Added support for mouse cursors, honoring ImGui::GetMouseCursor() value. // 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. // 2018-02-06: Inputs: Added mapping for ImGuiKey_Space. -// 2018-01-25: Inputs: Added gamepad support if ImGuiNavFlags_EnableGamepad is set. -// 2018-01-25: Inputs: Honoring the io.WantMoveMouse by repositioning the mouse by using navigation and ImGuiNavFlags_MoveMouse is set. +// 2018-01-25: Inputs: Added gamepad support if ImGuiConfigFlags_NavEnableGamepad is set. +// 2018-01-25: Inputs: Honoring the io.WantMoveMouse by repositioning the mouse (when using navigation and ImGuiConfigFlags_NavMoveMouse is set). // 2018-01-20: Inputs: Added Horizontal Mouse Wheel support. // 2018-01-18: Inputs: Added mapping for ImGuiKey_Insert. // 2017-08-25: Inputs: MousePos set to -FLT_MAX,-FLT_MAX when mouse is unavailable/missing (instead of -1,-1). @@ -241,7 +241,7 @@ void ImGui_ImplGlfw_NewFrame() // Gamepad navigation mapping [BETA] memset(io.NavInputs, 0, sizeof(io.NavInputs)); - if (io.NavFlags & ImGuiNavFlags_EnableGamepad) + if (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) { // Update gamepad inputs #define MAP_BUTTON(NAV_NO, BUTTON_NO) { if (buttons_count > BUTTON_NO && buttons[BUTTON_NO] == GLFW_PRESS) io.NavInputs[NAV_NO] = 1.0f; } diff --git a/examples/imgui_impl_glfw.h b/examples/imgui_impl_glfw.h index c6cb4c8a3c47..3e8fd6c7ecdc 100644 --- a/examples/imgui_impl_glfw.h +++ b/examples/imgui_impl_glfw.h @@ -3,7 +3,7 @@ // (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) // Implemented features: -// [X] Gamepad navigation mapping. Enable with 'io.NavFlags |= ImGuiNavFlags_EnableGamepad'. +// [X] Gamepad navigation mapping. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index 331b67ae0176..061f75d92f7b 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -13,7 +13,7 @@ // 2018-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformInterface // 2018-02-20: Inputs: Added support for mouse cursors (ImGui::GetMouseCursor() value and WM_SETCURSOR message handling). // 2018-02-06: Inputs: Added mapping for ImGuiKey_Space. -// 2018-02-06: Inputs: Honoring the io.WantMoveMouse by repositioning the mouse by using navigation and ImGuiNavFlags_MoveMouse is set. +// 2018-02-06: Inputs: Honoring the io.WantMoveMouse by repositioning the mouse (when using navigation and ImGuiConfigFlags_NavMoveMouse is set). // 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. // 2018-01-20: Inputs: Added Horizontal Mouse Wheel support. // 2018-01-08: Inputs: Added mapping for ImGuiKey_Insert. diff --git a/examples/marmalade_example/main.cpp b/examples/marmalade_example/main.cpp index 08f8b1d5ce16..817a28727e51 100644 --- a/examples/marmalade_example/main.cpp +++ b/examples/marmalade_example/main.cpp @@ -19,8 +19,8 @@ int main(int, char**) // Setup ImGui binding ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls ImGui_Marmalade_Init(true); - //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; // Enable Keyboard Controls // Setup style ImGui::StyleColorsDark(); diff --git a/examples/opengl2_example/main.cpp b/examples/opengl2_example/main.cpp index 6eefca8fffa6..0b2885c42d85 100644 --- a/examples/opengl2_example/main.cpp +++ b/examples/opengl2_example/main.cpp @@ -30,9 +30,9 @@ int main(int, char**) // Setup ImGui binding ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls ImGui_ImplGlfw_Init(window, true); ImGui_ImplOpenGL2_Init(); - //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; // Enable Keyboard Controls // Setup style ImGui::StyleColorsDark(); diff --git a/examples/opengl3_example/main.cpp b/examples/opengl3_example/main.cpp index e2d626e77a30..2cb6af1b8950 100644 --- a/examples/opengl3_example/main.cpp +++ b/examples/opengl3_example/main.cpp @@ -34,11 +34,10 @@ int main(int, char**) // Setup ImGui binding ImGui::CreateContext(); - ImGuiIO& io = ImGui::GetIO(); + ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_MultiViewports; - //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; // Enable Keyboard Controls - //io.NavFlags |= ImGuiNavFlags_EnableGamepad; // Enable Gamepad Controls - + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls ImGui_ImplGlfw_Init(window, true); ImGui_ImplOpenGL3_Init(); diff --git a/examples/sdl_opengl2_example/main.cpp b/examples/sdl_opengl2_example/main.cpp index 4e497c405b17..05b358c853bc 100644 --- a/examples/sdl_opengl2_example/main.cpp +++ b/examples/sdl_opengl2_example/main.cpp @@ -37,9 +37,10 @@ int main(int, char**) // Setup ImGui binding ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + ImGui_ImplSDL2_Init(window, gl_context); ImGui_ImplOpenGL2_Init(); - //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; // Enable Keyboard Controls // Setup style ImGui::StyleColorsDark(); diff --git a/examples/sdl_opengl3_example/main.cpp b/examples/sdl_opengl3_example/main.cpp index 756fe53e3b21..1fb38c2cae63 100644 --- a/examples/sdl_opengl3_example/main.cpp +++ b/examples/sdl_opengl3_example/main.cpp @@ -38,7 +38,7 @@ int main(int, char**) ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_MultiViewports; - //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls ImGui_ImplSDL2_Init(window, gl_context); ImGui_ImplOpenGL3_Init(); diff --git a/examples/sdl_vulkan_example/main.cpp b/examples/sdl_vulkan_example/main.cpp index 7772f8572f86..2f869de1cd7c 100644 --- a/examples/sdl_vulkan_example/main.cpp +++ b/examples/sdl_vulkan_example/main.cpp @@ -612,8 +612,6 @@ int main(int, char**) // Setup ImGui binding ImGui::CreateContext(); - ImGuiIO& io = ImGui::GetIO(); (void)io; - //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; ImGui_ImplVulkan_InitData init_data = {}; init_data.allocator = g_Allocator; @@ -623,6 +621,9 @@ int main(int, char**) init_data.pipeline_cache = g_PipelineCache; init_data.descriptor_pool = g_DescriptorPool; init_data.check_vk_result = check_vk_result; + + ImGuiIO& io = ImGui::GetIO(); (void)io; + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls ImGui_ImplVulkan_Init(&init_data); ImGui_ImplSDL2_Init(window, NULL); diff --git a/examples/vulkan_example/main.cpp b/examples/vulkan_example/main.cpp index 015d763f10ab..9c1b07963eef 100644 --- a/examples/vulkan_example/main.cpp +++ b/examples/vulkan_example/main.cpp @@ -615,7 +615,7 @@ int main(int, char**) // Setup ImGui binding ImGui::CreateContext(); - ImGuiIO& io = ImGui::GetIO(); (void)io; + ImGui_ImplVulkan_InitData init_data = {}; init_data.allocator = g_Allocator; init_data.gpu = g_Gpu; @@ -624,9 +624,11 @@ int main(int, char**) init_data.pipeline_cache = g_PipelineCache; init_data.descriptor_pool = g_DescriptorPool; init_data.check_vk_result = check_vk_result; + + ImGuiIO& io = ImGui::GetIO(); (void)io; + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls ImGui_ImplVulkan_Init(&init_data); ImGui_ImplGlfw_Init(window, true); - //io.NavFlags |= ImGuiNavFlags_EnableKeyboard; // Enable Keyboard Controls // Setup style ImGui::StyleColorsDark(); diff --git a/imgui.cpp b/imgui.cpp index 891c85d0179b..cee66bb3d396 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -216,15 +216,15 @@ - Ask questions and report issues at https://github.com/ocornut/imgui/issues/787 - The initial focus was to support game controllers, but keyboard is becoming increasingly and decently usable. - Keyboard: - - Set io.NavFlags |= ImGuiNavFlags_EnableKeyboard to enable. NewFrame() will automatically fill io.NavInputs[] based on your io.KeyDown[] + io.KeyMap[] arrays. - - When keyboard navigation is active (io.NavActive + NavFlags_EnableKeyboard), the io.WantCaptureKeyboard flag will be set. + - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable. NewFrame() will automatically fill io.NavInputs[] based on your io.KeyDown[] + io.KeyMap[] arrays. + - When keyboard navigation is active (io.NavActive + ImGuiConfigFlags_NavEnableKeyboard), the io.WantCaptureKeyboard flag will be set. For more advanced uses, you may want to read from: - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set. - io.NavVisible: true when the navigation cursor is visible (and usually goes false when mouse is used). - or query focus information with e.g. IsWindowFocused(), IsItemFocused() etc. functions. Please reach out if you think the game vs navigation input sharing could be improved. - Gamepad: - - Set io.NavFlags |= ImGuiNavFlags_EnableGamepad to enable. Fill the io.NavInputs[] fields before calling NewFrame(). Note that io.NavInputs[] is cleared by EndFrame(). + - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable. Fill the io.NavInputs[] fields before calling NewFrame(). Note that io.NavInputs[] is cleared by EndFrame(). - See 'enum ImGuiNavInput_' in imgui.h for a description of inputs. For each entry of io.NavInputs[], set the following values: 0.0f= not held. 1.0f= fully held. Pass intermediate 0.0f..1.0f values for analog triggers/sticks. - We uses a simple >0.0f test for activation testing, and won't attempt to test for a dead-zone. @@ -234,11 +234,11 @@ - Mouse: - PS4 users: Consider emulating a mouse cursor with DualShock4 touch pad or a spare analog stick as a mouse-emulation fallback. - Consoles/Tablet/Phone users: Consider using Synergy host (on your computer) + uSynergy.c (in your console/tablet/phone app) to use your PC mouse/keyboard. - - On a TV/console system where readability may be lower or mouse inputs may be awkward, you may want to set the ImGuiNavFlags_MoveMouse flag in io.NavFlags. - Enabling ImGuiNavFlags_MoveMouse instructs dear imgui to move your mouse cursor along with navigation movements. + - On a TV/console system where readability may be lower or mouse inputs may be awkward, you may want to set the ImGuiConfigFlags_NavMoveMouse flag. + Enabling ImGuiConfigFlags_NavMoveMouse instructs dear imgui to move your mouse cursor along with navigation movements. When enabled, the NewFrame() function may alter 'io.MousePos' and set 'io.WantMoveMouse' to notify you that it wants the mouse cursor to be moved. When that happens your back-end NEEDS to move the OS or underlying mouse cursor on the next frame. Some of the binding in examples/ do that. - (If you set the ImGuiNavFlags_MoveMouse flag but don't honor 'io.WantMoveMouse' properly, imgui will misbehave as it will see your mouse as moving back and forth.) + (If you set the NavMoveMouse flag but don't honor 'io.WantMoveMouse' properly, imgui will misbehave as it will see your mouse as moving back and forth!) (In a setup when you may not have easy control over the mouse cursor, e.g. uSynergy.c doesn't expose moving remote mouse cursor, you may want to set a boolean to ignore your other external mouse positions until the external source is moved again.) @@ -859,7 +859,6 @@ ImGuiIO::ImGuiIO() DisplaySize = ImVec2(-1.0f, -1.0f); DeltaTime = 1.0f/60.0f; ConfigFlags = 0x00; - NavFlags = 0x00; IniSavingRate = 5.0f; IniFilename = "imgui.ini"; LogFilename = "imgui_log.txt"; @@ -2834,7 +2833,7 @@ static void ImGui::NavUpdateWindowing() bool apply_toggle_layer = false; bool start_windowing_with_gamepad = !g.NavWindowingTarget && IsNavInputPressed(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed); - bool start_windowing_with_keyboard = !g.NavWindowingTarget && g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (g.IO.NavFlags & ImGuiNavFlags_EnableKeyboard); + bool start_windowing_with_keyboard = !g.NavWindowingTarget && g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard); if (start_windowing_with_gamepad || start_windowing_with_keyboard) if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavigable(g.Windows.Size - 1, -INT_MAX, -1)) { @@ -2989,7 +2988,7 @@ static void ImGui::NavUpdate() // Update Keyboard->Nav inputs mapping memset(g.IO.NavInputs + ImGuiNavInput_InternalStart_, 0, (ImGuiNavInput_COUNT - ImGuiNavInput_InternalStart_) * sizeof(g.IO.NavInputs[0])); - if (g.IO.NavFlags & ImGuiNavFlags_EnableKeyboard) + if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) { #define NAV_MAP_KEY(_KEY, _NAV_INPUT) if (g.IO.KeyMap[_KEY] != -1 && IsKeyDown(g.IO.KeyMap[_KEY])) g.IO.NavInputs[_NAV_INPUT] = 1.0f; NAV_MAP_KEY(ImGuiKey_Space, ImGuiNavInput_Activate ); @@ -3061,7 +3060,7 @@ static void ImGui::NavUpdate() if (g.NavMousePosDirty && g.NavIdIsAlive) { // Set mouse position given our knowledge of the nav widget position from last frame - if (g.IO.NavFlags & ImGuiNavFlags_MoveMouse) + if (g.IO.ConfigFlags & ImGuiConfigFlags_NavMoveMouse) { g.IO.MousePos = g.IO.MousePosPrev = NavCalcPreferredMousePos(); g.IO.WantMoveMouse = true; @@ -3081,7 +3080,7 @@ static void ImGui::NavUpdate() NavUpdateWindowing(); // Set output flags for user application - g.IO.NavActive = (g.IO.NavFlags & (ImGuiNavFlags_EnableGamepad | ImGuiNavFlags_EnableKeyboard)) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs); + g.IO.NavActive = (g.IO.ConfigFlags & (ImGuiConfigFlags_NavEnableGamepad | ImGuiConfigFlags_NavEnableKeyboard)) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs); g.IO.NavVisible = (g.IO.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL) || g.NavInitRequest; // Process NavCancel input (to close a popup, get back to parent, clear focus) @@ -3544,7 +3543,7 @@ void ImGui::NewFrame() IM_ASSERT(g.IO.KeyMap[n] >= -1 && g.IO.KeyMap[n] < IM_ARRAYSIZE(g.IO.KeysDown) && "io.KeyMap[] contains an out of bound value (need to be 0..512, or -1 for unmapped key)"); // Do a simple check for required key mapping (we intentionally do NOT check all keys to not pressure user into setting up everything, but Space is required and was super recently added in 1.60 WIP) - if (g.IO.NavFlags & ImGuiNavFlags_EnableKeyboard) + if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation."); if (g.IO.ConfigFlags & ImGuiConfigFlags_MultiViewports) @@ -3728,7 +3727,7 @@ void ImGui::NewFrame() g.IO.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0); else g.IO.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL); - if (g.IO.NavActive && (g.IO.NavFlags & ImGuiNavFlags_EnableKeyboard) && !(g.IO.NavFlags & ImGuiNavFlags_NoCaptureKeyboard)) + if (g.IO.NavActive && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard)) g.IO.WantCaptureKeyboard = true; g.IO.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : 0; @@ -6421,7 +6420,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) float sc = g.Style.MouseCursorScale; ImVec2 ref_pos = (!g.NavDisableHighlight && g.NavDisableMouseHover) ? NavCalcPreferredMousePos() : g.IO.MousePos; ImRect rect_to_avoid; - if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.NavFlags & ImGuiNavFlags_MoveMouse)) + if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavMoveMouse)) rect_to_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8); else rect_to_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24 * sc, ref_pos.y + 24 * sc); // FIXME: Hard-coded based on mouse cursor shape expectation. Exact dimension not very important. diff --git a/imgui.h b/imgui.h index 8d3a4efcdd06..8e05af609ef6 100644 --- a/imgui.h +++ b/imgui.h @@ -98,7 +98,6 @@ typedef int ImGuiComboFlags; // flags: for BeginCombo() typedef int ImGuiFocusedFlags; // flags: for IsWindowFocused() // enum ImGuiFocusedFlags_ typedef int ImGuiHoveredFlags; // flags: for IsItemHovered() etc. // enum ImGuiHoveredFlags_ typedef int ImGuiInputTextFlags; // flags: for InputText*() // enum ImGuiInputTextFlags_ -typedef int ImGuiNavFlags; // flags: for io.NavFlags // enum ImGuiNavFlags_ typedef int ImGuiSelectableFlags; // flags: for Selectable() // enum ImGuiSelectableFlags_ typedef int ImGuiTreeNodeFlags; // flags: for TreeNode*(),CollapsingHeader()// enum ImGuiTreeNodeFlags_ typedef int ImGuiWindowFlags; // flags: for Begin*() // enum ImGuiWindowFlags_ @@ -719,8 +718,8 @@ enum ImGuiKey_ }; // [BETA] Gamepad/Keyboard directional navigation -// Keyboard: Set io.NavFlags |= ImGuiNavFlags_EnableKeyboard to enable. NewFrame() will automatically fill io.NavInputs[] based on your io.KeyDown[] + io.KeyMap[] arrays. -// Gamepad: Set io.NavFlags |= ImGuiNavFlags_EnableGamepad to enable. Fill the io.NavInputs[] fields before calling NewFrame(). Note that io.NavInputs[] is cleared by EndFrame(). +// Keyboard: Set io.ConfigFlags |= ImGuiConfigFlags_EnableKeyboard to enable. NewFrame() will automatically fill io.NavInputs[] based on your io.KeyDown[] + io.KeyMap[] arrays. +// Gamepad: Set io.ConfigFlags |= ImGuiConfigFlags_EnableGamepad to enable. Fill the io.NavInputs[] fields before calling NewFrame(). Note that io.NavInputs[] is cleared by EndFrame(). // Read instructions in imgui.cpp for more details. enum ImGuiNavInput_ { @@ -753,13 +752,25 @@ enum ImGuiNavInput_ ImGuiNavInput_InternalStart_ = ImGuiNavInput_KeyMenu_ }; -// [BETA] Gamepad/Keyboard directional navigation flags, stored in io.NavFlags -enum ImGuiNavFlags_ +// Configuration flags stored in io.ConfigFlags +enum ImGuiConfigFlags_ { - ImGuiNavFlags_EnableKeyboard = 1 << 0, // Master keyboard navigation enable flag. NewFrame() will automatically fill io.NavInputs[] based on io.KeyDown[]. - ImGuiNavFlags_EnableGamepad = 1 << 1, // Master gamepad navigation enable flag. This is mostly to instruct your imgui back-end to fill io.NavInputs[]. - ImGuiNavFlags_MoveMouse = 1 << 2, // Request navigation to allow moving the mouse cursor. May be useful on TV/console systems where moving a virtual mouse is awkward. Will update io.MousePos and set io.WantMoveMouse=true. If enabled you MUST honor io.WantMoveMouse requests in your binding, otherwise ImGui will react as if the mouse is jumping around back and forth. - ImGuiNavFlags_NoCaptureKeyboard = 1 << 3 // Do not set the io.WantCaptureKeyboard flag with io.NavActive is set. + // Navigation + ImGuiConfigFlags_NavEnableKeyboard = 1 << 0, // Master keyboard navigation enable flag. NewFrame() will automatically fill io.NavInputs[] based on io.KeyDown[]. + ImGuiConfigFlags_NavEnableGamepad = 1 << 1, // Master gamepad navigation enable flag. This is mostly to instruct your imgui back-end to fill io.NavInputs[]. + ImGuiConfigFlags_NavMoveMouse = 1 << 2, // Request navigation to allow moving the mouse cursor. May be useful on TV/console systems where moving a virtual mouse is awkward. Will update io.MousePos and set io.WantMoveMouse=true. If enabled you MUST honor io.WantMoveMouse requests in your binding, otherwise ImGui will react as if the mouse is jumping around back and forth. + ImGuiConfigFlags_NavNoCaptureKeyboard = 1 << 3, // Do not set the io.WantCaptureKeyboard flag with io.NavActive is set. + + // [BETA] Viewports + ImGuiConfigFlags_MultiViewports = 1 << 4, // User enable multiple viewports (require io.PlatformInterface + io.RendererInterface) + ImGuiConfigFlags_PlatformHasMouseHoveredViewport = 1 << 5, // Back-end knows how to set io.MouseHoveredViewport to the viewport directly under the mouse _IGNORING_ viewports with the ImGuiViewportFlags_NoInputs flag and _REGARDLESS_ of whether another viewport is focused and may have mouse capture. This info is not easy to provide correctly with most high-level engines. + ImGuiConfigFlags_PlatformHasWantMoveMouseSupport = 1 << 6, // Back-end honors io.WantMoveMouse request by updating the OS mouse cursor position (currently only used by ImGuiConfigFlags_NavMoveMouse feature, will be useful for widgets teleporting/wrapping the cursor) + ImGuiConfigFlags_PlatformHasWindowAlpha = 1 << 7, // Back-end can have transparent windows + + // Platform Info (strictly for the user/application) + ImGuiConfigFlags_IsSRGB = 1 << 10, // Back-end is SRGB-aware (Storage flag to allow your back-end to communicate to shared widgets. Not used by core ImGui) + ImGuiConfigFlags_IsTouchScreen = 1 << 11, // Back-end is using a touch screen instead of a mouse (Storage flag to allow your back-end to communicate to shared widgets. Not used by core ImGui) + ImGuiConfigFlags_IsOnScreenKeyboard = 1 << 12 // Back-end uses an on-screen keyboard when io.WantTextInput is set. }; // Enumeration for PushStyleColor() / PopStyleColor() @@ -913,19 +924,6 @@ enum ImGuiCond_ #endif }; -// [BETA] Configuration flags, mostly setup by imgui back-end, stored in io.ConfigFlags -enum ImGuiConfigFlags_ -{ - ImGuiConfigFlags_MultiViewports = 1 << 0, // User enable multiple viewports (require io.PlatformInterface + io.RendererInterface) - ImGuiConfigFlags_PlatformHasMouseHoveredViewport = 1 << 1, // Back-end knows how to set io.MouseHoveredViewport to the viewport directly under the mouse _IGNORING_ viewports with the ImGuiViewportFlags_NoInputs flag and _REGARDLESS_ of whether another viewport is focused and may have mouse capture. This info is not easy to provide correctly with most high-level engines. - ImGuiConfigFlags_PlatformHasWantMoveMouseSupport = 1 << 2, // Back-end honors io.WantMoveMouse request by updating the OS mouse cursor position (currently only used by ImGuiNavFlags_MoveMouse feature, will be useful for widgets teleporting/wrapping the cursor) - ImGuiConfigFlags_PlatformHasWindowAlpha = 1 << 3, // Back-end can have transparent windows - - ImGuiConfigFlags_IsSRGB = 1 << 10, // Back-end is SRGB-aware (Storage flag to allow your back-end to communicate to shared widgets. Not used by core ImGui) - ImGuiConfigFlags_IsTouchScreen = 1 << 11, // Back-end is using a touch screen instead of a mouse (Storage flag to allow your back-end to communicate to shared widgets. Not used by core ImGui) - ImGuiConfigFlags_IsOnScreenKeyboard = 1 << 12 // Back-end uses an on-screen keyboard when io.WantTextInput is set. -}; - // (Optional) Setup required only if (io.ConfigFlags & ImGuiConfigFlags_EnableMultiViewport) is enabled struct ImGuiPlatformInterface { @@ -1002,8 +1000,7 @@ struct ImGuiIO ImVec2 DisplaySize; // // Main display size. Used e.g. to clamp windows positions. This is the default viewport. Use BeginViewport() for other viewports. float DeltaTime; // = 1.0f/60.0f // Time elapsed since last frame, in seconds. - ImGuiConfigFlags ConfigFlags; // = 0 // Set ImGuiConfigFlags_. Features/options flags for ImGui back-ends. - ImGuiNavFlags NavFlags; // = 0x00 // See ImGuiNavFlags_. Gamepad/keyboard navigation options. + ImGuiConfigFlags ConfigFlags; // = 0 // See ImGuiConfigFlags_ enum. Back-end options, Gamepad/keyboard navigation options, etc. float IniSavingRate; // = 5.0f // Maximum time between saving positions/sizes to .ini file, in seconds. const char* IniFilename; // = "imgui.ini" // Path to .ini file. NULL to disable .ini saving. const char* LogFilename; // = "imgui_log.txt" // Path to .log file (default parameter to ImGui::LogToFile when no file is specified). @@ -1082,7 +1079,7 @@ struct ImGuiIO bool WantCaptureMouse; // When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application. This is set by ImGui when it wants to use your mouse (e.g. unclicked mouse is hovering a window, or a widget is active). bool WantCaptureKeyboard; // When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. This is set by ImGui when it wants to use your keyboard inputs. bool WantTextInput; // Mobile/console: when io.WantTextInput is true, you may display an on-screen keyboard. This is set by ImGui when it wants textual keyboard input to happen (e.g. when a InputText widget is active). - bool WantMoveMouse; // MousePos has been altered, back-end should reposition mouse on next frame. Set only when ImGuiNavFlags_MoveMouse flag is enabled in io.NavFlags. + bool WantMoveMouse; // MousePos has been altered, back-end should reposition mouse on next frame. Set only when ImGuiConfigFlags_NavMoveMouse flag is enabled. bool NavActive; // Directional navigation is currently allowed (will handle ImGuiKey_NavXXX events) = a window is focused and it doesn't use the ImGuiWindowFlags_NoNavInputs flag. bool NavVisible; // Directional navigation is visible and allowed (will handle ImGuiKey_NavXXX events). float Framerate; // Application framerate estimation, in frame per second. Solely for convenience. Rolling average estimation based on IO.DeltaTime over 120 frames diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 6efa4e019000..416170ef1703 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1806,9 +1806,9 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::Checkbox("io.MouseDrawCursor", &io.MouseDrawCursor); ImGui::SameLine(); ShowHelpMarker("Request ImGui to render a mouse cursor for you in software. Note that a mouse cursor rendered via your application GPU rendering path will feel more laggy than hardware cursor, but will be more in sync with your other visuals.\n\nSome desktop applications may use both kinds of cursors (e.g. enable software cursor only when resizing/dragging something)."); - ImGui::CheckboxFlags("io.NavFlags: EnableGamepad", (unsigned int *)&io.NavFlags, ImGuiNavFlags_EnableGamepad); - ImGui::CheckboxFlags("io.NavFlags: EnableKeyboard", (unsigned int *)&io.NavFlags, ImGuiNavFlags_EnableKeyboard); - ImGui::CheckboxFlags("io.NavFlags: MoveMouse", (unsigned int *)&io.NavFlags, ImGuiNavFlags_MoveMouse); + ImGui::CheckboxFlags("io.ConfigFlags: NavEnableGamepad", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_NavEnableGamepad); + ImGui::CheckboxFlags("io.ConfigFlags: NavEnableKeyboard", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_NavEnableKeyboard); + ImGui::CheckboxFlags("io.ConfigFlags: NavMoveMouse", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_NavMoveMouse); ImGui::SameLine(); ShowHelpMarker("Request ImGui to move your move cursor when using gamepad/keyboard navigation. NewFrame() will change io.MousePos and set the io.WantMoveMouse flag, your backend will need to apply the new mouse position."); if (ImGui::TreeNode("Keyboard, Mouse & Navigation State")) diff --git a/imgui_internal.h b/imgui_internal.h index 6a12f0442f7e..7c297f6aaf73 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -683,8 +683,8 @@ struct ImGuiContext int NavLayer; // Layer we are navigating on. For now the system is hard-coded for 0=main contents and 1=menu/title bar, may expose layers later. int NavIdTabCounter; // == NavWindow->DC.FocusIdxTabCounter at time of NavId processing bool NavIdIsAlive; // Nav widget has been seen this frame ~~ NavRefRectRel is valid - bool NavMousePosDirty; // When set we will update mouse position if (NavFlags & ImGuiNavFlags_MoveMouse) if set (NB: this not enabled by default) - bool NavDisableHighlight; // When user starts using mouse, we hide gamepad/keyboard highlight (nb: but they are still available, which is why NavDisableHighlight isn't always != NavDisableMouseHover) + bool NavMousePosDirty; // When set we will update mouse position if (io.ConfigFlags & ImGuiConfigFlags_NavMoveMouse) if set (NB: this not enabled by default) + bool NavDisableHighlight; // When user starts using mouse, we hide gamepad/keyboard highlight (NB: but they are still available, which is why NavDisableHighlight isn't always != NavDisableMouseHover) bool NavDisableMouseHover; // When user starts using gamepad/keyboard, we hide mouse hovering highlight until mouse is touched again. bool NavAnyRequest; // ~~ NavMoveRequest || NavInitRequest bool NavInitRequest; // Init request for appearing window to select first item From cf365ed00b5ad222956ffc379908b557da8a4d9a Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 1 Mar 2018 18:41:19 +0100 Subject: [PATCH 040/828] Examples: Fixed enabling IMGUI_VULKAN_DEBUG_REPORT which was broken during refactor because of duplicate 'extensions' local variable. --- examples/sdl_vulkan_example/main.cpp | 19 +++++++++---------- examples/vulkan_example/main.cpp | 19 +++++++++---------- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/examples/sdl_vulkan_example/main.cpp b/examples/sdl_vulkan_example/main.cpp index 973d75b7b101..9e08a9656f27 100644 --- a/examples/sdl_vulkan_example/main.cpp +++ b/examples/sdl_vulkan_example/main.cpp @@ -189,9 +189,9 @@ static void resize_vulkan(SDL_Window*, int w, int h) } #ifdef IMGUI_VULKAN_DEBUG_REPORT -static VKAPI_ATTR VkBool32 VKAPI_CALL debug_report( - VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char* pLayerPrefix, const char* pMessage, void* pUserData) +static VKAPI_ATTR VkBool32 VKAPI_CALL debug_report(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char* pLayerPrefix, const char* pMessage, void* pUserData) { + (void)flags; (void)object; (void)location; (void)messageCode; (void)pUserData; (void)pLayerPrefix; // Unused arguemnts printf("[vulkan] ObjectType: %i\nMessage: %s\n\n", objectType, pMessage); return VK_FALSE; } @@ -209,25 +209,24 @@ static void setup_vulkan(SDL_Window* window, const char** extensions, uint32_t e create_info.ppEnabledExtensionNames = extensions; #ifdef IMGUI_VULKAN_DEBUG_REPORT - // enabling multiple validation layers grouped as lunarg standard validation + // Enabling multiple validation layers grouped as LunarG standard validation const char* layers[] = { "VK_LAYER_LUNARG_standard_validation" }; create_info.enabledLayerCount = 1; create_info.ppEnabledLayerNames = layers; - // need additional storage for char pointer to debug report extension - const char** extensions = (const char**)malloc(sizeof(const char*) * (extensions_count + 1)); - for (size_t i = 0; i < extensions_count; i++) - extensions[i] = extensions[i]; - extensions[extensions_count] = "VK_EXT_debug_report"; + // Enable debug report extension (we need additional storage, so we duplicate the user array to add our new extension to it) + const char** extensions_ext = (const char**)malloc(sizeof(const char*) * (extensions_count + 1)); + memcpy(extensions_ext, extensions, extensions_count * sizeof(const char*)); + extensions_ext[extensions_count] = "VK_EXT_debug_report"; create_info.enabledExtensionCount = extensions_count + 1; - create_info.ppEnabledExtensionNames = extensions; + create_info.ppEnabledExtensionNames = extensions_ext; #endif // IMGUI_VULKAN_DEBUG_REPORT err = vkCreateInstance(&create_info, g_Allocator, &g_Instance); check_vk_result(err); #ifdef IMGUI_VULKAN_DEBUG_REPORT - free(extensions); + free(extensions_ext); // create the debug report callback VkDebugReportCallbackCreateInfoEXT debug_report_ci = {}; diff --git a/examples/vulkan_example/main.cpp b/examples/vulkan_example/main.cpp index b979fc004df3..ca0cdcca072c 100644 --- a/examples/vulkan_example/main.cpp +++ b/examples/vulkan_example/main.cpp @@ -192,9 +192,9 @@ static void resize_vulkan(GLFWwindow*, int w, int h) } #ifdef IMGUI_VULKAN_DEBUG_REPORT -static VKAPI_ATTR VkBool32 VKAPI_CALL debug_report( - VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char* pLayerPrefix, const char* pMessage, void* pUserData) +static VKAPI_ATTR VkBool32 VKAPI_CALL debug_report(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char* pLayerPrefix, const char* pMessage, void* pUserData) { + (void)flags; (void)object; (void)location; (void)messageCode; (void)pUserData; (void)pLayerPrefix; // Unused arguemnts printf("[vulkan] ObjectType: %i\nMessage: %s\n\n", objectType, pMessage); return VK_FALSE; } @@ -212,25 +212,24 @@ static void setup_vulkan(GLFWwindow* window, const char** extensions, uint32_t e create_info.ppEnabledExtensionNames = extensions; #ifdef IMGUI_VULKAN_DEBUG_REPORT - // enabling multiple validation layers grouped as lunarg standard validation + // Enabling multiple validation layers grouped as LunarG standard validation const char* layers[] = { "VK_LAYER_LUNARG_standard_validation" }; create_info.enabledLayerCount = 1; create_info.ppEnabledLayerNames = layers; - // need additional storage for char pointer to debug report extension - const char** extensions = (const char**)malloc(sizeof(const char*) * (extensions_count + 1)); - for (size_t i = 0; i < extensions_count; i++) - extensions[i] = extensions[i]; - extensions[extensions_count] = "VK_EXT_debug_report"; + // Enable debug report extension (we need additional storage, so we duplicate the user array to add our new extension to it) + const char** extensions_ext = (const char**)malloc(sizeof(const char*) * (extensions_count + 1)); + memcpy(extensions_ext, extensions, extensions_count * sizeof(const char*)); + extensions_ext[extensions_count] = "VK_EXT_debug_report"; create_info.enabledExtensionCount = extensions_count + 1; - create_info.ppEnabledExtensionNames = extensions; + create_info.ppEnabledExtensionNames = extensions_ext; #endif // IMGUI_VULKAN_DEBUG_REPORT err = vkCreateInstance(&create_info, g_Allocator, &g_Instance); check_vk_result(err); #ifdef IMGUI_VULKAN_DEBUG_REPORT - free(extensions); + free(extensions_ext); // create the debug report callback VkDebugReportCallbackCreateInfoEXT debug_report_ci = {}; From 57e9f618529a7378284512e2ac01e617b76299df Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 1 Mar 2018 18:59:07 +0100 Subject: [PATCH 041/828] Examples: Vulkan: Debug report tweak + always enable in Debug build. --- examples/sdl_vulkan_example/main.cpp | 25 ++++++++++++------------- examples/vulkan_example/main.cpp | 25 ++++++++++++------------- 2 files changed, 24 insertions(+), 26 deletions(-) diff --git a/examples/sdl_vulkan_example/main.cpp b/examples/sdl_vulkan_example/main.cpp index 9e08a9656f27..973fd5d90c75 100644 --- a/examples/sdl_vulkan_example/main.cpp +++ b/examples/sdl_vulkan_example/main.cpp @@ -11,9 +11,9 @@ #define IMGUI_MAX_POSSIBLE_BACK_BUFFERS 16 #define IMGUI_UNLIMITED_FRAME_RATE -//#ifdef _DEBUG -//#define IMGUI_VULKAN_DEBUG_REPORT -//#endif +#ifdef _DEBUG +#define IMGUI_VULKAN_DEBUG_REPORT +#endif static VkAllocationCallbacks* g_Allocator = NULL; static VkInstance g_Instance = VK_NULL_HANDLE; @@ -24,7 +24,7 @@ static VkSwapchainKHR g_Swapchain = VK_NULL_HANDLE; static VkRenderPass g_RenderPass = VK_NULL_HANDLE; static uint32_t g_QueueFamily = 0; static VkQueue g_Queue = VK_NULL_HANDLE; -static VkDebugReportCallbackEXT g_Debug_Report = VK_NULL_HANDLE; +static VkDebugReportCallbackEXT g_DebugReport = VK_NULL_HANDLE; static VkSurfaceFormatKHR g_SurfaceFormat; static VkImageSubresourceRange g_ImageRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; @@ -228,18 +228,17 @@ static void setup_vulkan(SDL_Window* window, const char** extensions, uint32_t e #ifdef IMGUI_VULKAN_DEBUG_REPORT free(extensions_ext); - // create the debug report callback + // Get the function pointer (required for any extensions) + auto vkCreateDebugReportCallbackEXT = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(g_Instance, "vkCreateDebugReportCallbackEXT"); + IM_ASSERT(vkCreateDebugReportCallbackEXT != NULL); + + // Setup the debug report callback VkDebugReportCallbackCreateInfoEXT debug_report_ci = {}; debug_report_ci.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT; debug_report_ci.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT; debug_report_ci.pfnCallback = debug_report; debug_report_ci.pUserData = NULL; - - // get the proc address of the function pointer, required for used extensions - PFN_vkCreateDebugReportCallbackEXT vkCreateDebugReportCallbackEXT = - (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(g_Instance, "vkCreateDebugReportCallbackEXT"); - - err = vkCreateDebugReportCallbackEXT(g_Instance, &debug_report_ci, g_Allocator, &g_Debug_Report); + err = vkCreateDebugReportCallbackEXT(g_Instance, &debug_report_ci, g_Allocator, &g_DebugReport); check_vk_result(err); #endif // IMGUI_VULKAN_DEBUG_REPORT } @@ -492,9 +491,9 @@ static void cleanup_vulkan() vkDestroySurfaceKHR(g_Instance, g_Surface, g_Allocator); #ifdef IMGUI_VULKAN_DEBUG_REPORT - // get the proc address of the function pointer, required for used extensions + // Remove the debug report callback auto vkDestroyDebugReportCallbackEXT = (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr(g_Instance, "vkDestroyDebugReportCallbackEXT"); - vkDestroyDebugReportCallbackEXT(g_Instance, g_Debug_Report, g_Allocator); + vkDestroyDebugReportCallbackEXT(g_Instance, g_DebugReport, g_Allocator); #endif // IMGUI_VULKAN_DEBUG_REPORT vkDestroyDevice(g_Device, g_Allocator); diff --git a/examples/vulkan_example/main.cpp b/examples/vulkan_example/main.cpp index ca0cdcca072c..1c5100e16f59 100644 --- a/examples/vulkan_example/main.cpp +++ b/examples/vulkan_example/main.cpp @@ -14,9 +14,9 @@ #define IMGUI_MAX_POSSIBLE_BACK_BUFFERS 16 #define IMGUI_UNLIMITED_FRAME_RATE -//#ifdef _DEBUG -//#define IMGUI_VULKAN_DEBUG_REPORT -//#endif +#ifdef _DEBUG +#define IMGUI_VULKAN_DEBUG_REPORT +#endif static VkAllocationCallbacks* g_Allocator = NULL; static VkInstance g_Instance = VK_NULL_HANDLE; @@ -27,7 +27,7 @@ static VkSwapchainKHR g_Swapchain = VK_NULL_HANDLE; static VkRenderPass g_RenderPass = VK_NULL_HANDLE; static uint32_t g_QueueFamily = 0; static VkQueue g_Queue = VK_NULL_HANDLE; -static VkDebugReportCallbackEXT g_Debug_Report = VK_NULL_HANDLE; +static VkDebugReportCallbackEXT g_DebugReport = VK_NULL_HANDLE; static VkSurfaceFormatKHR g_SurfaceFormat; static VkImageSubresourceRange g_ImageRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; @@ -231,18 +231,17 @@ static void setup_vulkan(GLFWwindow* window, const char** extensions, uint32_t e #ifdef IMGUI_VULKAN_DEBUG_REPORT free(extensions_ext); - // create the debug report callback + // Get the function pointer (required for any extensions) + auto vkCreateDebugReportCallbackEXT = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(g_Instance, "vkCreateDebugReportCallbackEXT"); + IM_ASSERT(vkCreateDebugReportCallbackEXT != NULL); + + // Setup the debug report callback VkDebugReportCallbackCreateInfoEXT debug_report_ci = {}; debug_report_ci.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT; debug_report_ci.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT; debug_report_ci.pfnCallback = debug_report; debug_report_ci.pUserData = NULL; - - // get the proc address of the function pointer, required for used extensions - PFN_vkCreateDebugReportCallbackEXT vkCreateDebugReportCallbackEXT = - (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(g_Instance, "vkCreateDebugReportCallbackEXT"); - - err = vkCreateDebugReportCallbackEXT(g_Instance, &debug_report_ci, g_Allocator, &g_Debug_Report); + err = vkCreateDebugReportCallbackEXT(g_Instance, &debug_report_ci, g_Allocator, &g_DebugReport); check_vk_result(err); #endif // IMGUI_VULKAN_DEBUG_REPORT } @@ -492,9 +491,9 @@ static void cleanup_vulkan() vkDestroySurfaceKHR(g_Instance, g_Surface, g_Allocator); #ifdef IMGUI_VULKAN_DEBUG_REPORT - // get the proc address of the function pointer, required for used extensions + // Remove the debug report callback auto vkDestroyDebugReportCallbackEXT = (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr(g_Instance, "vkDestroyDebugReportCallbackEXT"); - vkDestroyDebugReportCallbackEXT(g_Instance, g_Debug_Report, g_Allocator); + vkDestroyDebugReportCallbackEXT(g_Instance, g_DebugReport, g_Allocator); #endif // IMGUI_VULKAN_DEBUG_REPORT vkDestroyDevice(g_Device, g_Allocator); From 00b9e70ba2733ac126147f3585ae5488481b70e4 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 1 Mar 2018 20:14:55 +0100 Subject: [PATCH 042/828] Viewport: Always be lenient with invalid mouse position during dragging, since they are provided by back-end and technically acceptable inputs + comments. (#1542) --- imgui.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index cee66bb3d396..e9f644b26139 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5891,7 +5891,7 @@ static void ImGui::UpdateWindowViewport(ImGuiWindow* window, bool window_pos_set } const bool window_is_mouse_tooltip = (flags & ImGuiWindowFlags_Tooltip) && g.NavDisableHighlight && !g.NavDisableMouseHover; - const bool window_follow_mouse_viewport = window_is_mouse_tooltip || (g.MovingWindow && g.MovingWindow->RootWindow == window); + const bool window_follow_mouse_viewport = (window_is_mouse_tooltip || (g.MovingWindow && g.MovingWindow->RootWindow == window)); bool created_viewport = false; @@ -5899,17 +5899,15 @@ static void ImGui::UpdateWindowViewport(ImGuiWindow* window, bool window_pos_set if (g.NextWindowData.ViewportCond) { window->Viewport = prev_viewport = FindViewportByID(g.NextWindowData.ViewportId); - window->ViewportId = g.NextWindowData.ViewportId; + window->ViewportId = g.NextWindowData.ViewportId; // Store ID even if Viewport isn't resolved. } else if (flags & ImGuiWindowFlags_ChildWindow)// || (flags & ImGuiWindowFlags_Popup)) { IM_ASSERT(window->ParentWindow); window->Viewport = prev_viewport = window->ParentWindow->Viewport; } - else if (window_follow_mouse_viewport) + else if (window_follow_mouse_viewport && IsMousePosValid()) { - IM_ASSERT(IsMousePosValid()); - // Calculate mouse position in OS/platform coordinates if (!window_is_mouse_tooltip && !GetViewportRect(window).Contains(window->Rect())) { From 3171d61dfc19606e29db0ec9d641cd57d459043b Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 1 Mar 2018 21:11:22 +0100 Subject: [PATCH 043/828] Examples: Vulkan: Various tweaks to name variable more consistently like Vulkan + a few comments + a few imgui style code compaction. --- examples/imgui_impl_vulkan.cpp | 35 +++---- examples/imgui_impl_vulkan.h | 18 ++-- examples/sdl_vulkan_example/main.cpp | 133 ++++++++++++--------------- examples/vulkan_example/main.cpp | 133 ++++++++++++--------------- 4 files changed, 147 insertions(+), 172 deletions(-) diff --git a/examples/imgui_impl_vulkan.cpp b/examples/imgui_impl_vulkan.cpp index 312bb486f732..70adbf5cabf5 100644 --- a/examples/imgui_impl_vulkan.cpp +++ b/examples/imgui_impl_vulkan.cpp @@ -11,7 +11,8 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2018-XX-XX: Vulkan: Offset projection matrix and clipping rectangle by io.DisplayPos (which will be non-zero for multi-viewport applications). +// 2018-03-01: Vulkan: Renamed ImGui_ImplVulkan_Init_Info to ImGui_ImplVulkan_InitInfo and fields to match more closely Vulkan terminology. +// 2018-02-18: Vulkan: Offset projection matrix and clipping rectangle by io.DisplayPos (which will be non-zero for multi-viewport applications). // 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback, ImGui_ImplVulkan_Render() calls ImGui_ImplVulkan_RenderDrawData() itself. // 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. // 2017-05-15: Vulkan: Fix scissor offset being negative. Fix new Vulkan validation warnings. Set required depth member for buffer image copy. @@ -23,13 +24,13 @@ #include "imgui_impl_vulkan.h" // Vulkan data -static VkAllocationCallbacks* g_Allocator = NULL; -static VkPhysicalDevice g_Gpu = VK_NULL_HANDLE; -static VkDevice g_Device = VK_NULL_HANDLE; -static VkRenderPass g_RenderPass = VK_NULL_HANDLE; -static VkPipelineCache g_PipelineCache = VK_NULL_HANDLE; -static VkDescriptorPool g_DescriptorPool = VK_NULL_HANDLE; -static void (*g_CheckVkResult)(VkResult err) = NULL; +static const VkAllocationCallbacks* g_Allocator = NULL; +static VkPhysicalDevice g_PhysicalDevice = VK_NULL_HANDLE; +static VkDevice g_Device = VK_NULL_HANDLE; +static VkRenderPass g_RenderPass = VK_NULL_HANDLE; +static VkPipelineCache g_PipelineCache = VK_NULL_HANDLE; +static VkDescriptorPool g_DescriptorPool = VK_NULL_HANDLE; +static void (*g_CheckVkResult)(VkResult err) = NULL; static VkCommandBuffer g_CommandBuffer = VK_NULL_HANDLE; static VkDeviceSize g_BufferMemoryAlignment = 256; @@ -133,7 +134,7 @@ static uint32_t __glsl_shader_frag_spv[] = static uint32_t ImGui_ImplVulkan_MemoryType(VkMemoryPropertyFlags properties, uint32_t type_bits) { VkPhysicalDeviceMemoryProperties prop; - vkGetPhysicalDeviceMemoryProperties(g_Gpu, &prop); + vkGetPhysicalDeviceMemoryProperties(g_PhysicalDevice, &prop); for (uint32_t i = 0; i < prop.memoryTypeCount; i++) if ((prop.memoryTypes[i].propertyFlags & properties) == properties && type_bits & (1<allocator; - g_Gpu = init_data->gpu; - g_Device = init_data->device; - g_RenderPass = init_data->render_pass; - g_PipelineCache = init_data->pipeline_cache; - g_DescriptorPool = init_data->descriptor_pool; - g_CheckVkResult = init_data->check_vk_result; + g_Allocator = init_data->Allocator; + g_PhysicalDevice = init_data->PhysicalDevice; + g_Device = init_data->Device; + g_RenderPass = init_data->RenderPass; + g_PipelineCache = init_data->PipelineCache; + g_DescriptorPool = init_data->DescriptorPool; + g_CheckVkResult = init_data->CheckVkResultFn; ImGui_ImplVulkan_CreateDeviceObjects(); return true; diff --git a/examples/imgui_impl_vulkan.h b/examples/imgui_impl_vulkan.h index c27c11eebb74..04ea473e9f8d 100644 --- a/examples/imgui_impl_vulkan.h +++ b/examples/imgui_impl_vulkan.h @@ -13,18 +13,18 @@ #define IMGUI_VK_QUEUED_FRAMES 2 -struct ImGui_ImplVulkan_InitData +struct ImGui_ImplVulkan_InitInfo { - VkAllocationCallbacks* allocator; - VkPhysicalDevice gpu; - VkDevice device; - VkRenderPass render_pass; - VkPipelineCache pipeline_cache; - VkDescriptorPool descriptor_pool; - void (*check_vk_result)(VkResult err); + const VkAllocationCallbacks* Allocator; + VkPhysicalDevice PhysicalDevice; + VkDevice Device; + VkRenderPass RenderPass; + VkPipelineCache PipelineCache; + VkDescriptorPool DescriptorPool; + void (*CheckVkResultFn)(VkResult err); }; -IMGUI_API bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitData *init_data); +IMGUI_API bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo *init_data); IMGUI_API void ImGui_ImplVulkan_Shutdown(); IMGUI_API void ImGui_ImplVulkan_NewFrame(); IMGUI_API void ImGui_ImplVulkan_Render(VkCommandBuffer command_buffer); diff --git a/examples/sdl_vulkan_example/main.cpp b/examples/sdl_vulkan_example/main.cpp index 973fd5d90c75..24b0221603ed 100644 --- a/examples/sdl_vulkan_example/main.cpp +++ b/examples/sdl_vulkan_example/main.cpp @@ -9,6 +9,7 @@ #include #include +// FIXME-VULKAN: Resizing with IMGUI_UNLIMITED_FRAME_RATE triggers errors from the validation layer. #define IMGUI_MAX_POSSIBLE_BACK_BUFFERS 16 #define IMGUI_UNLIMITED_FRAME_RATE #ifdef _DEBUG @@ -18,16 +19,15 @@ static VkAllocationCallbacks* g_Allocator = NULL; static VkInstance g_Instance = VK_NULL_HANDLE; static VkSurfaceKHR g_Surface = VK_NULL_HANDLE; -static VkPhysicalDevice g_Gpu = VK_NULL_HANDLE; +static VkPhysicalDevice g_PhysicalDevice = VK_NULL_HANDLE; static VkDevice g_Device = VK_NULL_HANDLE; static VkSwapchainKHR g_Swapchain = VK_NULL_HANDLE; static VkRenderPass g_RenderPass = VK_NULL_HANDLE; -static uint32_t g_QueueFamily = 0; +static uint32_t g_QueueFamily = (uint32_t)-1; static VkQueue g_Queue = VK_NULL_HANDLE; static VkDebugReportCallbackEXT g_DebugReport = VK_NULL_HANDLE; static VkSurfaceFormatKHR g_SurfaceFormat; -static VkImageSubresourceRange g_ImageRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; static VkPresentModeKHR g_PresentMode; static VkPipelineCache g_PipelineCache = VK_NULL_HANDLE; @@ -64,17 +64,18 @@ static void resize_vulkan(SDL_Window*, int w, int h) err = vkDeviceWaitIdle(g_Device); check_vk_result(err); - // Destroy old Framebuffer: + // Destroy old Framebuffer for (uint32_t i = 0; i < g_BackBufferCount; i++) + { if (g_BackBufferView[i]) vkDestroyImageView(g_Device, g_BackBufferView[i], g_Allocator); - for (uint32_t i = 0; i < g_BackBufferCount; i++) if (g_Framebuffer[i]) vkDestroyFramebuffer(g_Device, g_Framebuffer[i], g_Allocator); + } if (g_RenderPass) vkDestroyRenderPass(g_Device, g_RenderPass, g_Allocator); - // Create Swapchain: + // Create Swapchain { VkSwapchainCreateInfoKHR info = {}; info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; @@ -82,15 +83,15 @@ static void resize_vulkan(SDL_Window*, int w, int h) info.imageFormat = g_SurfaceFormat.format; info.imageColorSpace = g_SurfaceFormat.colorSpace; info.imageArrayLayers = 1; - info.imageUsage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; - info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; // Assume that graphics family == present family info.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; info.presentMode = g_PresentMode; info.clipped = VK_TRUE; info.oldSwapchain = old_swapchain; VkSurfaceCapabilitiesKHR cap; - err = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(g_Gpu, g_Surface, &cap); + err = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(g_PhysicalDevice, g_Surface, &cap); check_vk_result(err); if (cap.maxImageCount > 0) info.minImageCount = (cap.minImageCount + 2 < cap.maxImageCount) ? (cap.minImageCount + 2) : cap.maxImageCount; @@ -99,17 +100,13 @@ static void resize_vulkan(SDL_Window*, int w, int h) if (cap.currentExtent.width == 0xffffffff) { - fb_width = w; - fb_height = h; - info.imageExtent.width = fb_width; - info.imageExtent.height = fb_height; + info.imageExtent.width = fb_width = w; + info.imageExtent.height = fb_height = h; } else { - fb_width = cap.currentExtent.width; - fb_height = cap.currentExtent.height; - info.imageExtent.width = fb_width; - info.imageExtent.height = fb_height; + info.imageExtent.width = fb_width = cap.currentExtent.width; + info.imageExtent.height = fb_height = cap.currentExtent.height; } err = vkCreateSwapchainKHR(g_Device, &info, g_Allocator, &g_Swapchain); check_vk_result(err); @@ -121,7 +118,7 @@ static void resize_vulkan(SDL_Window*, int w, int h) if (old_swapchain) vkDestroySwapchainKHR(g_Device, old_swapchain, g_Allocator); - // Create the Render Pass: + // Create the Render Pass { VkAttachmentDescription attachment = {}; attachment.format = g_SurfaceFormat.format; @@ -159,7 +156,8 @@ static void resize_vulkan(SDL_Window*, int w, int h) info.components.g = VK_COMPONENT_SWIZZLE_G; info.components.b = VK_COMPONENT_SWIZZLE_B; info.components.a = VK_COMPONENT_SWIZZLE_A; - info.subresourceRange = g_ImageRange; + VkImageSubresourceRange image_range = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; + info.subresourceRange = image_range; for (uint32_t i = 0; i < g_BackBufferCount; i++) { info.image = g_BackBuffer[i]; @@ -168,7 +166,7 @@ static void resize_vulkan(SDL_Window*, int w, int h) } } - // Create Framebuffer: + // Create Framebuffer { VkImageView attachment[1]; VkFramebufferCreateInfo info = {}; @@ -253,7 +251,7 @@ static void setup_vulkan(SDL_Window* window, const char** extensions, uint32_t e } } - // Get GPU + // Select GPU { uint32_t gpu_count; err = vkEnumeratePhysicalDevices(g_Instance, &gpu_count, NULL); @@ -266,31 +264,30 @@ static void setup_vulkan(SDL_Window* window, const char** extensions, uint32_t e // If a number >1 of GPUs got reported, you should find the best fit GPU for your purpose // e.g. VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU if available, or with the greatest memory available, etc. // for sake of simplicity we'll just take the first one, assuming it has a graphics queue family. - g_Gpu = gpus[0]; + g_PhysicalDevice = gpus[0]; free(gpus); } - // Get queue + // Select graphics queue family { uint32_t count; - vkGetPhysicalDeviceQueueFamilyProperties(g_Gpu, &count, NULL); + vkGetPhysicalDeviceQueueFamilyProperties(g_PhysicalDevice, &count, NULL); VkQueueFamilyProperties* queues = (VkQueueFamilyProperties*)malloc(sizeof(VkQueueFamilyProperties) * count); - vkGetPhysicalDeviceQueueFamilyProperties(g_Gpu, &count, queues); + vkGetPhysicalDeviceQueueFamilyProperties(g_PhysicalDevice, &count, queues); for (uint32_t i = 0; i < count; i++) - { if (queues[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) { g_QueueFamily = i; break; } - } free(queues); + IM_ASSERT(g_QueueFamily != -1); } // Check for WSI support { VkBool32 res; - vkGetPhysicalDeviceSurfaceSupportKHR(g_Gpu, g_QueueFamily, g_Surface, &res); + vkGetPhysicalDeviceSurfaceSupportKHR(g_PhysicalDevice, g_QueueFamily, g_Surface, &res); if (res != VK_TRUE) { fprintf(stderr, "Error no WSI support on physical device 0\n"); @@ -301,15 +298,15 @@ static void setup_vulkan(SDL_Window* window, const char** extensions, uint32_t e // Get Surface Format { // Per Spec Format and View Format are expected to be the same unless VK_IMAGE_CREATE_MUTABLE_BIT was set at image creation - // Assuming that the default behavior is without setting this bit, there is no need for separate Spawchain image and image view format - // additionally several new color spaces were introduced with Vulkan Spec v1.0.40 - // hence we must make sure that a format with the mostly available color space, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, is found and used + // Assuming that the default behavior is without setting this bit, there is no need for separate Swapchain image and image view format + // Additionally several new color spaces were introduced with Vulkan Spec v1.0.40, + // hence we must make sure that a format with the mostly available color space, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, is found and used. uint32_t count; - vkGetPhysicalDeviceSurfaceFormatsKHR(g_Gpu, g_Surface, &count, NULL); - VkSurfaceFormatKHR *formats = (VkSurfaceFormatKHR*)malloc(sizeof(VkSurfaceFormatKHR) * count); - vkGetPhysicalDeviceSurfaceFormatsKHR(g_Gpu, g_Surface, &count, formats); + vkGetPhysicalDeviceSurfaceFormatsKHR(g_PhysicalDevice, g_Surface, &count, NULL); + VkSurfaceFormatKHR* formats = (VkSurfaceFormatKHR*)malloc(sizeof(VkSurfaceFormatKHR) * count); + vkGetPhysicalDeviceSurfaceFormatsKHR(g_PhysicalDevice, g_Surface, &count, formats); - // first check if only one format, VK_FORMAT_UNDEFINED, is available, which would imply that any format is available + // First check if only one format, VK_FORMAT_UNDEFINED, is available, which would imply that any format is available if (count == 1) { if (formats[0].format == VK_FORMAT_UNDEFINED) @@ -318,32 +315,27 @@ static void setup_vulkan(SDL_Window* window, const char** extensions, uint32_t e g_SurfaceFormat.colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; } else - { // no point in searching another format + { + // No point in searching another format g_SurfaceFormat = formats[0]; } } else { - // request several formats, the first found will be used + // Request several formats, the first found will be used VkFormat requestSurfaceImageFormat[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM }; VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; - bool requestedFound = false; - for (size_t i = 0; i < sizeof(requestSurfaceImageFormat) / sizeof(requestSurfaceImageFormat[0]); i++) - { - if (requestedFound) - break; + bool found = false; + for (size_t i = 0; found == false && i < sizeof(requestSurfaceImageFormat) / sizeof(requestSurfaceImageFormat[0]); i++) for (uint32_t j = 0; j < count; j++) - { if (formats[j].format == requestSurfaceImageFormat[i] && formats[j].colorSpace == requestSurfaceColorSpace) { g_SurfaceFormat = formats[j]; - requestedFound = true; + found = true; } - } - } - // if none of the requested image formats could be found, use the first available - if (!requestedFound) + // If none of the requested image formats could be found, use the first available + if (!found) g_SurfaceFormat = formats[0]; } free(formats); @@ -352,41 +344,34 @@ static void setup_vulkan(SDL_Window* window, const char** extensions, uint32_t e // Get Present Mode { - // Requst a certain mode and confirm that it is available. If not use VK_PRESENT_MODE_FIFO_KHR which is mandatory + // Request a certain mode and confirm that it is available. If not use VK_PRESENT_MODE_FIFO_KHR which is mandatory #ifdef IMGUI_UNLIMITED_FRAME_RATE g_PresentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; #else g_PresentMode = VK_PRESENT_MODE_FIFO_KHR; #endif uint32_t count = 0; - vkGetPhysicalDeviceSurfacePresentModesKHR(g_Gpu, g_Surface, &count, nullptr); + vkGetPhysicalDeviceSurfacePresentModesKHR(g_PhysicalDevice, g_Surface, &count, nullptr); VkPresentModeKHR* presentModes = (VkPresentModeKHR*)malloc(sizeof(VkQueueFamilyProperties) * count); - vkGetPhysicalDeviceSurfacePresentModesKHR(g_Gpu, g_Surface, &count, presentModes); + vkGetPhysicalDeviceSurfacePresentModesKHR(g_PhysicalDevice, g_Surface, &count, presentModes); bool presentModeAvailable = false; - for (size_t i = 0; i < count; i++) - { + for (size_t i = 0; i < count && !presentModeAvailable; i++) if (presentModes[i] == g_PresentMode) - { presentModeAvailable = true; - break; - } - } if (!presentModeAvailable) - g_PresentMode = VK_PRESENT_MODE_FIFO_KHR; // always available + g_PresentMode = VK_PRESENT_MODE_FIFO_KHR; // Always available } - // Create Logical Device + // Create Logical Device (with 1 queue) { int device_extension_count = 1; const char* device_extensions[] = { "VK_KHR_swapchain" }; - const uint32_t queue_index = 0; - const uint32_t queue_count = 1; const float queue_priority[] = { 1.0f }; VkDeviceQueueCreateInfo queue_info[1] = {}; queue_info[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; queue_info[0].queueFamilyIndex = g_QueueFamily; - queue_info[0].queueCount = queue_count; + queue_info[0].queueCount = 1; queue_info[0].pQueuePriorities = queue_priority; VkDeviceCreateInfo create_info = {}; create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; @@ -394,11 +379,12 @@ static void setup_vulkan(SDL_Window* window, const char** extensions, uint32_t e create_info.pQueueCreateInfos = queue_info; create_info.enabledExtensionCount = device_extension_count; create_info.ppEnabledExtensionNames = device_extensions; - err = vkCreateDevice(g_Gpu, &create_info, g_Allocator, &g_Device); + err = vkCreateDevice(g_PhysicalDevice, &create_info, g_Allocator, &g_Device); check_vk_result(err); - vkGetDeviceQueue(g_Device, g_QueueFamily, queue_index, &g_Queue); + vkGetDeviceQueue(g_Device, g_QueueFamily, 0, &g_Queue); } + // Create Framebuffers { int w, h; @@ -406,6 +392,7 @@ static void setup_vulkan(SDL_Window* window, const char** extensions, uint32_t e resize_vulkan(window, w, h); } + // Create Command Buffers for (int i = 0; i < IMGUI_VK_QUEUED_FRAMES; i++) { @@ -611,18 +598,18 @@ int main(int, char**) // Setup ImGui binding ImGui::CreateContext(); - ImGui_ImplVulkan_InitData init_data = {}; - init_data.allocator = g_Allocator; - init_data.gpu = g_Gpu; - init_data.device = g_Device; - init_data.render_pass = g_RenderPass; - init_data.pipeline_cache = g_PipelineCache; - init_data.descriptor_pool = g_DescriptorPool; - init_data.check_vk_result = check_vk_result; + ImGui_ImplVulkan_InitInfo init_info = {}; + init_info.Allocator = g_Allocator; + init_info.PhysicalDevice = g_PhysicalDevice; + init_info.Device = g_Device; + init_info.RenderPass = g_RenderPass; + init_info.PipelineCache = g_PipelineCache; + init_info.DescriptorPool = g_DescriptorPool; + init_info.CheckVkResultFn = check_vk_result; ImGuiIO& io = ImGui::GetIO(); (void)io; //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls - ImGui_ImplVulkan_Init(&init_data); + ImGui_ImplVulkan_Init(&init_info); ImGui_ImplSDL2_Init(window); // Setup style diff --git a/examples/vulkan_example/main.cpp b/examples/vulkan_example/main.cpp index 1c5100e16f59..e0c0fab35616 100644 --- a/examples/vulkan_example/main.cpp +++ b/examples/vulkan_example/main.cpp @@ -12,6 +12,7 @@ #include #include +// FIXME-VULKAN: Resizing with IMGUI_UNLIMITED_FRAME_RATE triggers errors from the validation layer. #define IMGUI_MAX_POSSIBLE_BACK_BUFFERS 16 #define IMGUI_UNLIMITED_FRAME_RATE #ifdef _DEBUG @@ -21,16 +22,15 @@ static VkAllocationCallbacks* g_Allocator = NULL; static VkInstance g_Instance = VK_NULL_HANDLE; static VkSurfaceKHR g_Surface = VK_NULL_HANDLE; -static VkPhysicalDevice g_Gpu = VK_NULL_HANDLE; +static VkPhysicalDevice g_PhysicalDevice = VK_NULL_HANDLE; static VkDevice g_Device = VK_NULL_HANDLE; static VkSwapchainKHR g_Swapchain = VK_NULL_HANDLE; static VkRenderPass g_RenderPass = VK_NULL_HANDLE; -static uint32_t g_QueueFamily = 0; +static uint32_t g_QueueFamily = (uint32_t)-1; static VkQueue g_Queue = VK_NULL_HANDLE; static VkDebugReportCallbackEXT g_DebugReport = VK_NULL_HANDLE; static VkSurfaceFormatKHR g_SurfaceFormat; -static VkImageSubresourceRange g_ImageRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; static VkPresentModeKHR g_PresentMode; static VkPipelineCache g_PipelineCache = VK_NULL_HANDLE; @@ -67,17 +67,18 @@ static void resize_vulkan(GLFWwindow*, int w, int h) err = vkDeviceWaitIdle(g_Device); check_vk_result(err); - // Destroy old Framebuffer: + // Destroy old Framebuffer for (uint32_t i = 0; i < g_BackBufferCount; i++) + { if (g_BackBufferView[i]) vkDestroyImageView(g_Device, g_BackBufferView[i], g_Allocator); - for (uint32_t i = 0; i < g_BackBufferCount; i++) if (g_Framebuffer[i]) vkDestroyFramebuffer(g_Device, g_Framebuffer[i], g_Allocator); + } if (g_RenderPass) vkDestroyRenderPass(g_Device, g_RenderPass, g_Allocator); - // Create Swapchain: + // Create Swapchain { VkSwapchainCreateInfoKHR info = {}; info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; @@ -85,15 +86,15 @@ static void resize_vulkan(GLFWwindow*, int w, int h) info.imageFormat = g_SurfaceFormat.format; info.imageColorSpace = g_SurfaceFormat.colorSpace; info.imageArrayLayers = 1; - info.imageUsage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; - info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; // Assume that graphics family == present family info.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; info.presentMode = g_PresentMode; info.clipped = VK_TRUE; info.oldSwapchain = old_swapchain; VkSurfaceCapabilitiesKHR cap; - err = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(g_Gpu, g_Surface, &cap); + err = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(g_PhysicalDevice, g_Surface, &cap); check_vk_result(err); if (cap.maxImageCount > 0) info.minImageCount = (cap.minImageCount + 2 < cap.maxImageCount) ? (cap.minImageCount + 2) : cap.maxImageCount; @@ -102,17 +103,13 @@ static void resize_vulkan(GLFWwindow*, int w, int h) if (cap.currentExtent.width == 0xffffffff) { - fb_width = w; - fb_height = h; - info.imageExtent.width = fb_width; - info.imageExtent.height = fb_height; + info.imageExtent.width = fb_width = w; + info.imageExtent.height = fb_height = h; } else { - fb_width = cap.currentExtent.width; - fb_height = cap.currentExtent.height; - info.imageExtent.width = fb_width; - info.imageExtent.height = fb_height; + info.imageExtent.width = fb_width = cap.currentExtent.width; + info.imageExtent.height = fb_height = cap.currentExtent.height; } err = vkCreateSwapchainKHR(g_Device, &info, g_Allocator, &g_Swapchain); check_vk_result(err); @@ -124,7 +121,7 @@ static void resize_vulkan(GLFWwindow*, int w, int h) if (old_swapchain) vkDestroySwapchainKHR(g_Device, old_swapchain, g_Allocator); - // Create the Render Pass: + // Create the Render Pass { VkAttachmentDescription attachment = {}; attachment.format = g_SurfaceFormat.format; @@ -162,7 +159,8 @@ static void resize_vulkan(GLFWwindow*, int w, int h) info.components.g = VK_COMPONENT_SWIZZLE_G; info.components.b = VK_COMPONENT_SWIZZLE_B; info.components.a = VK_COMPONENT_SWIZZLE_A; - info.subresourceRange = g_ImageRange; + VkImageSubresourceRange image_range = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; + info.subresourceRange = image_range; for (uint32_t i = 0; i < g_BackBufferCount; i++) { info.image = g_BackBuffer[i]; @@ -171,7 +169,7 @@ static void resize_vulkan(GLFWwindow*, int w, int h) } } - // Create Framebuffer: + // Create Framebuffer { VkImageView attachment[1]; VkFramebufferCreateInfo info = {}; @@ -252,7 +250,7 @@ static void setup_vulkan(GLFWwindow* window, const char** extensions, uint32_t e check_vk_result(err); } - // Get GPU + // Select GPU { uint32_t gpu_count; err = vkEnumeratePhysicalDevices(g_Instance, &gpu_count, NULL); @@ -265,31 +263,30 @@ static void setup_vulkan(GLFWwindow* window, const char** extensions, uint32_t e // If a number >1 of GPUs got reported, you should find the best fit GPU for your purpose // e.g. VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU if available, or with the greatest memory available, etc. // for sake of simplicity we'll just take the first one, assuming it has a graphics queue family. - g_Gpu = gpus[0]; + g_PhysicalDevice = gpus[0]; free(gpus); } - // Get queue + // Select graphics queue family { uint32_t count; - vkGetPhysicalDeviceQueueFamilyProperties(g_Gpu, &count, NULL); + vkGetPhysicalDeviceQueueFamilyProperties(g_PhysicalDevice, &count, NULL); VkQueueFamilyProperties* queues = (VkQueueFamilyProperties*)malloc(sizeof(VkQueueFamilyProperties) * count); - vkGetPhysicalDeviceQueueFamilyProperties(g_Gpu, &count, queues); + vkGetPhysicalDeviceQueueFamilyProperties(g_PhysicalDevice, &count, queues); for (uint32_t i = 0; i < count; i++) - { if (queues[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) { g_QueueFamily = i; break; } - } free(queues); + IM_ASSERT(g_QueueFamily != -1); } // Check for WSI support { VkBool32 res; - vkGetPhysicalDeviceSurfaceSupportKHR(g_Gpu, g_QueueFamily, g_Surface, &res); + vkGetPhysicalDeviceSurfaceSupportKHR(g_PhysicalDevice, g_QueueFamily, g_Surface, &res); if (res != VK_TRUE) { fprintf(stderr, "Error no WSI support on physical device 0\n"); @@ -300,15 +297,15 @@ static void setup_vulkan(GLFWwindow* window, const char** extensions, uint32_t e // Get Surface Format { // Per Spec Format and View Format are expected to be the same unless VK_IMAGE_CREATE_MUTABLE_BIT was set at image creation - // Assuming that the default behavior is without setting this bit, there is no need for separate Spawchain image and image view format - // additionally several new color spaces were introduced with Vulkan Spec v1.0.40 - // hence we must make sure that a format with the mostly available color space, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, is found and used + // Assuming that the default behavior is without setting this bit, there is no need for separate Swapchain image and image view format + // Additionally several new color spaces were introduced with Vulkan Spec v1.0.40, + // hence we must make sure that a format with the mostly available color space, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, is found and used. uint32_t count; - vkGetPhysicalDeviceSurfaceFormatsKHR(g_Gpu, g_Surface, &count, NULL); - VkSurfaceFormatKHR *formats = (VkSurfaceFormatKHR*)malloc(sizeof(VkSurfaceFormatKHR) * count); - vkGetPhysicalDeviceSurfaceFormatsKHR(g_Gpu, g_Surface, &count, formats); + vkGetPhysicalDeviceSurfaceFormatsKHR(g_PhysicalDevice, g_Surface, &count, NULL); + VkSurfaceFormatKHR* formats = (VkSurfaceFormatKHR*)malloc(sizeof(VkSurfaceFormatKHR) * count); + vkGetPhysicalDeviceSurfaceFormatsKHR(g_PhysicalDevice, g_Surface, &count, formats); - // first check if only one format, VK_FORMAT_UNDEFINED, is available, which would imply that any format is available + // First check if only one format, VK_FORMAT_UNDEFINED, is available, which would imply that any format is available if (count == 1) { if (formats[0].format == VK_FORMAT_UNDEFINED) @@ -317,32 +314,27 @@ static void setup_vulkan(GLFWwindow* window, const char** extensions, uint32_t e g_SurfaceFormat.colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; } else - { // no point in searching another format + { + // No point in searching another format g_SurfaceFormat = formats[0]; } } else { - // request several formats, the first found will be used + // Request several formats, the first found will be used VkFormat requestSurfaceImageFormat[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM }; VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; - bool requestedFound = false; - for (size_t i = 0; i < sizeof(requestSurfaceImageFormat) / sizeof(requestSurfaceImageFormat[0]); i++) - { - if (requestedFound) - break; + bool found = false; + for (size_t i = 0; found == false && i < sizeof(requestSurfaceImageFormat) / sizeof(requestSurfaceImageFormat[0]); i++) for (uint32_t j = 0; j < count; j++) - { if (formats[j].format == requestSurfaceImageFormat[i] && formats[j].colorSpace == requestSurfaceColorSpace) { g_SurfaceFormat = formats[j]; - requestedFound = true; + found = true; } - } - } - // if none of the requested image formats could be found, use the first available - if (!requestedFound) + // If none of the requested image formats could be found, use the first available + if (!found) g_SurfaceFormat = formats[0]; } free(formats); @@ -351,41 +343,34 @@ static void setup_vulkan(GLFWwindow* window, const char** extensions, uint32_t e // Get Present Mode { - // Requst a certain mode and confirm that it is available. If not use VK_PRESENT_MODE_FIFO_KHR which is mandatory + // Request a certain mode and confirm that it is available. If not use VK_PRESENT_MODE_FIFO_KHR which is mandatory #ifdef IMGUI_UNLIMITED_FRAME_RATE g_PresentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; #else g_PresentMode = VK_PRESENT_MODE_FIFO_KHR; #endif uint32_t count = 0; - vkGetPhysicalDeviceSurfacePresentModesKHR(g_Gpu, g_Surface, &count, nullptr); + vkGetPhysicalDeviceSurfacePresentModesKHR(g_PhysicalDevice, g_Surface, &count, nullptr); VkPresentModeKHR* presentModes = (VkPresentModeKHR*)malloc(sizeof(VkQueueFamilyProperties) * count); - vkGetPhysicalDeviceSurfacePresentModesKHR(g_Gpu, g_Surface, &count, presentModes); + vkGetPhysicalDeviceSurfacePresentModesKHR(g_PhysicalDevice, g_Surface, &count, presentModes); bool presentModeAvailable = false; - for (size_t i = 0; i < count; i++) - { + for (size_t i = 0; i < count && !presentModeAvailable; i++) if (presentModes[i] == g_PresentMode) - { presentModeAvailable = true; - break; - } - } if (!presentModeAvailable) - g_PresentMode = VK_PRESENT_MODE_FIFO_KHR; // always available + g_PresentMode = VK_PRESENT_MODE_FIFO_KHR; // Always available } - // Create Logical Device + // Create Logical Device (with 1 queue) { int device_extension_count = 1; const char* device_extensions[] = { "VK_KHR_swapchain" }; - const uint32_t queue_index = 0; - const uint32_t queue_count = 1; const float queue_priority[] = { 1.0f }; VkDeviceQueueCreateInfo queue_info[1] = {}; queue_info[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; queue_info[0].queueFamilyIndex = g_QueueFamily; - queue_info[0].queueCount = queue_count; + queue_info[0].queueCount = 1; queue_info[0].pQueuePriorities = queue_priority; VkDeviceCreateInfo create_info = {}; create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; @@ -393,11 +378,12 @@ static void setup_vulkan(GLFWwindow* window, const char** extensions, uint32_t e create_info.pQueueCreateInfos = queue_info; create_info.enabledExtensionCount = device_extension_count; create_info.ppEnabledExtensionNames = device_extensions; - err = vkCreateDevice(g_Gpu, &create_info, g_Allocator, &g_Device); + err = vkCreateDevice(g_PhysicalDevice, &create_info, g_Allocator, &g_Device); check_vk_result(err); - vkGetDeviceQueue(g_Device, g_QueueFamily, queue_index, &g_Queue); + vkGetDeviceQueue(g_Device, g_QueueFamily, 0, &g_Queue); } + // Create Framebuffers { int w, h; @@ -406,6 +392,7 @@ static void setup_vulkan(GLFWwindow* window, const char** extensions, uint32_t e glfwSetFramebufferSizeCallback(window, resize_vulkan); } + // Create Command Buffers for (int i = 0; i < IMGUI_VK_QUEUED_FRAMES; i++) { @@ -614,18 +601,18 @@ int main(int, char**) // Setup ImGui binding ImGui::CreateContext(); - ImGui_ImplVulkan_InitData init_data = {}; - init_data.allocator = g_Allocator; - init_data.gpu = g_Gpu; - init_data.device = g_Device; - init_data.render_pass = g_RenderPass; - init_data.pipeline_cache = g_PipelineCache; - init_data.descriptor_pool = g_DescriptorPool; - init_data.check_vk_result = check_vk_result; + ImGui_ImplVulkan_InitInfo init_info = {}; + init_info.Allocator = g_Allocator; + init_info.PhysicalDevice = g_PhysicalDevice; + init_info.Device = g_Device; + init_info.RenderPass = g_RenderPass; + init_info.PipelineCache = g_PipelineCache; + init_info.DescriptorPool = g_DescriptorPool; + init_info.CheckVkResultFn = check_vk_result; ImGuiIO& io = ImGui::GetIO(); (void)io; //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls - ImGui_ImplVulkan_Init(&init_data); + ImGui_ImplVulkan_Init(&init_info); ImGui_ImplGlfw_Init(window, true); // Setup style From 296db2ed3341bed19b233a83065b3c2c00087e2b Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 1 Mar 2018 22:16:51 +0100 Subject: [PATCH 044/828] Examples: Vulkan: Moved code into shared helpers: ImGui_ImplVulkan_SelectSurfaceFormat, ImGui_ImplVulkan_SelectPresentMode. --- examples/imgui_impl_vulkan.cpp | 70 ++++++++++++++++++++++++++++ examples/imgui_impl_vulkan.h | 4 ++ examples/sdl_vulkan_example/main.cpp | 61 +++--------------------- examples/vulkan_example/main.cpp | 62 +++--------------------- 4 files changed, 87 insertions(+), 110 deletions(-) diff --git a/examples/imgui_impl_vulkan.cpp b/examples/imgui_impl_vulkan.cpp index 70adbf5cabf5..2e4ca7af379b 100644 --- a/examples/imgui_impl_vulkan.cpp +++ b/examples/imgui_impl_vulkan.cpp @@ -720,3 +720,73 @@ void ImGui_ImplVulkan_Render(VkCommandBuffer command_buffer) g_CommandBuffer = VK_NULL_HANDLE; g_FrameIndex = (g_FrameIndex + 1) % IMGUI_VK_QUEUED_FRAMES; } + +//------------------------------------------------------------------------- +// Miscellaneous Vulkan Helpers +// (Those are currently not strictly needed by the binding, but will be once if we support multi-viewports) +//------------------------------------------------------------------------- + +#include // malloc + +VkSurfaceFormatKHR ImGui_ImplVulkan_SelectSurfaceFormat(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkFormat* request_formats, int request_formats_count, VkColorSpaceKHR request_color_space) +{ + IM_ASSERT(request_formats != NULL); + IM_ASSERT(request_formats_count > 0); + + // Per Spec Format and View Format are expected to be the same unless VK_IMAGE_CREATE_MUTABLE_BIT was set at image creation + // Assuming that the default behavior is without setting this bit, there is no need for separate Swapchain image and image view format + // Additionally several new color spaces were introduced with Vulkan Spec v1.0.40, + // hence we must make sure that a format with the mostly available color space, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, is found and used. + uint32_t avail_count; + vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &avail_count, NULL); + ImVector avail_format; + avail_format.resize((int)avail_count); + vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &avail_count, avail_format.Data); + + // First check if only one format, VK_FORMAT_UNDEFINED, is available, which would imply that any format is available + if (avail_count == 1) + { + if (avail_format[0].format == VK_FORMAT_UNDEFINED) + { + VkSurfaceFormatKHR ret; + ret.format = request_formats[0]; + ret.colorSpace = request_color_space; + return ret; + } + else + { + // No point in searching another format + return avail_format[0]; + } + } + else + { + // Request several formats, the first found will be used + for (int request_i = 0; request_i < request_formats_count; request_i++) + for (uint32_t avail_i = 0; avail_i < avail_count; avail_i++) + if (avail_format[avail_i].format == request_formats[request_i] && avail_format[avail_i].colorSpace == request_color_space) + return avail_format[avail_i]; + + // If none of the requested image formats could be found, use the first available + return avail_format[0]; + } +} + +VkPresentModeKHR ImGui_ImplVulkan_SelectPresentMode(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkPresentModeKHR* request_modes, int request_modes_count) +{ + IM_ASSERT(request_modes != NULL); + IM_ASSERT(request_modes_count > 0); + + // Request a certain mode and confirm that it is available. If not use VK_PRESENT_MODE_FIFO_KHR which is mandatory + uint32_t avail_count = 0; + vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, surface, &avail_count, NULL); + ImVector avail_modes; + avail_modes.resize((int)avail_count); + vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, surface, &avail_count, avail_modes.Data); + for (int request_i = 0; request_i < request_modes_count; request_i++) + for (uint32_t avail_i = 0; avail_i < avail_count; avail_i++) + if (request_modes[request_i] == avail_modes[avail_i]) + return request_modes[request_i]; + + return VK_PRESENT_MODE_FIFO_KHR; // Always available +} diff --git a/examples/imgui_impl_vulkan.h b/examples/imgui_impl_vulkan.h index 04ea473e9f8d..188f0e111d57 100644 --- a/examples/imgui_impl_vulkan.h +++ b/examples/imgui_impl_vulkan.h @@ -34,3 +34,7 @@ IMGUI_API void ImGui_ImplVulkan_InvalidateFontUploadObjects(); IMGUI_API void ImGui_ImplVulkan_InvalidateDeviceObjects(); IMGUI_API bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer); IMGUI_API bool ImGui_ImplVulkan_CreateDeviceObjects(); + +// Miscellaneous Vulkan Helpers +IMGUI_API VkSurfaceFormatKHR ImGui_ImplVulkan_SelectSurfaceFormat(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkFormat* request_formats, int request_formats_count, VkColorSpaceKHR request_color_space); +IMGUI_API VkPresentModeKHR ImGui_ImplVulkan_SelectPresentMode(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkPresentModeKHR* request_modes, int request_modes_count); diff --git a/examples/sdl_vulkan_example/main.cpp b/examples/sdl_vulkan_example/main.cpp index 24b0221603ed..1be9584d5a66 100644 --- a/examples/sdl_vulkan_example/main.cpp +++ b/examples/sdl_vulkan_example/main.cpp @@ -297,69 +297,20 @@ static void setup_vulkan(SDL_Window* window, const char** extensions, uint32_t e // Get Surface Format { - // Per Spec Format and View Format are expected to be the same unless VK_IMAGE_CREATE_MUTABLE_BIT was set at image creation - // Assuming that the default behavior is without setting this bit, there is no need for separate Swapchain image and image view format - // Additionally several new color spaces were introduced with Vulkan Spec v1.0.40, - // hence we must make sure that a format with the mostly available color space, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, is found and used. - uint32_t count; - vkGetPhysicalDeviceSurfaceFormatsKHR(g_PhysicalDevice, g_Surface, &count, NULL); - VkSurfaceFormatKHR* formats = (VkSurfaceFormatKHR*)malloc(sizeof(VkSurfaceFormatKHR) * count); - vkGetPhysicalDeviceSurfaceFormatsKHR(g_PhysicalDevice, g_Surface, &count, formats); - - // First check if only one format, VK_FORMAT_UNDEFINED, is available, which would imply that any format is available - if (count == 1) - { - if (formats[0].format == VK_FORMAT_UNDEFINED) - { - g_SurfaceFormat.format = VK_FORMAT_B8G8R8A8_UNORM; - g_SurfaceFormat.colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; - } - else - { - // No point in searching another format - g_SurfaceFormat = formats[0]; - } - } - else - { - // Request several formats, the first found will be used - VkFormat requestSurfaceImageFormat[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM }; - VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; - bool found = false; - for (size_t i = 0; found == false && i < sizeof(requestSurfaceImageFormat) / sizeof(requestSurfaceImageFormat[0]); i++) - for (uint32_t j = 0; j < count; j++) - if (formats[j].format == requestSurfaceImageFormat[i] && formats[j].colorSpace == requestSurfaceColorSpace) - { - g_SurfaceFormat = formats[j]; - found = true; - } - - // If none of the requested image formats could be found, use the first available - if (!found) - g_SurfaceFormat = formats[0]; - } - free(formats); + const VkFormat requestSurfaceImageFormat[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM }; + const VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; + g_SurfaceFormat = ImGui_ImplVulkan_SelectSurfaceFormat(g_PhysicalDevice, g_Surface, requestSurfaceImageFormat, (size_t)IM_ARRAYSIZE(requestSurfaceImageFormat), requestSurfaceColorSpace); } // Get Present Mode { - // Request a certain mode and confirm that it is available. If not use VK_PRESENT_MODE_FIFO_KHR which is mandatory #ifdef IMGUI_UNLIMITED_FRAME_RATE - g_PresentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; + VkPresentModeKHR present_mode = VK_PRESENT_MODE_IMMEDIATE_KHR; #else - g_PresentMode = VK_PRESENT_MODE_FIFO_KHR; + VkPresentModeKHR present_mode = VK_PRESENT_MODE_FIFO_KHR; #endif - uint32_t count = 0; - vkGetPhysicalDeviceSurfacePresentModesKHR(g_PhysicalDevice, g_Surface, &count, nullptr); - VkPresentModeKHR* presentModes = (VkPresentModeKHR*)malloc(sizeof(VkQueueFamilyProperties) * count); - vkGetPhysicalDeviceSurfacePresentModesKHR(g_PhysicalDevice, g_Surface, &count, presentModes); - bool presentModeAvailable = false; - for (size_t i = 0; i < count && !presentModeAvailable; i++) - if (presentModes[i] == g_PresentMode) - presentModeAvailable = true; - if (!presentModeAvailable) - g_PresentMode = VK_PRESENT_MODE_FIFO_KHR; // Always available + g_PresentMode = ImGui_ImplVulkan_SelectPresentMode(g_PhysicalDevice, g_Surface, &present_mode, 1); } diff --git a/examples/vulkan_example/main.cpp b/examples/vulkan_example/main.cpp index e0c0fab35616..5b82f5bccaf9 100644 --- a/examples/vulkan_example/main.cpp +++ b/examples/vulkan_example/main.cpp @@ -294,71 +294,23 @@ static void setup_vulkan(GLFWwindow* window, const char** extensions, uint32_t e } } + // Get Surface Format { - // Per Spec Format and View Format are expected to be the same unless VK_IMAGE_CREATE_MUTABLE_BIT was set at image creation - // Assuming that the default behavior is without setting this bit, there is no need for separate Swapchain image and image view format - // Additionally several new color spaces were introduced with Vulkan Spec v1.0.40, - // hence we must make sure that a format with the mostly available color space, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, is found and used. - uint32_t count; - vkGetPhysicalDeviceSurfaceFormatsKHR(g_PhysicalDevice, g_Surface, &count, NULL); - VkSurfaceFormatKHR* formats = (VkSurfaceFormatKHR*)malloc(sizeof(VkSurfaceFormatKHR) * count); - vkGetPhysicalDeviceSurfaceFormatsKHR(g_PhysicalDevice, g_Surface, &count, formats); - - // First check if only one format, VK_FORMAT_UNDEFINED, is available, which would imply that any format is available - if (count == 1) - { - if (formats[0].format == VK_FORMAT_UNDEFINED) - { - g_SurfaceFormat.format = VK_FORMAT_B8G8R8A8_UNORM; - g_SurfaceFormat.colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; - } - else - { - // No point in searching another format - g_SurfaceFormat = formats[0]; - } - } - else - { - // Request several formats, the first found will be used - VkFormat requestSurfaceImageFormat[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM }; - VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; - bool found = false; - for (size_t i = 0; found == false && i < sizeof(requestSurfaceImageFormat) / sizeof(requestSurfaceImageFormat[0]); i++) - for (uint32_t j = 0; j < count; j++) - if (formats[j].format == requestSurfaceImageFormat[i] && formats[j].colorSpace == requestSurfaceColorSpace) - { - g_SurfaceFormat = formats[j]; - found = true; - } - - // If none of the requested image formats could be found, use the first available - if (!found) - g_SurfaceFormat = formats[0]; - } - free(formats); + const VkFormat requestSurfaceImageFormat[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM }; + const VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; + g_SurfaceFormat = ImGui_ImplVulkan_SelectSurfaceFormat(g_PhysicalDevice, g_Surface, requestSurfaceImageFormat, (size_t)IM_ARRAYSIZE(requestSurfaceImageFormat), requestSurfaceColorSpace); } // Get Present Mode { - // Request a certain mode and confirm that it is available. If not use VK_PRESENT_MODE_FIFO_KHR which is mandatory #ifdef IMGUI_UNLIMITED_FRAME_RATE - g_PresentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; + VkPresentModeKHR present_mode = VK_PRESENT_MODE_IMMEDIATE_KHR; #else - g_PresentMode = VK_PRESENT_MODE_FIFO_KHR; + VkPresentModeKHR present_mode = VK_PRESENT_MODE_FIFO_KHR; #endif - uint32_t count = 0; - vkGetPhysicalDeviceSurfacePresentModesKHR(g_PhysicalDevice, g_Surface, &count, nullptr); - VkPresentModeKHR* presentModes = (VkPresentModeKHR*)malloc(sizeof(VkQueueFamilyProperties) * count); - vkGetPhysicalDeviceSurfacePresentModesKHR(g_PhysicalDevice, g_Surface, &count, presentModes); - bool presentModeAvailable = false; - for (size_t i = 0; i < count && !presentModeAvailable; i++) - if (presentModes[i] == g_PresentMode) - presentModeAvailable = true; - if (!presentModeAvailable) - g_PresentMode = VK_PRESENT_MODE_FIFO_KHR; // Always available + g_PresentMode = ImGui_ImplVulkan_SelectPresentMode(g_PhysicalDevice, g_Surface, &present_mode, 1); } From 68e9ef9885c9c3f103364161b2e6d2c154345cd2 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 1 Mar 2018 22:39:06 +0100 Subject: [PATCH 045/828] Examples: Vulkan: SDL: Fixed missing resize handler (not properly merged from #1367) + tweaks. --- examples/sdl_vulkan_example/main.cpp | 6 ++++-- examples/vulkan_example/main.cpp | 11 ++++++++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/examples/sdl_vulkan_example/main.cpp b/examples/sdl_vulkan_example/main.cpp index 1be9584d5a66..7756880b7a2f 100644 --- a/examples/sdl_vulkan_example/main.cpp +++ b/examples/sdl_vulkan_example/main.cpp @@ -57,7 +57,7 @@ static void check_vk_result(VkResult err) abort(); } -static void resize_vulkan(SDL_Window*, int w, int h) +static void CreateOrResizeSwapChainAndFrameBuffer(int w, int h) { VkResult err; VkSwapchainKHR old_swapchain = g_Swapchain; @@ -340,7 +340,7 @@ static void setup_vulkan(SDL_Window* window, const char** extensions, uint32_t e { int w, h; SDL_GetWindowSize(window, &w, &h); - resize_vulkan(window, w, h); + CreateOrResizeSwapChainAndFrameBuffer(w, h); } @@ -639,6 +639,8 @@ int main(int, char**) ImGui_ImplSDL2_ProcessEvent(&event); if (event.type == SDL_QUIT) done = true; + if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_RESIZED && event.window.windowID == SDL_GetWindowID(window)) + CreateOrResizeSwapChainAndFrameBuffer((int)event.window.data1, (int)event.window.data2); } ImGui_ImplVulkan_NewFrame(); ImGui_ImplSDL2_NewFrame(window); diff --git a/examples/vulkan_example/main.cpp b/examples/vulkan_example/main.cpp index 5b82f5bccaf9..78b6615ed71b 100644 --- a/examples/vulkan_example/main.cpp +++ b/examples/vulkan_example/main.cpp @@ -60,7 +60,7 @@ static void check_vk_result(VkResult err) abort(); } -static void resize_vulkan(GLFWwindow*, int w, int h) +static void CreateOrResizeSwapChainAndFrameBuffer(int w, int h) { VkResult err; VkSwapchainKHR old_swapchain = g_Swapchain; @@ -189,6 +189,11 @@ static void resize_vulkan(GLFWwindow*, int w, int h) } } +static void GlfwResizeCallback(GLFWwindow*, int w, int h) +{ + CreateOrResizeSwapChainAndFrameBuffer(w, h); +} + #ifdef IMGUI_VULKAN_DEBUG_REPORT static VKAPI_ATTR VkBool32 VKAPI_CALL debug_report(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char* pLayerPrefix, const char* pMessage, void* pUserData) { @@ -340,8 +345,8 @@ static void setup_vulkan(GLFWwindow* window, const char** extensions, uint32_t e { int w, h; glfwGetFramebufferSize(window, &w, &h); - resize_vulkan(window, w, h); - glfwSetFramebufferSizeCallback(window, resize_vulkan); + CreateOrResizeSwapChainAndFrameBuffer(w, h); + glfwSetFramebufferSizeCallback(window, GlfwResizeCallback); } From 5b282bdd488d8e8653e93b69eec84a3042df9486 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 2 Mar 2018 19:20:58 +0100 Subject: [PATCH 046/828] Examples: Renamed glfw error callback so it's more clear what it is. --- examples/opengl2_example/main.cpp | 6 +++--- examples/opengl3_example/main.cpp | 6 +++--- examples/vulkan_example/main.cpp | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/examples/opengl2_example/main.cpp b/examples/opengl2_example/main.cpp index 0b2885c42d85..7c5f8d772a87 100644 --- a/examples/opengl2_example/main.cpp +++ b/examples/opengl2_example/main.cpp @@ -12,15 +12,15 @@ #include #include -static void error_callback(int error, const char* description) +static void glfw_error_callback(int error, const char* description) { - fprintf(stderr, "Error %d: %s\n", error, description); + fprintf(stderr, "Glfw Error %d: %s\n", error, description); } int main(int, char**) { // Setup window - glfwSetErrorCallback(error_callback); + glfwSetErrorCallback(glfw_error_callback); if (!glfwInit()) return 1; GLFWwindow* window = glfwCreateWindow(1280, 720, "ImGui OpenGL2 example", NULL, NULL); diff --git a/examples/opengl3_example/main.cpp b/examples/opengl3_example/main.cpp index cfdfb0c0daf3..5d8dba0fbc43 100644 --- a/examples/opengl3_example/main.cpp +++ b/examples/opengl3_example/main.cpp @@ -10,15 +10,15 @@ #include // This example is using gl3w to access OpenGL functions (because it is small). You may use glew/glad/glLoadGen/etc. whatever already works for you. #include -static void error_callback(int error, const char* description) +static void glfw_error_callback(int error, const char* description) { - fprintf(stderr, "Error %d: %s\n", error, description); + fprintf(stderr, "Glfw Error %d: %s\n", error, description); } int main(int, char**) { // Setup window - glfwSetErrorCallback(error_callback); + glfwSetErrorCallback(glfw_error_callback); if (!glfwInit()) return 1; glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); diff --git a/examples/vulkan_example/main.cpp b/examples/vulkan_example/main.cpp index 4cfc49acba42..ebd458a96562 100644 --- a/examples/vulkan_example/main.cpp +++ b/examples/vulkan_example/main.cpp @@ -530,15 +530,15 @@ static void frame_present() g_FrameIndex = (g_FrameIndex + 1) % IMGUI_VK_QUEUED_FRAMES; } -static void error_callback(int error, const char* description) +static void glfw_error_callback(int error, const char* description) { - fprintf(stderr, "Error %d: %s\n", error, description); + fprintf(stderr, "Glfw Error %d: %s\n", error, description); } int main(int, char**) { // Setup window - glfwSetErrorCallback(error_callback); + glfwSetErrorCallback(glfw_error_callback); if (!glfwInit()) return 1; From 9da475e4e8b697b68c3dd07990e5136c53a9bcbe Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 2 Mar 2018 19:23:01 +0100 Subject: [PATCH 047/828] Examples: GLFW, Vulkan: GLFW binding viewport tweaks to supports Vulkan better (do not call SwapBuffer, share context etc.). Added DUMMY (empty) platform/viewport interface in the viewport code. --- examples/imgui_impl_glfw.cpp | 37 +++++++++---- examples/imgui_impl_glfw.h | 1 + examples/imgui_impl_vulkan.cpp | 89 ++++++++++++++++++++++++++++++++ examples/vulkan_example/main.cpp | 6 ++- 4 files changed, 123 insertions(+), 10 deletions(-) diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index f5fce96c2c87..2e28a52a0f73 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -36,10 +36,17 @@ #endif // Data -static GLFWwindow* g_Window = NULL; -static double g_Time = 0.0f; -static bool g_MouseJustPressed[5] = { false, false, false, false, false }; -static GLFWcursor* g_MouseCursors[ImGuiMouseCursor_Count_] = { 0 }; +enum GlfwClientApi +{ + GlfwClientApi_Unknown, + GlfwClientApi_OpenGL, + GlfwClientApi_Vulkan +}; +static GLFWwindow* g_Window = NULL; +static GlfwClientApi g_ClientApi = GlfwClientApi_Unknown; +static double g_Time = 0.0f; +static bool g_MouseJustPressed[5] = { false, false, false, false, false }; +static GLFWcursor* g_MouseCursors[ImGuiMouseCursor_Count_] = { 0 }; // Forward Declarations static void ImGui_ImplGlfw_InitPlatformInterface(); @@ -98,7 +105,7 @@ void ImGui_ImplGlfw_InstallCallbacks(GLFWwindow* window) glfwSetCharCallback(window, ImGui_ImplGlfw_CharCallback); } -bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks) +bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks) { g_Window = window; @@ -149,6 +156,15 @@ bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks) if (io.ConfigFlags & ImGuiConfigFlags_MultiViewports) ImGui_ImplGlfw_InitPlatformInterface(); + g_ClientApi = GlfwClientApi_OpenGL; + return true; +} + +bool ImGui_ImplGlfw_InitForVulkan(GLFWwindow* window, bool install_callbacks) +{ + if (!ImGui_ImplGlfw_Init(window, install_callbacks)) + return false; + g_ClientApi = GlfwClientApi_Vulkan; return true; } @@ -161,6 +177,7 @@ void ImGui_ImplGlfw_Shutdown() glfwDestroyCursor(g_MouseCursors[cursor_n]); g_MouseCursors[cursor_n] = NULL; } + g_ClientApi = GlfwClientApi_Unknown; } static void ImGui_ImplGlfw_UpdateMouse() @@ -176,7 +193,6 @@ static void ImGui_ImplGlfw_UpdateMouse() io.MouseHoveredViewport = 0; // Update buttons - ImGuiIO& io = ImGui::GetIO(); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) { // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame. @@ -296,7 +312,8 @@ static void ImGui_ImplGlfw_CreateViewport(ImGuiViewport* viewport) glfwWindowHint(GLFW_VISIBLE, false); glfwWindowHint(GLFW_FOCUSED, false); glfwWindowHint(GLFW_DECORATED, (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? false : true); - data->Window = glfwCreateWindow((int)viewport->Size.x, (int)viewport->Size.y, "No Title Yet", NULL, g_Window); + GLFWwindow* share_window = (g_ClientApi == GlfwClientApi_OpenGL) ? g_Window : NULL; + data->Window = glfwCreateWindow((int)viewport->Size.x, (int)viewport->Size.y, "No Title Yet", NULL, share_window); data->WindowOwned = true; viewport->PlatformHandle = (void*)data->Window; viewport->Name = NULL; @@ -405,7 +422,8 @@ static void ImGui_ImplGlfw_SetWindowTitle(ImGuiViewport* viewport, const char* t static void ImGui_ImplGlfw_RenderViewport(ImGuiViewport* viewport) { ImGuiPlatformDataGlfw* data = (ImGuiPlatformDataGlfw*)viewport->PlatformUserData; - glfwMakeContextCurrent(data->Window); + if (g_ClientApi == GlfwClientApi_OpenGL) + glfwMakeContextCurrent(data->Window); if (glfwWindowShouldClose(data->Window)) viewport->PlatformRequestClose = true; @@ -414,7 +432,8 @@ static void ImGui_ImplGlfw_RenderViewport(ImGuiViewport* viewport) static void ImGui_ImplGlfw_SwapBuffers(ImGuiViewport* viewport) { ImGuiPlatformDataGlfw* data = (ImGuiPlatformDataGlfw*)viewport->PlatformUserData; - glfwSwapBuffers(data->Window); + if (g_ClientApi == GlfwClientApi_OpenGL) + glfwSwapBuffers(data->Window); } static void ImGui_ImplGlfw_InitPlatformInterface() diff --git a/examples/imgui_impl_glfw.h b/examples/imgui_impl_glfw.h index 3e8fd6c7ecdc..c19089d0330e 100644 --- a/examples/imgui_impl_glfw.h +++ b/examples/imgui_impl_glfw.h @@ -13,6 +13,7 @@ struct GLFWwindow; IMGUI_API bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks); +IMGUI_API bool ImGui_ImplGlfw_InitForVulkan(GLFWwindow* window, bool install_callbacks); IMGUI_API void ImGui_ImplGlfw_Shutdown(); IMGUI_API void ImGui_ImplGlfw_NewFrame(); diff --git a/examples/imgui_impl_vulkan.cpp b/examples/imgui_impl_vulkan.cpp index 2e4ca7af379b..2a728ea6a530 100644 --- a/examples/imgui_impl_vulkan.cpp +++ b/examples/imgui_impl_vulkan.cpp @@ -57,6 +57,10 @@ static VkBuffer g_IndexBuffer[IMGUI_VK_QUEUED_FRAMES] = {}; static VkDeviceMemory g_UploadBufferMemory = VK_NULL_HANDLE; static VkBuffer g_UploadBuffer = VK_NULL_HANDLE; +// Forward Declarations +static void ImGui_ImplVulkan_InitPlatformInterface(); +static void ImGui_ImplVulkan_ShutdownPlatformInterface(); + static uint32_t __glsl_shader_vert_spv[] = { 0x07230203,0x00010000,0x00080001,0x0000002e,0x00000000,0x00020011,0x00000001,0x0006000b, @@ -699,12 +703,17 @@ bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo *init_data) g_DescriptorPool = init_data->DescriptorPool; g_CheckVkResult = init_data->CheckVkResultFn; + ImGuiIO& io = ImGui::GetIO(); ImGui_ImplVulkan_CreateDeviceObjects(); + if (io.ConfigFlags & ImGuiConfigFlags_MultiViewports) + ImGui_ImplVulkan_InitPlatformInterface(); + return true; } void ImGui_ImplVulkan_Shutdown() { + ImGui_ImplVulkan_ShutdownPlatformInterface(); ImGui_ImplVulkan_InvalidateDeviceObjects(); } @@ -790,3 +799,83 @@ VkPresentModeKHR ImGui_ImplVulkan_SelectPresentMode(VkPhysicalDevice physical_de return VK_PRESENT_MODE_FIFO_KHR; // Always available } + +// -------------------------------------------------------------------------------------------------------- +// Platform Windows (OPTIONAL/EXPERIMENTAL) +// -------------------------------------------------------------------------------------------------------- + +#include "imgui_internal.h" // ImGuiViewport + +struct ImGuiPlatformDataVulkan +{ + // store swap chain, render target/frame buffer, etc. + + ImGuiPlatformDataVulkan() { } + ~ImGuiPlatformDataVulkan() { } +}; + +static void ImGui_ImplVulkan_CreateViewport(ImGuiViewport* viewport) +{ + ImGuiPlatformDataVulkan* data = IM_NEW(ImGuiPlatformDataVulkan)(); + viewport->RendererUserData = data; + + // FIXME-PLATFORM + //HWND hwnd = (HWND)viewport->PlatformHandle; + //IM_ASSERT(hwnd != 0); + + //... +} + +static void ImGui_ImplVulkan_DestroyViewport(ImGuiViewport* viewport) +{ + if (ImGuiPlatformDataVulkan* data = (ImGuiPlatformDataVulkan*)viewport->RendererUserData) + { + //... + IM_DELETE(data); + } + viewport->RendererUserData = NULL; +} + +static void ImGui_ImplVulkan_ResizeViewport(ImGuiViewport* viewport, int w, int h) +{ + ImGuiPlatformDataVulkan* data = (ImGuiPlatformDataVulkan*)viewport->RendererUserData; + //... + (void)data; + (void)w; + (void)h; +} + +static void ImGui_ImplVulkan_RenderViewport(ImGuiViewport* viewport) +{ + ImGuiPlatformDataVulkan* data = (ImGuiPlatformDataVulkan*)viewport->RendererUserData; + ImVec4 clear_color = ImGui::GetStyle().Colors[ImGuiCol_WindowBg]; // FIXME-PLATFORM + clear_color.w = 1.0f; + + (void)data; + // clear + // call ImGui_ImplVulkan_RenderDrawData(&viewport->DrawData) +} + +static void ImGui_ImplVulkan_SwapBuffers(ImGuiViewport* viewport) +{ + ImGuiPlatformDataVulkan* data = (ImGuiPlatformDataVulkan*)viewport->RendererUserData; + (void)data; + //... +} + +void ImGui_ImplVulkan_InitPlatformInterface() +{ + ImGuiIO& io = ImGui::GetIO(); + io.RendererInterface.CreateViewport = ImGui_ImplVulkan_CreateViewport; + io.RendererInterface.DestroyViewport = ImGui_ImplVulkan_DestroyViewport; + io.RendererInterface.ResizeViewport = ImGui_ImplVulkan_ResizeViewport; + io.RendererInterface.RenderViewport = ImGui_ImplVulkan_RenderViewport; + io.RendererInterface.SwapBuffers = ImGui_ImplVulkan_SwapBuffers; +} + +void ImGui_ImplVulkan_ShutdownPlatformInterface() +{ + ImGuiIO& io = ImGui::GetIO(); + memset(&io.RendererInterface, 0, sizeof(io.RendererInterface)); +} + diff --git a/examples/vulkan_example/main.cpp b/examples/vulkan_example/main.cpp index ebd458a96562..2e6c8bbd81bc 100644 --- a/examples/vulkan_example/main.cpp +++ b/examples/vulkan_example/main.cpp @@ -568,10 +568,11 @@ int main(int, char**) init_info.CheckVkResultFn = check_vk_result; ImGuiIO& io = ImGui::GetIO(); (void)io; + //io.ConfigFlags |= ImGuiConfigFlags_MultiViewports; //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls ImGui_ImplVulkan_Init(&init_info); - ImGui_ImplGlfw_Init(window, true); + ImGui_ImplGlfw_InitForVulkan(window, true); // Setup style ImGui::StyleColorsDark(); @@ -689,6 +690,9 @@ int main(int, char**) ImGui_ImplVulkan_Render(g_CommandBuffer[g_FrameIndex]); frame_end(); frame_present(); + + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindows(); } // Cleanup From e927a6ac4a8dfd653754dc28a0c621faa4bd4ed1 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 2 Mar 2018 16:34:47 +0100 Subject: [PATCH 048/828] Examples: Vulkan: Various tweak. Misc refactor into per-frame data. Duplicate buffer creation code moved to CreateOrResizeBuffer(). --- examples/imgui_impl_vulkan.cpp | 184 +++++++++++++-------------- examples/imgui_impl_vulkan.h | 1 + examples/sdl_vulkan_example/main.cpp | 104 ++++++++------- examples/vulkan_example/main.cpp | 103 ++++++++------- 4 files changed, 203 insertions(+), 189 deletions(-) diff --git a/examples/imgui_impl_vulkan.cpp b/examples/imgui_impl_vulkan.cpp index 2a728ea6a530..434459a252b3 100644 --- a/examples/imgui_impl_vulkan.cpp +++ b/examples/imgui_impl_vulkan.cpp @@ -32,28 +32,32 @@ static VkPipelineCache g_PipelineCache = VK_NULL_HANDLE; static VkDescriptorPool g_DescriptorPool = VK_NULL_HANDLE; static void (*g_CheckVkResult)(VkResult err) = NULL; -static VkCommandBuffer g_CommandBuffer = VK_NULL_HANDLE; static VkDeviceSize g_BufferMemoryAlignment = 256; static VkPipelineCreateFlags g_PipelineCreateFlags = 0; -static int g_FrameIndex = 0; static VkDescriptorSetLayout g_DescriptorSetLayout = VK_NULL_HANDLE; static VkPipelineLayout g_PipelineLayout = VK_NULL_HANDLE; static VkDescriptorSet g_DescriptorSet = VK_NULL_HANDLE; static VkPipeline g_Pipeline = VK_NULL_HANDLE; +// Frame data +struct FrameDataForRender +{ + VkDeviceMemory VertexBufferMemory; + VkDeviceMemory IndexBufferMemory; + VkDeviceSize VertexBufferSize; + VkDeviceSize IndexBufferSize; + VkBuffer VertexBuffer; + VkBuffer IndexBuffer; +}; +static int g_FrameIndex = 0; +static FrameDataForRender g_FramesDataBuffers[IMGUI_VK_QUEUED_FRAMES]; + +// Font data static VkSampler g_FontSampler = VK_NULL_HANDLE; static VkDeviceMemory g_FontMemory = VK_NULL_HANDLE; static VkImage g_FontImage = VK_NULL_HANDLE; static VkImageView g_FontView = VK_NULL_HANDLE; - -static VkDeviceMemory g_VertexBufferMemory[IMGUI_VK_QUEUED_FRAMES] = {}; -static VkDeviceMemory g_IndexBufferMemory[IMGUI_VK_QUEUED_FRAMES] = {}; -static VkDeviceSize g_VertexBufferSize[IMGUI_VK_QUEUED_FRAMES] = {}; -static VkDeviceSize g_IndexBufferSize[IMGUI_VK_QUEUED_FRAMES] = {}; -static VkBuffer g_VertexBuffer[IMGUI_VK_QUEUED_FRAMES] = {}; -static VkBuffer g_IndexBuffer[IMGUI_VK_QUEUED_FRAMES] = {}; - static VkDeviceMemory g_UploadBufferMemory = VK_NULL_HANDLE; static VkBuffer g_UploadBuffer = VK_NULL_HANDLE; @@ -61,6 +65,8 @@ static VkBuffer g_UploadBuffer = VK_NULL_HANDLE; static void ImGui_ImplVulkan_InitPlatformInterface(); static void ImGui_ImplVulkan_ShutdownPlatformInterface(); +// glsl_shader.vert, compiled with: +// # glslangValidator -V -x -o glsl_shader.vert.u32 glsl_shader.vert static uint32_t __glsl_shader_vert_spv[] = { 0x07230203,0x00010000,0x00080001,0x0000002e,0x00000000,0x00020011,0x00000001,0x0006000b, @@ -106,6 +112,8 @@ static uint32_t __glsl_shader_vert_spv[] = 0x0000002d,0x0000002c,0x000100fd,0x00010038 }; +// glsl_shader.frag, compiled with: +// # glslangValidator -V -x -o glsl_shader.frag.u32 glsl_shader.frag static uint32_t __glsl_shader_frag_spv[] = { 0x07230203,0x00010000,0x00080001,0x0000001e,0x00000000,0x00020011,0x00000001,0x0006000b, @@ -151,81 +159,63 @@ static void ImGui_ImplVulkan_VkResult(VkResult err) g_CheckVkResult(err); } +static void CreateOrResizeBuffer(VkBuffer& buffer, VkDeviceMemory& buffer_memory, VkDeviceSize& p_buffer_size, size_t new_size, VkBufferUsageFlagBits usage) +{ + VkResult err; + if (buffer != NULL) + vkDestroyBuffer(g_Device, buffer, g_Allocator); + if (buffer_memory) + vkFreeMemory(g_Device, buffer_memory, g_Allocator); + + VkDeviceSize vertex_buffer_size_aligned = ((new_size - 1) / g_BufferMemoryAlignment + 1) * g_BufferMemoryAlignment; + VkBufferCreateInfo buffer_info = {}; + buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + buffer_info.size = vertex_buffer_size_aligned; + buffer_info.usage = usage; + buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + err = vkCreateBuffer(g_Device, &buffer_info, g_Allocator, &buffer); + ImGui_ImplVulkan_VkResult(err); + + VkMemoryRequirements req; + vkGetBufferMemoryRequirements(g_Device, buffer, &req); + g_BufferMemoryAlignment = (g_BufferMemoryAlignment > req.alignment) ? g_BufferMemoryAlignment : req.alignment; + VkMemoryAllocateInfo alloc_info = {}; + alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + alloc_info.allocationSize = req.size; + alloc_info.memoryTypeIndex = ImGui_ImplVulkan_MemoryType(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, req.memoryTypeBits); + err = vkAllocateMemory(g_Device, &alloc_info, g_Allocator, &buffer_memory); + ImGui_ImplVulkan_VkResult(err); + + err = vkBindBufferMemory(g_Device, buffer, buffer_memory, 0); + ImGui_ImplVulkan_VkResult(err); + p_buffer_size = new_size; +} + // This is the main rendering function that you have to implement and provide to ImGui (via setting up 'RenderDrawListsFn' in the ImGuiIO structure) -void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data) +void ImGui_ImplVulkan_RenderDrawData(VkCommandBuffer command_buffer, ImDrawData* draw_data) { VkResult err; ImGuiIO& io = ImGui::GetIO(); if (draw_data->TotalVtxCount == 0) return; - // Create the Vertex Buffer: - size_t vertex_size = draw_data->TotalVtxCount * sizeof(ImDrawVert); - if (!g_VertexBuffer[g_FrameIndex] || g_VertexBufferSize[g_FrameIndex] < vertex_size) - { - if (g_VertexBuffer[g_FrameIndex]) - vkDestroyBuffer(g_Device, g_VertexBuffer[g_FrameIndex], g_Allocator); - if (g_VertexBufferMemory[g_FrameIndex]) - vkFreeMemory(g_Device, g_VertexBufferMemory[g_FrameIndex], g_Allocator); - VkDeviceSize vertex_buffer_size = ((vertex_size-1) / g_BufferMemoryAlignment+1) * g_BufferMemoryAlignment; - VkBufferCreateInfo buffer_info = {}; - buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; - buffer_info.size = vertex_buffer_size; - buffer_info.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; - buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - err = vkCreateBuffer(g_Device, &buffer_info, g_Allocator, &g_VertexBuffer[g_FrameIndex]); - ImGui_ImplVulkan_VkResult(err); - VkMemoryRequirements req; - vkGetBufferMemoryRequirements(g_Device, g_VertexBuffer[g_FrameIndex], &req); - g_BufferMemoryAlignment = (g_BufferMemoryAlignment > req.alignment) ? g_BufferMemoryAlignment : req.alignment; - VkMemoryAllocateInfo alloc_info = {}; - alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - alloc_info.allocationSize = req.size; - alloc_info.memoryTypeIndex = ImGui_ImplVulkan_MemoryType(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, req.memoryTypeBits); - err = vkAllocateMemory(g_Device, &alloc_info, g_Allocator, &g_VertexBufferMemory[g_FrameIndex]); - ImGui_ImplVulkan_VkResult(err); - err = vkBindBufferMemory(g_Device, g_VertexBuffer[g_FrameIndex], g_VertexBufferMemory[g_FrameIndex], 0); - ImGui_ImplVulkan_VkResult(err); - g_VertexBufferSize[g_FrameIndex] = vertex_buffer_size; - } + FrameDataForRender* fd = &g_FramesDataBuffers[g_FrameIndex]; - // Create the Index Buffer: + // Create the Vertex and Index buffers: + size_t vertex_size = draw_data->TotalVtxCount * sizeof(ImDrawVert); size_t index_size = draw_data->TotalIdxCount * sizeof(ImDrawIdx); - if (!g_IndexBuffer[g_FrameIndex] || g_IndexBufferSize[g_FrameIndex] < index_size) - { - if (g_IndexBuffer[g_FrameIndex]) - vkDestroyBuffer(g_Device, g_IndexBuffer[g_FrameIndex], g_Allocator); - if (g_IndexBufferMemory[g_FrameIndex]) - vkFreeMemory(g_Device, g_IndexBufferMemory[g_FrameIndex], g_Allocator); - VkDeviceSize index_buffer_size = ((index_size-1) / g_BufferMemoryAlignment+1) * g_BufferMemoryAlignment; - VkBufferCreateInfo buffer_info = {}; - buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; - buffer_info.size = index_buffer_size; - buffer_info.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT; - buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - err = vkCreateBuffer(g_Device, &buffer_info, g_Allocator, &g_IndexBuffer[g_FrameIndex]); - ImGui_ImplVulkan_VkResult(err); - VkMemoryRequirements req; - vkGetBufferMemoryRequirements(g_Device, g_IndexBuffer[g_FrameIndex], &req); - g_BufferMemoryAlignment = (g_BufferMemoryAlignment > req.alignment) ? g_BufferMemoryAlignment : req.alignment; - VkMemoryAllocateInfo alloc_info = {}; - alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - alloc_info.allocationSize = req.size; - alloc_info.memoryTypeIndex = ImGui_ImplVulkan_MemoryType(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, req.memoryTypeBits); - err = vkAllocateMemory(g_Device, &alloc_info, g_Allocator, &g_IndexBufferMemory[g_FrameIndex]); - ImGui_ImplVulkan_VkResult(err); - err = vkBindBufferMemory(g_Device, g_IndexBuffer[g_FrameIndex], g_IndexBufferMemory[g_FrameIndex], 0); - ImGui_ImplVulkan_VkResult(err); - g_IndexBufferSize[g_FrameIndex] = index_buffer_size; - } + if (!fd->VertexBuffer || fd->VertexBufferSize < vertex_size) + CreateOrResizeBuffer(fd->VertexBuffer, fd->VertexBufferMemory, fd->VertexBufferSize, vertex_size, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT); + if (!fd->IndexBuffer || fd->IndexBufferSize < index_size) + CreateOrResizeBuffer(fd->IndexBuffer, fd->IndexBufferMemory, fd->IndexBufferSize, index_size, VK_BUFFER_USAGE_INDEX_BUFFER_BIT); // Upload Vertex and index Data: { ImDrawVert* vtx_dst; ImDrawIdx* idx_dst; - err = vkMapMemory(g_Device, g_VertexBufferMemory[g_FrameIndex], 0, vertex_size, 0, (void**)(&vtx_dst)); + err = vkMapMemory(g_Device, fd->VertexBufferMemory, 0, vertex_size, 0, (void**)(&vtx_dst)); ImGui_ImplVulkan_VkResult(err); - err = vkMapMemory(g_Device, g_IndexBufferMemory[g_FrameIndex], 0, index_size, 0, (void**)(&idx_dst)); + err = vkMapMemory(g_Device, fd->IndexBufferMemory, 0, index_size, 0, (void**)(&idx_dst)); ImGui_ImplVulkan_VkResult(err); for (int n = 0; n < draw_data->CmdListsCount; n++) { @@ -237,30 +227,30 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data) } VkMappedMemoryRange range[2] = {}; range[0].sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; - range[0].memory = g_VertexBufferMemory[g_FrameIndex]; + range[0].memory = fd->VertexBufferMemory; range[0].size = VK_WHOLE_SIZE; range[1].sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; - range[1].memory = g_IndexBufferMemory[g_FrameIndex]; + range[1].memory = fd->IndexBufferMemory; range[1].size = VK_WHOLE_SIZE; err = vkFlushMappedMemoryRanges(g_Device, 2, range); ImGui_ImplVulkan_VkResult(err); - vkUnmapMemory(g_Device, g_VertexBufferMemory[g_FrameIndex]); - vkUnmapMemory(g_Device, g_IndexBufferMemory[g_FrameIndex]); + vkUnmapMemory(g_Device, fd->VertexBufferMemory); + vkUnmapMemory(g_Device, fd->IndexBufferMemory); } // Bind pipeline and descriptor sets: { - vkCmdBindPipeline(g_CommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, g_Pipeline); - VkDescriptorSet desc_set[1] = {g_DescriptorSet}; - vkCmdBindDescriptorSets(g_CommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, g_PipelineLayout, 0, 1, desc_set, 0, NULL); + vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, g_Pipeline); + VkDescriptorSet desc_set[1] = { g_DescriptorSet }; + vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, g_PipelineLayout, 0, 1, desc_set, 0, NULL); } // Bind Vertex And Index Buffer: { - VkBuffer vertex_buffers[1] = {g_VertexBuffer[g_FrameIndex]}; - VkDeviceSize vertex_offset[1] = {0}; - vkCmdBindVertexBuffers(g_CommandBuffer, 0, 1, vertex_buffers, vertex_offset); - vkCmdBindIndexBuffer(g_CommandBuffer, g_IndexBuffer[g_FrameIndex], 0, VK_INDEX_TYPE_UINT16); + VkBuffer vertex_buffers[1] = { fd->VertexBuffer }; + VkDeviceSize vertex_offset[1] = { 0 }; + vkCmdBindVertexBuffers(command_buffer, 0, 1, vertex_buffers, vertex_offset); + vkCmdBindIndexBuffer(command_buffer, fd->IndexBuffer, 0, VK_INDEX_TYPE_UINT16); } // Setup viewport: @@ -272,7 +262,7 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data) viewport.height = io.DisplaySize.y; viewport.minDepth = 0.0f; viewport.maxDepth = 1.0f; - vkCmdSetViewport(g_CommandBuffer, 0, 1, &viewport); + vkCmdSetViewport(command_buffer, 0, 1, &viewport); } // Setup scale and translation: @@ -284,8 +274,8 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data) float translate[2]; translate[0] = -1.0f - io.DisplayPos.x * scale[0]; translate[1] = -1.0f - io.DisplayPos.y * scale[1]; - vkCmdPushConstants(g_CommandBuffer, g_PipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, sizeof(float) * 0, sizeof(float) * 2, scale); - vkCmdPushConstants(g_CommandBuffer, g_PipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, sizeof(float) * 2, sizeof(float) * 2, translate); + vkCmdPushConstants(command_buffer, g_PipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, sizeof(float) * 0, sizeof(float) * 2, scale); + vkCmdPushConstants(command_buffer, g_PipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, sizeof(float) * 2, sizeof(float) * 2, translate); } // Render the command lists: @@ -310,10 +300,10 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data) scissor.offset.y = (int32_t)(pcmd->ClipRect.y - io.DisplayPos.y) > 0 ? (int32_t)(pcmd->ClipRect.y - io.DisplayPos.y) : 0; scissor.extent.width = (uint32_t)(pcmd->ClipRect.z - pcmd->ClipRect.x); scissor.extent.height = (uint32_t)(pcmd->ClipRect.w - pcmd->ClipRect.y + 1); // FIXME: Why +1 here? - vkCmdSetScissor(g_CommandBuffer, 0, 1, &scissor); + vkCmdSetScissor(command_buffer, 0, 1, &scissor); // Draw - vkCmdDrawIndexed(g_CommandBuffer, pcmd->ElemCount, 1, idx_offset, vtx_offset, 0); + vkCmdDrawIndexed(command_buffer, pcmd->ElemCount, 1, idx_offset, vtx_offset, 0); } idx_offset += pcmd->ElemCount; } @@ -540,11 +530,12 @@ bool ImGui_ImplVulkan_CreateDeviceObjects() if (!g_PipelineLayout) { + // Constants: we are using 'vec2 offset' and 'vec2 scale' instead of a full 3d projection matrix VkPushConstantRange push_constants[1] = {}; push_constants[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; push_constants[0].offset = sizeof(float) * 0; push_constants[0].size = sizeof(float) * 4; - VkDescriptorSetLayout set_layout[1] = {g_DescriptorSetLayout}; + VkDescriptorSetLayout set_layout[1] = { g_DescriptorSetLayout }; VkPipelineLayoutCreateInfo layout_info = {}; layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; layout_info.setLayoutCount = 1; @@ -573,15 +564,15 @@ bool ImGui_ImplVulkan_CreateDeviceObjects() attribute_desc[0].location = 0; attribute_desc[0].binding = binding_desc[0].binding; attribute_desc[0].format = VK_FORMAT_R32G32_SFLOAT; - attribute_desc[0].offset = (size_t)(&((ImDrawVert*)0)->pos); + attribute_desc[0].offset = IM_OFFSETOF(ImDrawVert, pos); attribute_desc[1].location = 1; attribute_desc[1].binding = binding_desc[0].binding; attribute_desc[1].format = VK_FORMAT_R32G32_SFLOAT; - attribute_desc[1].offset = (size_t)(&((ImDrawVert*)0)->uv); + attribute_desc[1].offset = IM_OFFSETOF(ImDrawVert, uv); attribute_desc[2].location = 2; attribute_desc[2].binding = binding_desc[0].binding; attribute_desc[2].format = VK_FORMAT_R8G8B8A8_UNORM; - attribute_desc[2].offset = (size_t)(&((ImDrawVert*)0)->col); + attribute_desc[2].offset = IM_OFFSETOF(ImDrawVert, col); VkPipelineVertexInputStateCreateInfo vertex_info = {}; vertex_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; @@ -678,10 +669,11 @@ void ImGui_ImplVulkan_InvalidateDeviceObjects() for (int i = 0; i < IMGUI_VK_QUEUED_FRAMES; i++) { - if (g_VertexBuffer[i]) { vkDestroyBuffer(g_Device, g_VertexBuffer[i], g_Allocator); g_VertexBuffer[i] = VK_NULL_HANDLE; } - if (g_VertexBufferMemory[i]) { vkFreeMemory(g_Device, g_VertexBufferMemory[i], g_Allocator); g_VertexBufferMemory[i] = VK_NULL_HANDLE; } - if (g_IndexBuffer[i]) { vkDestroyBuffer(g_Device, g_IndexBuffer[i], g_Allocator); g_IndexBuffer[i] = VK_NULL_HANDLE; } - if (g_IndexBufferMemory[i]) { vkFreeMemory(g_Device, g_IndexBufferMemory[i], g_Allocator); g_IndexBufferMemory[i] = VK_NULL_HANDLE; } + FrameDataForRender* fd = &g_FramesDataBuffers[i]; + if (fd->VertexBuffer) { vkDestroyBuffer (g_Device, fd->VertexBuffer, g_Allocator); fd->VertexBuffer = VK_NULL_HANDLE; } + if (fd->VertexBufferMemory) { vkFreeMemory (g_Device, fd->VertexBufferMemory, g_Allocator); fd->VertexBufferMemory = VK_NULL_HANDLE; } + if (fd->IndexBuffer) { vkDestroyBuffer (g_Device, fd->IndexBuffer, g_Allocator); fd->IndexBuffer = VK_NULL_HANDLE; } + if (fd->IndexBufferMemory) { vkFreeMemory (g_Device, fd->IndexBufferMemory, g_Allocator); fd->IndexBufferMemory = VK_NULL_HANDLE; } } if (g_FontView) { vkDestroyImageView(g_Device, g_FontView, g_Allocator); g_FontView = VK_NULL_HANDLE; } @@ -723,10 +715,8 @@ void ImGui_ImplVulkan_NewFrame() void ImGui_ImplVulkan_Render(VkCommandBuffer command_buffer) { - g_CommandBuffer = command_buffer; ImGui::Render(); - ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData()); - g_CommandBuffer = VK_NULL_HANDLE; + ImGui_ImplVulkan_RenderDrawData(command_buffer, ImGui::GetDrawData()); g_FrameIndex = (g_FrameIndex + 1) % IMGUI_VK_QUEUED_FRAMES; } diff --git a/examples/imgui_impl_vulkan.h b/examples/imgui_impl_vulkan.h index 188f0e111d57..044795a2fad9 100644 --- a/examples/imgui_impl_vulkan.h +++ b/examples/imgui_impl_vulkan.h @@ -28,6 +28,7 @@ IMGUI_API bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo *init_data IMGUI_API void ImGui_ImplVulkan_Shutdown(); IMGUI_API void ImGui_ImplVulkan_NewFrame(); IMGUI_API void ImGui_ImplVulkan_Render(VkCommandBuffer command_buffer); +IMGUI_API void ImGui_ImplVulkan_RenderDrawData(VkCommandBuffer command_buffer, ImDrawData* draw_data); // Called by Init/NewFrame/Shutdown IMGUI_API void ImGui_ImplVulkan_InvalidateFontUploadObjects(); diff --git a/examples/sdl_vulkan_example/main.cpp b/examples/sdl_vulkan_example/main.cpp index 42d6c349a6fa..8d24b6bf605b 100644 --- a/examples/sdl_vulkan_example/main.cpp +++ b/examples/sdl_vulkan_example/main.cpp @@ -34,18 +34,22 @@ static VkPipelineCache g_PipelineCache = VK_NULL_HANDLE; static VkDescriptorPool g_DescriptorPool = VK_NULL_HANDLE; static int fb_width, fb_height; -static uint32_t g_BackbufferIndices[IMGUI_VK_QUEUED_FRAMES]; // keep track of recently rendered swapchain frame indices static uint32_t g_BackBufferCount = 0; static VkImage g_BackBuffer[IMGUI_MAX_POSSIBLE_BACK_BUFFERS] = {}; static VkImageView g_BackBufferView[IMGUI_MAX_POSSIBLE_BACK_BUFFERS] = {}; static VkFramebuffer g_Framebuffer[IMGUI_MAX_POSSIBLE_BACK_BUFFERS] = {}; +struct FrameData +{ + uint32_t BackbufferIndex; // keep track of recently rendered swapchain frame indices + VkCommandPool CommandPool; + VkCommandBuffer CommandBuffer; + VkFence Fence; + VkSemaphore PresentCompleteSemaphore; + VkSemaphore RenderCompleteSemaphore; +}; static uint32_t g_FrameIndex = 0; -static VkCommandPool g_CommandPool[IMGUI_VK_QUEUED_FRAMES]; -static VkCommandBuffer g_CommandBuffer[IMGUI_VK_QUEUED_FRAMES]; -static VkFence g_Fence[IMGUI_VK_QUEUED_FRAMES]; -static VkSemaphore g_PresentCompleteSemaphore[IMGUI_VK_QUEUED_FRAMES]; -static VkSemaphore g_RenderCompleteSemaphore[IMGUI_VK_QUEUED_FRAMES]; +static FrameData g_Frames[IMGUI_VK_QUEUED_FRAMES] = {}; static VkClearValue g_ClearValue = {}; @@ -295,6 +299,7 @@ static void setup_vulkan(SDL_Window* window, const char** extensions, uint32_t e } } + // Get Surface Format { const VkFormat requestSurfaceImageFormat[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM }; @@ -347,43 +352,44 @@ static void setup_vulkan(SDL_Window* window, const char** extensions, uint32_t e // Create Command Buffers for (int i = 0; i < IMGUI_VK_QUEUED_FRAMES; i++) { + FrameData* fd = &g_Frames[i]; { VkCommandPoolCreateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; info.queueFamilyIndex = g_QueueFamily; - err = vkCreateCommandPool(g_Device, &info, g_Allocator, &g_CommandPool[i]); + err = vkCreateCommandPool(g_Device, &info, g_Allocator, &fd->CommandPool); check_vk_result(err); } { VkCommandBufferAllocateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - info.commandPool = g_CommandPool[i]; + info.commandPool = fd->CommandPool; info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; info.commandBufferCount = 1; - err = vkAllocateCommandBuffers(g_Device, &info, &g_CommandBuffer[i]); + err = vkAllocateCommandBuffers(g_Device, &info, &fd->CommandBuffer); check_vk_result(err); } { VkFenceCreateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; info.flags = VK_FENCE_CREATE_SIGNALED_BIT; - err = vkCreateFence(g_Device, &info, g_Allocator, &g_Fence[i]); + err = vkCreateFence(g_Device, &info, g_Allocator, &fd->Fence); check_vk_result(err); } { VkSemaphoreCreateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - err = vkCreateSemaphore(g_Device, &info, g_Allocator, &g_PresentCompleteSemaphore[i]); + err = vkCreateSemaphore(g_Device, &info, g_Allocator, &fd->PresentCompleteSemaphore); check_vk_result(err); - err = vkCreateSemaphore(g_Device, &info, g_Allocator, &g_RenderCompleteSemaphore[i]); + err = vkCreateSemaphore(g_Device, &info, g_Allocator, &fd->RenderCompleteSemaphore); check_vk_result(err); } } // Create Descriptor Pool { - VkDescriptorPoolSize pool_size[11] = + VkDescriptorPoolSize pool_sizes[] = { { VK_DESCRIPTOR_TYPE_SAMPLER, 1000 }, { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000 }, @@ -400,9 +406,9 @@ static void setup_vulkan(SDL_Window* window, const char** extensions, uint32_t e VkDescriptorPoolCreateInfo pool_info = {}; pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT; - pool_info.maxSets = 1000 * 11; - pool_info.poolSizeCount = 11; - pool_info.pPoolSizes = pool_size; + pool_info.maxSets = 1000 * IM_ARRAYSIZE(pool_sizes); + pool_info.poolSizeCount = (uint32_t)IM_ARRAYSIZE(pool_sizes); + pool_info.pPoolSizes = pool_sizes; err = vkCreateDescriptorPool(g_Device, &pool_info, g_Allocator, &g_DescriptorPool); check_vk_result(err); } @@ -413,11 +419,12 @@ static void cleanup_vulkan() vkDestroyDescriptorPool(g_Device, g_DescriptorPool, g_Allocator); for (int i = 0; i < IMGUI_VK_QUEUED_FRAMES; i++) { - vkDestroyFence(g_Device, g_Fence[i], g_Allocator); - vkFreeCommandBuffers(g_Device, g_CommandPool[i], 1, &g_CommandBuffer[i]); - vkDestroyCommandPool(g_Device, g_CommandPool[i], g_Allocator); - vkDestroySemaphore(g_Device, g_PresentCompleteSemaphore[i], g_Allocator); - vkDestroySemaphore(g_Device, g_RenderCompleteSemaphore[i], g_Allocator); + FrameData* fd = &g_Frames[i]; + vkDestroyFence(g_Device, fd->Fence, g_Allocator); + vkFreeCommandBuffers(g_Device, fd->CommandPool, 1, &fd->CommandBuffer); + vkDestroyCommandPool(g_Device, fd->CommandPool, g_Allocator); + vkDestroySemaphore(g_Device, fd->PresentCompleteSemaphore, g_Allocator); + vkDestroySemaphore(g_Device, fd->RenderCompleteSemaphore, g_Allocator); } for (uint32_t i = 0; i < g_BackBufferCount; i++) { @@ -440,61 +447,63 @@ static void cleanup_vulkan() static void frame_begin() { + FrameData* fd = &g_Frames[g_FrameIndex]; VkResult err; for (;;) { - err = vkWaitForFences(g_Device, 1, &g_Fence[g_FrameIndex], VK_TRUE, 100); + err = vkWaitForFences(g_Device, 1, &fd->Fence, VK_TRUE, 100); if (err == VK_SUCCESS) break; if (err == VK_TIMEOUT) continue; check_vk_result(err); } { - err = vkAcquireNextImageKHR(g_Device, g_Swapchain, UINT64_MAX, g_PresentCompleteSemaphore[g_FrameIndex], VK_NULL_HANDLE, &g_BackbufferIndices[g_FrameIndex]); + err = vkAcquireNextImageKHR(g_Device, g_Swapchain, UINT64_MAX, fd->PresentCompleteSemaphore, VK_NULL_HANDLE, &fd->BackbufferIndex); check_vk_result(err); } { - err = vkResetCommandPool(g_Device, g_CommandPool[g_FrameIndex], 0); + err = vkResetCommandPool(g_Device, fd->CommandPool, 0); check_vk_result(err); VkCommandBufferBeginInfo info = {}; info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - err = vkBeginCommandBuffer(g_CommandBuffer[g_FrameIndex], &info); + err = vkBeginCommandBuffer(fd->CommandBuffer, &info); check_vk_result(err); } { VkRenderPassBeginInfo info = {}; info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; info.renderPass = g_RenderPass; - info.framebuffer = g_Framebuffer[g_BackbufferIndices[g_FrameIndex]]; + info.framebuffer = g_Framebuffer[fd->BackbufferIndex]; info.renderArea.extent.width = fb_width; info.renderArea.extent.height = fb_height; info.clearValueCount = 1; info.pClearValues = &g_ClearValue; - vkCmdBeginRenderPass(g_CommandBuffer[g_FrameIndex], &info, VK_SUBPASS_CONTENTS_INLINE); + vkCmdBeginRenderPass(fd->CommandBuffer, &info, VK_SUBPASS_CONTENTS_INLINE); } } static void frame_end() { + FrameData* fd = &g_Frames[g_FrameIndex]; VkResult err; - vkCmdEndRenderPass(g_CommandBuffer[g_FrameIndex]); + vkCmdEndRenderPass(fd->CommandBuffer); { VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; VkSubmitInfo info = {}; info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; info.waitSemaphoreCount = 1; - info.pWaitSemaphores = &g_PresentCompleteSemaphore[g_FrameIndex]; + info.pWaitSemaphores = &fd->PresentCompleteSemaphore; info.pWaitDstStageMask = &wait_stage; info.commandBufferCount = 1; - info.pCommandBuffers = &g_CommandBuffer[g_FrameIndex]; + info.pCommandBuffers = &fd->CommandBuffer; info.signalSemaphoreCount = 1; - info.pSignalSemaphores = &g_RenderCompleteSemaphore[g_FrameIndex]; + info.pSignalSemaphores = &fd->RenderCompleteSemaphore; - err = vkEndCommandBuffer(g_CommandBuffer[g_FrameIndex]); + err = vkEndCommandBuffer(fd->CommandBuffer); check_vk_result(err); - err = vkResetFences(g_Device, 1, &g_Fence[g_FrameIndex]); + err = vkResetFences(g_Device, 1, &fd->Fence); check_vk_result(err); - err = vkQueueSubmit(g_Queue, 1, &info, g_Fence[g_FrameIndex]); + err = vkQueueSubmit(g_Queue, 1, &info, fd->Fence); check_vk_result(err); } } @@ -509,15 +518,14 @@ static void frame_present() uint32_t PresentIndex = g_FrameIndex; #endif // IMGUI_UNLIMITED_FRAME_RATE - VkSwapchainKHR swapchains[1] = { g_Swapchain }; - uint32_t indices[1] = { g_BackbufferIndices[PresentIndex] }; + FrameData* fd = &g_Frames[PresentIndex]; VkPresentInfoKHR info = {}; info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; info.waitSemaphoreCount = 1; - info.pWaitSemaphores = &g_RenderCompleteSemaphore[PresentIndex]; + info.pWaitSemaphores = &fd->RenderCompleteSemaphore; info.swapchainCount = 1; - info.pSwapchains = swapchains; - info.pImageIndices = indices; + info.pSwapchains = &g_Swapchain; + info.pImageIndices = &fd->BackbufferIndex; err = vkQueuePresentKHR(g_Queue, &info); check_vk_result(err); @@ -584,22 +592,26 @@ int main(int, char**) // Upload Fonts { + // Use any command queue + VkCommandPool command_pool = g_Frames[g_FrameIndex].CommandPool; + VkCommandBuffer command_buffer = g_Frames[g_FrameIndex].CommandBuffer; + VkResult err; - err = vkResetCommandPool(g_Device, g_CommandPool[g_FrameIndex], 0); + err = vkResetCommandPool(g_Device, command_pool, 0); check_vk_result(err); VkCommandBufferBeginInfo begin_info = {}; begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; begin_info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - err = vkBeginCommandBuffer(g_CommandBuffer[g_FrameIndex], &begin_info); + err = vkBeginCommandBuffer(command_buffer, &begin_info); check_vk_result(err); - ImGui_ImplVulkan_CreateFontsTexture(g_CommandBuffer[g_FrameIndex]); + ImGui_ImplVulkan_CreateFontsTexture(command_buffer); VkSubmitInfo end_info = {}; end_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; end_info.commandBufferCount = 1; - end_info.pCommandBuffers = &g_CommandBuffer[g_FrameIndex]; - err = vkEndCommandBuffer(g_CommandBuffer[g_FrameIndex]); + end_info.pCommandBuffers = &command_buffer; + err = vkEndCommandBuffer(command_buffer); check_vk_result(err); err = vkQueueSubmit(g_Queue, 1, &end_info, VK_NULL_HANDLE); check_vk_result(err); @@ -620,7 +632,7 @@ int main(int, char**) ImGui_ImplVulkan_NewFrame(); ImGui_ImplSDL2_NewFrame(window); frame_begin(); - ImGui_ImplVulkan_Render(g_CommandBuffer[g_FrameIndex]); + ImGui_ImplVulkan_Render(g_Frames[g_FrameIndex].CommandBuffer); frame_end(); g_FrameIndex = (g_FrameIndex + 1) % IMGUI_VK_QUEUED_FRAMES; #endif // IMGUI_UNLIMITED_FRAME_RATE @@ -685,7 +697,7 @@ int main(int, char**) // Rendering memcpy(&g_ClearValue.color.float32[0], &clear_color, 4 * sizeof(float)); frame_begin(); - ImGui_ImplVulkan_Render(g_CommandBuffer[g_FrameIndex]); + ImGui_ImplVulkan_Render(g_Frames[g_FrameIndex].CommandBuffer); frame_end(); frame_present(); } diff --git a/examples/vulkan_example/main.cpp b/examples/vulkan_example/main.cpp index 2e6c8bbd81bc..a25592de8936 100644 --- a/examples/vulkan_example/main.cpp +++ b/examples/vulkan_example/main.cpp @@ -37,18 +37,22 @@ static VkPipelineCache g_PipelineCache = VK_NULL_HANDLE; static VkDescriptorPool g_DescriptorPool = VK_NULL_HANDLE; static int fb_width, fb_height; -static uint32_t g_BackbufferIndices[IMGUI_VK_QUEUED_FRAMES]; // keep track of recently rendered swapchain frame indices static uint32_t g_BackBufferCount = 0; static VkImage g_BackBuffer[IMGUI_MAX_POSSIBLE_BACK_BUFFERS] = {}; static VkImageView g_BackBufferView[IMGUI_MAX_POSSIBLE_BACK_BUFFERS] = {}; static VkFramebuffer g_Framebuffer[IMGUI_MAX_POSSIBLE_BACK_BUFFERS] = {}; +struct FrameData +{ + uint32_t BackbufferIndex; // keep track of recently rendered swapchain frame indices + VkCommandPool CommandPool; + VkCommandBuffer CommandBuffer; + VkFence Fence; + VkSemaphore PresentCompleteSemaphore; + VkSemaphore RenderCompleteSemaphore; +}; static uint32_t g_FrameIndex = 0; -static VkCommandPool g_CommandPool[IMGUI_VK_QUEUED_FRAMES]; -static VkCommandBuffer g_CommandBuffer[IMGUI_VK_QUEUED_FRAMES]; -static VkFence g_Fence[IMGUI_VK_QUEUED_FRAMES]; -static VkSemaphore g_PresentCompleteSemaphore[IMGUI_VK_QUEUED_FRAMES]; -static VkSemaphore g_RenderCompleteSemaphore[IMGUI_VK_QUEUED_FRAMES]; +static FrameData g_Frames[IMGUI_VK_QUEUED_FRAMES] = {}; static VkClearValue g_ClearValue = {}; @@ -353,43 +357,44 @@ static void setup_vulkan(GLFWwindow* window, const char** extensions, uint32_t e // Create Command Buffers for (int i = 0; i < IMGUI_VK_QUEUED_FRAMES; i++) { + FrameData* fd = &g_Frames[i]; { VkCommandPoolCreateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; info.queueFamilyIndex = g_QueueFamily; - err = vkCreateCommandPool(g_Device, &info, g_Allocator, &g_CommandPool[i]); + err = vkCreateCommandPool(g_Device, &info, g_Allocator, &fd->CommandPool); check_vk_result(err); } { VkCommandBufferAllocateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - info.commandPool = g_CommandPool[i]; + info.commandPool = fd->CommandPool; info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; info.commandBufferCount = 1; - err = vkAllocateCommandBuffers(g_Device, &info, &g_CommandBuffer[i]); + err = vkAllocateCommandBuffers(g_Device, &info, &fd->CommandBuffer); check_vk_result(err); } { VkFenceCreateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; info.flags = VK_FENCE_CREATE_SIGNALED_BIT; - err = vkCreateFence(g_Device, &info, g_Allocator, &g_Fence[i]); + err = vkCreateFence(g_Device, &info, g_Allocator, &fd->Fence); check_vk_result(err); } { VkSemaphoreCreateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - err = vkCreateSemaphore(g_Device, &info, g_Allocator, &g_PresentCompleteSemaphore[i]); + err = vkCreateSemaphore(g_Device, &info, g_Allocator, &fd->PresentCompleteSemaphore); check_vk_result(err); - err = vkCreateSemaphore(g_Device, &info, g_Allocator, &g_RenderCompleteSemaphore[i]); + err = vkCreateSemaphore(g_Device, &info, g_Allocator, &fd->RenderCompleteSemaphore); check_vk_result(err); } } // Create Descriptor Pool { - VkDescriptorPoolSize pool_size[11] = + VkDescriptorPoolSize pool_sizes[] = { { VK_DESCRIPTOR_TYPE_SAMPLER, 1000 }, { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000 }, @@ -406,9 +411,9 @@ static void setup_vulkan(GLFWwindow* window, const char** extensions, uint32_t e VkDescriptorPoolCreateInfo pool_info = {}; pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT; - pool_info.maxSets = 1000 * 11; - pool_info.poolSizeCount = 11; - pool_info.pPoolSizes = pool_size; + pool_info.maxSets = 1000 * IM_ARRAYSIZE(pool_sizes); + pool_info.poolSizeCount = (uint32_t)IM_ARRAYSIZE(pool_sizes); + pool_info.pPoolSizes = pool_sizes; err = vkCreateDescriptorPool(g_Device, &pool_info, g_Allocator, &g_DescriptorPool); check_vk_result(err); } @@ -419,11 +424,12 @@ static void cleanup_vulkan() vkDestroyDescriptorPool(g_Device, g_DescriptorPool, g_Allocator); for (int i = 0; i < IMGUI_VK_QUEUED_FRAMES; i++) { - vkDestroyFence(g_Device, g_Fence[i], g_Allocator); - vkFreeCommandBuffers(g_Device, g_CommandPool[i], 1, &g_CommandBuffer[i]); - vkDestroyCommandPool(g_Device, g_CommandPool[i], g_Allocator); - vkDestroySemaphore(g_Device, g_PresentCompleteSemaphore[i], g_Allocator); - vkDestroySemaphore(g_Device, g_RenderCompleteSemaphore[i], g_Allocator); + FrameData* fd = &g_Frames[i]; + vkDestroyFence(g_Device, fd->Fence, g_Allocator); + vkFreeCommandBuffers(g_Device, fd->CommandPool, 1, &fd->CommandBuffer); + vkDestroyCommandPool(g_Device, fd->CommandPool, g_Allocator); + vkDestroySemaphore(g_Device, fd->PresentCompleteSemaphore, g_Allocator); + vkDestroySemaphore(g_Device, fd->RenderCompleteSemaphore, g_Allocator); } for (uint32_t i = 0; i < g_BackBufferCount; i++) { @@ -446,61 +452,63 @@ static void cleanup_vulkan() static void frame_begin() { + FrameData* fd = &g_Frames[g_FrameIndex]; VkResult err; for (;;) { - err = vkWaitForFences(g_Device, 1, &g_Fence[g_FrameIndex], VK_TRUE, 100); + err = vkWaitForFences(g_Device, 1, &fd->Fence, VK_TRUE, 100); if (err == VK_SUCCESS) break; if (err == VK_TIMEOUT) continue; check_vk_result(err); } { - err = vkAcquireNextImageKHR(g_Device, g_Swapchain, UINT64_MAX, g_PresentCompleteSemaphore[g_FrameIndex], VK_NULL_HANDLE, &g_BackbufferIndices[g_FrameIndex]); + err = vkAcquireNextImageKHR(g_Device, g_Swapchain, UINT64_MAX, fd->PresentCompleteSemaphore, VK_NULL_HANDLE, &fd->BackbufferIndex); check_vk_result(err); } { - err = vkResetCommandPool(g_Device, g_CommandPool[g_FrameIndex], 0); + err = vkResetCommandPool(g_Device, fd->CommandPool, 0); check_vk_result(err); VkCommandBufferBeginInfo info = {}; info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - err = vkBeginCommandBuffer(g_CommandBuffer[g_FrameIndex], &info); + err = vkBeginCommandBuffer(fd->CommandBuffer, &info); check_vk_result(err); } { VkRenderPassBeginInfo info = {}; info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; info.renderPass = g_RenderPass; - info.framebuffer = g_Framebuffer[g_BackbufferIndices[g_FrameIndex]]; + info.framebuffer = g_Framebuffer[fd->BackbufferIndex]; info.renderArea.extent.width = fb_width; info.renderArea.extent.height = fb_height; info.clearValueCount = 1; info.pClearValues = &g_ClearValue; - vkCmdBeginRenderPass(g_CommandBuffer[g_FrameIndex], &info, VK_SUBPASS_CONTENTS_INLINE); + vkCmdBeginRenderPass(fd->CommandBuffer, &info, VK_SUBPASS_CONTENTS_INLINE); } } static void frame_end() { + FrameData* fd = &g_Frames[g_FrameIndex]; VkResult err; - vkCmdEndRenderPass(g_CommandBuffer[g_FrameIndex]); + vkCmdEndRenderPass(fd->CommandBuffer); { VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; VkSubmitInfo info = {}; info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; info.waitSemaphoreCount = 1; - info.pWaitSemaphores = &g_PresentCompleteSemaphore[g_FrameIndex]; + info.pWaitSemaphores = &fd->PresentCompleteSemaphore; info.pWaitDstStageMask = &wait_stage; info.commandBufferCount = 1; - info.pCommandBuffers = &g_CommandBuffer[g_FrameIndex]; + info.pCommandBuffers = &fd->CommandBuffer; info.signalSemaphoreCount = 1; - info.pSignalSemaphores = &g_RenderCompleteSemaphore[g_FrameIndex]; + info.pSignalSemaphores = &fd->RenderCompleteSemaphore; - err = vkEndCommandBuffer(g_CommandBuffer[g_FrameIndex]); + err = vkEndCommandBuffer(fd->CommandBuffer); check_vk_result(err); - err = vkResetFences(g_Device, 1, &g_Fence[g_FrameIndex]); + err = vkResetFences(g_Device, 1, &fd->Fence); check_vk_result(err); - err = vkQueueSubmit(g_Queue, 1, &info, g_Fence[g_FrameIndex]); + err = vkQueueSubmit(g_Queue, 1, &info, fd->Fence); check_vk_result(err); } } @@ -515,15 +523,14 @@ static void frame_present() uint32_t PresentIndex = g_FrameIndex; #endif // IMGUI_UNLIMITED_FRAME_RATE - VkSwapchainKHR swapchains[1] = { g_Swapchain }; - uint32_t indices[1] = { g_BackbufferIndices[PresentIndex] }; + FrameData* fd = &g_Frames[PresentIndex]; VkPresentInfoKHR info = {}; info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; info.waitSemaphoreCount = 1; - info.pWaitSemaphores = &g_RenderCompleteSemaphore[PresentIndex]; + info.pWaitSemaphores = &fd->RenderCompleteSemaphore; info.swapchainCount = 1; - info.pSwapchains = swapchains; - info.pImageIndices = indices; + info.pSwapchains = &g_Swapchain; + info.pImageIndices = &fd->BackbufferIndex; err = vkQueuePresentKHR(g_Queue, &info); check_vk_result(err); @@ -595,22 +602,26 @@ int main(int, char**) // Upload Fonts { + // Use any command queue + VkCommandPool command_pool = g_Frames[g_FrameIndex].CommandPool; + VkCommandBuffer command_buffer = g_Frames[g_FrameIndex].CommandBuffer; + VkResult err; - err = vkResetCommandPool(g_Device, g_CommandPool[g_FrameIndex], 0); + err = vkResetCommandPool(g_Device, command_pool, 0); check_vk_result(err); VkCommandBufferBeginInfo begin_info = {}; begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; begin_info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - err = vkBeginCommandBuffer(g_CommandBuffer[g_FrameIndex], &begin_info); + err = vkBeginCommandBuffer(command_buffer, &begin_info); check_vk_result(err); - ImGui_ImplVulkan_CreateFontsTexture(g_CommandBuffer[g_FrameIndex]); + ImGui_ImplVulkan_CreateFontsTexture(command_buffer); VkSubmitInfo end_info = {}; end_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; end_info.commandBufferCount = 1; - end_info.pCommandBuffers = &g_CommandBuffer[g_FrameIndex]; - err = vkEndCommandBuffer(g_CommandBuffer[g_FrameIndex]); + end_info.pCommandBuffers = &command_buffer; + err = vkEndCommandBuffer(command_buffer); check_vk_result(err); err = vkQueueSubmit(g_Queue, 1, &end_info, VK_NULL_HANDLE); check_vk_result(err); @@ -631,7 +642,7 @@ int main(int, char**) ImGui_ImplVulkan_NewFrame(); ImGui_ImplGlfw_NewFrame(); frame_begin(); - ImGui_ImplVulkan_Render(g_CommandBuffer[g_FrameIndex]); + ImGui_ImplVulkan_Render(g_Frames[g_FrameIndex].CommandBuffer); frame_end(); g_FrameIndex = (g_FrameIndex + 1) % IMGUI_VK_QUEUED_FRAMES; #endif // IMGUI_UNLIMITED_FRAME_RATE @@ -687,7 +698,7 @@ int main(int, char**) // Rendering memcpy(&g_ClearValue.color.float32[0], &clear_color, 4 * sizeof(float)); frame_begin(); - ImGui_ImplVulkan_Render(g_CommandBuffer[g_FrameIndex]); + ImGui_ImplVulkan_Render(g_Frames[g_FrameIndex].CommandBuffer); frame_end(); frame_present(); From 7b968b098eeb1e162504d42183ca6618568e859c Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 2 Mar 2018 19:59:34 +0100 Subject: [PATCH 049/828] Examples: Vulkan: Reduced duplicate code by skipping present on the first frame. Amend 201d589714c6ef7ff40c87140818a2208b8686b7 by @ParticlePeter --- examples/sdl_vulkan_example/main.cpp | 21 ++++++++++----------- examples/vulkan_example/main.cpp | 20 +++++++++----------- 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/examples/sdl_vulkan_example/main.cpp b/examples/sdl_vulkan_example/main.cpp index 8d24b6bf605b..e8e89ac24835 100644 --- a/examples/sdl_vulkan_example/main.cpp +++ b/examples/sdl_vulkan_example/main.cpp @@ -625,17 +625,7 @@ int main(int, char**) bool show_another_window = false; ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); - // When IMGUI_UNLIMITED_FRAME_RATE is defined we render into latest image acquired from the swapchain but we display the image which was rendered before. - // Hence we must render once and increase the g_FrameIndex without presenting, which we do before entering the render loop. - // This is also the reason why frame_end() is split into frame_end() and frame_present(), the later one not being called here. -#ifdef IMGUI_UNLIMITED_FRAME_RATE - ImGui_ImplVulkan_NewFrame(); - ImGui_ImplSDL2_NewFrame(window); - frame_begin(); - ImGui_ImplVulkan_Render(g_Frames[g_FrameIndex].CommandBuffer); - frame_end(); - g_FrameIndex = (g_FrameIndex + 1) % IMGUI_VK_QUEUED_FRAMES; -#endif // IMGUI_UNLIMITED_FRAME_RATE + bool swap_chain_has_at_least_one_image = false; // Main loop bool done = false; @@ -699,7 +689,16 @@ int main(int, char**) frame_begin(); ImGui_ImplVulkan_Render(g_Frames[g_FrameIndex].CommandBuffer); frame_end(); + + // When IMGUI_UNLIMITED_FRAME_RATE is defined we render into latest image acquired from the swapchain but we display the image which was rendered before. + // Hence we must render once and increase the g_FrameIndex without presenting, which we do before entering the render loop. +#ifdef IMGUI_UNLIMITED_FRAME_RATE + if (swap_chain_has_at_least_one_image) + frame_present(); +#else frame_present(); +#endif + swap_chain_has_at_least_one_image = true; } // Cleanup diff --git a/examples/vulkan_example/main.cpp b/examples/vulkan_example/main.cpp index a25592de8936..8ca526b92248 100644 --- a/examples/vulkan_example/main.cpp +++ b/examples/vulkan_example/main.cpp @@ -635,17 +635,7 @@ int main(int, char**) bool show_another_window = false; ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); - // When IMGUI_UNLIMITED_FRAME_RATE is defined we render into latest image acquired from the swapchain but we display the image which was rendered before. - // Hence we must render once and increase the g_FrameIndex without presenting, which we do before entering the render loop. - // This is also the reason why frame_end() is split into frame_end() and frame_present(), the later one not being called here. -#ifdef IMGUI_UNLIMITED_FRAME_RATE - ImGui_ImplVulkan_NewFrame(); - ImGui_ImplGlfw_NewFrame(); - frame_begin(); - ImGui_ImplVulkan_Render(g_Frames[g_FrameIndex].CommandBuffer); - frame_end(); - g_FrameIndex = (g_FrameIndex + 1) % IMGUI_VK_QUEUED_FRAMES; -#endif // IMGUI_UNLIMITED_FRAME_RATE + bool swap_chain_has_at_least_one_image = false; // Main loop while (!glfwWindowShouldClose(window)) @@ -700,7 +690,15 @@ int main(int, char**) frame_begin(); ImGui_ImplVulkan_Render(g_Frames[g_FrameIndex].CommandBuffer); frame_end(); + + // When IMGUI_UNLIMITED_FRAME_RATE is defined we render into latest image acquired from the swapchain but we display the image which was rendered before. + // Hence we must render once and increase the g_FrameIndex without presenting, which we do before entering the render loop. +#ifdef IMGUI_UNLIMITED_FRAME_RATE + if (swap_chain_has_at_least_one_image) + frame_present(); +#else frame_present(); +#endif ImGui::UpdatePlatformWindows(); ImGui::RenderPlatformWindows(); From e0f283cfcbbc6b04a6c045ddf61134f4c0e10e1d Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 2 Mar 2018 20:08:15 +0100 Subject: [PATCH 050/828] Examples: Vulkan: Fix 7b968b098eeb1e162504d42183ca6618568e859c --- examples/sdl_vulkan_example/main.cpp | 3 +-- examples/vulkan_example/main.cpp | 5 +++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/sdl_vulkan_example/main.cpp b/examples/sdl_vulkan_example/main.cpp index e8e89ac24835..9cab5c38b20f 100644 --- a/examples/sdl_vulkan_example/main.cpp +++ b/examples/sdl_vulkan_example/main.cpp @@ -528,8 +528,6 @@ static void frame_present() info.pImageIndices = &fd->BackbufferIndex; err = vkQueuePresentKHR(g_Queue, &info); check_vk_result(err); - - g_FrameIndex = (g_FrameIndex + 1) % IMGUI_VK_QUEUED_FRAMES; } int main(int, char**) @@ -699,6 +697,7 @@ int main(int, char**) frame_present(); #endif swap_chain_has_at_least_one_image = true; + g_FrameIndex = (g_FrameIndex + 1) % IMGUI_VK_QUEUED_FRAMES; } // Cleanup diff --git a/examples/vulkan_example/main.cpp b/examples/vulkan_example/main.cpp index 8ca526b92248..a030a2b0ebc1 100644 --- a/examples/vulkan_example/main.cpp +++ b/examples/vulkan_example/main.cpp @@ -533,8 +533,6 @@ static void frame_present() info.pImageIndices = &fd->BackbufferIndex; err = vkQueuePresentKHR(g_Queue, &info); check_vk_result(err); - - g_FrameIndex = (g_FrameIndex + 1) % IMGUI_VK_QUEUED_FRAMES; } static void glfw_error_callback(int error, const char* description) @@ -699,7 +697,10 @@ int main(int, char**) #else frame_present(); #endif + swap_chain_has_at_least_one_image = true; + g_FrameIndex = (g_FrameIndex + 1) % IMGUI_VK_QUEUED_FRAMES; + // FIXME-PLATFORM ImGui::UpdatePlatformWindows(); ImGui::RenderPlatformWindows(); } From 7113fc7dee21244420fdb77bddf6f77dfe6504fe Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 2 Mar 2018 21:21:52 +0100 Subject: [PATCH 051/828] Examples: Vulkan: Moved variables into per-window data, refactored/tweaks to that the bug functions main.cpp for both SDL and GLFW examples match (they'll probably be moved to imgui_impl_vulkan as helpers). --- examples/sdl_vulkan_example/main.cpp | 261 +++++++++++++++----------- examples/vulkan_example/main.cpp | 267 +++++++++++++++------------ 2 files changed, 301 insertions(+), 227 deletions(-) diff --git a/examples/sdl_vulkan_example/main.cpp b/examples/sdl_vulkan_example/main.cpp index 9cab5c38b20f..b494cf5cf7ec 100644 --- a/examples/sdl_vulkan_example/main.cpp +++ b/examples/sdl_vulkan_example/main.cpp @@ -18,27 +18,14 @@ static VkAllocationCallbacks* g_Allocator = NULL; static VkInstance g_Instance = VK_NULL_HANDLE; -static VkSurfaceKHR g_Surface = VK_NULL_HANDLE; static VkPhysicalDevice g_PhysicalDevice = VK_NULL_HANDLE; static VkDevice g_Device = VK_NULL_HANDLE; -static VkSwapchainKHR g_Swapchain = VK_NULL_HANDLE; -static VkRenderPass g_RenderPass = VK_NULL_HANDLE; static uint32_t g_QueueFamily = (uint32_t)-1; static VkQueue g_Queue = VK_NULL_HANDLE; static VkDebugReportCallbackEXT g_DebugReport = VK_NULL_HANDLE; - -static VkSurfaceFormatKHR g_SurfaceFormat; -static VkPresentModeKHR g_PresentMode; - static VkPipelineCache g_PipelineCache = VK_NULL_HANDLE; static VkDescriptorPool g_DescriptorPool = VK_NULL_HANDLE; -static int fb_width, fb_height; -static uint32_t g_BackBufferCount = 0; -static VkImage g_BackBuffer[IMGUI_MAX_POSSIBLE_BACK_BUFFERS] = {}; -static VkImageView g_BackBufferView[IMGUI_MAX_POSSIBLE_BACK_BUFFERS] = {}; -static VkFramebuffer g_Framebuffer[IMGUI_MAX_POSSIBLE_BACK_BUFFERS] = {}; - struct FrameData { uint32_t BackbufferIndex; // keep track of recently rendered swapchain frame indices @@ -47,11 +34,51 @@ struct FrameData VkFence Fence; VkSemaphore PresentCompleteSemaphore; VkSemaphore RenderCompleteSemaphore; + + FrameData() + { + BackbufferIndex = 0; + CommandPool = VK_NULL_HANDLE; + CommandBuffer = VK_NULL_HANDLE; + Fence = VK_NULL_HANDLE; + PresentCompleteSemaphore = VK_NULL_HANDLE; + RenderCompleteSemaphore = VK_NULL_HANDLE; + } }; -static uint32_t g_FrameIndex = 0; -static FrameData g_Frames[IMGUI_VK_QUEUED_FRAMES] = {}; -static VkClearValue g_ClearValue = {}; +struct WindowData +{ + int Width, Height; + VkSwapchainKHR Swapchain; + VkSurfaceKHR Surface; + VkSurfaceFormatKHR SurfaceFormat; + VkPresentModeKHR PresentMode; + VkRenderPass RenderPass; + VkClearValue ClearValue; + uint32_t BackBufferCount; + VkImage BackBuffer[IMGUI_MAX_POSSIBLE_BACK_BUFFERS]; + VkImageView BackBufferView[IMGUI_MAX_POSSIBLE_BACK_BUFFERS]; + VkFramebuffer Framebuffer[IMGUI_MAX_POSSIBLE_BACK_BUFFERS]; + uint32_t FrameIndex; + FrameData Frames[IMGUI_VK_QUEUED_FRAMES]; + + WindowData() + { + Width = Height = 0; + Swapchain = VK_NULL_HANDLE; + Surface = VK_NULL_HANDLE; + memset(&SurfaceFormat, 0, sizeof(SurfaceFormat)); + PresentMode = VK_PRESENT_MODE_MAX_ENUM_KHR; + RenderPass = VK_NULL_HANDLE; + memset(&ClearValue, 0, sizeof(ClearValue)); + BackBufferCount = 0; + memset(&BackBuffer, 0, sizeof(BackBuffer)); + memset(&BackBufferView, 0, sizeof(BackBufferView)); + memset(&Framebuffer, 0, sizeof(Framebuffer)); + FrameIndex = 0; + } +}; +static WindowData g_WindowData; static void check_vk_result(VkResult err) { @@ -61,41 +88,42 @@ static void check_vk_result(VkResult err) abort(); } -static void CreateOrResizeSwapChainAndFrameBuffer(int w, int h) +static void CreateOrResizeSwapChainAndFrameBuffer(WindowData* wd, int w, int h) { VkResult err; - VkSwapchainKHR old_swapchain = g_Swapchain; + VkSwapchainKHR old_swapchain = wd->Swapchain; err = vkDeviceWaitIdle(g_Device); check_vk_result(err); // Destroy old Framebuffer - for (uint32_t i = 0; i < g_BackBufferCount; i++) + for (uint32_t i = 0; i < wd->BackBufferCount; i++) { - if (g_BackBufferView[i]) - vkDestroyImageView(g_Device, g_BackBufferView[i], g_Allocator); - if (g_Framebuffer[i]) - vkDestroyFramebuffer(g_Device, g_Framebuffer[i], g_Allocator); + if (wd->BackBufferView[i]) + vkDestroyImageView(g_Device, wd->BackBufferView[i], g_Allocator); + if (wd->Framebuffer[i]) + vkDestroyFramebuffer(g_Device, wd->Framebuffer[i], g_Allocator); } - if (g_RenderPass) - vkDestroyRenderPass(g_Device, g_RenderPass, g_Allocator); + wd->BackBufferCount = 0; + if (wd->RenderPass) + vkDestroyRenderPass(g_Device, wd->RenderPass, g_Allocator); // Create Swapchain { VkSwapchainCreateInfoKHR info = {}; info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; - info.surface = g_Surface; - info.imageFormat = g_SurfaceFormat.format; - info.imageColorSpace = g_SurfaceFormat.colorSpace; + info.surface = wd->Surface; + info.imageFormat = wd->SurfaceFormat.format; + info.imageColorSpace = wd->SurfaceFormat.colorSpace; info.imageArrayLayers = 1; info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; // Assume that graphics family == present family info.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; - info.presentMode = g_PresentMode; + info.presentMode = wd->PresentMode; info.clipped = VK_TRUE; info.oldSwapchain = old_swapchain; VkSurfaceCapabilitiesKHR cap; - err = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(g_PhysicalDevice, g_Surface, &cap); + err = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(g_PhysicalDevice, wd->Surface, &cap); check_vk_result(err); if (cap.maxImageCount > 0) info.minImageCount = (cap.minImageCount + 2 < cap.maxImageCount) ? (cap.minImageCount + 2) : cap.maxImageCount; @@ -104,19 +132,19 @@ static void CreateOrResizeSwapChainAndFrameBuffer(int w, int h) if (cap.currentExtent.width == 0xffffffff) { - info.imageExtent.width = fb_width = w; - info.imageExtent.height = fb_height = h; + info.imageExtent.width = wd->Width = w; + info.imageExtent.height = wd->Height = h; } else { - info.imageExtent.width = fb_width = cap.currentExtent.width; - info.imageExtent.height = fb_height = cap.currentExtent.height; + info.imageExtent.width = wd->Width = cap.currentExtent.width; + info.imageExtent.height = wd->Height = cap.currentExtent.height; } - err = vkCreateSwapchainKHR(g_Device, &info, g_Allocator, &g_Swapchain); + err = vkCreateSwapchainKHR(g_Device, &info, g_Allocator, &wd->Swapchain); check_vk_result(err); - err = vkGetSwapchainImagesKHR(g_Device, g_Swapchain, &g_BackBufferCount, NULL); + err = vkGetSwapchainImagesKHR(g_Device, wd->Swapchain, &wd->BackBufferCount, NULL); check_vk_result(err); - err = vkGetSwapchainImagesKHR(g_Device, g_Swapchain, &g_BackBufferCount, g_BackBuffer); + err = vkGetSwapchainImagesKHR(g_Device, wd->Swapchain, &wd->BackBufferCount, wd->BackBuffer); check_vk_result(err); } if (old_swapchain) @@ -125,7 +153,7 @@ static void CreateOrResizeSwapChainAndFrameBuffer(int w, int h) // Create the Render Pass { VkAttachmentDescription attachment = {}; - attachment.format = g_SurfaceFormat.format; + attachment.format = wd->SurfaceFormat.format; attachment.samples = VK_SAMPLE_COUNT_1_BIT; attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; @@ -146,7 +174,7 @@ static void CreateOrResizeSwapChainAndFrameBuffer(int w, int h) info.pAttachments = &attachment; info.subpassCount = 1; info.pSubpasses = &subpass; - err = vkCreateRenderPass(g_Device, &info, g_Allocator, &g_RenderPass); + err = vkCreateRenderPass(g_Device, &info, g_Allocator, &wd->RenderPass); check_vk_result(err); } @@ -155,17 +183,17 @@ static void CreateOrResizeSwapChainAndFrameBuffer(int w, int h) VkImageViewCreateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; info.viewType = VK_IMAGE_VIEW_TYPE_2D; - info.format = g_SurfaceFormat.format; + info.format = wd->SurfaceFormat.format; info.components.r = VK_COMPONENT_SWIZZLE_R; info.components.g = VK_COMPONENT_SWIZZLE_G; info.components.b = VK_COMPONENT_SWIZZLE_B; info.components.a = VK_COMPONENT_SWIZZLE_A; VkImageSubresourceRange image_range = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; info.subresourceRange = image_range; - for (uint32_t i = 0; i < g_BackBufferCount; i++) + for (uint32_t i = 0; i < wd->BackBufferCount; i++) { - info.image = g_BackBuffer[i]; - err = vkCreateImageView(g_Device, &info, g_Allocator, &g_BackBufferView[i]); + info.image = wd->BackBuffer[i]; + err = vkCreateImageView(g_Device, &info, g_Allocator, &wd->BackBufferView[i]); check_vk_result(err); } } @@ -175,16 +203,16 @@ static void CreateOrResizeSwapChainAndFrameBuffer(int w, int h) VkImageView attachment[1]; VkFramebufferCreateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; - info.renderPass = g_RenderPass; + info.renderPass = wd->RenderPass; info.attachmentCount = 1; info.pAttachments = attachment; - info.width = fb_width; - info.height = fb_height; + info.width = wd->Width; + info.height = wd->Height; info.layers = 1; - for (uint32_t i = 0; i < g_BackBufferCount; i++) + for (uint32_t i = 0; i < wd->BackBufferCount; i++) { - attachment[0] = g_BackBufferView[i]; - err = vkCreateFramebuffer(g_Device, &info, g_Allocator, &g_Framebuffer[i]); + attachment[0] = wd->BackBufferView[i]; + err = vkCreateFramebuffer(g_Device, &info, g_Allocator, &wd->Framebuffer[i]); check_vk_result(err); } } @@ -199,7 +227,7 @@ static VKAPI_ATTR VkBool32 VKAPI_CALL debug_report(VkDebugReportFlagsEXT flags, } #endif // IMGUI_VULKAN_DEBUG_REPORT -static void setup_vulkan(SDL_Window* window, const char** extensions, uint32_t extensions_count) +static void CreateVulkanInstance(const char** extensions, uint32_t extensions_count) { VkResult err; @@ -244,16 +272,11 @@ static void setup_vulkan(SDL_Window* window, const char** extensions, uint32_t e check_vk_result(err); #endif // IMGUI_VULKAN_DEBUG_REPORT } +} - // Create Window Surface (with SDL) - { - SDL_bool result = SDL_Vulkan_CreateSurface(window, g_Instance, &g_Surface); - if (result == 0) - { - printf("failed to create Vulkan surface\n"); - abort(); - } - } +static void SetupVulkan(WindowData* wd) +{ + VkResult err; // Select GPU { @@ -291,7 +314,7 @@ static void setup_vulkan(SDL_Window* window, const char** extensions, uint32_t e // Check for WSI support { VkBool32 res; - vkGetPhysicalDeviceSurfaceSupportKHR(g_PhysicalDevice, g_QueueFamily, g_Surface, &res); + vkGetPhysicalDeviceSurfaceSupportKHR(g_PhysicalDevice, g_QueueFamily, wd->Surface, &res); if (res != VK_TRUE) { fprintf(stderr, "Error no WSI support on physical device 0\n"); @@ -304,7 +327,7 @@ static void setup_vulkan(SDL_Window* window, const char** extensions, uint32_t e { const VkFormat requestSurfaceImageFormat[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM }; const VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; - g_SurfaceFormat = ImGui_ImplVulkan_SelectSurfaceFormat(g_PhysicalDevice, g_Surface, requestSurfaceImageFormat, (size_t)IM_ARRAYSIZE(requestSurfaceImageFormat), requestSurfaceColorSpace); + wd->SurfaceFormat = ImGui_ImplVulkan_SelectSurfaceFormat(g_PhysicalDevice, wd->Surface, requestSurfaceImageFormat, (size_t)IM_ARRAYSIZE(requestSurfaceImageFormat), requestSurfaceColorSpace); } @@ -315,7 +338,7 @@ static void setup_vulkan(SDL_Window* window, const char** extensions, uint32_t e #else VkPresentModeKHR present_mode = VK_PRESENT_MODE_FIFO_KHR; #endif - g_PresentMode = ImGui_ImplVulkan_SelectPresentMode(g_PhysicalDevice, g_Surface, &present_mode, 1); + wd->PresentMode = ImGui_ImplVulkan_SelectPresentMode(g_PhysicalDevice, wd->Surface, &present_mode, 1); } @@ -340,19 +363,10 @@ static void setup_vulkan(SDL_Window* window, const char** extensions, uint32_t e vkGetDeviceQueue(g_Device, g_QueueFamily, 0, &g_Queue); } - - // Create Framebuffers - { - int w, h; - SDL_GetWindowSize(window, &w, &h); - CreateOrResizeSwapChainAndFrameBuffer(w, h); - } - - // Create Command Buffers for (int i = 0; i < IMGUI_VK_QUEUED_FRAMES; i++) { - FrameData* fd = &g_Frames[i]; + FrameData* fd = &wd->Frames[i]; { VkCommandPoolCreateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; @@ -416,24 +430,25 @@ static void setup_vulkan(SDL_Window* window, const char** extensions, uint32_t e static void cleanup_vulkan() { + WindowData* wd = &g_WindowData; vkDestroyDescriptorPool(g_Device, g_DescriptorPool, g_Allocator); for (int i = 0; i < IMGUI_VK_QUEUED_FRAMES; i++) { - FrameData* fd = &g_Frames[i]; + FrameData* fd = &wd->Frames[i]; vkDestroyFence(g_Device, fd->Fence, g_Allocator); vkFreeCommandBuffers(g_Device, fd->CommandPool, 1, &fd->CommandBuffer); vkDestroyCommandPool(g_Device, fd->CommandPool, g_Allocator); vkDestroySemaphore(g_Device, fd->PresentCompleteSemaphore, g_Allocator); vkDestroySemaphore(g_Device, fd->RenderCompleteSemaphore, g_Allocator); } - for (uint32_t i = 0; i < g_BackBufferCount; i++) + for (uint32_t i = 0; i < wd->BackBufferCount; i++) { - vkDestroyImageView(g_Device, g_BackBufferView[i], g_Allocator); - vkDestroyFramebuffer(g_Device, g_Framebuffer[i], g_Allocator); + vkDestroyImageView(g_Device, wd->BackBufferView[i], g_Allocator); + vkDestroyFramebuffer(g_Device, wd->Framebuffer[i], g_Allocator); } - vkDestroyRenderPass(g_Device, g_RenderPass, g_Allocator); - vkDestroySwapchainKHR(g_Device, g_Swapchain, g_Allocator); - vkDestroySurfaceKHR(g_Instance, g_Surface, g_Allocator); + vkDestroyRenderPass(g_Device, wd->RenderPass, g_Allocator); + vkDestroySwapchainKHR(g_Device, wd->Swapchain, g_Allocator); + vkDestroySurfaceKHR(g_Instance, wd->Surface, g_Allocator); #ifdef IMGUI_VULKAN_DEBUG_REPORT // Remove the debug report callback @@ -445,9 +460,9 @@ static void cleanup_vulkan() vkDestroyInstance(g_Instance, g_Allocator); } -static void frame_begin() +static void frame_begin(WindowData* wd) { - FrameData* fd = &g_Frames[g_FrameIndex]; + FrameData* fd = &wd->Frames[wd->FrameIndex]; VkResult err; for (;;) { @@ -457,7 +472,7 @@ static void frame_begin() check_vk_result(err); } { - err = vkAcquireNextImageKHR(g_Device, g_Swapchain, UINT64_MAX, fd->PresentCompleteSemaphore, VK_NULL_HANDLE, &fd->BackbufferIndex); + err = vkAcquireNextImageKHR(g_Device, wd->Swapchain, UINT64_MAX, fd->PresentCompleteSemaphore, VK_NULL_HANDLE, &fd->BackbufferIndex); check_vk_result(err); } { @@ -472,19 +487,19 @@ static void frame_begin() { VkRenderPassBeginInfo info = {}; info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; - info.renderPass = g_RenderPass; - info.framebuffer = g_Framebuffer[fd->BackbufferIndex]; - info.renderArea.extent.width = fb_width; - info.renderArea.extent.height = fb_height; + info.renderPass = wd->RenderPass; + info.framebuffer = wd->Framebuffer[fd->BackbufferIndex]; + info.renderArea.extent.width = wd->Width; + info.renderArea.extent.height = wd->Height; info.clearValueCount = 1; - info.pClearValues = &g_ClearValue; + info.pClearValues = &wd->ClearValue; vkCmdBeginRenderPass(fd->CommandBuffer, &info, VK_SUBPASS_CONTENTS_INLINE); } } -static void frame_end() +static void frame_end(WindowData* wd) { - FrameData* fd = &g_Frames[g_FrameIndex]; + FrameData* fd = &wd->Frames[wd->FrameIndex]; VkResult err; vkCmdEndRenderPass(fd->CommandBuffer); { @@ -508,23 +523,23 @@ static void frame_end() } } -static void frame_present() +static void frame_present(WindowData* wd) { VkResult err; // If IMGUI_UNLIMITED_FRAME_RATE is defined we present the latest but one frame. Otherwise we present the latest rendered frame #ifdef IMGUI_UNLIMITED_FRAME_RATE - uint32_t PresentIndex = (g_FrameIndex + IMGUI_VK_QUEUED_FRAMES - 1) % IMGUI_VK_QUEUED_FRAMES; + uint32_t PresentIndex = (wd->FrameIndex + IMGUI_VK_QUEUED_FRAMES - 1) % IMGUI_VK_QUEUED_FRAMES; #else uint32_t PresentIndex = g_FrameIndex; #endif // IMGUI_UNLIMITED_FRAME_RATE - FrameData* fd = &g_Frames[PresentIndex]; + FrameData* fd = &wd->Frames[PresentIndex]; VkPresentInfoKHR info = {}; info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; info.waitSemaphoreCount = 1; info.pWaitSemaphores = &fd->RenderCompleteSemaphore; info.swapchainCount = 1; - info.pSwapchains = &g_Swapchain; + info.pSwapchains = &wd->Swapchain; info.pImageIndices = &fd->BackbufferIndex; err = vkQueuePresentKHR(g_Queue, &info); check_vk_result(err); @@ -545,13 +560,31 @@ int main(int, char**) SDL_Window* window = SDL_CreateWindow("ImGui SDL2+Vulkan example", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, SDL_WINDOW_VULKAN|SDL_WINDOW_RESIZABLE); // Setup Vulkan - uint32_t extensions_count; - SDL_Vulkan_GetInstanceExtensions(window, &extensions_count, NULL); - const char** sdl_extensions = new const char*[extensions_count]; - SDL_Vulkan_GetInstanceExtensions(window, &extensions_count, sdl_extensions); - setup_vulkan(window, sdl_extensions, extensions_count); + uint32_t sdl_extensions_count; + SDL_Vulkan_GetInstanceExtensions(window, &sdl_extensions_count, NULL); + const char** sdl_extensions = new const char*[sdl_extensions_count]; + SDL_Vulkan_GetInstanceExtensions(window, &sdl_extensions_count, sdl_extensions); + CreateVulkanInstance(sdl_extensions, sdl_extensions_count); delete[] sdl_extensions; + // Create Window Surface + WindowData* wd = &g_WindowData; + SDL_bool result = SDL_Vulkan_CreateSurface(window, g_Instance, &wd->Surface); + if (result == 0) + { + printf("Failed to create Vulkan surface.\n"); + return 1; + } + + SetupVulkan(wd); + + // Create Framebuffers + { + int w, h; + SDL_GetWindowSize(window, &w, &h); + CreateOrResizeSwapChainAndFrameBuffer(wd, w, h); + } + // Setup ImGui binding ImGui::CreateContext(); @@ -559,13 +592,15 @@ int main(int, char**) init_info.Allocator = g_Allocator; init_info.PhysicalDevice = g_PhysicalDevice; init_info.Device = g_Device; - init_info.RenderPass = g_RenderPass; + init_info.RenderPass = wd->RenderPass; init_info.PipelineCache = g_PipelineCache; init_info.DescriptorPool = g_DescriptorPool; init_info.CheckVkResultFn = check_vk_result; ImGuiIO& io = ImGui::GetIO(); (void)io; + //io.ConfigFlags |= ImGuiConfigFlags_MultiViewports; //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + ImGui_ImplVulkan_Init(&init_info); ImGui_ImplSDL2_Init(window, NULL); @@ -591,8 +626,8 @@ int main(int, char**) // Upload Fonts { // Use any command queue - VkCommandPool command_pool = g_Frames[g_FrameIndex].CommandPool; - VkCommandBuffer command_buffer = g_Frames[g_FrameIndex].CommandBuffer; + VkCommandPool command_pool = wd->Frames[wd->FrameIndex].CommandPool; + VkCommandBuffer command_buffer = wd->Frames[wd->FrameIndex].CommandBuffer; VkResult err; err = vkResetCommandPool(g_Device, command_pool, 0); @@ -640,7 +675,7 @@ int main(int, char**) if (event.type == SDL_QUIT) done = true; if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_RESIZED && event.window.windowID == SDL_GetWindowID(window)) - CreateOrResizeSwapChainAndFrameBuffer((int)event.window.data1, (int)event.window.data2); + CreateOrResizeSwapChainAndFrameBuffer(wd, (int)event.window.data1, (int)event.window.data2); } ImGui_ImplVulkan_NewFrame(); ImGui_ImplSDL2_NewFrame(window); @@ -683,21 +718,25 @@ int main(int, char**) } // Rendering - memcpy(&g_ClearValue.color.float32[0], &clear_color, 4 * sizeof(float)); - frame_begin(); - ImGui_ImplVulkan_Render(g_Frames[g_FrameIndex].CommandBuffer); - frame_end(); + memcpy(&wd->ClearValue.color.float32[0], &clear_color, 4 * sizeof(float)); + frame_begin(wd); + ImGui_ImplVulkan_Render(wd->Frames[wd->FrameIndex].CommandBuffer); + frame_end(wd); - // When IMGUI_UNLIMITED_FRAME_RATE is defined we render into latest image acquired from the swapchain but we display the image which was rendered before. - // Hence we must render once and increase the g_FrameIndex without presenting, which we do before entering the render loop. #ifdef IMGUI_UNLIMITED_FRAME_RATE + // When IMGUI_UNLIMITED_FRAME_RATE is defined we render into latest image acquired from the swapchain but we display the image which was rendered before. + // Hence we must render once and increase the FrameIndex without presenting. if (swap_chain_has_at_least_one_image) - frame_present(); + frame_present(wd); #else - frame_present(); + frame_present(wd); #endif swap_chain_has_at_least_one_image = true; - g_FrameIndex = (g_FrameIndex + 1) % IMGUI_VK_QUEUED_FRAMES; + wd->FrameIndex = (wd->FrameIndex + 1) % IMGUI_VK_QUEUED_FRAMES; + + // FIXME-PLATFORM + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindows(); } // Cleanup diff --git a/examples/vulkan_example/main.cpp b/examples/vulkan_example/main.cpp index a030a2b0ebc1..bc05919f20cf 100644 --- a/examples/vulkan_example/main.cpp +++ b/examples/vulkan_example/main.cpp @@ -21,40 +21,67 @@ static VkAllocationCallbacks* g_Allocator = NULL; static VkInstance g_Instance = VK_NULL_HANDLE; -static VkSurfaceKHR g_Surface = VK_NULL_HANDLE; static VkPhysicalDevice g_PhysicalDevice = VK_NULL_HANDLE; static VkDevice g_Device = VK_NULL_HANDLE; -static VkSwapchainKHR g_Swapchain = VK_NULL_HANDLE; -static VkRenderPass g_RenderPass = VK_NULL_HANDLE; static uint32_t g_QueueFamily = (uint32_t)-1; static VkQueue g_Queue = VK_NULL_HANDLE; static VkDebugReportCallbackEXT g_DebugReport = VK_NULL_HANDLE; - -static VkSurfaceFormatKHR g_SurfaceFormat; -static VkPresentModeKHR g_PresentMode; - static VkPipelineCache g_PipelineCache = VK_NULL_HANDLE; static VkDescriptorPool g_DescriptorPool = VK_NULL_HANDLE; -static int fb_width, fb_height; -static uint32_t g_BackBufferCount = 0; -static VkImage g_BackBuffer[IMGUI_MAX_POSSIBLE_BACK_BUFFERS] = {}; -static VkImageView g_BackBufferView[IMGUI_MAX_POSSIBLE_BACK_BUFFERS] = {}; -static VkFramebuffer g_Framebuffer[IMGUI_MAX_POSSIBLE_BACK_BUFFERS] = {}; - struct FrameData { - uint32_t BackbufferIndex; // keep track of recently rendered swapchain frame indices - VkCommandPool CommandPool; - VkCommandBuffer CommandBuffer; - VkFence Fence; - VkSemaphore PresentCompleteSemaphore; - VkSemaphore RenderCompleteSemaphore; + uint32_t BackbufferIndex; // keep track of recently rendered swapchain frame indices + VkCommandPool CommandPool; + VkCommandBuffer CommandBuffer; + VkFence Fence; + VkSemaphore PresentCompleteSemaphore; + VkSemaphore RenderCompleteSemaphore; + + FrameData() + { + BackbufferIndex = 0; + CommandPool = VK_NULL_HANDLE; + CommandBuffer = VK_NULL_HANDLE; + Fence = VK_NULL_HANDLE; + PresentCompleteSemaphore = VK_NULL_HANDLE; + RenderCompleteSemaphore = VK_NULL_HANDLE; + } }; -static uint32_t g_FrameIndex = 0; -static FrameData g_Frames[IMGUI_VK_QUEUED_FRAMES] = {}; -static VkClearValue g_ClearValue = {}; +struct WindowData +{ + int Width, Height; + VkSwapchainKHR Swapchain; + VkSurfaceKHR Surface; + VkSurfaceFormatKHR SurfaceFormat; + VkPresentModeKHR PresentMode; + VkRenderPass RenderPass; + VkClearValue ClearValue; + uint32_t BackBufferCount; + VkImage BackBuffer[IMGUI_MAX_POSSIBLE_BACK_BUFFERS]; + VkImageView BackBufferView[IMGUI_MAX_POSSIBLE_BACK_BUFFERS]; + VkFramebuffer Framebuffer[IMGUI_MAX_POSSIBLE_BACK_BUFFERS]; + uint32_t FrameIndex; + FrameData Frames[IMGUI_VK_QUEUED_FRAMES]; + + WindowData() + { + Width = Height = 0; + Swapchain = VK_NULL_HANDLE; + Surface = VK_NULL_HANDLE; + memset(&SurfaceFormat, 0, sizeof(SurfaceFormat)); + PresentMode = VK_PRESENT_MODE_MAX_ENUM_KHR; + RenderPass = VK_NULL_HANDLE; + memset(&ClearValue, 0, sizeof(ClearValue)); + BackBufferCount = 0; + memset(&BackBuffer, 0, sizeof(BackBuffer)); + memset(&BackBufferView, 0, sizeof(BackBufferView)); + memset(&Framebuffer, 0, sizeof(Framebuffer)); + FrameIndex = 0; + } +}; +static WindowData g_WindowData; static void check_vk_result(VkResult err) { @@ -64,41 +91,42 @@ static void check_vk_result(VkResult err) abort(); } -static void CreateOrResizeSwapChainAndFrameBuffer(int w, int h) +static void CreateOrResizeSwapChainAndFrameBuffer(WindowData* wd, int w, int h) { VkResult err; - VkSwapchainKHR old_swapchain = g_Swapchain; + VkSwapchainKHR old_swapchain = wd->Swapchain; err = vkDeviceWaitIdle(g_Device); check_vk_result(err); // Destroy old Framebuffer - for (uint32_t i = 0; i < g_BackBufferCount; i++) + for (uint32_t i = 0; i < wd->BackBufferCount; i++) { - if (g_BackBufferView[i]) - vkDestroyImageView(g_Device, g_BackBufferView[i], g_Allocator); - if (g_Framebuffer[i]) - vkDestroyFramebuffer(g_Device, g_Framebuffer[i], g_Allocator); + if (wd->BackBufferView[i]) + vkDestroyImageView(g_Device, wd->BackBufferView[i], g_Allocator); + if (wd->Framebuffer[i]) + vkDestroyFramebuffer(g_Device, wd->Framebuffer[i], g_Allocator); } - if (g_RenderPass) - vkDestroyRenderPass(g_Device, g_RenderPass, g_Allocator); + wd->BackBufferCount = 0; + if (wd->RenderPass) + vkDestroyRenderPass(g_Device, wd->RenderPass, g_Allocator); // Create Swapchain { VkSwapchainCreateInfoKHR info = {}; info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; - info.surface = g_Surface; - info.imageFormat = g_SurfaceFormat.format; - info.imageColorSpace = g_SurfaceFormat.colorSpace; + info.surface = wd->Surface; + info.imageFormat = wd->SurfaceFormat.format; + info.imageColorSpace = wd->SurfaceFormat.colorSpace; info.imageArrayLayers = 1; info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; // Assume that graphics family == present family info.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; - info.presentMode = g_PresentMode; + info.presentMode = wd->PresentMode; info.clipped = VK_TRUE; info.oldSwapchain = old_swapchain; VkSurfaceCapabilitiesKHR cap; - err = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(g_PhysicalDevice, g_Surface, &cap); + err = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(g_PhysicalDevice, wd->Surface, &cap); check_vk_result(err); if (cap.maxImageCount > 0) info.minImageCount = (cap.minImageCount + 2 < cap.maxImageCount) ? (cap.minImageCount + 2) : cap.maxImageCount; @@ -107,19 +135,19 @@ static void CreateOrResizeSwapChainAndFrameBuffer(int w, int h) if (cap.currentExtent.width == 0xffffffff) { - info.imageExtent.width = fb_width = w; - info.imageExtent.height = fb_height = h; + info.imageExtent.width = wd->Width = w; + info.imageExtent.height = wd->Height = h; } else { - info.imageExtent.width = fb_width = cap.currentExtent.width; - info.imageExtent.height = fb_height = cap.currentExtent.height; + info.imageExtent.width = wd->Width = cap.currentExtent.width; + info.imageExtent.height = wd->Height = cap.currentExtent.height; } - err = vkCreateSwapchainKHR(g_Device, &info, g_Allocator, &g_Swapchain); + err = vkCreateSwapchainKHR(g_Device, &info, g_Allocator, &wd->Swapchain); check_vk_result(err); - err = vkGetSwapchainImagesKHR(g_Device, g_Swapchain, &g_BackBufferCount, NULL); + err = vkGetSwapchainImagesKHR(g_Device, wd->Swapchain, &wd->BackBufferCount, NULL); check_vk_result(err); - err = vkGetSwapchainImagesKHR(g_Device, g_Swapchain, &g_BackBufferCount, g_BackBuffer); + err = vkGetSwapchainImagesKHR(g_Device, wd->Swapchain, &wd->BackBufferCount, wd->BackBuffer); check_vk_result(err); } if (old_swapchain) @@ -128,7 +156,7 @@ static void CreateOrResizeSwapChainAndFrameBuffer(int w, int h) // Create the Render Pass { VkAttachmentDescription attachment = {}; - attachment.format = g_SurfaceFormat.format; + attachment.format = wd->SurfaceFormat.format; attachment.samples = VK_SAMPLE_COUNT_1_BIT; attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; @@ -149,7 +177,7 @@ static void CreateOrResizeSwapChainAndFrameBuffer(int w, int h) info.pAttachments = &attachment; info.subpassCount = 1; info.pSubpasses = &subpass; - err = vkCreateRenderPass(g_Device, &info, g_Allocator, &g_RenderPass); + err = vkCreateRenderPass(g_Device, &info, g_Allocator, &wd->RenderPass); check_vk_result(err); } @@ -158,17 +186,17 @@ static void CreateOrResizeSwapChainAndFrameBuffer(int w, int h) VkImageViewCreateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; info.viewType = VK_IMAGE_VIEW_TYPE_2D; - info.format = g_SurfaceFormat.format; + info.format = wd->SurfaceFormat.format; info.components.r = VK_COMPONENT_SWIZZLE_R; info.components.g = VK_COMPONENT_SWIZZLE_G; info.components.b = VK_COMPONENT_SWIZZLE_B; info.components.a = VK_COMPONENT_SWIZZLE_A; VkImageSubresourceRange image_range = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; info.subresourceRange = image_range; - for (uint32_t i = 0; i < g_BackBufferCount; i++) + for (uint32_t i = 0; i < wd->BackBufferCount; i++) { - info.image = g_BackBuffer[i]; - err = vkCreateImageView(g_Device, &info, g_Allocator, &g_BackBufferView[i]); + info.image = wd->BackBuffer[i]; + err = vkCreateImageView(g_Device, &info, g_Allocator, &wd->BackBufferView[i]); check_vk_result(err); } } @@ -178,26 +206,21 @@ static void CreateOrResizeSwapChainAndFrameBuffer(int w, int h) VkImageView attachment[1]; VkFramebufferCreateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; - info.renderPass = g_RenderPass; + info.renderPass = wd->RenderPass; info.attachmentCount = 1; info.pAttachments = attachment; - info.width = fb_width; - info.height = fb_height; + info.width = wd->Width; + info.height = wd->Height; info.layers = 1; - for (uint32_t i = 0; i < g_BackBufferCount; i++) + for (uint32_t i = 0; i < wd->BackBufferCount; i++) { - attachment[0] = g_BackBufferView[i]; - err = vkCreateFramebuffer(g_Device, &info, g_Allocator, &g_Framebuffer[i]); + attachment[0] = wd->BackBufferView[i]; + err = vkCreateFramebuffer(g_Device, &info, g_Allocator, &wd->Framebuffer[i]); check_vk_result(err); } } } -static void GlfwResizeCallback(GLFWwindow*, int w, int h) -{ - CreateOrResizeSwapChainAndFrameBuffer(w, h); -} - #ifdef IMGUI_VULKAN_DEBUG_REPORT static VKAPI_ATTR VkBool32 VKAPI_CALL debug_report(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char* pLayerPrefix, const char* pMessage, void* pUserData) { @@ -207,7 +230,7 @@ static VKAPI_ATTR VkBool32 VKAPI_CALL debug_report(VkDebugReportFlagsEXT flags, } #endif // IMGUI_VULKAN_DEBUG_REPORT -static void setup_vulkan(GLFWwindow* window, const char** extensions, uint32_t extensions_count) +static void CreateVulkanInstance(const char** extensions, uint32_t extensions_count) { VkResult err; @@ -252,12 +275,11 @@ static void setup_vulkan(GLFWwindow* window, const char** extensions, uint32_t e check_vk_result(err); #endif // IMGUI_VULKAN_DEBUG_REPORT } +} - // Create Window Surface (with GLFW) - { - err = glfwCreateWindowSurface(g_Instance, window, g_Allocator, &g_Surface); - check_vk_result(err); - } +static void SetupVulkan(WindowData* wd) +{ + VkResult err; // Select GPU { @@ -295,7 +317,7 @@ static void setup_vulkan(GLFWwindow* window, const char** extensions, uint32_t e // Check for WSI support { VkBool32 res; - vkGetPhysicalDeviceSurfaceSupportKHR(g_PhysicalDevice, g_QueueFamily, g_Surface, &res); + vkGetPhysicalDeviceSurfaceSupportKHR(g_PhysicalDevice, g_QueueFamily, wd->Surface, &res); if (res != VK_TRUE) { fprintf(stderr, "Error no WSI support on physical device 0\n"); @@ -308,7 +330,7 @@ static void setup_vulkan(GLFWwindow* window, const char** extensions, uint32_t e { const VkFormat requestSurfaceImageFormat[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM }; const VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; - g_SurfaceFormat = ImGui_ImplVulkan_SelectSurfaceFormat(g_PhysicalDevice, g_Surface, requestSurfaceImageFormat, (size_t)IM_ARRAYSIZE(requestSurfaceImageFormat), requestSurfaceColorSpace); + wd->SurfaceFormat = ImGui_ImplVulkan_SelectSurfaceFormat(g_PhysicalDevice, wd->Surface, requestSurfaceImageFormat, (size_t)IM_ARRAYSIZE(requestSurfaceImageFormat), requestSurfaceColorSpace); } @@ -319,7 +341,7 @@ static void setup_vulkan(GLFWwindow* window, const char** extensions, uint32_t e #else VkPresentModeKHR present_mode = VK_PRESENT_MODE_FIFO_KHR; #endif - g_PresentMode = ImGui_ImplVulkan_SelectPresentMode(g_PhysicalDevice, g_Surface, &present_mode, 1); + wd->PresentMode = ImGui_ImplVulkan_SelectPresentMode(g_PhysicalDevice, wd->Surface, &present_mode, 1); } @@ -344,20 +366,10 @@ static void setup_vulkan(GLFWwindow* window, const char** extensions, uint32_t e vkGetDeviceQueue(g_Device, g_QueueFamily, 0, &g_Queue); } - - // Create Framebuffers - { - int w, h; - glfwGetFramebufferSize(window, &w, &h); - CreateOrResizeSwapChainAndFrameBuffer(w, h); - glfwSetFramebufferSizeCallback(window, GlfwResizeCallback); - } - - // Create Command Buffers for (int i = 0; i < IMGUI_VK_QUEUED_FRAMES; i++) { - FrameData* fd = &g_Frames[i]; + FrameData* fd = &wd->Frames[i]; { VkCommandPoolCreateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; @@ -421,24 +433,25 @@ static void setup_vulkan(GLFWwindow* window, const char** extensions, uint32_t e static void cleanup_vulkan() { + WindowData* wd = &g_WindowData; vkDestroyDescriptorPool(g_Device, g_DescriptorPool, g_Allocator); for (int i = 0; i < IMGUI_VK_QUEUED_FRAMES; i++) { - FrameData* fd = &g_Frames[i]; + FrameData* fd = &wd->Frames[i]; vkDestroyFence(g_Device, fd->Fence, g_Allocator); vkFreeCommandBuffers(g_Device, fd->CommandPool, 1, &fd->CommandBuffer); vkDestroyCommandPool(g_Device, fd->CommandPool, g_Allocator); vkDestroySemaphore(g_Device, fd->PresentCompleteSemaphore, g_Allocator); vkDestroySemaphore(g_Device, fd->RenderCompleteSemaphore, g_Allocator); } - for (uint32_t i = 0; i < g_BackBufferCount; i++) + for (uint32_t i = 0; i < wd->BackBufferCount; i++) { - vkDestroyImageView(g_Device, g_BackBufferView[i], g_Allocator); - vkDestroyFramebuffer(g_Device, g_Framebuffer[i], g_Allocator); + vkDestroyImageView(g_Device, wd->BackBufferView[i], g_Allocator); + vkDestroyFramebuffer(g_Device, wd->Framebuffer[i], g_Allocator); } - vkDestroyRenderPass(g_Device, g_RenderPass, g_Allocator); - vkDestroySwapchainKHR(g_Device, g_Swapchain, g_Allocator); - vkDestroySurfaceKHR(g_Instance, g_Surface, g_Allocator); + vkDestroyRenderPass(g_Device, wd->RenderPass, g_Allocator); + vkDestroySwapchainKHR(g_Device, wd->Swapchain, g_Allocator); + vkDestroySurfaceKHR(g_Instance, wd->Surface, g_Allocator); #ifdef IMGUI_VULKAN_DEBUG_REPORT // Remove the debug report callback @@ -450,9 +463,9 @@ static void cleanup_vulkan() vkDestroyInstance(g_Instance, g_Allocator); } -static void frame_begin() +static void frame_begin(WindowData* wd) { - FrameData* fd = &g_Frames[g_FrameIndex]; + FrameData* fd = &wd->Frames[wd->FrameIndex]; VkResult err; for (;;) { @@ -462,7 +475,7 @@ static void frame_begin() check_vk_result(err); } { - err = vkAcquireNextImageKHR(g_Device, g_Swapchain, UINT64_MAX, fd->PresentCompleteSemaphore, VK_NULL_HANDLE, &fd->BackbufferIndex); + err = vkAcquireNextImageKHR(g_Device, wd->Swapchain, UINT64_MAX, fd->PresentCompleteSemaphore, VK_NULL_HANDLE, &fd->BackbufferIndex); check_vk_result(err); } { @@ -477,19 +490,19 @@ static void frame_begin() { VkRenderPassBeginInfo info = {}; info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; - info.renderPass = g_RenderPass; - info.framebuffer = g_Framebuffer[fd->BackbufferIndex]; - info.renderArea.extent.width = fb_width; - info.renderArea.extent.height = fb_height; + info.renderPass = wd->RenderPass; + info.framebuffer = wd->Framebuffer[fd->BackbufferIndex]; + info.renderArea.extent.width = wd->Width; + info.renderArea.extent.height = wd->Height; info.clearValueCount = 1; - info.pClearValues = &g_ClearValue; + info.pClearValues = &wd->ClearValue; vkCmdBeginRenderPass(fd->CommandBuffer, &info, VK_SUBPASS_CONTENTS_INLINE); } } -static void frame_end() +static void frame_end(WindowData* wd) { - FrameData* fd = &g_Frames[g_FrameIndex]; + FrameData* fd = &wd->Frames[wd->FrameIndex]; VkResult err; vkCmdEndRenderPass(fd->CommandBuffer); { @@ -513,23 +526,23 @@ static void frame_end() } } -static void frame_present() +static void frame_present(WindowData* wd) { VkResult err; // If IMGUI_UNLIMITED_FRAME_RATE is defined we present the latest but one frame. Otherwise we present the latest rendered frame #ifdef IMGUI_UNLIMITED_FRAME_RATE - uint32_t PresentIndex = (g_FrameIndex + IMGUI_VK_QUEUED_FRAMES - 1) % IMGUI_VK_QUEUED_FRAMES; + uint32_t PresentIndex = (wd->FrameIndex + IMGUI_VK_QUEUED_FRAMES - 1) % IMGUI_VK_QUEUED_FRAMES; #else uint32_t PresentIndex = g_FrameIndex; #endif // IMGUI_UNLIMITED_FRAME_RATE - FrameData* fd = &g_Frames[PresentIndex]; + FrameData* fd = &wd->Frames[PresentIndex]; VkPresentInfoKHR info = {}; info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; info.waitSemaphoreCount = 1; info.pWaitSemaphores = &fd->RenderCompleteSemaphore; info.swapchainCount = 1; - info.pSwapchains = &g_Swapchain; + info.pSwapchains = &wd->Swapchain; info.pImageIndices = &fd->BackbufferIndex; err = vkQueuePresentKHR(g_Queue, &info); check_vk_result(err); @@ -540,6 +553,11 @@ static void glfw_error_callback(int error, const char* description) fprintf(stderr, "Glfw Error %d: %s\n", error, description); } +static void glfw_resize_callback(GLFWwindow*, int w, int h) +{ + CreateOrResizeSwapChainAndFrameBuffer(&g_WindowData, w, h); +} + int main(int, char**) { // Setup window @@ -556,9 +574,26 @@ int main(int, char**) printf("GLFW: Vulkan Not Supported\n"); return 1; } - uint32_t extensions_count = 0; - const char** glfw_extensions = glfwGetRequiredInstanceExtensions(&extensions_count); - setup_vulkan(window, glfw_extensions, extensions_count); + uint32_t glfw_extensions_count = 0; + const char** glfw_extensions = glfwGetRequiredInstanceExtensions(&glfw_extensions_count); + CreateVulkanInstance(glfw_extensions, glfw_extensions_count); + + // Create Window Surface + WindowData* wd = &g_WindowData; + { + VkResult err = glfwCreateWindowSurface(g_Instance, window, g_Allocator, &wd->Surface); + check_vk_result(err); + } + + SetupVulkan(wd); + + // Create Framebuffers + { + int w, h; + glfwGetFramebufferSize(window, &w, &h); + CreateOrResizeSwapChainAndFrameBuffer(wd, w, h); + glfwSetFramebufferSizeCallback(window, glfw_resize_callback); + } // Setup ImGui binding ImGui::CreateContext(); @@ -567,7 +602,7 @@ int main(int, char**) init_info.Allocator = g_Allocator; init_info.PhysicalDevice = g_PhysicalDevice; init_info.Device = g_Device; - init_info.RenderPass = g_RenderPass; + init_info.RenderPass = wd->RenderPass; init_info.PipelineCache = g_PipelineCache; init_info.DescriptorPool = g_DescriptorPool; init_info.CheckVkResultFn = check_vk_result; @@ -601,8 +636,8 @@ int main(int, char**) // Upload Fonts { // Use any command queue - VkCommandPool command_pool = g_Frames[g_FrameIndex].CommandPool; - VkCommandBuffer command_buffer = g_Frames[g_FrameIndex].CommandBuffer; + VkCommandPool command_pool = wd->Frames[wd->FrameIndex].CommandPool; + VkCommandBuffer command_buffer = wd->Frames[wd->FrameIndex].CommandBuffer; VkResult err; err = vkResetCommandPool(g_Device, command_pool, 0); @@ -684,21 +719,21 @@ int main(int, char**) } // Rendering - memcpy(&g_ClearValue.color.float32[0], &clear_color, 4 * sizeof(float)); - frame_begin(); - ImGui_ImplVulkan_Render(g_Frames[g_FrameIndex].CommandBuffer); - frame_end(); + memcpy(&wd->ClearValue.color.float32[0], &clear_color, 4 * sizeof(float)); + frame_begin(wd); + ImGui_ImplVulkan_Render(wd->Frames[wd->FrameIndex].CommandBuffer); + frame_end(wd); - // When IMGUI_UNLIMITED_FRAME_RATE is defined we render into latest image acquired from the swapchain but we display the image which was rendered before. - // Hence we must render once and increase the g_FrameIndex without presenting, which we do before entering the render loop. #ifdef IMGUI_UNLIMITED_FRAME_RATE + // When IMGUI_UNLIMITED_FRAME_RATE is defined we render into latest image acquired from the swapchain but we display the image which was rendered before. + // Hence we must render once and increase the FrameIndex without presenting. if (swap_chain_has_at_least_one_image) - frame_present(); + frame_present(wd); #else - frame_present(); + frame_present(wd); #endif swap_chain_has_at_least_one_image = true; - g_FrameIndex = (g_FrameIndex + 1) % IMGUI_VK_QUEUED_FRAMES; + wd->FrameIndex = (wd->FrameIndex + 1) % IMGUI_VK_QUEUED_FRAMES; // FIXME-PLATFORM ImGui::UpdatePlatformWindows(); From b807347e94b9d69d7782f8c2f8c490542cd20d5a Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 2 Mar 2018 22:22:14 +0100 Subject: [PATCH 052/828] Viewport: When enabled we assert against using RenderDrawListFn to make user more aware of rendering order. (#1542) --- imgui.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/imgui.cpp b/imgui.cpp index e9f644b26139..4f1a3d81431d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3548,6 +3548,9 @@ void ImGui::NewFrame() if (g.IO.ConfigFlags & ImGuiConfigFlags_MultiViewports) { +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + IM_ASSERT(g.IO.RenderDrawListsFn == NULL); // Call ImGui::Render() then pass ImGui::GetDrawData() yourself to your render function! +#endif IM_ASSERT(g.IO.PlatformInterface.CreateViewport != NULL); IM_ASSERT(g.IO.PlatformInterface.DestroyViewport != NULL); //IM_ASSERT(g.IO.PlatformInterface.RenderViewport != NULL || g.IO.RendererInterface.RenderViewport != NULL); // Missing rendering function From e9fa17e1bf403df793243c39eff6cbe0fb61f42a Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 2 Mar 2018 22:44:06 +0100 Subject: [PATCH 053/828] Viewport: Replaced UpdatePlatformWindows/RenderPlatformWindows by RenderAdditionalViewports(). The update is always called in EndFrame(). (#1542) --- examples/directx10_example/main.cpp | 3 +-- examples/directx11_example/main.cpp | 3 +-- examples/directx12_example/main.cpp | 3 +-- examples/imgui_impl_dx12.cpp | 3 +++ examples/opengl3_example/main.cpp | 5 ++--- examples/sdl_opengl3_example/main.cpp | 4 +--- examples/sdl_vulkan_example/main.cpp | 5 +---- examples/vulkan_example/main.cpp | 4 +--- imgui.cpp | 28 ++++++++++++++++++++------- imgui.h | 5 +---- 10 files changed, 33 insertions(+), 30 deletions(-) diff --git a/examples/directx10_example/main.cpp b/examples/directx10_example/main.cpp index eb6e5a66161d..ab6a0fdb3d8b 100644 --- a/examples/directx10_example/main.cpp +++ b/examples/directx10_example/main.cpp @@ -205,8 +205,7 @@ int main(int, char**) ImGui::Render(); ImGui_ImplDX10_RenderDrawData(ImGui::GetDrawData()); - ImGui::UpdatePlatformWindows(); - ImGui::RenderPlatformWindows(); + ImGui::RenderAdditionalViewports(); g_pSwapChain->Present(1, 0); // Present with vsync //g_pSwapChain->Present(0, 0); // Present without vsync diff --git a/examples/directx11_example/main.cpp b/examples/directx11_example/main.cpp index f949037b4d77..8c0a98573dbf 100644 --- a/examples/directx11_example/main.cpp +++ b/examples/directx11_example/main.cpp @@ -208,8 +208,7 @@ int main(int, char**) ImGui::Render(); ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData()); - ImGui::UpdatePlatformWindows(); - ImGui::RenderPlatformWindows(); + ImGui::RenderAdditionalViewports(); g_pSwapChain->Present(1, 0); // Present with vsync //g_pSwapChain->Present(0, 0); // Present without vsync diff --git a/examples/directx12_example/main.cpp b/examples/directx12_example/main.cpp index 61d65b028f96..1e2d81578cae 100644 --- a/examples/directx12_example/main.cpp +++ b/examples/directx12_example/main.cpp @@ -401,8 +401,7 @@ int main(int, char**) g_pd3dCommandQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*)&g_pd3dCommandList); - ImGui::UpdatePlatformWindows(); - ImGui::RenderPlatformWindows(); + ImGui::RenderAdditionalViewports(); g_pSwapChain->Present(1, 0); // Present with vsync //g_pSwapChain->Present(0, 0); // Present without vsync diff --git a/examples/imgui_impl_dx12.cpp b/examples/imgui_impl_dx12.cpp index 9c1de56baa7e..7ed9a3949902 100644 --- a/examples/imgui_impl_dx12.cpp +++ b/examples/imgui_impl_dx12.cpp @@ -709,6 +709,7 @@ static void ImGui_ImplDX12_ResizeViewport(ImGuiViewport* viewport, int w, int h) { ImGuiPlatformDataDx12* data = (ImGuiPlatformDataDx12*)viewport->RendererUserData; IM_ASSERT(0); + (void)data; (void)w; (void)h; /* if (data->RTView) { @@ -730,6 +731,7 @@ static void ImGui_ImplDX12_RenderViewport(ImGuiViewport* viewport) { ImGuiPlatformDataDx12* data = (ImGuiPlatformDataDx12*)viewport->RendererUserData; IM_ASSERT(0); + (void)data; /* ImVec4 clear_color = ImGui::GetStyle().Colors[ImGuiCol_WindowBg]; // FIXME-PLATFORM clear_color.w = 1.0f; @@ -743,6 +745,7 @@ static void ImGui_ImplDX12_SwapBuffers(ImGuiViewport* viewport) { ImGuiPlatformDataDx12* data = (ImGuiPlatformDataDx12*)viewport->RendererUserData; IM_ASSERT(0); + (void)data; /* data->SwapChain->Present(0, 0); // Present without vsync */ diff --git a/examples/opengl3_example/main.cpp b/examples/opengl3_example/main.cpp index 5d8dba0fbc43..381954266165 100644 --- a/examples/opengl3_example/main.cpp +++ b/examples/opengl3_example/main.cpp @@ -122,9 +122,8 @@ int main(int, char**) glClear(GL_COLOR_BUFFER_BIT); ImGui::Render(); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); - - ImGui::UpdatePlatformWindows(); - ImGui::RenderPlatformWindows(); + + ImGui::RenderAdditionalViewports(); glfwMakeContextCurrent(window); glfwSwapBuffers(window); diff --git a/examples/sdl_opengl3_example/main.cpp b/examples/sdl_opengl3_example/main.cpp index 1fb38c2cae63..0b9f94854468 100644 --- a/examples/sdl_opengl3_example/main.cpp +++ b/examples/sdl_opengl3_example/main.cpp @@ -130,9 +130,7 @@ int main(int, char**) glClear(GL_COLOR_BUFFER_BIT); ImGui::Render(); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); - - ImGui::UpdatePlatformWindows(); - ImGui::RenderPlatformWindows(); + ImGui::RenderAdditionalViewports(); SDL_GL_MakeCurrent(window, gl_context); SDL_GL_SwapWindow(window); diff --git a/examples/sdl_vulkan_example/main.cpp b/examples/sdl_vulkan_example/main.cpp index b494cf5cf7ec..c1358a4fa6c8 100644 --- a/examples/sdl_vulkan_example/main.cpp +++ b/examples/sdl_vulkan_example/main.cpp @@ -733,10 +733,7 @@ int main(int, char**) #endif swap_chain_has_at_least_one_image = true; wd->FrameIndex = (wd->FrameIndex + 1) % IMGUI_VK_QUEUED_FRAMES; - - // FIXME-PLATFORM - ImGui::UpdatePlatformWindows(); - ImGui::RenderPlatformWindows(); + ImGui::RenderAdditionalViewports(); } // Cleanup diff --git a/examples/vulkan_example/main.cpp b/examples/vulkan_example/main.cpp index bc05919f20cf..106f58eb1815 100644 --- a/examples/vulkan_example/main.cpp +++ b/examples/vulkan_example/main.cpp @@ -735,9 +735,7 @@ int main(int, char**) swap_chain_has_at_least_one_image = true; wd->FrameIndex = (wd->FrameIndex + 1) % IMGUI_VK_QUEUED_FRAMES; - // FIXME-PLATFORM - ImGui::UpdatePlatformWindows(); - ImGui::RenderPlatformWindows(); + //ImGui::RenderAdditionalViewports(); } // Cleanup diff --git a/imgui.cpp b/imgui.cpp index 4f1a3d81431d..7ee87a36cdfb 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3450,12 +3450,10 @@ static void ImGui::UpdateViewports() IM_ASSERT(g.MouseViewport != NULL); } -void ImGui::UpdatePlatformWindows() +static void UpdatePlatformWindows() { // Create/resize windows ImGuiContext& g = *GImGui; - if (!(g.IO.ConfigFlags & ImGuiConfigFlags_MultiViewports)) - return; for (int i = 0; i < g.Viewports.Size; i++) { ImGuiViewport* viewport = g.Viewports[i]; @@ -3492,12 +3490,12 @@ void ImGui::UpdatePlatformWindows() } } -void ImGui::RenderPlatformWindows() +static void RenderPlatformWindows() { // Render ImGuiContext& g = *GImGui; - if (!(g.IO.ConfigFlags & ImGuiConfigFlags_MultiViewports)) - return; + ImVec2 backup_display_pos = g.IO.DisplayPos; + ImVec2 backup_display_size = g.IO.DisplaySize; for (int i = 0; i < g.Viewports.Size; i++) { ImGuiViewport* viewport = g.Viewports[i]; @@ -3510,6 +3508,8 @@ void ImGui::RenderPlatformWindows() if (g.IO.RendererInterface.RenderViewport) g.IO.RendererInterface.RenderViewport(viewport); } + g.IO.DisplayPos = backup_display_pos; + g.IO.DisplaySize = backup_display_size; // Swap for (int i = 0; i < g.Viewports.Size; i++) @@ -4347,6 +4347,9 @@ void ImGui::EndFrame() memset(g.IO.NavInputs, 0, sizeof(g.IO.NavInputs)); g.FrameCountEnded = g.FrameCount; + + if (g.IO.ConfigFlags & ImGuiConfigFlags_MultiViewports) + UpdatePlatformWindows(); } void ImGui::Render() @@ -4360,7 +4363,8 @@ void ImGui::Render() // Skip render altogether if alpha is 0.0 // Note that vertex buffers have been created and are wasted, so it is best practice that you don't create windows in the first place, or consistently respond to Begin() returning false. - if (g.Style.Alpha > 0.0f) + bool disable_render = (g.Style.Alpha <= 0.0f); + if (!disable_render) { // Gather windows to render g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = g.IO.MetricsActiveWindows = 0; @@ -4402,7 +4406,10 @@ void ImGui::Render() g.IO.MetricsRenderVertices += viewport->DrawData.TotalVtxCount; g.IO.MetricsRenderIndices += viewport->DrawData.TotalIdxCount; } + } + if (!disable_render) + { // Render. If user hasn't set a callback then they may retrieve the draw data via GetDrawData() #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS if (g.Viewports[0]->DrawData.CmdListsCount > 0 && g.IO.RenderDrawListsFn != NULL) @@ -4411,6 +4418,13 @@ void ImGui::Render() } } +void ImGui::RenderAdditionalViewports() +{ + ImGuiContext& g = *GImGui; + if (g.IO.ConfigFlags & ImGuiConfigFlags_MultiViewports) + RenderPlatformWindows(); +} + ImGuiViewport* ImGui::FindViewportByID(ImGuiID id) { ImGuiContext& g = *GImGui; diff --git a/imgui.h b/imgui.h index 8e05af609ef6..f039c1602c9b 100644 --- a/imgui.h +++ b/imgui.h @@ -151,6 +151,7 @@ namespace ImGui IMGUI_API ImGuiStyle& GetStyle(); IMGUI_API void NewFrame(); // start a new ImGui frame, you can submit any command from this point until Render()/EndFrame(). IMGUI_API void Render(); // ends the ImGui frame, finalize the draw data. (Obsolete: optionally call io.RenderDrawListsFn if set. Nowadays, prefer calling your render function yourself.) + IMGUI_API void RenderAdditionalViewports(); IMGUI_API ImDrawData* GetDrawData(); // valid after Render() and until the next call to NewFrame(). this is what you have to render. (Obsolete: this used to be passed to your io.RenderDrawListsFn() function.) IMGUI_API ImDrawData* GetDrawDataForViewport(ImGuiID viewport_id);// ImDrawData filtered to hold only the ImDrawList covering a given viewport. valid after Render() and until the next call to NewFrame() IMGUI_API void EndFrame(); // ends the ImGui frame. automatically called by Render(), so most likely don't need to ever call that yourself directly. If you don't need to render you may call EndFrame() but you'll have wasted CPU already. If you don't need to render, better to not create any imgui windows instead! @@ -530,10 +531,6 @@ namespace ImGui // Clipboard Utilities (also see the LogToClipboard() function to capture or output text data to the clipboard) IMGUI_API const char* GetClipboardText(); IMGUI_API void SetClipboardText(const char* text); - - // Additional OS/Platform Windows (when ImGuiConfigFlags_MultiViewports is set) - IMGUI_API void UpdatePlatformWindows(); // FIXME-PLATFORM - IMGUI_API void RenderPlatformWindows(); // FIXME-PLATFORM // Memory Utilities // All those functions are not reliant on the current context. From 426930e028c1f3ac71577ee8f11b2dbb43f65ba4 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 2 Mar 2018 23:04:56 +0100 Subject: [PATCH 054/828] Examples: Vulkan: Moved main.cpp data structures to imgui_impl_vulkan.h as shared helpers for the benefit of both the example and the platform/viewport renderer interface. --- examples/imgui_impl_vulkan.cpp | 33 +++++- examples/imgui_impl_vulkan.h | 37 ++++++ examples/sdl_vulkan_example/main.cpp | 160 +++++++++----------------- examples/vulkan_example/main.cpp | 162 +++++++++------------------ 4 files changed, 172 insertions(+), 220 deletions(-) diff --git a/examples/imgui_impl_vulkan.cpp b/examples/imgui_impl_vulkan.cpp index 434459a252b3..7156ea10146f 100644 --- a/examples/imgui_impl_vulkan.cpp +++ b/examples/imgui_impl_vulkan.cpp @@ -727,6 +727,34 @@ void ImGui_ImplVulkan_Render(VkCommandBuffer command_buffer) #include // malloc +ImGui_ImplVulkan_FrameData::ImGui_ImplVulkan_FrameData() +{ + BackbufferIndex = 0; + CommandPool = VK_NULL_HANDLE; + CommandBuffer = VK_NULL_HANDLE; + Fence = VK_NULL_HANDLE; + PresentCompleteSemaphore = VK_NULL_HANDLE; + RenderCompleteSemaphore = VK_NULL_HANDLE; +} + +ImGui_ImplVulkan_WindowData::ImGui_ImplVulkan_WindowData() +{ + Width = Height = 0; + Swapchain = VK_NULL_HANDLE; + Surface = VK_NULL_HANDLE; + memset(&SurfaceFormat, 0, sizeof(SurfaceFormat)); + PresentMode = VK_PRESENT_MODE_MAX_ENUM_KHR; + RenderPass = VK_NULL_HANDLE; + memset(&ClearValue, 0, sizeof(ClearValue)); + BackBufferCount = 0; + memset(&BackBuffer, 0, sizeof(BackBuffer)); + memset(&BackBufferView, 0, sizeof(BackBufferView)); + memset(&Framebuffer, 0, sizeof(Framebuffer)); + FrameIndex = 0; +} + + + VkSurfaceFormatKHR ImGui_ImplVulkan_SelectSurfaceFormat(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkFormat* request_formats, int request_formats_count, VkColorSpaceKHR request_color_space) { IM_ASSERT(request_formats != NULL); @@ -790,9 +818,9 @@ VkPresentModeKHR ImGui_ImplVulkan_SelectPresentMode(VkPhysicalDevice physical_de return VK_PRESENT_MODE_FIFO_KHR; // Always available } -// -------------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------------- // Platform Windows (OPTIONAL/EXPERIMENTAL) -// -------------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------------- #include "imgui_internal.h" // ImGuiViewport @@ -868,4 +896,3 @@ void ImGui_ImplVulkan_ShutdownPlatformInterface() ImGuiIO& io = ImGui::GetIO(); memset(&io.RendererInterface, 0, sizeof(io.RendererInterface)); } - diff --git a/examples/imgui_impl_vulkan.h b/examples/imgui_impl_vulkan.h index 044795a2fad9..cb095e379f7e 100644 --- a/examples/imgui_impl_vulkan.h +++ b/examples/imgui_impl_vulkan.h @@ -36,6 +36,43 @@ IMGUI_API void ImGui_ImplVulkan_InvalidateDeviceObjects(); IMGUI_API bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer); IMGUI_API bool ImGui_ImplVulkan_CreateDeviceObjects(); +//------------------------------------------------------------------------- // Miscellaneous Vulkan Helpers +// Generally we try to NOT provide any kind of superfluous high-level helpers in the examples. +// But for the purpose of allowing multi-windows, we need those internally anyway. The code being not trivial are exposing it for the benefit of the example code. +// If your application/engine already has code to create all that data (swap chain, render pass, frame buffers, etc.) you can ignore all of this. +//------------------------------------------------------------------------- + IMGUI_API VkSurfaceFormatKHR ImGui_ImplVulkan_SelectSurfaceFormat(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkFormat* request_formats, int request_formats_count, VkColorSpaceKHR request_color_space); IMGUI_API VkPresentModeKHR ImGui_ImplVulkan_SelectPresentMode(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkPresentModeKHR* request_modes, int request_modes_count); + +struct ImGui_ImplVulkan_FrameData +{ + uint32_t BackbufferIndex; // keep track of recently rendered swapchain frame indices + VkCommandPool CommandPool; + VkCommandBuffer CommandBuffer; + VkFence Fence; + VkSemaphore PresentCompleteSemaphore; + VkSemaphore RenderCompleteSemaphore; + + IMGUI_API ImGui_ImplVulkan_FrameData(); +}; + +struct ImGui_ImplVulkan_WindowData +{ + int Width, Height; + VkSwapchainKHR Swapchain; + VkSurfaceKHR Surface; + VkSurfaceFormatKHR SurfaceFormat; + VkPresentModeKHR PresentMode; + VkRenderPass RenderPass; + VkClearValue ClearValue; + uint32_t BackBufferCount; + VkImage BackBuffer[16]; + VkImageView BackBufferView[16]; + VkFramebuffer Framebuffer[16]; + uint32_t FrameIndex; + ImGui_ImplVulkan_FrameData Frames[IMGUI_VK_QUEUED_FRAMES]; + + IMGUI_API ImGui_ImplVulkan_WindowData(); +}; diff --git a/examples/sdl_vulkan_example/main.cpp b/examples/sdl_vulkan_example/main.cpp index c1358a4fa6c8..95e255ecd3b1 100644 --- a/examples/sdl_vulkan_example/main.cpp +++ b/examples/sdl_vulkan_example/main.cpp @@ -10,7 +10,6 @@ #include // FIXME-VULKAN: Resizing with IMGUI_UNLIMITED_FRAME_RATE triggers errors from the validation layer. -#define IMGUI_MAX_POSSIBLE_BACK_BUFFERS 16 #define IMGUI_UNLIMITED_FRAME_RATE #ifdef _DEBUG #define IMGUI_VULKAN_DEBUG_REPORT @@ -26,59 +25,7 @@ static VkDebugReportCallbackEXT g_DebugReport = VK_NULL_HANDLE; static VkPipelineCache g_PipelineCache = VK_NULL_HANDLE; static VkDescriptorPool g_DescriptorPool = VK_NULL_HANDLE; -struct FrameData -{ - uint32_t BackbufferIndex; // keep track of recently rendered swapchain frame indices - VkCommandPool CommandPool; - VkCommandBuffer CommandBuffer; - VkFence Fence; - VkSemaphore PresentCompleteSemaphore; - VkSemaphore RenderCompleteSemaphore; - - FrameData() - { - BackbufferIndex = 0; - CommandPool = VK_NULL_HANDLE; - CommandBuffer = VK_NULL_HANDLE; - Fence = VK_NULL_HANDLE; - PresentCompleteSemaphore = VK_NULL_HANDLE; - RenderCompleteSemaphore = VK_NULL_HANDLE; - } -}; - -struct WindowData -{ - int Width, Height; - VkSwapchainKHR Swapchain; - VkSurfaceKHR Surface; - VkSurfaceFormatKHR SurfaceFormat; - VkPresentModeKHR PresentMode; - VkRenderPass RenderPass; - VkClearValue ClearValue; - uint32_t BackBufferCount; - VkImage BackBuffer[IMGUI_MAX_POSSIBLE_BACK_BUFFERS]; - VkImageView BackBufferView[IMGUI_MAX_POSSIBLE_BACK_BUFFERS]; - VkFramebuffer Framebuffer[IMGUI_MAX_POSSIBLE_BACK_BUFFERS]; - uint32_t FrameIndex; - FrameData Frames[IMGUI_VK_QUEUED_FRAMES]; - - WindowData() - { - Width = Height = 0; - Swapchain = VK_NULL_HANDLE; - Surface = VK_NULL_HANDLE; - memset(&SurfaceFormat, 0, sizeof(SurfaceFormat)); - PresentMode = VK_PRESENT_MODE_MAX_ENUM_KHR; - RenderPass = VK_NULL_HANDLE; - memset(&ClearValue, 0, sizeof(ClearValue)); - BackBufferCount = 0; - memset(&BackBuffer, 0, sizeof(BackBuffer)); - memset(&BackBufferView, 0, sizeof(BackBufferView)); - memset(&Framebuffer, 0, sizeof(Framebuffer)); - FrameIndex = 0; - } -}; -static WindowData g_WindowData; +static ImGui_ImplVulkan_WindowData g_WindowData; static void check_vk_result(VkResult err) { @@ -88,7 +35,7 @@ static void check_vk_result(VkResult err) abort(); } -static void CreateOrResizeSwapChainAndFrameBuffer(WindowData* wd, int w, int h) +static void CreateOrResizeSwapChainAndFrameBuffer(ImGui_ImplVulkan_WindowData* wd, int w, int h) { VkResult err; VkSwapchainKHR old_swapchain = wd->Swapchain; @@ -231,50 +178,49 @@ static void CreateVulkanInstance(const char** extensions, uint32_t extensions_co { VkResult err; - // Create Vulkan Instance - { - VkInstanceCreateInfo create_info = {}; - create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; - create_info.enabledExtensionCount = extensions_count; - create_info.ppEnabledExtensionNames = extensions; + VkInstanceCreateInfo create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + create_info.enabledExtensionCount = extensions_count; + create_info.ppEnabledExtensionNames = extensions; #ifdef IMGUI_VULKAN_DEBUG_REPORT - // Enabling multiple validation layers grouped as LunarG standard validation - const char* layers[] = { "VK_LAYER_LUNARG_standard_validation" }; - create_info.enabledLayerCount = 1; - create_info.ppEnabledLayerNames = layers; - - // Enable debug report extension (we need additional storage, so we duplicate the user array to add our new extension to it) - const char** extensions_ext = (const char**)malloc(sizeof(const char*) * (extensions_count + 1)); - memcpy(extensions_ext, extensions, extensions_count * sizeof(const char*)); - extensions_ext[extensions_count] = "VK_EXT_debug_report"; - create_info.enabledExtensionCount = extensions_count + 1; - create_info.ppEnabledExtensionNames = extensions_ext; -#endif // IMGUI_VULKAN_DEBUG_REPORT + // Enabling multiple validation layers grouped as LunarG standard validation + const char* layers[] = { "VK_LAYER_LUNARG_standard_validation" }; + create_info.enabledLayerCount = 1; + create_info.ppEnabledLayerNames = layers; + + // Enable debug report extension (we need additional storage, so we duplicate the user array to add our new extension to it) + const char** extensions_ext = (const char**)malloc(sizeof(const char*) * (extensions_count + 1)); + memcpy(extensions_ext, extensions, extensions_count * sizeof(const char*)); + extensions_ext[extensions_count] = "VK_EXT_debug_report"; + create_info.enabledExtensionCount = extensions_count + 1; + create_info.ppEnabledExtensionNames = extensions_ext; - err = vkCreateInstance(&create_info, g_Allocator, &g_Instance); - check_vk_result(err); - -#ifdef IMGUI_VULKAN_DEBUG_REPORT - free(extensions_ext); - - // Get the function pointer (required for any extensions) - auto vkCreateDebugReportCallbackEXT = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(g_Instance, "vkCreateDebugReportCallbackEXT"); - IM_ASSERT(vkCreateDebugReportCallbackEXT != NULL); - - // Setup the debug report callback - VkDebugReportCallbackCreateInfoEXT debug_report_ci = {}; - debug_report_ci.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT; - debug_report_ci.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT; - debug_report_ci.pfnCallback = debug_report; - debug_report_ci.pUserData = NULL; - err = vkCreateDebugReportCallbackEXT(g_Instance, &debug_report_ci, g_Allocator, &g_DebugReport); - check_vk_result(err); -#endif // IMGUI_VULKAN_DEBUG_REPORT - } + // Create Vulkan Instance + err = vkCreateInstance(&create_info, g_Allocator, &g_Instance); + check_vk_result(err); + free(extensions_ext); + + // Get the function pointer (required for any extensions) + auto vkCreateDebugReportCallbackEXT = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(g_Instance, "vkCreateDebugReportCallbackEXT"); + IM_ASSERT(vkCreateDebugReportCallbackEXT != NULL); + + // Setup the debug report callback + VkDebugReportCallbackCreateInfoEXT debug_report_ci = {}; + debug_report_ci.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT; + debug_report_ci.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT; + debug_report_ci.pfnCallback = debug_report; + debug_report_ci.pUserData = NULL; + err = vkCreateDebugReportCallbackEXT(g_Instance, &debug_report_ci, g_Allocator, &g_DebugReport); + check_vk_result(err); +#else + // Create Vulkan Instance without any debug feature + err = vkCreateInstance(&create_info, g_Allocator, &g_Instance); + check_vk_result(err); +#endif } -static void SetupVulkan(WindowData* wd) +static void SetupVulkan(ImGui_ImplVulkan_WindowData* wd) { VkResult err; @@ -322,7 +268,6 @@ static void SetupVulkan(WindowData* wd) } } - // Get Surface Format { const VkFormat requestSurfaceImageFormat[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM }; @@ -330,7 +275,6 @@ static void SetupVulkan(WindowData* wd) wd->SurfaceFormat = ImGui_ImplVulkan_SelectSurfaceFormat(g_PhysicalDevice, wd->Surface, requestSurfaceImageFormat, (size_t)IM_ARRAYSIZE(requestSurfaceImageFormat), requestSurfaceColorSpace); } - // Get Present Mode { #ifdef IMGUI_UNLIMITED_FRAME_RATE @@ -341,7 +285,6 @@ static void SetupVulkan(WindowData* wd) wd->PresentMode = ImGui_ImplVulkan_SelectPresentMode(g_PhysicalDevice, wd->Surface, &present_mode, 1); } - // Create Logical Device (with 1 queue) { int device_extension_count = 1; @@ -366,7 +309,7 @@ static void SetupVulkan(WindowData* wd) // Create Command Buffers for (int i = 0; i < IMGUI_VK_QUEUED_FRAMES; i++) { - FrameData* fd = &wd->Frames[i]; + ImGui_ImplVulkan_FrameData* fd = &wd->Frames[i]; { VkCommandPoolCreateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; @@ -430,11 +373,11 @@ static void SetupVulkan(WindowData* wd) static void cleanup_vulkan() { - WindowData* wd = &g_WindowData; + ImGui_ImplVulkan_WindowData* wd = &g_WindowData; vkDestroyDescriptorPool(g_Device, g_DescriptorPool, g_Allocator); for (int i = 0; i < IMGUI_VK_QUEUED_FRAMES; i++) { - FrameData* fd = &wd->Frames[i]; + ImGui_ImplVulkan_FrameData* fd = &wd->Frames[i]; vkDestroyFence(g_Device, fd->Fence, g_Allocator); vkFreeCommandBuffers(g_Device, fd->CommandPool, 1, &fd->CommandBuffer); vkDestroyCommandPool(g_Device, fd->CommandPool, g_Allocator); @@ -460,9 +403,9 @@ static void cleanup_vulkan() vkDestroyInstance(g_Instance, g_Allocator); } -static void frame_begin(WindowData* wd) +static void frame_begin(ImGui_ImplVulkan_WindowData* wd) { - FrameData* fd = &wd->Frames[wd->FrameIndex]; + ImGui_ImplVulkan_FrameData* fd = &wd->Frames[wd->FrameIndex]; VkResult err; for (;;) { @@ -497,9 +440,9 @@ static void frame_begin(WindowData* wd) } } -static void frame_end(WindowData* wd) +static void frame_end(ImGui_ImplVulkan_WindowData* wd) { - FrameData* fd = &wd->Frames[wd->FrameIndex]; + ImGui_ImplVulkan_FrameData* fd = &wd->Frames[wd->FrameIndex]; VkResult err; vkCmdEndRenderPass(fd->CommandBuffer); { @@ -523,7 +466,7 @@ static void frame_end(WindowData* wd) } } -static void frame_present(WindowData* wd) +static void frame_present(ImGui_ImplVulkan_WindowData* wd) { VkResult err; // If IMGUI_UNLIMITED_FRAME_RATE is defined we present the latest but one frame. Otherwise we present the latest rendered frame @@ -533,7 +476,7 @@ static void frame_present(WindowData* wd) uint32_t PresentIndex = g_FrameIndex; #endif // IMGUI_UNLIMITED_FRAME_RATE - FrameData* fd = &wd->Frames[PresentIndex]; + ImGui_ImplVulkan_FrameData* fd = &wd->Frames[PresentIndex]; VkPresentInfoKHR info = {}; info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; info.waitSemaphoreCount = 1; @@ -568,7 +511,7 @@ int main(int, char**) delete[] sdl_extensions; // Create Window Surface - WindowData* wd = &g_WindowData; + ImGui_ImplVulkan_WindowData* wd = &g_WindowData; SDL_bool result = SDL_Vulkan_CreateSurface(window, g_Instance, &wd->Surface); if (result == 0) { @@ -723,6 +666,8 @@ int main(int, char**) ImGui_ImplVulkan_Render(wd->Frames[wd->FrameIndex].CommandBuffer); frame_end(wd); + ImGui::RenderAdditionalViewports(); + #ifdef IMGUI_UNLIMITED_FRAME_RATE // When IMGUI_UNLIMITED_FRAME_RATE is defined we render into latest image acquired from the swapchain but we display the image which was rendered before. // Hence we must render once and increase the FrameIndex without presenting. @@ -733,7 +678,6 @@ int main(int, char**) #endif swap_chain_has_at_least_one_image = true; wd->FrameIndex = (wd->FrameIndex + 1) % IMGUI_VK_QUEUED_FRAMES; - ImGui::RenderAdditionalViewports(); } // Cleanup diff --git a/examples/vulkan_example/main.cpp b/examples/vulkan_example/main.cpp index 106f58eb1815..7f076768cab3 100644 --- a/examples/vulkan_example/main.cpp +++ b/examples/vulkan_example/main.cpp @@ -13,7 +13,6 @@ #include // FIXME-VULKAN: Resizing with IMGUI_UNLIMITED_FRAME_RATE triggers errors from the validation layer. -#define IMGUI_MAX_POSSIBLE_BACK_BUFFERS 16 #define IMGUI_UNLIMITED_FRAME_RATE #ifdef _DEBUG #define IMGUI_VULKAN_DEBUG_REPORT @@ -29,59 +28,7 @@ static VkDebugReportCallbackEXT g_DebugReport = VK_NULL_HANDLE; static VkPipelineCache g_PipelineCache = VK_NULL_HANDLE; static VkDescriptorPool g_DescriptorPool = VK_NULL_HANDLE; -struct FrameData -{ - uint32_t BackbufferIndex; // keep track of recently rendered swapchain frame indices - VkCommandPool CommandPool; - VkCommandBuffer CommandBuffer; - VkFence Fence; - VkSemaphore PresentCompleteSemaphore; - VkSemaphore RenderCompleteSemaphore; - - FrameData() - { - BackbufferIndex = 0; - CommandPool = VK_NULL_HANDLE; - CommandBuffer = VK_NULL_HANDLE; - Fence = VK_NULL_HANDLE; - PresentCompleteSemaphore = VK_NULL_HANDLE; - RenderCompleteSemaphore = VK_NULL_HANDLE; - } -}; - -struct WindowData -{ - int Width, Height; - VkSwapchainKHR Swapchain; - VkSurfaceKHR Surface; - VkSurfaceFormatKHR SurfaceFormat; - VkPresentModeKHR PresentMode; - VkRenderPass RenderPass; - VkClearValue ClearValue; - uint32_t BackBufferCount; - VkImage BackBuffer[IMGUI_MAX_POSSIBLE_BACK_BUFFERS]; - VkImageView BackBufferView[IMGUI_MAX_POSSIBLE_BACK_BUFFERS]; - VkFramebuffer Framebuffer[IMGUI_MAX_POSSIBLE_BACK_BUFFERS]; - uint32_t FrameIndex; - FrameData Frames[IMGUI_VK_QUEUED_FRAMES]; - - WindowData() - { - Width = Height = 0; - Swapchain = VK_NULL_HANDLE; - Surface = VK_NULL_HANDLE; - memset(&SurfaceFormat, 0, sizeof(SurfaceFormat)); - PresentMode = VK_PRESENT_MODE_MAX_ENUM_KHR; - RenderPass = VK_NULL_HANDLE; - memset(&ClearValue, 0, sizeof(ClearValue)); - BackBufferCount = 0; - memset(&BackBuffer, 0, sizeof(BackBuffer)); - memset(&BackBufferView, 0, sizeof(BackBufferView)); - memset(&Framebuffer, 0, sizeof(Framebuffer)); - FrameIndex = 0; - } -}; -static WindowData g_WindowData; +static ImGui_ImplVulkan_WindowData g_WindowData; static void check_vk_result(VkResult err) { @@ -91,7 +38,7 @@ static void check_vk_result(VkResult err) abort(); } -static void CreateOrResizeSwapChainAndFrameBuffer(WindowData* wd, int w, int h) +static void CreateOrResizeSwapChainAndFrameBuffer(ImGui_ImplVulkan_WindowData* wd, int w, int h) { VkResult err; VkSwapchainKHR old_swapchain = wd->Swapchain; @@ -147,6 +94,7 @@ static void CreateOrResizeSwapChainAndFrameBuffer(WindowData* wd, int w, int h) check_vk_result(err); err = vkGetSwapchainImagesKHR(g_Device, wd->Swapchain, &wd->BackBufferCount, NULL); check_vk_result(err); + IM_ASSERT(wd->BackBufferCount < IM_ARRAYSIZE(wd->BackBuffer)); err = vkGetSwapchainImagesKHR(g_Device, wd->Swapchain, &wd->BackBufferCount, wd->BackBuffer); check_vk_result(err); } @@ -234,50 +182,49 @@ static void CreateVulkanInstance(const char** extensions, uint32_t extensions_co { VkResult err; - // Create Vulkan Instance - { - VkInstanceCreateInfo create_info = {}; - create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; - create_info.enabledExtensionCount = extensions_count; - create_info.ppEnabledExtensionNames = extensions; + VkInstanceCreateInfo create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + create_info.enabledExtensionCount = extensions_count; + create_info.ppEnabledExtensionNames = extensions; #ifdef IMGUI_VULKAN_DEBUG_REPORT - // Enabling multiple validation layers grouped as LunarG standard validation - const char* layers[] = { "VK_LAYER_LUNARG_standard_validation" }; - create_info.enabledLayerCount = 1; - create_info.ppEnabledLayerNames = layers; - - // Enable debug report extension (we need additional storage, so we duplicate the user array to add our new extension to it) - const char** extensions_ext = (const char**)malloc(sizeof(const char*) * (extensions_count + 1)); - memcpy(extensions_ext, extensions, extensions_count * sizeof(const char*)); - extensions_ext[extensions_count] = "VK_EXT_debug_report"; - create_info.enabledExtensionCount = extensions_count + 1; - create_info.ppEnabledExtensionNames = extensions_ext; -#endif // IMGUI_VULKAN_DEBUG_REPORT + // Enabling multiple validation layers grouped as LunarG standard validation + const char* layers[] = { "VK_LAYER_LUNARG_standard_validation" }; + create_info.enabledLayerCount = 1; + create_info.ppEnabledLayerNames = layers; + + // Enable debug report extension (we need additional storage, so we duplicate the user array to add our new extension to it) + const char** extensions_ext = (const char**)malloc(sizeof(const char*) * (extensions_count + 1)); + memcpy(extensions_ext, extensions, extensions_count * sizeof(const char*)); + extensions_ext[extensions_count] = "VK_EXT_debug_report"; + create_info.enabledExtensionCount = extensions_count + 1; + create_info.ppEnabledExtensionNames = extensions_ext; - err = vkCreateInstance(&create_info, g_Allocator, &g_Instance); - check_vk_result(err); - -#ifdef IMGUI_VULKAN_DEBUG_REPORT - free(extensions_ext); - - // Get the function pointer (required for any extensions) - auto vkCreateDebugReportCallbackEXT = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(g_Instance, "vkCreateDebugReportCallbackEXT"); - IM_ASSERT(vkCreateDebugReportCallbackEXT != NULL); - - // Setup the debug report callback - VkDebugReportCallbackCreateInfoEXT debug_report_ci = {}; - debug_report_ci.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT; - debug_report_ci.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT; - debug_report_ci.pfnCallback = debug_report; - debug_report_ci.pUserData = NULL; - err = vkCreateDebugReportCallbackEXT(g_Instance, &debug_report_ci, g_Allocator, &g_DebugReport); - check_vk_result(err); -#endif // IMGUI_VULKAN_DEBUG_REPORT - } + // Create Vulkan Instance + err = vkCreateInstance(&create_info, g_Allocator, &g_Instance); + check_vk_result(err); + free(extensions_ext); + + // Get the function pointer (required for any extensions) + auto vkCreateDebugReportCallbackEXT = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(g_Instance, "vkCreateDebugReportCallbackEXT"); + IM_ASSERT(vkCreateDebugReportCallbackEXT != NULL); + + // Setup the debug report callback + VkDebugReportCallbackCreateInfoEXT debug_report_ci = {}; + debug_report_ci.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT; + debug_report_ci.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT; + debug_report_ci.pfnCallback = debug_report; + debug_report_ci.pUserData = NULL; + err = vkCreateDebugReportCallbackEXT(g_Instance, &debug_report_ci, g_Allocator, &g_DebugReport); + check_vk_result(err); +#else + // Create Vulkan Instance without any debug feature + err = vkCreateInstance(&create_info, g_Allocator, &g_Instance); + check_vk_result(err); +#endif } -static void SetupVulkan(WindowData* wd) +static void SetupVulkan(ImGui_ImplVulkan_WindowData* wd) { VkResult err; @@ -325,7 +272,6 @@ static void SetupVulkan(WindowData* wd) } } - // Get Surface Format { const VkFormat requestSurfaceImageFormat[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM }; @@ -333,7 +279,6 @@ static void SetupVulkan(WindowData* wd) wd->SurfaceFormat = ImGui_ImplVulkan_SelectSurfaceFormat(g_PhysicalDevice, wd->Surface, requestSurfaceImageFormat, (size_t)IM_ARRAYSIZE(requestSurfaceImageFormat), requestSurfaceColorSpace); } - // Get Present Mode { #ifdef IMGUI_UNLIMITED_FRAME_RATE @@ -344,7 +289,6 @@ static void SetupVulkan(WindowData* wd) wd->PresentMode = ImGui_ImplVulkan_SelectPresentMode(g_PhysicalDevice, wd->Surface, &present_mode, 1); } - // Create Logical Device (with 1 queue) { int device_extension_count = 1; @@ -369,7 +313,7 @@ static void SetupVulkan(WindowData* wd) // Create Command Buffers for (int i = 0; i < IMGUI_VK_QUEUED_FRAMES; i++) { - FrameData* fd = &wd->Frames[i]; + ImGui_ImplVulkan_FrameData* fd = &wd->Frames[i]; { VkCommandPoolCreateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; @@ -433,11 +377,11 @@ static void SetupVulkan(WindowData* wd) static void cleanup_vulkan() { - WindowData* wd = &g_WindowData; + ImGui_ImplVulkan_WindowData* wd = &g_WindowData; vkDestroyDescriptorPool(g_Device, g_DescriptorPool, g_Allocator); for (int i = 0; i < IMGUI_VK_QUEUED_FRAMES; i++) { - FrameData* fd = &wd->Frames[i]; + ImGui_ImplVulkan_FrameData* fd = &wd->Frames[i]; vkDestroyFence(g_Device, fd->Fence, g_Allocator); vkFreeCommandBuffers(g_Device, fd->CommandPool, 1, &fd->CommandBuffer); vkDestroyCommandPool(g_Device, fd->CommandPool, g_Allocator); @@ -463,9 +407,9 @@ static void cleanup_vulkan() vkDestroyInstance(g_Instance, g_Allocator); } -static void frame_begin(WindowData* wd) +static void frame_begin(ImGui_ImplVulkan_WindowData* wd) { - FrameData* fd = &wd->Frames[wd->FrameIndex]; + ImGui_ImplVulkan_FrameData* fd = &wd->Frames[wd->FrameIndex]; VkResult err; for (;;) { @@ -500,9 +444,9 @@ static void frame_begin(WindowData* wd) } } -static void frame_end(WindowData* wd) +static void frame_end(ImGui_ImplVulkan_WindowData* wd) { - FrameData* fd = &wd->Frames[wd->FrameIndex]; + ImGui_ImplVulkan_FrameData* fd = &wd->Frames[wd->FrameIndex]; VkResult err; vkCmdEndRenderPass(fd->CommandBuffer); { @@ -526,7 +470,7 @@ static void frame_end(WindowData* wd) } } -static void frame_present(WindowData* wd) +static void frame_present(ImGui_ImplVulkan_WindowData* wd) { VkResult err; // If IMGUI_UNLIMITED_FRAME_RATE is defined we present the latest but one frame. Otherwise we present the latest rendered frame @@ -536,7 +480,7 @@ static void frame_present(WindowData* wd) uint32_t PresentIndex = g_FrameIndex; #endif // IMGUI_UNLIMITED_FRAME_RATE - FrameData* fd = &wd->Frames[PresentIndex]; + ImGui_ImplVulkan_FrameData* fd = &wd->Frames[PresentIndex]; VkPresentInfoKHR info = {}; info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; info.waitSemaphoreCount = 1; @@ -579,7 +523,7 @@ int main(int, char**) CreateVulkanInstance(glfw_extensions, glfw_extensions_count); // Create Window Surface - WindowData* wd = &g_WindowData; + ImGui_ImplVulkan_WindowData* wd = &g_WindowData; { VkResult err = glfwCreateWindowSurface(g_Instance, window, g_Allocator, &wd->Surface); check_vk_result(err); @@ -724,6 +668,8 @@ int main(int, char**) ImGui_ImplVulkan_Render(wd->Frames[wd->FrameIndex].CommandBuffer); frame_end(wd); + ImGui::RenderAdditionalViewports(); + #ifdef IMGUI_UNLIMITED_FRAME_RATE // When IMGUI_UNLIMITED_FRAME_RATE is defined we render into latest image acquired from the swapchain but we display the image which was rendered before. // Hence we must render once and increase the FrameIndex without presenting. @@ -734,8 +680,6 @@ int main(int, char**) #endif swap_chain_has_at_least_one_image = true; wd->FrameIndex = (wd->FrameIndex + 1) % IMGUI_VK_QUEUED_FRAMES; - - //ImGui::RenderAdditionalViewports(); } // Cleanup From e0cbfd74d788c0e7fcd8a8e38a5e4f04322b6d4b Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 2 Mar 2018 23:59:21 +0100 Subject: [PATCH 055/828] Examples: Vulkan: Moved CreateOrResizeWindowData func to imgui_impl_vulkan.h --- examples/imgui_impl_vulkan.cpp | 238 ++++++++++++++++++++++----- examples/imgui_impl_vulkan.h | 19 ++- examples/sdl_vulkan_example/main.cpp | 195 +++------------------- examples/vulkan_example/main.cpp | 212 ++++-------------------- 4 files changed, 267 insertions(+), 397 deletions(-) diff --git a/examples/imgui_impl_vulkan.cpp b/examples/imgui_impl_vulkan.cpp index 7156ea10146f..e465d0697c84 100644 --- a/examples/imgui_impl_vulkan.cpp +++ b/examples/imgui_impl_vulkan.cpp @@ -26,11 +26,12 @@ // Vulkan data static const VkAllocationCallbacks* g_Allocator = NULL; static VkPhysicalDevice g_PhysicalDevice = VK_NULL_HANDLE; +static VkInstance g_Instance = VK_NULL_HANDLE; static VkDevice g_Device = VK_NULL_HANDLE; -static VkRenderPass g_RenderPass = VK_NULL_HANDLE; static VkPipelineCache g_PipelineCache = VK_NULL_HANDLE; static VkDescriptorPool g_DescriptorPool = VK_NULL_HANDLE; -static void (*g_CheckVkResult)(VkResult err) = NULL; +static VkRenderPass g_RenderPass = VK_NULL_HANDLE; +static void (*g_CheckVkResultFn)(VkResult err) = NULL; static VkDeviceSize g_BufferMemoryAlignment = 256; static VkPipelineCreateFlags g_PipelineCreateFlags = 0; @@ -150,13 +151,13 @@ static uint32_t ImGui_ImplVulkan_MemoryType(VkMemoryPropertyFlags properties, ui for (uint32_t i = 0; i < prop.memoryTypeCount; i++) if ((prop.memoryTypes[i].propertyFlags & properties) == properties && type_bits & (1<VertexBufferMemory, 0, vertex_size, 0, (void**)(&vtx_dst)); - ImGui_ImplVulkan_VkResult(err); + check_vk_result(err); err = vkMapMemory(g_Device, fd->IndexBufferMemory, 0, index_size, 0, (void**)(&idx_dst)); - ImGui_ImplVulkan_VkResult(err); + check_vk_result(err); for (int n = 0; n < draw_data->CmdListsCount; n++) { const ImDrawList* cmd_list = draw_data->CmdLists[n]; @@ -233,7 +234,7 @@ void ImGui_ImplVulkan_RenderDrawData(VkCommandBuffer command_buffer, ImDrawData* range[1].memory = fd->IndexBufferMemory; range[1].size = VK_WHOLE_SIZE; err = vkFlushMappedMemoryRanges(g_Device, 2, range); - ImGui_ImplVulkan_VkResult(err); + check_vk_result(err); vkUnmapMemory(g_Device, fd->VertexBufferMemory); vkUnmapMemory(g_Device, fd->IndexBufferMemory); } @@ -339,7 +340,7 @@ bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer) info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; err = vkCreateImage(g_Device, &info, g_Allocator, &g_FontImage); - ImGui_ImplVulkan_VkResult(err); + check_vk_result(err); VkMemoryRequirements req; vkGetImageMemoryRequirements(g_Device, g_FontImage, &req); VkMemoryAllocateInfo alloc_info = {}; @@ -347,9 +348,9 @@ bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer) alloc_info.allocationSize = req.size; alloc_info.memoryTypeIndex = ImGui_ImplVulkan_MemoryType(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, req.memoryTypeBits); err = vkAllocateMemory(g_Device, &alloc_info, g_Allocator, &g_FontMemory); - ImGui_ImplVulkan_VkResult(err); + check_vk_result(err); err = vkBindImageMemory(g_Device, g_FontImage, g_FontMemory, 0); - ImGui_ImplVulkan_VkResult(err); + check_vk_result(err); } // Create the Image View: @@ -363,7 +364,7 @@ bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer) info.subresourceRange.levelCount = 1; info.subresourceRange.layerCount = 1; err = vkCreateImageView(g_Device, &info, g_Allocator, &g_FontView); - ImGui_ImplVulkan_VkResult(err); + check_vk_result(err); } // Update the Descriptor Set: @@ -389,7 +390,7 @@ bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer) buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; err = vkCreateBuffer(g_Device, &buffer_info, g_Allocator, &g_UploadBuffer); - ImGui_ImplVulkan_VkResult(err); + check_vk_result(err); VkMemoryRequirements req; vkGetBufferMemoryRequirements(g_Device, g_UploadBuffer, &req); g_BufferMemoryAlignment = (g_BufferMemoryAlignment > req.alignment) ? g_BufferMemoryAlignment : req.alignment; @@ -398,23 +399,23 @@ bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer) alloc_info.allocationSize = req.size; alloc_info.memoryTypeIndex = ImGui_ImplVulkan_MemoryType(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, req.memoryTypeBits); err = vkAllocateMemory(g_Device, &alloc_info, g_Allocator, &g_UploadBufferMemory); - ImGui_ImplVulkan_VkResult(err); + check_vk_result(err); err = vkBindBufferMemory(g_Device, g_UploadBuffer, g_UploadBufferMemory, 0); - ImGui_ImplVulkan_VkResult(err); + check_vk_result(err); } // Upload to Buffer: { char* map = NULL; err = vkMapMemory(g_Device, g_UploadBufferMemory, 0, upload_size, 0, (void**)(&map)); - ImGui_ImplVulkan_VkResult(err); + check_vk_result(err); memcpy(map, pixels, upload_size); VkMappedMemoryRange range[1] = {}; range[0].sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; range[0].memory = g_UploadBufferMemory; range[0].size = upload_size; err = vkFlushMappedMemoryRanges(g_Device, 1, range); - ImGui_ImplVulkan_VkResult(err); + check_vk_result(err); vkUnmapMemory(g_Device, g_UploadBufferMemory); } @@ -475,13 +476,13 @@ bool ImGui_ImplVulkan_CreateDeviceObjects() vert_info.codeSize = sizeof(__glsl_shader_vert_spv); vert_info.pCode = (uint32_t*)__glsl_shader_vert_spv; err = vkCreateShaderModule(g_Device, &vert_info, g_Allocator, &vert_module); - ImGui_ImplVulkan_VkResult(err); + check_vk_result(err); VkShaderModuleCreateInfo frag_info = {}; frag_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; frag_info.codeSize = sizeof(__glsl_shader_frag_spv); frag_info.pCode = (uint32_t*)__glsl_shader_frag_spv; err = vkCreateShaderModule(g_Device, &frag_info, g_Allocator, &frag_module); - ImGui_ImplVulkan_VkResult(err); + check_vk_result(err); } if (!g_FontSampler) @@ -498,7 +499,7 @@ bool ImGui_ImplVulkan_CreateDeviceObjects() info.maxLod = 1000; info.maxAnisotropy = 1.0f; err = vkCreateSampler(g_Device, &info, g_Allocator, &g_FontSampler); - ImGui_ImplVulkan_VkResult(err); + check_vk_result(err); } if (!g_DescriptorSetLayout) @@ -514,7 +515,7 @@ bool ImGui_ImplVulkan_CreateDeviceObjects() info.bindingCount = 1; info.pBindings = binding; err = vkCreateDescriptorSetLayout(g_Device, &info, g_Allocator, &g_DescriptorSetLayout); - ImGui_ImplVulkan_VkResult(err); + check_vk_result(err); } // Create Descriptor Set: @@ -525,7 +526,7 @@ bool ImGui_ImplVulkan_CreateDeviceObjects() alloc_info.descriptorSetCount = 1; alloc_info.pSetLayouts = &g_DescriptorSetLayout; err = vkAllocateDescriptorSets(g_Device, &alloc_info, &g_DescriptorSet); - ImGui_ImplVulkan_VkResult(err); + check_vk_result(err); } if (!g_PipelineLayout) @@ -543,7 +544,7 @@ bool ImGui_ImplVulkan_CreateDeviceObjects() layout_info.pushConstantRangeCount = 1; layout_info.pPushConstantRanges = push_constants; err = vkCreatePipelineLayout(g_Device, &layout_info, g_Allocator, &g_PipelineLayout); - ImGui_ImplVulkan_VkResult(err); + check_vk_result(err); } VkPipelineShaderStageCreateInfo stage[2] = {}; @@ -641,7 +642,7 @@ bool ImGui_ImplVulkan_CreateDeviceObjects() info.layout = g_PipelineLayout; info.renderPass = g_RenderPass; err = vkCreateGraphicsPipelines(g_Device, g_PipelineCache, 1, &info, g_Allocator, &g_Pipeline); - ImGui_ImplVulkan_VkResult(err); + check_vk_result(err); vkDestroyShaderModule(g_Device, vert_module, g_Allocator); vkDestroyShaderModule(g_Device, frag_module, g_Allocator); @@ -685,15 +686,19 @@ void ImGui_ImplVulkan_InvalidateDeviceObjects() if (g_Pipeline) { vkDestroyPipeline(g_Device, g_Pipeline, g_Allocator); g_Pipeline = VK_NULL_HANDLE; } } -bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo *init_data) +bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info, VkRenderPass render_pass) { - g_Allocator = init_data->Allocator; - g_PhysicalDevice = init_data->PhysicalDevice; - g_Device = init_data->Device; - g_RenderPass = init_data->RenderPass; - g_PipelineCache = init_data->PipelineCache; - g_DescriptorPool = init_data->DescriptorPool; - g_CheckVkResult = init_data->CheckVkResultFn; + IM_ASSERT(info->Instance != NULL); + IM_ASSERT(info->PhysicalDevice != NULL); + IM_ASSERT(info->Device != NULL); + + g_Allocator = info->Allocator; + g_PhysicalDevice = info->PhysicalDevice; + g_Device = info->Device; + g_RenderPass = render_pass; + g_PipelineCache = info->PipelineCache; + g_DescriptorPool = info->DescriptorPool; + g_CheckVkResultFn = info->CheckVkResultFn; ImGuiIO& io = ImGui::GetIO(); ImGui_ImplVulkan_CreateDeviceObjects(); @@ -753,9 +758,7 @@ ImGui_ImplVulkan_WindowData::ImGui_ImplVulkan_WindowData() FrameIndex = 0; } - - -VkSurfaceFormatKHR ImGui_ImplVulkan_SelectSurfaceFormat(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkFormat* request_formats, int request_formats_count, VkColorSpaceKHR request_color_space) +VkSurfaceFormatKHR ImGui_ImplVulkanH_SelectSurfaceFormat(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkFormat* request_formats, int request_formats_count, VkColorSpaceKHR request_color_space) { IM_ASSERT(request_formats != NULL); IM_ASSERT(request_formats_count > 0); @@ -799,7 +802,7 @@ VkSurfaceFormatKHR ImGui_ImplVulkan_SelectSurfaceFormat(VkPhysicalDevice physica } } -VkPresentModeKHR ImGui_ImplVulkan_SelectPresentMode(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkPresentModeKHR* request_modes, int request_modes_count) +VkPresentModeKHR ImGui_ImplVulkanH_SelectPresentMode(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkPresentModeKHR* request_modes, int request_modes_count) { IM_ASSERT(request_modes != NULL); IM_ASSERT(request_modes_count > 0); @@ -818,6 +821,159 @@ VkPresentModeKHR ImGui_ImplVulkan_SelectPresentMode(VkPhysicalDevice physical_de return VK_PRESENT_MODE_FIFO_KHR; // Always available } +void ImGui_ImplVulkanH_CreateOrResizeWindowData(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkan_WindowData* wd, const VkAllocationCallbacks* allocator, int w, int h) +{ + IM_ASSERT(physical_device != NULL && device != NULL); + + VkResult err; + VkSwapchainKHR old_swapchain = wd->Swapchain; + err = vkDeviceWaitIdle(device); + check_vk_result(err); + + // Destroy old Framebuffer + for (uint32_t i = 0; i < wd->BackBufferCount; i++) + { + if (wd->BackBufferView[i]) + vkDestroyImageView(device, wd->BackBufferView[i], allocator); + if (wd->Framebuffer[i]) + vkDestroyFramebuffer(device, wd->Framebuffer[i], allocator); + } + wd->BackBufferCount = 0; + if (wd->RenderPass) + vkDestroyRenderPass(device, wd->RenderPass, allocator); + + // Create Swapchain + { + VkSwapchainCreateInfoKHR info = {}; + info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + info.surface = wd->Surface; + info.imageFormat = wd->SurfaceFormat.format; + info.imageColorSpace = wd->SurfaceFormat.colorSpace; + info.imageArrayLayers = 1; + info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; // Assume that graphics family == present family + info.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; + info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + info.presentMode = wd->PresentMode; + info.clipped = VK_TRUE; + info.oldSwapchain = old_swapchain; + VkSurfaceCapabilitiesKHR cap; + err = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, wd->Surface, &cap); + check_vk_result(err); + if (cap.maxImageCount > 0) + info.minImageCount = (cap.minImageCount + 2 < cap.maxImageCount) ? (cap.minImageCount + 2) : cap.maxImageCount; + else + info.minImageCount = cap.minImageCount + 2; + + if (cap.currentExtent.width == 0xffffffff) + { + info.imageExtent.width = wd->Width = w; + info.imageExtent.height = wd->Height = h; + } + else + { + info.imageExtent.width = wd->Width = cap.currentExtent.width; + info.imageExtent.height = wd->Height = cap.currentExtent.height; + } + err = vkCreateSwapchainKHR(device, &info, allocator, &wd->Swapchain); + check_vk_result(err); + err = vkGetSwapchainImagesKHR(device, wd->Swapchain, &wd->BackBufferCount, NULL); + check_vk_result(err); + err = vkGetSwapchainImagesKHR(device, wd->Swapchain, &wd->BackBufferCount, wd->BackBuffer); + check_vk_result(err); + } + if (old_swapchain) + vkDestroySwapchainKHR(device, old_swapchain, allocator); + + // Create the Render Pass + { + VkAttachmentDescription attachment = {}; + attachment.format = wd->SurfaceFormat.format; + attachment.samples = VK_SAMPLE_COUNT_1_BIT; + attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + VkAttachmentReference color_attachment = {}; + color_attachment.attachment = 0; + color_attachment.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + VkSubpassDescription subpass = {}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &color_attachment; + VkRenderPassCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + info.attachmentCount = 1; + info.pAttachments = &attachment; + info.subpassCount = 1; + info.pSubpasses = &subpass; + err = vkCreateRenderPass(device, &info, allocator, &wd->RenderPass); + check_vk_result(err); + } + + // Create The Image Views + { + VkImageViewCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + info.viewType = VK_IMAGE_VIEW_TYPE_2D; + info.format = wd->SurfaceFormat.format; + info.components.r = VK_COMPONENT_SWIZZLE_R; + info.components.g = VK_COMPONENT_SWIZZLE_G; + info.components.b = VK_COMPONENT_SWIZZLE_B; + info.components.a = VK_COMPONENT_SWIZZLE_A; + VkImageSubresourceRange image_range = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; + info.subresourceRange = image_range; + for (uint32_t i = 0; i < wd->BackBufferCount; i++) + { + info.image = wd->BackBuffer[i]; + err = vkCreateImageView(device, &info, allocator, &wd->BackBufferView[i]); + check_vk_result(err); + } + } + + // Create Framebuffer + { + VkImageView attachment[1]; + VkFramebufferCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + info.renderPass = wd->RenderPass; + info.attachmentCount = 1; + info.pAttachments = attachment; + info.width = wd->Width; + info.height = wd->Height; + info.layers = 1; + for (uint32_t i = 0; i < wd->BackBufferCount; i++) + { + attachment[0] = wd->BackBufferView[i]; + err = vkCreateFramebuffer(device, &info, allocator, &wd->Framebuffer[i]); + check_vk_result(err); + } + } +} + +void ImGui_ImplVulkanH_DestroyWindowData(VkInstance instance, VkDevice device, ImGui_ImplVulkan_WindowData* wd, const VkAllocationCallbacks* allocator) +{ + for (int i = 0; i < IMGUI_VK_QUEUED_FRAMES; i++) + { + ImGui_ImplVulkan_FrameData* fd = &wd->Frames[i]; + vkDestroyFence(device, fd->Fence, allocator); + vkFreeCommandBuffers(device, fd->CommandPool, 1, &fd->CommandBuffer); + vkDestroyCommandPool(device, fd->CommandPool, allocator); + vkDestroySemaphore(device, fd->PresentCompleteSemaphore, allocator); + vkDestroySemaphore(device, fd->RenderCompleteSemaphore, allocator); + } + for (uint32_t i = 0; i < wd->BackBufferCount; i++) + { + vkDestroyImageView(device, wd->BackBufferView[i], allocator); + vkDestroyFramebuffer(device, wd->Framebuffer[i], allocator); + } + vkDestroyRenderPass(device, wd->RenderPass, allocator); + vkDestroySwapchainKHR(device, wd->Swapchain, allocator); + vkDestroySurfaceKHR(instance, wd->Surface, allocator); +} + //-------------------------------------------------------------------------------------------------------- // Platform Windows (OPTIONAL/EXPERIMENTAL) //-------------------------------------------------------------------------------------------------------- @@ -826,7 +982,7 @@ VkPresentModeKHR ImGui_ImplVulkan_SelectPresentMode(VkPhysicalDevice physical_de struct ImGuiPlatformDataVulkan { - // store swap chain, render target/frame buffer, etc. + ImGui_ImplVulkan_WindowData Wd; ImGuiPlatformDataVulkan() { } ~ImGuiPlatformDataVulkan() { } diff --git a/examples/imgui_impl_vulkan.h b/examples/imgui_impl_vulkan.h index cb095e379f7e..f6a4b71aaf4c 100644 --- a/examples/imgui_impl_vulkan.h +++ b/examples/imgui_impl_vulkan.h @@ -15,16 +15,16 @@ struct ImGui_ImplVulkan_InitInfo { - const VkAllocationCallbacks* Allocator; + VkInstance Instance; VkPhysicalDevice PhysicalDevice; VkDevice Device; - VkRenderPass RenderPass; VkPipelineCache PipelineCache; VkDescriptorPool DescriptorPool; + const VkAllocationCallbacks* Allocator; void (*CheckVkResultFn)(VkResult err); }; -IMGUI_API bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo *init_data); +IMGUI_API bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info, VkRenderPass render_pass); IMGUI_API void ImGui_ImplVulkan_Shutdown(); IMGUI_API void ImGui_ImplVulkan_NewFrame(); IMGUI_API void ImGui_ImplVulkan_Render(VkCommandBuffer command_buffer); @@ -42,9 +42,17 @@ IMGUI_API bool ImGui_ImplVulkan_CreateDeviceObjects(); // But for the purpose of allowing multi-windows, we need those internally anyway. The code being not trivial are exposing it for the benefit of the example code. // If your application/engine already has code to create all that data (swap chain, render pass, frame buffers, etc.) you can ignore all of this. //------------------------------------------------------------------------- +// NB: Those functions do NOT use any of the state used/affected by the regular ImGui_ImplVulkan_XXX functions. +//------------------------------------------------------------------------- -IMGUI_API VkSurfaceFormatKHR ImGui_ImplVulkan_SelectSurfaceFormat(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkFormat* request_formats, int request_formats_count, VkColorSpaceKHR request_color_space); -IMGUI_API VkPresentModeKHR ImGui_ImplVulkan_SelectPresentMode(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkPresentModeKHR* request_modes, int request_modes_count); +struct ImGui_ImplVulkan_FrameData; +struct ImGui_ImplVulkan_WindowData; +struct ImGui_ImplVulkan_WindowDataCreateInfo; + +IMGUI_API VkSurfaceFormatKHR ImGui_ImplVulkanH_SelectSurfaceFormat(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkFormat* request_formats, int request_formats_count, VkColorSpaceKHR request_color_space); +IMGUI_API VkPresentModeKHR ImGui_ImplVulkanH_SelectPresentMode(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkPresentModeKHR* request_modes, int request_modes_count); +IMGUI_API void ImGui_ImplVulkanH_CreateOrResizeWindowData(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkan_WindowData* wd, const VkAllocationCallbacks* allocator, int w, int h); +IMGUI_API void ImGui_ImplVulkanH_DestroyWindowData(VkInstance instance, VkDevice device, ImGui_ImplVulkan_WindowData* wd, const VkAllocationCallbacks* allocator); struct ImGui_ImplVulkan_FrameData { @@ -76,3 +84,4 @@ struct ImGui_ImplVulkan_WindowData IMGUI_API ImGui_ImplVulkan_WindowData(); }; + diff --git a/examples/sdl_vulkan_example/main.cpp b/examples/sdl_vulkan_example/main.cpp index 95e255ecd3b1..cd8bab81599b 100644 --- a/examples/sdl_vulkan_example/main.cpp +++ b/examples/sdl_vulkan_example/main.cpp @@ -15,17 +15,17 @@ #define IMGUI_VULKAN_DEBUG_REPORT #endif -static VkAllocationCallbacks* g_Allocator = NULL; -static VkInstance g_Instance = VK_NULL_HANDLE; -static VkPhysicalDevice g_PhysicalDevice = VK_NULL_HANDLE; -static VkDevice g_Device = VK_NULL_HANDLE; -static uint32_t g_QueueFamily = (uint32_t)-1; -static VkQueue g_Queue = VK_NULL_HANDLE; -static VkDebugReportCallbackEXT g_DebugReport = VK_NULL_HANDLE; -static VkPipelineCache g_PipelineCache = VK_NULL_HANDLE; -static VkDescriptorPool g_DescriptorPool = VK_NULL_HANDLE; - -static ImGui_ImplVulkan_WindowData g_WindowData; +static VkAllocationCallbacks* g_Allocator = NULL; +static VkInstance g_Instance = VK_NULL_HANDLE; +static VkPhysicalDevice g_PhysicalDevice = VK_NULL_HANDLE; +static VkDevice g_Device = VK_NULL_HANDLE; +static uint32_t g_QueueFamily = (uint32_t)-1; +static VkQueue g_Queue = VK_NULL_HANDLE; +static VkDebugReportCallbackEXT g_DebugReport = VK_NULL_HANDLE; +static VkPipelineCache g_PipelineCache = VK_NULL_HANDLE; +static VkDescriptorPool g_DescriptorPool = VK_NULL_HANDLE; + +static ImGui_ImplVulkan_WindowData g_WindowData; static void check_vk_result(VkResult err) { @@ -35,136 +35,6 @@ static void check_vk_result(VkResult err) abort(); } -static void CreateOrResizeSwapChainAndFrameBuffer(ImGui_ImplVulkan_WindowData* wd, int w, int h) -{ - VkResult err; - VkSwapchainKHR old_swapchain = wd->Swapchain; - err = vkDeviceWaitIdle(g_Device); - check_vk_result(err); - - // Destroy old Framebuffer - for (uint32_t i = 0; i < wd->BackBufferCount; i++) - { - if (wd->BackBufferView[i]) - vkDestroyImageView(g_Device, wd->BackBufferView[i], g_Allocator); - if (wd->Framebuffer[i]) - vkDestroyFramebuffer(g_Device, wd->Framebuffer[i], g_Allocator); - } - wd->BackBufferCount = 0; - if (wd->RenderPass) - vkDestroyRenderPass(g_Device, wd->RenderPass, g_Allocator); - - // Create Swapchain - { - VkSwapchainCreateInfoKHR info = {}; - info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; - info.surface = wd->Surface; - info.imageFormat = wd->SurfaceFormat.format; - info.imageColorSpace = wd->SurfaceFormat.colorSpace; - info.imageArrayLayers = 1; - info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; - info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; // Assume that graphics family == present family - info.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; - info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; - info.presentMode = wd->PresentMode; - info.clipped = VK_TRUE; - info.oldSwapchain = old_swapchain; - VkSurfaceCapabilitiesKHR cap; - err = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(g_PhysicalDevice, wd->Surface, &cap); - check_vk_result(err); - if (cap.maxImageCount > 0) - info.minImageCount = (cap.minImageCount + 2 < cap.maxImageCount) ? (cap.minImageCount + 2) : cap.maxImageCount; - else - info.minImageCount = cap.minImageCount + 2; - - if (cap.currentExtent.width == 0xffffffff) - { - info.imageExtent.width = wd->Width = w; - info.imageExtent.height = wd->Height = h; - } - else - { - info.imageExtent.width = wd->Width = cap.currentExtent.width; - info.imageExtent.height = wd->Height = cap.currentExtent.height; - } - err = vkCreateSwapchainKHR(g_Device, &info, g_Allocator, &wd->Swapchain); - check_vk_result(err); - err = vkGetSwapchainImagesKHR(g_Device, wd->Swapchain, &wd->BackBufferCount, NULL); - check_vk_result(err); - err = vkGetSwapchainImagesKHR(g_Device, wd->Swapchain, &wd->BackBufferCount, wd->BackBuffer); - check_vk_result(err); - } - if (old_swapchain) - vkDestroySwapchainKHR(g_Device, old_swapchain, g_Allocator); - - // Create the Render Pass - { - VkAttachmentDescription attachment = {}; - attachment.format = wd->SurfaceFormat.format; - attachment.samples = VK_SAMPLE_COUNT_1_BIT; - attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - VkAttachmentReference color_attachment = {}; - color_attachment.attachment = 0; - color_attachment.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - VkSubpassDescription subpass = {}; - subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpass.colorAttachmentCount = 1; - subpass.pColorAttachments = &color_attachment; - VkRenderPassCreateInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; - info.attachmentCount = 1; - info.pAttachments = &attachment; - info.subpassCount = 1; - info.pSubpasses = &subpass; - err = vkCreateRenderPass(g_Device, &info, g_Allocator, &wd->RenderPass); - check_vk_result(err); - } - - // Create The Image Views - { - VkImageViewCreateInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - info.viewType = VK_IMAGE_VIEW_TYPE_2D; - info.format = wd->SurfaceFormat.format; - info.components.r = VK_COMPONENT_SWIZZLE_R; - info.components.g = VK_COMPONENT_SWIZZLE_G; - info.components.b = VK_COMPONENT_SWIZZLE_B; - info.components.a = VK_COMPONENT_SWIZZLE_A; - VkImageSubresourceRange image_range = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; - info.subresourceRange = image_range; - for (uint32_t i = 0; i < wd->BackBufferCount; i++) - { - info.image = wd->BackBuffer[i]; - err = vkCreateImageView(g_Device, &info, g_Allocator, &wd->BackBufferView[i]); - check_vk_result(err); - } - } - - // Create Framebuffer - { - VkImageView attachment[1]; - VkFramebufferCreateInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; - info.renderPass = wd->RenderPass; - info.attachmentCount = 1; - info.pAttachments = attachment; - info.width = wd->Width; - info.height = wd->Height; - info.layers = 1; - for (uint32_t i = 0; i < wd->BackBufferCount; i++) - { - attachment[0] = wd->BackBufferView[i]; - err = vkCreateFramebuffer(g_Device, &info, g_Allocator, &wd->Framebuffer[i]); - check_vk_result(err); - } - } -} - #ifdef IMGUI_VULKAN_DEBUG_REPORT static VKAPI_ATTR VkBool32 VKAPI_CALL debug_report(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char* pLayerPrefix, const char* pMessage, void* pUserData) { @@ -222,6 +92,8 @@ static void CreateVulkanInstance(const char** extensions, uint32_t extensions_co static void SetupVulkan(ImGui_ImplVulkan_WindowData* wd) { + IM_ASSERT(wd->Surface != NULL); + VkResult err; // Select GPU @@ -272,7 +144,7 @@ static void SetupVulkan(ImGui_ImplVulkan_WindowData* wd) { const VkFormat requestSurfaceImageFormat[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM }; const VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; - wd->SurfaceFormat = ImGui_ImplVulkan_SelectSurfaceFormat(g_PhysicalDevice, wd->Surface, requestSurfaceImageFormat, (size_t)IM_ARRAYSIZE(requestSurfaceImageFormat), requestSurfaceColorSpace); + wd->SurfaceFormat = ImGui_ImplVulkanH_SelectSurfaceFormat(g_PhysicalDevice, wd->Surface, requestSurfaceImageFormat, (size_t)IM_ARRAYSIZE(requestSurfaceImageFormat), requestSurfaceColorSpace); } // Get Present Mode @@ -282,7 +154,7 @@ static void SetupVulkan(ImGui_ImplVulkan_WindowData* wd) #else VkPresentModeKHR present_mode = VK_PRESENT_MODE_FIFO_KHR; #endif - wd->PresentMode = ImGui_ImplVulkan_SelectPresentMode(g_PhysicalDevice, wd->Surface, &present_mode, 1); + wd->PresentMode = ImGui_ImplVulkanH_SelectPresentMode(g_PhysicalDevice, wd->Surface, &present_mode, 1); } // Create Logical Device (with 1 queue) @@ -375,23 +247,7 @@ static void cleanup_vulkan() { ImGui_ImplVulkan_WindowData* wd = &g_WindowData; vkDestroyDescriptorPool(g_Device, g_DescriptorPool, g_Allocator); - for (int i = 0; i < IMGUI_VK_QUEUED_FRAMES; i++) - { - ImGui_ImplVulkan_FrameData* fd = &wd->Frames[i]; - vkDestroyFence(g_Device, fd->Fence, g_Allocator); - vkFreeCommandBuffers(g_Device, fd->CommandPool, 1, &fd->CommandBuffer); - vkDestroyCommandPool(g_Device, fd->CommandPool, g_Allocator); - vkDestroySemaphore(g_Device, fd->PresentCompleteSemaphore, g_Allocator); - vkDestroySemaphore(g_Device, fd->RenderCompleteSemaphore, g_Allocator); - } - for (uint32_t i = 0; i < wd->BackBufferCount; i++) - { - vkDestroyImageView(g_Device, wd->BackBufferView[i], g_Allocator); - vkDestroyFramebuffer(g_Device, wd->Framebuffer[i], g_Allocator); - } - vkDestroyRenderPass(g_Device, wd->RenderPass, g_Allocator); - vkDestroySwapchainKHR(g_Device, wd->Swapchain, g_Allocator); - vkDestroySurfaceKHR(g_Instance, wd->Surface, g_Allocator); + ImGui_ImplVulkanH_DestroyWindowData(g_Instance, g_Device, wd, g_Allocator); #ifdef IMGUI_VULKAN_DEBUG_REPORT // Remove the debug report callback @@ -525,26 +381,25 @@ int main(int, char**) { int w, h; SDL_GetWindowSize(window, &w, &h); - CreateOrResizeSwapChainAndFrameBuffer(wd, w, h); + ImGui_ImplVulkanH_CreateOrResizeWindowData(g_PhysicalDevice, g_Device, wd, g_Allocator, w, h); } // Setup ImGui binding ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); (void)io; + io.ConfigFlags |= ImGuiConfigFlags_MultiViewports; + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + ImGui_ImplVulkan_InitInfo init_info = {}; - init_info.Allocator = g_Allocator; + init_info.Instance = g_Instance; init_info.PhysicalDevice = g_PhysicalDevice; init_info.Device = g_Device; - init_info.RenderPass = wd->RenderPass; init_info.PipelineCache = g_PipelineCache; init_info.DescriptorPool = g_DescriptorPool; + init_info.Allocator = g_Allocator; init_info.CheckVkResultFn = check_vk_result; - - ImGuiIO& io = ImGui::GetIO(); (void)io; - //io.ConfigFlags |= ImGuiConfigFlags_MultiViewports; - //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls - - ImGui_ImplVulkan_Init(&init_info); + ImGui_ImplVulkan_Init(&init_info, wd->RenderPass); ImGui_ImplSDL2_Init(window, NULL); // Setup style @@ -618,7 +473,7 @@ int main(int, char**) if (event.type == SDL_QUIT) done = true; if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_RESIZED && event.window.windowID == SDL_GetWindowID(window)) - CreateOrResizeSwapChainAndFrameBuffer(wd, (int)event.window.data1, (int)event.window.data2); + ImGui_ImplVulkanH_CreateOrResizeWindowData(g_PhysicalDevice, g_Device, &g_WindowData, g_Allocator, (int)event.window.data1, (int)event.window.data2); } ImGui_ImplVulkan_NewFrame(); ImGui_ImplSDL2_NewFrame(window); diff --git a/examples/vulkan_example/main.cpp b/examples/vulkan_example/main.cpp index 7f076768cab3..9384dd25b218 100644 --- a/examples/vulkan_example/main.cpp +++ b/examples/vulkan_example/main.cpp @@ -18,17 +18,17 @@ #define IMGUI_VULKAN_DEBUG_REPORT #endif -static VkAllocationCallbacks* g_Allocator = NULL; -static VkInstance g_Instance = VK_NULL_HANDLE; -static VkPhysicalDevice g_PhysicalDevice = VK_NULL_HANDLE; -static VkDevice g_Device = VK_NULL_HANDLE; -static uint32_t g_QueueFamily = (uint32_t)-1; -static VkQueue g_Queue = VK_NULL_HANDLE; -static VkDebugReportCallbackEXT g_DebugReport = VK_NULL_HANDLE; -static VkPipelineCache g_PipelineCache = VK_NULL_HANDLE; -static VkDescriptorPool g_DescriptorPool = VK_NULL_HANDLE; - -static ImGui_ImplVulkan_WindowData g_WindowData; +static VkAllocationCallbacks* g_Allocator = NULL; +static VkInstance g_Instance = VK_NULL_HANDLE; +static VkPhysicalDevice g_PhysicalDevice = VK_NULL_HANDLE; +static VkDevice g_Device = VK_NULL_HANDLE; +static uint32_t g_QueueFamily = (uint32_t)-1; +static VkQueue g_Queue = VK_NULL_HANDLE; +static VkDebugReportCallbackEXT g_DebugReport = VK_NULL_HANDLE; +static VkPipelineCache g_PipelineCache = VK_NULL_HANDLE; +static VkDescriptorPool g_DescriptorPool = VK_NULL_HANDLE; + +static ImGui_ImplVulkan_WindowData g_WindowData; static void check_vk_result(VkResult err) { @@ -38,137 +38,6 @@ static void check_vk_result(VkResult err) abort(); } -static void CreateOrResizeSwapChainAndFrameBuffer(ImGui_ImplVulkan_WindowData* wd, int w, int h) -{ - VkResult err; - VkSwapchainKHR old_swapchain = wd->Swapchain; - err = vkDeviceWaitIdle(g_Device); - check_vk_result(err); - - // Destroy old Framebuffer - for (uint32_t i = 0; i < wd->BackBufferCount; i++) - { - if (wd->BackBufferView[i]) - vkDestroyImageView(g_Device, wd->BackBufferView[i], g_Allocator); - if (wd->Framebuffer[i]) - vkDestroyFramebuffer(g_Device, wd->Framebuffer[i], g_Allocator); - } - wd->BackBufferCount = 0; - if (wd->RenderPass) - vkDestroyRenderPass(g_Device, wd->RenderPass, g_Allocator); - - // Create Swapchain - { - VkSwapchainCreateInfoKHR info = {}; - info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; - info.surface = wd->Surface; - info.imageFormat = wd->SurfaceFormat.format; - info.imageColorSpace = wd->SurfaceFormat.colorSpace; - info.imageArrayLayers = 1; - info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; - info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; // Assume that graphics family == present family - info.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; - info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; - info.presentMode = wd->PresentMode; - info.clipped = VK_TRUE; - info.oldSwapchain = old_swapchain; - VkSurfaceCapabilitiesKHR cap; - err = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(g_PhysicalDevice, wd->Surface, &cap); - check_vk_result(err); - if (cap.maxImageCount > 0) - info.minImageCount = (cap.minImageCount + 2 < cap.maxImageCount) ? (cap.minImageCount + 2) : cap.maxImageCount; - else - info.minImageCount = cap.minImageCount + 2; - - if (cap.currentExtent.width == 0xffffffff) - { - info.imageExtent.width = wd->Width = w; - info.imageExtent.height = wd->Height = h; - } - else - { - info.imageExtent.width = wd->Width = cap.currentExtent.width; - info.imageExtent.height = wd->Height = cap.currentExtent.height; - } - err = vkCreateSwapchainKHR(g_Device, &info, g_Allocator, &wd->Swapchain); - check_vk_result(err); - err = vkGetSwapchainImagesKHR(g_Device, wd->Swapchain, &wd->BackBufferCount, NULL); - check_vk_result(err); - IM_ASSERT(wd->BackBufferCount < IM_ARRAYSIZE(wd->BackBuffer)); - err = vkGetSwapchainImagesKHR(g_Device, wd->Swapchain, &wd->BackBufferCount, wd->BackBuffer); - check_vk_result(err); - } - if (old_swapchain) - vkDestroySwapchainKHR(g_Device, old_swapchain, g_Allocator); - - // Create the Render Pass - { - VkAttachmentDescription attachment = {}; - attachment.format = wd->SurfaceFormat.format; - attachment.samples = VK_SAMPLE_COUNT_1_BIT; - attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - VkAttachmentReference color_attachment = {}; - color_attachment.attachment = 0; - color_attachment.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - VkSubpassDescription subpass = {}; - subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpass.colorAttachmentCount = 1; - subpass.pColorAttachments = &color_attachment; - VkRenderPassCreateInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; - info.attachmentCount = 1; - info.pAttachments = &attachment; - info.subpassCount = 1; - info.pSubpasses = &subpass; - err = vkCreateRenderPass(g_Device, &info, g_Allocator, &wd->RenderPass); - check_vk_result(err); - } - - // Create The Image Views - { - VkImageViewCreateInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - info.viewType = VK_IMAGE_VIEW_TYPE_2D; - info.format = wd->SurfaceFormat.format; - info.components.r = VK_COMPONENT_SWIZZLE_R; - info.components.g = VK_COMPONENT_SWIZZLE_G; - info.components.b = VK_COMPONENT_SWIZZLE_B; - info.components.a = VK_COMPONENT_SWIZZLE_A; - VkImageSubresourceRange image_range = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; - info.subresourceRange = image_range; - for (uint32_t i = 0; i < wd->BackBufferCount; i++) - { - info.image = wd->BackBuffer[i]; - err = vkCreateImageView(g_Device, &info, g_Allocator, &wd->BackBufferView[i]); - check_vk_result(err); - } - } - - // Create Framebuffer - { - VkImageView attachment[1]; - VkFramebufferCreateInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; - info.renderPass = wd->RenderPass; - info.attachmentCount = 1; - info.pAttachments = attachment; - info.width = wd->Width; - info.height = wd->Height; - info.layers = 1; - for (uint32_t i = 0; i < wd->BackBufferCount; i++) - { - attachment[0] = wd->BackBufferView[i]; - err = vkCreateFramebuffer(g_Device, &info, g_Allocator, &wd->Framebuffer[i]); - check_vk_result(err); - } - } -} - #ifdef IMGUI_VULKAN_DEBUG_REPORT static VKAPI_ATTR VkBool32 VKAPI_CALL debug_report(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char* pLayerPrefix, const char* pMessage, void* pUserData) { @@ -226,6 +95,8 @@ static void CreateVulkanInstance(const char** extensions, uint32_t extensions_co static void SetupVulkan(ImGui_ImplVulkan_WindowData* wd) { + IM_ASSERT(wd->Surface != NULL); + VkResult err; // Select GPU @@ -276,7 +147,7 @@ static void SetupVulkan(ImGui_ImplVulkan_WindowData* wd) { const VkFormat requestSurfaceImageFormat[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM }; const VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; - wd->SurfaceFormat = ImGui_ImplVulkan_SelectSurfaceFormat(g_PhysicalDevice, wd->Surface, requestSurfaceImageFormat, (size_t)IM_ARRAYSIZE(requestSurfaceImageFormat), requestSurfaceColorSpace); + wd->SurfaceFormat = ImGui_ImplVulkanH_SelectSurfaceFormat(g_PhysicalDevice, wd->Surface, requestSurfaceImageFormat, (size_t)IM_ARRAYSIZE(requestSurfaceImageFormat), requestSurfaceColorSpace); } // Get Present Mode @@ -286,7 +157,7 @@ static void SetupVulkan(ImGui_ImplVulkan_WindowData* wd) #else VkPresentModeKHR present_mode = VK_PRESENT_MODE_FIFO_KHR; #endif - wd->PresentMode = ImGui_ImplVulkan_SelectPresentMode(g_PhysicalDevice, wd->Surface, &present_mode, 1); + wd->PresentMode = ImGui_ImplVulkanH_SelectPresentMode(g_PhysicalDevice, wd->Surface, &present_mode, 1); } // Create Logical Device (with 1 queue) @@ -379,23 +250,7 @@ static void cleanup_vulkan() { ImGui_ImplVulkan_WindowData* wd = &g_WindowData; vkDestroyDescriptorPool(g_Device, g_DescriptorPool, g_Allocator); - for (int i = 0; i < IMGUI_VK_QUEUED_FRAMES; i++) - { - ImGui_ImplVulkan_FrameData* fd = &wd->Frames[i]; - vkDestroyFence(g_Device, fd->Fence, g_Allocator); - vkFreeCommandBuffers(g_Device, fd->CommandPool, 1, &fd->CommandBuffer); - vkDestroyCommandPool(g_Device, fd->CommandPool, g_Allocator); - vkDestroySemaphore(g_Device, fd->PresentCompleteSemaphore, g_Allocator); - vkDestroySemaphore(g_Device, fd->RenderCompleteSemaphore, g_Allocator); - } - for (uint32_t i = 0; i < wd->BackBufferCount; i++) - { - vkDestroyImageView(g_Device, wd->BackBufferView[i], g_Allocator); - vkDestroyFramebuffer(g_Device, wd->Framebuffer[i], g_Allocator); - } - vkDestroyRenderPass(g_Device, wd->RenderPass, g_Allocator); - vkDestroySwapchainKHR(g_Device, wd->Swapchain, g_Allocator); - vkDestroySurfaceKHR(g_Instance, wd->Surface, g_Allocator); + ImGui_ImplVulkanH_DestroyWindowData(g_Instance, g_Device, wd, g_Allocator); #ifdef IMGUI_VULKAN_DEBUG_REPORT // Remove the debug report callback @@ -499,7 +354,7 @@ static void glfw_error_callback(int error, const char* description) static void glfw_resize_callback(GLFWwindow*, int w, int h) { - CreateOrResizeSwapChainAndFrameBuffer(&g_WindowData, w, h); + ImGui_ImplVulkanH_CreateOrResizeWindowData(g_PhysicalDevice, g_Device, &g_WindowData, g_Allocator, w, h); } int main(int, char**) @@ -524,38 +379,33 @@ int main(int, char**) // Create Window Surface ImGui_ImplVulkan_WindowData* wd = &g_WindowData; - { - VkResult err = glfwCreateWindowSurface(g_Instance, window, g_Allocator, &wd->Surface); - check_vk_result(err); - } + VkResult err = glfwCreateWindowSurface(g_Instance, window, g_Allocator, &wd->Surface); + check_vk_result(err); SetupVulkan(wd); // Create Framebuffers - { - int w, h; - glfwGetFramebufferSize(window, &w, &h); - CreateOrResizeSwapChainAndFrameBuffer(wd, w, h); - glfwSetFramebufferSizeCallback(window, glfw_resize_callback); - } + int w, h; + glfwGetFramebufferSize(window, &w, &h); + ImGui_ImplVulkanH_CreateOrResizeWindowData(g_PhysicalDevice, g_Device, wd, g_Allocator, w, h); + glfwSetFramebufferSizeCallback(window, glfw_resize_callback); // Setup ImGui binding ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); (void)io; + io.ConfigFlags |= ImGuiConfigFlags_MultiViewports; + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + ImGui_ImplVulkan_InitInfo init_info = {}; - init_info.Allocator = g_Allocator; + init_info.Instance = g_Instance; init_info.PhysicalDevice = g_PhysicalDevice; init_info.Device = g_Device; - init_info.RenderPass = wd->RenderPass; init_info.PipelineCache = g_PipelineCache; init_info.DescriptorPool = g_DescriptorPool; + init_info.Allocator = g_Allocator; init_info.CheckVkResultFn = check_vk_result; - - ImGuiIO& io = ImGui::GetIO(); (void)io; - //io.ConfigFlags |= ImGuiConfigFlags_MultiViewports; - //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls - - ImGui_ImplVulkan_Init(&init_info); + ImGui_ImplVulkan_Init(&init_info, wd->RenderPass); ImGui_ImplGlfw_InitForVulkan(window, true); // Setup style @@ -683,7 +533,7 @@ int main(int, char**) } // Cleanup - VkResult err = vkDeviceWaitIdle(g_Device); + err = vkDeviceWaitIdle(g_Device); check_vk_result(err); ImGui_ImplVulkan_Shutdown(); ImGui_ImplGlfw_Shutdown(); From cab41d954e740ab0fbe15a6ad7f437360eea19cc Mon Sep 17 00:00:00 2001 From: omar Date: Sat, 3 Mar 2018 00:29:17 +0100 Subject: [PATCH 056/828] Examples: Vulkan: Further refactor. --- examples/imgui_impl_vulkan.cpp | 48 +++++- examples/imgui_impl_vulkan.h | 9 +- examples/sdl_vulkan_example/main.cpp | 237 +++++++++++---------------- examples/vulkan_example/main.cpp | 224 +++++++++++-------------- 4 files changed, 240 insertions(+), 278 deletions(-) diff --git a/examples/imgui_impl_vulkan.cpp b/examples/imgui_impl_vulkan.cpp index e465d0697c84..6f3c782f0f14 100644 --- a/examples/imgui_impl_vulkan.cpp +++ b/examples/imgui_impl_vulkan.cpp @@ -11,6 +11,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2018-03-03: Vulkan: Various refactor, created a couple of ImGui_ImplVulkanH_XXX helper that the example can use and that viewport support will use. // 2018-03-01: Vulkan: Renamed ImGui_ImplVulkan_Init_Info to ImGui_ImplVulkan_InitInfo and fields to match more closely Vulkan terminology. // 2018-02-18: Vulkan: Offset projection matrix and clipping rectangle by io.DisplayPos (which will be non-zero for multi-viewport applications). // 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback, ImGui_ImplVulkan_Render() calls ImGui_ImplVulkan_RenderDrawData() itself. @@ -720,7 +721,6 @@ void ImGui_ImplVulkan_NewFrame() void ImGui_ImplVulkan_Render(VkCommandBuffer command_buffer) { - ImGui::Render(); ImGui_ImplVulkan_RenderDrawData(command_buffer, ImGui::GetDrawData()); g_FrameIndex = (g_FrameIndex + 1) % IMGUI_VK_QUEUED_FRAMES; } @@ -821,10 +821,53 @@ VkPresentModeKHR ImGui_ImplVulkanH_SelectPresentMode(VkPhysicalDevice physical_d return VK_PRESENT_MODE_FIFO_KHR; // Always available } -void ImGui_ImplVulkanH_CreateOrResizeWindowData(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkan_WindowData* wd, const VkAllocationCallbacks* allocator, int w, int h) +void ImGui_ImplVulkanH_CreateWindowDataCommandBuffers(VkPhysicalDevice physical_device, VkDevice device, uint32_t queue_family, ImGui_ImplVulkan_WindowData* wd, const VkAllocationCallbacks* allocator) { IM_ASSERT(physical_device != NULL && device != NULL); + (void)allocator; + // Create Command Buffers + VkResult err; + for (int i = 0; i < IMGUI_VK_QUEUED_FRAMES; i++) + { + ImGui_ImplVulkan_FrameData* fd = &wd->Frames[i]; + { + VkCommandPoolCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + info.queueFamilyIndex = queue_family; + err = vkCreateCommandPool(device, &info, allocator, &fd->CommandPool); + check_vk_result(err); + } + { + VkCommandBufferAllocateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + info.commandPool = fd->CommandPool; + info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + info.commandBufferCount = 1; + err = vkAllocateCommandBuffers(device, &info, &fd->CommandBuffer); + check_vk_result(err); + } + { + VkFenceCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + info.flags = VK_FENCE_CREATE_SIGNALED_BIT; + err = vkCreateFence(device, &info, allocator, &fd->Fence); + check_vk_result(err); + } + { + VkSemaphoreCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + err = vkCreateSemaphore(device, &info, allocator, &fd->PresentCompleteSemaphore); + check_vk_result(err); + err = vkCreateSemaphore(device, &info, allocator, &fd->RenderCompleteSemaphore); + check_vk_result(err); + } + } +} + +void ImGui_ImplVulkanH_CreateWindowDataSwapChainAndFramebuffer(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkan_WindowData* wd, const VkAllocationCallbacks* allocator, int w, int h) +{ VkResult err; VkSwapchainKHR old_swapchain = wd->Swapchain; err = vkDeviceWaitIdle(device); @@ -972,6 +1015,7 @@ void ImGui_ImplVulkanH_DestroyWindowData(VkInstance instance, VkDevice device, I vkDestroyRenderPass(device, wd->RenderPass, allocator); vkDestroySwapchainKHR(device, wd->Swapchain, allocator); vkDestroySurfaceKHR(instance, wd->Surface, allocator); + *wd = ImGui_ImplVulkan_WindowData(); } //-------------------------------------------------------------------------------------------------------- diff --git a/examples/imgui_impl_vulkan.h b/examples/imgui_impl_vulkan.h index f6a4b71aaf4c..50a223d1734f 100644 --- a/examples/imgui_impl_vulkan.h +++ b/examples/imgui_impl_vulkan.h @@ -47,12 +47,12 @@ IMGUI_API bool ImGui_ImplVulkan_CreateDeviceObjects(); struct ImGui_ImplVulkan_FrameData; struct ImGui_ImplVulkan_WindowData; -struct ImGui_ImplVulkan_WindowDataCreateInfo; +IMGUI_API void ImGui_ImplVulkanH_CreateWindowDataCommandBuffers(VkPhysicalDevice physical_device, VkDevice device, uint32_t queue_family, ImGui_ImplVulkan_WindowData* wd, const VkAllocationCallbacks* allocator); +IMGUI_API void ImGui_ImplVulkanH_CreateWindowDataSwapChainAndFramebuffer(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkan_WindowData* wd, const VkAllocationCallbacks* allocator, int w, int h); +IMGUI_API void ImGui_ImplVulkanH_DestroyWindowData(VkInstance instance, VkDevice device, ImGui_ImplVulkan_WindowData* wd, const VkAllocationCallbacks* allocator); IMGUI_API VkSurfaceFormatKHR ImGui_ImplVulkanH_SelectSurfaceFormat(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkFormat* request_formats, int request_formats_count, VkColorSpaceKHR request_color_space); IMGUI_API VkPresentModeKHR ImGui_ImplVulkanH_SelectPresentMode(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkPresentModeKHR* request_modes, int request_modes_count); -IMGUI_API void ImGui_ImplVulkanH_CreateOrResizeWindowData(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkan_WindowData* wd, const VkAllocationCallbacks* allocator, int w, int h); -IMGUI_API void ImGui_ImplVulkanH_DestroyWindowData(VkInstance instance, VkDevice device, ImGui_ImplVulkan_WindowData* wd, const VkAllocationCallbacks* allocator); struct ImGui_ImplVulkan_FrameData { @@ -68,7 +68,8 @@ struct ImGui_ImplVulkan_FrameData struct ImGui_ImplVulkan_WindowData { - int Width, Height; + int Width; + int Height; VkSwapchainKHR Swapchain; VkSurfaceKHR Surface; VkSurfaceFormatKHR SurfaceFormat; diff --git a/examples/sdl_vulkan_example/main.cpp b/examples/sdl_vulkan_example/main.cpp index cd8bab81599b..a453d5582a1d 100644 --- a/examples/sdl_vulkan_example/main.cpp +++ b/examples/sdl_vulkan_example/main.cpp @@ -44,57 +44,53 @@ static VKAPI_ATTR VkBool32 VKAPI_CALL debug_report(VkDebugReportFlagsEXT flags, } #endif // IMGUI_VULKAN_DEBUG_REPORT -static void CreateVulkanInstance(const char** extensions, uint32_t extensions_count) +static void SetupVulkan(const char** extensions, uint32_t extensions_count) { VkResult err; - VkInstanceCreateInfo create_info = {}; - create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; - create_info.enabledExtensionCount = extensions_count; - create_info.ppEnabledExtensionNames = extensions; + // Create Vulkan Instance + { + VkInstanceCreateInfo create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + create_info.enabledExtensionCount = extensions_count; + create_info.ppEnabledExtensionNames = extensions; #ifdef IMGUI_VULKAN_DEBUG_REPORT - // Enabling multiple validation layers grouped as LunarG standard validation - const char* layers[] = { "VK_LAYER_LUNARG_standard_validation" }; - create_info.enabledLayerCount = 1; - create_info.ppEnabledLayerNames = layers; - - // Enable debug report extension (we need additional storage, so we duplicate the user array to add our new extension to it) - const char** extensions_ext = (const char**)malloc(sizeof(const char*) * (extensions_count + 1)); - memcpy(extensions_ext, extensions, extensions_count * sizeof(const char*)); - extensions_ext[extensions_count] = "VK_EXT_debug_report"; - create_info.enabledExtensionCount = extensions_count + 1; - create_info.ppEnabledExtensionNames = extensions_ext; - - // Create Vulkan Instance - err = vkCreateInstance(&create_info, g_Allocator, &g_Instance); - check_vk_result(err); - free(extensions_ext); - - // Get the function pointer (required for any extensions) - auto vkCreateDebugReportCallbackEXT = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(g_Instance, "vkCreateDebugReportCallbackEXT"); - IM_ASSERT(vkCreateDebugReportCallbackEXT != NULL); - - // Setup the debug report callback - VkDebugReportCallbackCreateInfoEXT debug_report_ci = {}; - debug_report_ci.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT; - debug_report_ci.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT; - debug_report_ci.pfnCallback = debug_report; - debug_report_ci.pUserData = NULL; - err = vkCreateDebugReportCallbackEXT(g_Instance, &debug_report_ci, g_Allocator, &g_DebugReport); - check_vk_result(err); + // Enabling multiple validation layers grouped as LunarG standard validation + const char* layers[] = { "VK_LAYER_LUNARG_standard_validation" }; + create_info.enabledLayerCount = 1; + create_info.ppEnabledLayerNames = layers; + + // Enable debug report extension (we need additional storage, so we duplicate the user array to add our new extension to it) + const char** extensions_ext = (const char**)malloc(sizeof(const char*) * (extensions_count + 1)); + memcpy(extensions_ext, extensions, extensions_count * sizeof(const char*)); + extensions_ext[extensions_count] = "VK_EXT_debug_report"; + create_info.enabledExtensionCount = extensions_count + 1; + create_info.ppEnabledExtensionNames = extensions_ext; + + // Create Vulkan Instance + err = vkCreateInstance(&create_info, g_Allocator, &g_Instance); + check_vk_result(err); + free(extensions_ext); + + // Get the function pointer (required for any extensions) + auto vkCreateDebugReportCallbackEXT = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(g_Instance, "vkCreateDebugReportCallbackEXT"); + IM_ASSERT(vkCreateDebugReportCallbackEXT != NULL); + + // Setup the debug report callback + VkDebugReportCallbackCreateInfoEXT debug_report_ci = {}; + debug_report_ci.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT; + debug_report_ci.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT; + debug_report_ci.pfnCallback = debug_report; + debug_report_ci.pUserData = NULL; + err = vkCreateDebugReportCallbackEXT(g_Instance, &debug_report_ci, g_Allocator, &g_DebugReport); + check_vk_result(err); #else - // Create Vulkan Instance without any debug feature - err = vkCreateInstance(&create_info, g_Allocator, &g_Instance); - check_vk_result(err); + // Create Vulkan Instance without any debug feature + err = vkCreateInstance(&create_info, g_Allocator, &g_Instance); + check_vk_result(err); #endif -} - -static void SetupVulkan(ImGui_ImplVulkan_WindowData* wd) -{ - IM_ASSERT(wd->Surface != NULL); - - VkResult err; + } // Select GPU { @@ -129,34 +125,6 @@ static void SetupVulkan(ImGui_ImplVulkan_WindowData* wd) IM_ASSERT(g_QueueFamily != -1); } - // Check for WSI support - { - VkBool32 res; - vkGetPhysicalDeviceSurfaceSupportKHR(g_PhysicalDevice, g_QueueFamily, wd->Surface, &res); - if (res != VK_TRUE) - { - fprintf(stderr, "Error no WSI support on physical device 0\n"); - exit(-1); - } - } - - // Get Surface Format - { - const VkFormat requestSurfaceImageFormat[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM }; - const VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; - wd->SurfaceFormat = ImGui_ImplVulkanH_SelectSurfaceFormat(g_PhysicalDevice, wd->Surface, requestSurfaceImageFormat, (size_t)IM_ARRAYSIZE(requestSurfaceImageFormat), requestSurfaceColorSpace); - } - - // Get Present Mode - { -#ifdef IMGUI_UNLIMITED_FRAME_RATE - VkPresentModeKHR present_mode = VK_PRESENT_MODE_IMMEDIATE_KHR; -#else - VkPresentModeKHR present_mode = VK_PRESENT_MODE_FIFO_KHR; -#endif - wd->PresentMode = ImGui_ImplVulkanH_SelectPresentMode(g_PhysicalDevice, wd->Surface, &present_mode, 1); - } - // Create Logical Device (with 1 queue) { int device_extension_count = 1; @@ -178,44 +146,6 @@ static void SetupVulkan(ImGui_ImplVulkan_WindowData* wd) vkGetDeviceQueue(g_Device, g_QueueFamily, 0, &g_Queue); } - // Create Command Buffers - for (int i = 0; i < IMGUI_VK_QUEUED_FRAMES; i++) - { - ImGui_ImplVulkan_FrameData* fd = &wd->Frames[i]; - { - VkCommandPoolCreateInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; - info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; - info.queueFamilyIndex = g_QueueFamily; - err = vkCreateCommandPool(g_Device, &info, g_Allocator, &fd->CommandPool); - check_vk_result(err); - } - { - VkCommandBufferAllocateInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - info.commandPool = fd->CommandPool; - info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - info.commandBufferCount = 1; - err = vkAllocateCommandBuffers(g_Device, &info, &fd->CommandBuffer); - check_vk_result(err); - } - { - VkFenceCreateInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; - info.flags = VK_FENCE_CREATE_SIGNALED_BIT; - err = vkCreateFence(g_Device, &info, g_Allocator, &fd->Fence); - check_vk_result(err); - } - { - VkSemaphoreCreateInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - err = vkCreateSemaphore(g_Device, &info, g_Allocator, &fd->PresentCompleteSemaphore); - check_vk_result(err); - err = vkCreateSemaphore(g_Device, &info, g_Allocator, &fd->RenderCompleteSemaphore); - check_vk_result(err); - } - } - // Create Descriptor Pool { VkDescriptorPoolSize pool_sizes[] = @@ -243,11 +173,42 @@ static void SetupVulkan(ImGui_ImplVulkan_WindowData* wd) } } -static void cleanup_vulkan() +static void SetupVulkanWindowData(ImGui_ImplVulkan_WindowData* wd, VkSurfaceKHR surface, int width, int height) +{ + wd->Surface = surface; + + // Check for WSI support + VkBool32 res; + vkGetPhysicalDeviceSurfaceSupportKHR(g_PhysicalDevice, g_QueueFamily, wd->Surface, &res); + if (res != VK_TRUE) + { + fprintf(stderr, "Error no WSI support on physical device 0\n"); + exit(-1); + } + + // Get Surface Format + const VkFormat requestSurfaceImageFormat[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM }; + const VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; + wd->SurfaceFormat = ImGui_ImplVulkanH_SelectSurfaceFormat(g_PhysicalDevice, wd->Surface, requestSurfaceImageFormat, (size_t)IM_ARRAYSIZE(requestSurfaceImageFormat), requestSurfaceColorSpace); + + // Get Present Mode +#ifdef IMGUI_UNLIMITED_FRAME_RATE + VkPresentModeKHR present_mode = VK_PRESENT_MODE_IMMEDIATE_KHR; +#else + VkPresentModeKHR present_mode = VK_PRESENT_MODE_FIFO_KHR; +#endif + wd->PresentMode = ImGui_ImplVulkanH_SelectPresentMode(g_PhysicalDevice, wd->Surface, &present_mode, 1); + + // Create SwapChain, RenderPass, Framebuffer, etc. + ImGui_ImplVulkanH_CreateWindowDataCommandBuffers(g_PhysicalDevice, g_Device, g_QueueFamily, wd, g_Allocator); + ImGui_ImplVulkanH_CreateWindowDataSwapChainAndFramebuffer(g_PhysicalDevice, g_Device, wd, g_Allocator, width, height); +} + +static void CleanupVulkan() { ImGui_ImplVulkan_WindowData* wd = &g_WindowData; - vkDestroyDescriptorPool(g_Device, g_DescriptorPool, g_Allocator); ImGui_ImplVulkanH_DestroyWindowData(g_Instance, g_Device, wd, g_Allocator); + vkDestroyDescriptorPool(g_Device, g_DescriptorPool, g_Allocator); #ifdef IMGUI_VULKAN_DEBUG_REPORT // Remove the debug report callback @@ -259,7 +220,7 @@ static void cleanup_vulkan() vkDestroyInstance(g_Instance, g_Allocator); } -static void frame_begin(ImGui_ImplVulkan_WindowData* wd) +static void FrameBegin(ImGui_ImplVulkan_WindowData* wd) { ImGui_ImplVulkan_FrameData* fd = &wd->Frames[wd->FrameIndex]; VkResult err; @@ -296,7 +257,7 @@ static void frame_begin(ImGui_ImplVulkan_WindowData* wd) } } -static void frame_end(ImGui_ImplVulkan_WindowData* wd) +static void FrameEnd(ImGui_ImplVulkan_WindowData* wd) { ImGui_ImplVulkan_FrameData* fd = &wd->Frames[wd->FrameIndex]; VkResult err; @@ -322,14 +283,14 @@ static void frame_end(ImGui_ImplVulkan_WindowData* wd) } } -static void frame_present(ImGui_ImplVulkan_WindowData* wd) +static void FramePresent(ImGui_ImplVulkan_WindowData* wd) { VkResult err; // If IMGUI_UNLIMITED_FRAME_RATE is defined we present the latest but one frame. Otherwise we present the latest rendered frame #ifdef IMGUI_UNLIMITED_FRAME_RATE uint32_t PresentIndex = (wd->FrameIndex + IMGUI_VK_QUEUED_FRAMES - 1) % IMGUI_VK_QUEUED_FRAMES; #else - uint32_t PresentIndex = g_FrameIndex; + uint32_t PresentIndex = wd->FrameIndex; #endif // IMGUI_UNLIMITED_FRAME_RATE ImGui_ImplVulkan_FrameData* fd = &wd->Frames[PresentIndex]; @@ -359,34 +320,29 @@ int main(int, char**) SDL_Window* window = SDL_CreateWindow("ImGui SDL2+Vulkan example", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, SDL_WINDOW_VULKAN|SDL_WINDOW_RESIZABLE); // Setup Vulkan - uint32_t sdl_extensions_count; - SDL_Vulkan_GetInstanceExtensions(window, &sdl_extensions_count, NULL); - const char** sdl_extensions = new const char*[sdl_extensions_count]; - SDL_Vulkan_GetInstanceExtensions(window, &sdl_extensions_count, sdl_extensions); - CreateVulkanInstance(sdl_extensions, sdl_extensions_count); - delete[] sdl_extensions; + uint32_t extensions_count = 0; + SDL_Vulkan_GetInstanceExtensions(window, &extensions_count, NULL); + const char** extensions = new const char*[extensions_count]; + SDL_Vulkan_GetInstanceExtensions(window, &extensions_count, extensions); + SetupVulkan(extensions, extensions_count); + delete[] extensions; // Create Window Surface - ImGui_ImplVulkan_WindowData* wd = &g_WindowData; - SDL_bool result = SDL_Vulkan_CreateSurface(window, g_Instance, &wd->Surface); - if (result == 0) + VkSurfaceKHR surface; + if (SDL_Vulkan_CreateSurface(window, g_Instance, &surface) == 0) { printf("Failed to create Vulkan surface.\n"); return 1; } - SetupVulkan(wd); - // Create Framebuffers - { - int w, h; - SDL_GetWindowSize(window, &w, &h); - ImGui_ImplVulkanH_CreateOrResizeWindowData(g_PhysicalDevice, g_Device, wd, g_Allocator, w, h); - } + int w, h; + SDL_GetWindowSize(window, &w, &h); + ImGui_ImplVulkan_WindowData* wd = &g_WindowData; + SetupVulkanWindowData(wd, surface, w, h); // Setup ImGui binding ImGui::CreateContext(); - ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_MultiViewports; //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls @@ -399,8 +355,8 @@ int main(int, char**) init_info.DescriptorPool = g_DescriptorPool; init_info.Allocator = g_Allocator; init_info.CheckVkResultFn = check_vk_result; - ImGui_ImplVulkan_Init(&init_info, wd->RenderPass); ImGui_ImplSDL2_Init(window, NULL); + ImGui_ImplVulkan_Init(&init_info, wd->RenderPass); // Setup style ImGui::StyleColorsDark(); @@ -473,7 +429,7 @@ int main(int, char**) if (event.type == SDL_QUIT) done = true; if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_RESIZED && event.window.windowID == SDL_GetWindowID(window)) - ImGui_ImplVulkanH_CreateOrResizeWindowData(g_PhysicalDevice, g_Device, &g_WindowData, g_Allocator, (int)event.window.data1, (int)event.window.data2); + ImGui_ImplVulkanH_CreateWindowDataSwapChainAndFramebuffer(g_PhysicalDevice, g_Device, &g_WindowData, g_Allocator, (int)event.window.data1, (int)event.window.data2); } ImGui_ImplVulkan_NewFrame(); ImGui_ImplSDL2_NewFrame(window); @@ -516,10 +472,11 @@ int main(int, char**) } // Rendering + ImGui::Render(); memcpy(&wd->ClearValue.color.float32[0], &clear_color, 4 * sizeof(float)); - frame_begin(wd); + FrameBegin(wd); ImGui_ImplVulkan_Render(wd->Frames[wd->FrameIndex].CommandBuffer); - frame_end(wd); + FrameEnd(wd); ImGui::RenderAdditionalViewports(); @@ -527,9 +484,9 @@ int main(int, char**) // When IMGUI_UNLIMITED_FRAME_RATE is defined we render into latest image acquired from the swapchain but we display the image which was rendered before. // Hence we must render once and increase the FrameIndex without presenting. if (swap_chain_has_at_least_one_image) - frame_present(wd); + FramePresent(wd); #else - frame_present(wd); + FramePresent(wd); #endif swap_chain_has_at_least_one_image = true; wd->FrameIndex = (wd->FrameIndex + 1) % IMGUI_VK_QUEUED_FRAMES; @@ -542,7 +499,7 @@ int main(int, char**) ImGui_ImplSDL2_Shutdown(); ImGui::DestroyContext(); SDL_DestroyWindow(window); - cleanup_vulkan(); + CleanupVulkan(); SDL_Quit(); return 0; diff --git a/examples/vulkan_example/main.cpp b/examples/vulkan_example/main.cpp index 9384dd25b218..9c2ec795e03b 100644 --- a/examples/vulkan_example/main.cpp +++ b/examples/vulkan_example/main.cpp @@ -47,57 +47,53 @@ static VKAPI_ATTR VkBool32 VKAPI_CALL debug_report(VkDebugReportFlagsEXT flags, } #endif // IMGUI_VULKAN_DEBUG_REPORT -static void CreateVulkanInstance(const char** extensions, uint32_t extensions_count) +static void SetupVulkan(const char** extensions, uint32_t extensions_count) { VkResult err; - VkInstanceCreateInfo create_info = {}; - create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; - create_info.enabledExtensionCount = extensions_count; - create_info.ppEnabledExtensionNames = extensions; + // Create Vulkan Instance + { + VkInstanceCreateInfo create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + create_info.enabledExtensionCount = extensions_count; + create_info.ppEnabledExtensionNames = extensions; #ifdef IMGUI_VULKAN_DEBUG_REPORT - // Enabling multiple validation layers grouped as LunarG standard validation - const char* layers[] = { "VK_LAYER_LUNARG_standard_validation" }; - create_info.enabledLayerCount = 1; - create_info.ppEnabledLayerNames = layers; - - // Enable debug report extension (we need additional storage, so we duplicate the user array to add our new extension to it) - const char** extensions_ext = (const char**)malloc(sizeof(const char*) * (extensions_count + 1)); - memcpy(extensions_ext, extensions, extensions_count * sizeof(const char*)); - extensions_ext[extensions_count] = "VK_EXT_debug_report"; - create_info.enabledExtensionCount = extensions_count + 1; - create_info.ppEnabledExtensionNames = extensions_ext; - - // Create Vulkan Instance - err = vkCreateInstance(&create_info, g_Allocator, &g_Instance); - check_vk_result(err); - free(extensions_ext); - - // Get the function pointer (required for any extensions) - auto vkCreateDebugReportCallbackEXT = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(g_Instance, "vkCreateDebugReportCallbackEXT"); - IM_ASSERT(vkCreateDebugReportCallbackEXT != NULL); - - // Setup the debug report callback - VkDebugReportCallbackCreateInfoEXT debug_report_ci = {}; - debug_report_ci.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT; - debug_report_ci.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT; - debug_report_ci.pfnCallback = debug_report; - debug_report_ci.pUserData = NULL; - err = vkCreateDebugReportCallbackEXT(g_Instance, &debug_report_ci, g_Allocator, &g_DebugReport); - check_vk_result(err); + // Enabling multiple validation layers grouped as LunarG standard validation + const char* layers[] = { "VK_LAYER_LUNARG_standard_validation" }; + create_info.enabledLayerCount = 1; + create_info.ppEnabledLayerNames = layers; + + // Enable debug report extension (we need additional storage, so we duplicate the user array to add our new extension to it) + const char** extensions_ext = (const char**)malloc(sizeof(const char*) * (extensions_count + 1)); + memcpy(extensions_ext, extensions, extensions_count * sizeof(const char*)); + extensions_ext[extensions_count] = "VK_EXT_debug_report"; + create_info.enabledExtensionCount = extensions_count + 1; + create_info.ppEnabledExtensionNames = extensions_ext; + + // Create Vulkan Instance + err = vkCreateInstance(&create_info, g_Allocator, &g_Instance); + check_vk_result(err); + free(extensions_ext); + + // Get the function pointer (required for any extensions) + auto vkCreateDebugReportCallbackEXT = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(g_Instance, "vkCreateDebugReportCallbackEXT"); + IM_ASSERT(vkCreateDebugReportCallbackEXT != NULL); + + // Setup the debug report callback + VkDebugReportCallbackCreateInfoEXT debug_report_ci = {}; + debug_report_ci.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT; + debug_report_ci.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT; + debug_report_ci.pfnCallback = debug_report; + debug_report_ci.pUserData = NULL; + err = vkCreateDebugReportCallbackEXT(g_Instance, &debug_report_ci, g_Allocator, &g_DebugReport); + check_vk_result(err); #else - // Create Vulkan Instance without any debug feature - err = vkCreateInstance(&create_info, g_Allocator, &g_Instance); - check_vk_result(err); + // Create Vulkan Instance without any debug feature + err = vkCreateInstance(&create_info, g_Allocator, &g_Instance); + check_vk_result(err); #endif -} - -static void SetupVulkan(ImGui_ImplVulkan_WindowData* wd) -{ - IM_ASSERT(wd->Surface != NULL); - - VkResult err; + } // Select GPU { @@ -132,34 +128,6 @@ static void SetupVulkan(ImGui_ImplVulkan_WindowData* wd) IM_ASSERT(g_QueueFamily != -1); } - // Check for WSI support - { - VkBool32 res; - vkGetPhysicalDeviceSurfaceSupportKHR(g_PhysicalDevice, g_QueueFamily, wd->Surface, &res); - if (res != VK_TRUE) - { - fprintf(stderr, "Error no WSI support on physical device 0\n"); - exit(-1); - } - } - - // Get Surface Format - { - const VkFormat requestSurfaceImageFormat[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM }; - const VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; - wd->SurfaceFormat = ImGui_ImplVulkanH_SelectSurfaceFormat(g_PhysicalDevice, wd->Surface, requestSurfaceImageFormat, (size_t)IM_ARRAYSIZE(requestSurfaceImageFormat), requestSurfaceColorSpace); - } - - // Get Present Mode - { -#ifdef IMGUI_UNLIMITED_FRAME_RATE - VkPresentModeKHR present_mode = VK_PRESENT_MODE_IMMEDIATE_KHR; -#else - VkPresentModeKHR present_mode = VK_PRESENT_MODE_FIFO_KHR; -#endif - wd->PresentMode = ImGui_ImplVulkanH_SelectPresentMode(g_PhysicalDevice, wd->Surface, &present_mode, 1); - } - // Create Logical Device (with 1 queue) { int device_extension_count = 1; @@ -181,44 +149,6 @@ static void SetupVulkan(ImGui_ImplVulkan_WindowData* wd) vkGetDeviceQueue(g_Device, g_QueueFamily, 0, &g_Queue); } - // Create Command Buffers - for (int i = 0; i < IMGUI_VK_QUEUED_FRAMES; i++) - { - ImGui_ImplVulkan_FrameData* fd = &wd->Frames[i]; - { - VkCommandPoolCreateInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; - info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; - info.queueFamilyIndex = g_QueueFamily; - err = vkCreateCommandPool(g_Device, &info, g_Allocator, &fd->CommandPool); - check_vk_result(err); - } - { - VkCommandBufferAllocateInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - info.commandPool = fd->CommandPool; - info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - info.commandBufferCount = 1; - err = vkAllocateCommandBuffers(g_Device, &info, &fd->CommandBuffer); - check_vk_result(err); - } - { - VkFenceCreateInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; - info.flags = VK_FENCE_CREATE_SIGNALED_BIT; - err = vkCreateFence(g_Device, &info, g_Allocator, &fd->Fence); - check_vk_result(err); - } - { - VkSemaphoreCreateInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - err = vkCreateSemaphore(g_Device, &info, g_Allocator, &fd->PresentCompleteSemaphore); - check_vk_result(err); - err = vkCreateSemaphore(g_Device, &info, g_Allocator, &fd->RenderCompleteSemaphore); - check_vk_result(err); - } - } - // Create Descriptor Pool { VkDescriptorPoolSize pool_sizes[] = @@ -246,11 +176,42 @@ static void SetupVulkan(ImGui_ImplVulkan_WindowData* wd) } } -static void cleanup_vulkan() +static void SetupVulkanWindowData(ImGui_ImplVulkan_WindowData* wd, VkSurfaceKHR surface, int width, int height) +{ + wd->Surface = surface; + + // Check for WSI support + VkBool32 res; + vkGetPhysicalDeviceSurfaceSupportKHR(g_PhysicalDevice, g_QueueFamily, wd->Surface, &res); + if (res != VK_TRUE) + { + fprintf(stderr, "Error no WSI support on physical device 0\n"); + exit(-1); + } + + // Get Surface Format + const VkFormat requestSurfaceImageFormat[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM }; + const VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; + wd->SurfaceFormat = ImGui_ImplVulkanH_SelectSurfaceFormat(g_PhysicalDevice, wd->Surface, requestSurfaceImageFormat, (size_t)IM_ARRAYSIZE(requestSurfaceImageFormat), requestSurfaceColorSpace); + + // Get Present Mode +#ifdef IMGUI_UNLIMITED_FRAME_RATE + VkPresentModeKHR present_mode = VK_PRESENT_MODE_IMMEDIATE_KHR; +#else + VkPresentModeKHR present_mode = VK_PRESENT_MODE_FIFO_KHR; +#endif + wd->PresentMode = ImGui_ImplVulkanH_SelectPresentMode(g_PhysicalDevice, wd->Surface, &present_mode, 1); + + // Create SwapChain, RenderPass, Framebuffer, etc. + ImGui_ImplVulkanH_CreateWindowDataCommandBuffers(g_PhysicalDevice, g_Device, g_QueueFamily, wd, g_Allocator); + ImGui_ImplVulkanH_CreateWindowDataSwapChainAndFramebuffer(g_PhysicalDevice, g_Device, wd, g_Allocator, width, height); +} + +static void CleanupVulkan() { ImGui_ImplVulkan_WindowData* wd = &g_WindowData; - vkDestroyDescriptorPool(g_Device, g_DescriptorPool, g_Allocator); ImGui_ImplVulkanH_DestroyWindowData(g_Instance, g_Device, wd, g_Allocator); + vkDestroyDescriptorPool(g_Device, g_DescriptorPool, g_Allocator); #ifdef IMGUI_VULKAN_DEBUG_REPORT // Remove the debug report callback @@ -262,7 +223,7 @@ static void cleanup_vulkan() vkDestroyInstance(g_Instance, g_Allocator); } -static void frame_begin(ImGui_ImplVulkan_WindowData* wd) +static void FrameBegin(ImGui_ImplVulkan_WindowData* wd) { ImGui_ImplVulkan_FrameData* fd = &wd->Frames[wd->FrameIndex]; VkResult err; @@ -299,7 +260,7 @@ static void frame_begin(ImGui_ImplVulkan_WindowData* wd) } } -static void frame_end(ImGui_ImplVulkan_WindowData* wd) +static void FrameEnd(ImGui_ImplVulkan_WindowData* wd) { ImGui_ImplVulkan_FrameData* fd = &wd->Frames[wd->FrameIndex]; VkResult err; @@ -325,14 +286,14 @@ static void frame_end(ImGui_ImplVulkan_WindowData* wd) } } -static void frame_present(ImGui_ImplVulkan_WindowData* wd) +static void FramePresent(ImGui_ImplVulkan_WindowData* wd) { VkResult err; // If IMGUI_UNLIMITED_FRAME_RATE is defined we present the latest but one frame. Otherwise we present the latest rendered frame #ifdef IMGUI_UNLIMITED_FRAME_RATE uint32_t PresentIndex = (wd->FrameIndex + IMGUI_VK_QUEUED_FRAMES - 1) % IMGUI_VK_QUEUED_FRAMES; #else - uint32_t PresentIndex = g_FrameIndex; + uint32_t PresentIndex = wd->FrameIndex; #endif // IMGUI_UNLIMITED_FRAME_RATE ImGui_ImplVulkan_FrameData* fd = &wd->Frames[PresentIndex]; @@ -354,7 +315,7 @@ static void glfw_error_callback(int error, const char* description) static void glfw_resize_callback(GLFWwindow*, int w, int h) { - ImGui_ImplVulkanH_CreateOrResizeWindowData(g_PhysicalDevice, g_Device, &g_WindowData, g_Allocator, w, h); + ImGui_ImplVulkanH_CreateWindowDataSwapChainAndFramebuffer(g_PhysicalDevice, g_Device, &g_WindowData, g_Allocator, w, h); } int main(int, char**) @@ -373,26 +334,24 @@ int main(int, char**) printf("GLFW: Vulkan Not Supported\n"); return 1; } - uint32_t glfw_extensions_count = 0; - const char** glfw_extensions = glfwGetRequiredInstanceExtensions(&glfw_extensions_count); - CreateVulkanInstance(glfw_extensions, glfw_extensions_count); + uint32_t extensions_count = 0; + const char** extensions = glfwGetRequiredInstanceExtensions(&extensions_count); + SetupVulkan(extensions, extensions_count); // Create Window Surface - ImGui_ImplVulkan_WindowData* wd = &g_WindowData; - VkResult err = glfwCreateWindowSurface(g_Instance, window, g_Allocator, &wd->Surface); + VkSurfaceKHR surface; + VkResult err = glfwCreateWindowSurface(g_Instance, window, g_Allocator, &surface); check_vk_result(err); - SetupVulkan(wd); - // Create Framebuffers int w, h; glfwGetFramebufferSize(window, &w, &h); - ImGui_ImplVulkanH_CreateOrResizeWindowData(g_PhysicalDevice, g_Device, wd, g_Allocator, w, h); glfwSetFramebufferSizeCallback(window, glfw_resize_callback); + ImGui_ImplVulkan_WindowData* wd = &g_WindowData; + SetupVulkanWindowData(wd, surface, w, h); // Setup ImGui binding ImGui::CreateContext(); - ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_MultiViewports; //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls @@ -405,8 +364,8 @@ int main(int, char**) init_info.DescriptorPool = g_DescriptorPool; init_info.Allocator = g_Allocator; init_info.CheckVkResultFn = check_vk_result; - ImGui_ImplVulkan_Init(&init_info, wd->RenderPass); ImGui_ImplGlfw_InitForVulkan(window, true); + ImGui_ImplVulkan_Init(&init_info, wd->RenderPass); // Setup style ImGui::StyleColorsDark(); @@ -513,10 +472,11 @@ int main(int, char**) } // Rendering + ImGui::Render(); memcpy(&wd->ClearValue.color.float32[0], &clear_color, 4 * sizeof(float)); - frame_begin(wd); + FrameBegin(wd); ImGui_ImplVulkan_Render(wd->Frames[wd->FrameIndex].CommandBuffer); - frame_end(wd); + FrameEnd(wd); ImGui::RenderAdditionalViewports(); @@ -524,9 +484,9 @@ int main(int, char**) // When IMGUI_UNLIMITED_FRAME_RATE is defined we render into latest image acquired from the swapchain but we display the image which was rendered before. // Hence we must render once and increase the FrameIndex without presenting. if (swap_chain_has_at_least_one_image) - frame_present(wd); + FramePresent(wd); #else - frame_present(wd); + FramePresent(wd); #endif swap_chain_has_at_least_one_image = true; wd->FrameIndex = (wd->FrameIndex + 1) % IMGUI_VK_QUEUED_FRAMES; @@ -538,7 +498,7 @@ int main(int, char**) ImGui_ImplVulkan_Shutdown(); ImGui_ImplGlfw_Shutdown(); ImGui::DestroyContext(); - cleanup_vulkan(); + CleanupVulkan(); glfwTerminate(); return 0; From e026c8d3b7bce7abfccbd1014110a37d80ddfc35 Mon Sep 17 00:00:00 2001 From: omar Date: Sat, 3 Mar 2018 22:53:31 +0100 Subject: [PATCH 057/828] Viewport: Give a chance to platform and renderer to actually destroy their data. Otherwise the regular ImplXXX_Shutdown + following by DestroyContext() order fails to fullifl this. (#1542) --- examples/imgui_impl_dx10.cpp | 1 + examples/imgui_impl_dx11.cpp | 1 + examples/imgui_impl_dx12.cpp | 1 + examples/imgui_impl_opengl3.cpp | 1 + imgui.cpp | 23 ++++++++++++++++------- imgui_internal.h | 2 ++ 6 files changed, 22 insertions(+), 7 deletions(-) diff --git a/examples/imgui_impl_dx10.cpp b/examples/imgui_impl_dx10.cpp index d88e2cc8b49f..014c4c37dfd0 100644 --- a/examples/imgui_impl_dx10.cpp +++ b/examples/imgui_impl_dx10.cpp @@ -611,6 +611,7 @@ void ImGui_ImplDX10_InitPlatformInterface() void ImGui_ImplDX10_ShutdownPlatformInterface() { + ImGui::DestroyViewportsRendererData(ImGui::GetCurrentContext()); ImGuiIO& io = ImGui::GetIO(); memset(&io.RendererInterface, 0, sizeof(io.RendererInterface)); } diff --git a/examples/imgui_impl_dx11.cpp b/examples/imgui_impl_dx11.cpp index b7c26cd38a37..56fc7f6aece6 100644 --- a/examples/imgui_impl_dx11.cpp +++ b/examples/imgui_impl_dx11.cpp @@ -619,6 +619,7 @@ void ImGui_ImplDX11_InitPlatformInterface() void ImGui_ImplDX11_ShutdownPlatformInterface() { + ImGui::DestroyViewportsRendererData(ImGui::GetCurrentContext()); ImGuiIO& io = ImGui::GetIO(); memset(&io.RendererInterface, 0, sizeof(io.RendererInterface)); } diff --git a/examples/imgui_impl_dx12.cpp b/examples/imgui_impl_dx12.cpp index 7ed9a3949902..6de48c627e26 100644 --- a/examples/imgui_impl_dx12.cpp +++ b/examples/imgui_impl_dx12.cpp @@ -763,6 +763,7 @@ void ImGui_ImplDX12_InitPlatformInterface() void ImGui_ImplDX12_ShutdownPlatformInterface() { + ImGui::DestroyViewportsRendererData(ImGui::GetCurrentContext()); ImGuiIO& io = ImGui::GetIO(); memset(&io.RendererInterface, 0, sizeof(io.RendererInterface)); } diff --git a/examples/imgui_impl_opengl3.cpp b/examples/imgui_impl_opengl3.cpp index d2a459ddd634..91d363f25d45 100644 --- a/examples/imgui_impl_opengl3.cpp +++ b/examples/imgui_impl_opengl3.cpp @@ -327,6 +327,7 @@ void ImGui_ImplOpenGL3_InitPlatformInterface() void ImGui_ImplOpenGL3_ShutdownPlatformInterface() { + ImGui::DestroyViewportsRendererData(ImGui::GetCurrentContext()); ImGuiIO& io = ImGui::GetIO(); memset(&io.RendererInterface, 0, sizeof(io.RendererInterface)); } diff --git a/imgui.cpp b/imgui.cpp index 7ee87a36cdfb..f6e099c9b50c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3909,6 +3909,20 @@ void ImGui::Initialize(ImGuiContext* context) g.Initialized = true; } +void ImGui::DestroyViewportsPlaformData(ImGuiContext* context) +{ + if (context->IO.PlatformInterface.DestroyViewport) + for (int i = 0; i < context->Viewports.Size; i++) + context->IO.PlatformInterface.DestroyViewport(context->Viewports[i]); +} + +void ImGui::DestroyViewportsRendererData(ImGuiContext* context) +{ + if (context->IO.RendererInterface.DestroyViewport) + for (int i = 0; i < context->Viewports.Size; i++) + context->IO.RendererInterface.DestroyViewport(context->Viewports[i]); +} + // This function is merely here to free heap allocations. void ImGui::Shutdown(ImGuiContext* context) { @@ -3945,16 +3959,11 @@ void ImGui::Shutdown(ImGuiContext* context) g.OpenPopupStack.clear(); g.CurrentPopupStack.clear(); g.MouseViewport = g.MouseLastHoveredViewport = NULL; + DestroyViewportsPlaformData(context); + DestroyViewportsRendererData(context); for (int i = 0; i < g.Viewports.Size; i++) { ImGuiViewport* viewport = g.Viewports[i]; - if (!(viewport->Flags & ImGuiViewportFlags_MainViewport)) // FIXME-VIEWPORT - { - if (g.IO.RendererInterface.DestroyViewport) - g.IO.RendererInterface.DestroyViewport(viewport); - if (g.IO.PlatformInterface.DestroyViewport) - g.IO.PlatformInterface.DestroyViewport(viewport); - } viewport->PlatformUserData = viewport->PlatformHandle = viewport->RendererUserData = NULL; IM_DELETE(viewport); } diff --git a/imgui_internal.h b/imgui_internal.h index 7c297f6aaf73..cca7ff8feed4 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1093,6 +1093,8 @@ namespace ImGui IMGUI_API ImGuiViewport* FindViewportByPlatformHandle(void* platform_handle); IMGUI_API void SetNextWindowViewport(ImGuiID id); IMGUI_API void ShowViewportThumbnails(); + IMGUI_API void DestroyViewportsPlaformData(ImGuiContext* context); + IMGUI_API void DestroyViewportsRendererData(ImGuiContext* context); IMGUI_API void MarkIniSettingsDirty(); IMGUI_API ImGuiSettingsHandler* FindSettingsHandler(const char* type_name); From 8e3274e1370b3cebefeeaace041a1be8231fa69b Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 5 Mar 2018 22:31:12 +0100 Subject: [PATCH 058/828] Viewport: Tracking current viewport we are appending to + added callback in PlatformInterface for DPI purpose (WIP). (#1542) --- imgui.cpp | 34 +++++++++++++++++++++++++--------- imgui.h | 9 ++++++--- imgui_internal.h | 2 ++ 3 files changed, 33 insertions(+), 12 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index f2a28de3d856..8aa2f974cc21 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -745,6 +745,7 @@ static inline ImVec2 ConvertViewportPosToOsDesktopPos(const ImVec2& imgui_pos static inline ImVec2 ConvertOsDesktopPosToViewportPos(const ImVec2& os_pos, ImGuiViewport* viewport) { return os_pos - viewport->PlatformOsDesktopPos + viewport->Pos; } static void UpdateViewports(); static void UpdateWindowViewport(ImGuiWindow* window, bool window_pos_set_by_api); +static void SetCurrentViewport(ImGuiViewport* viewport); static void SetWindowViewportTranslateToPreservePlatformPos(ImGuiWindow* window, ImGuiViewport* old_viewport, ImGuiViewport* new_viewport); static void ResizeViewport(ImGuiViewport* viewport, const ImVec2& size); static void ResizeViewportTranslateWindows(int viewport_idx_min, int viewport_idx_max, float pos_x_delta, int idx_delta, ImGuiViewport* viewport_to_erase); @@ -893,6 +894,9 @@ ImGuiIO::ImGuiIO() ImeSetInputScreenPosFn = ImeSetInputScreenPosFn_DefaultImpl; ImeWindowHandle = NULL; + memset(&PlatformInterface, 0, sizeof(PlatformInterface)); + memset(&RendererInterface, 0, sizeof(RendererInterface)); + #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS RenderDrawListsFn = NULL; #endif @@ -3347,6 +3351,7 @@ static void ImGui::UpdateViewports() ImGuiViewport* viewport_ref = g.IO.MousePosViewport ? FindViewportByID(g.IO.MousePosViewport) : g.Viewports[0]; const ImVec2 mouse_os_pos = ConvertViewportPosToOsDesktopPos(g.IO.MousePos, viewport_ref); + g.CurrentViewport = NULL; for (int n = 1; n < g.Viewports.Size; n++) { // Erase unused viewports @@ -3953,7 +3958,7 @@ void ImGui::Shutdown(ImGuiContext* context) g.FontStack.clear(); g.OpenPopupStack.clear(); g.CurrentPopupStack.clear(); - g.MouseViewport = g.MouseLastHoveredViewport = NULL; + g.CurrentViewport = g.MouseViewport = g.MouseLastViewport = g.MouseLastHoveredViewport = NULL; DestroyViewportsPlaformData(context); DestroyViewportsRendererData(context); for (int i = 0; i < g.Viewports.Size; i++) @@ -4283,6 +4288,8 @@ void ImGui::EndFrame() g.CurrentWindow->Active = false; End(); + SetCurrentViewport(NULL); + if (g.ActiveId == 0 && g.HoveredId == 0) { if (!g.NavWindow || !g.NavWindow->Appearing) // Unless we just made a window/popup appear @@ -4465,6 +4472,7 @@ static void ImGui::ResizeViewportTranslateWindows(int viewport_idx_min, int view { ImGuiContext& g = *GImGui; IM_ASSERT(pos_x_delta != 0.0f || idx_delta != 0); + IM_ASSERT(g.CurrentViewport == NULL); // We only resize at the beginning of the frame for (int n = 0; n < g.Windows.Size; n++) { ImGuiWindow* window = g.Windows[n]; @@ -4486,14 +4494,9 @@ static void ImGui::ResizeViewportTranslateWindows(int viewport_idx_min, int view static void ImGui::ResizeViewport(ImGuiViewport* viewport, const ImVec2& size) { ImGuiContext& g = *GImGui; - if (viewport->Size.x != size.x || viewport->Size.y != size.y) - { - // We defer translating windows to the beginning of the frame. - // Our viewport system already works with fully overlapped viewports, it's only certain user interactions that don't and they can't be performed while resizing. - viewport->Size = size; - //if (viewport->Idx + 1 < g.Viewports.Size) // If this isn't the last viewport, translate following viewports - // ResizeViewportTranslateWindows(viewport->Idx + 1, g.Viewports.Size, viewport->GetNextX() - g.Viewports[viewport->Idx + 1]->Pos.x, 0, NULL); - } + // We defer translating windows to the beginning of the frame. + // Our viewport system already works with fully overlapped viewports, it's only certain user interactions that don't and they can't be performed while resizing. + viewport->Size = size; if (viewport == g.Viewports[0]) { g.IO.DisplayPos = viewport->Pos; @@ -4501,6 +4504,18 @@ static void ImGui::ResizeViewport(ImGuiViewport* viewport, const ImVec2& size) } } +void ImGui::SetCurrentViewport(ImGuiViewport* viewport) +{ + ImGuiContext& g = *GImGui; + if (g.CurrentViewport == viewport) + return; + if (g.CurrentViewport && g.IO.PlatformInterface.EndViewport) + g.IO.PlatformInterface.EndViewport(g.CurrentViewport); + g.CurrentViewport = viewport; + if (g.CurrentViewport && g.IO.PlatformInterface.BeginViewport) + g.IO.PlatformInterface.BeginViewport(g.CurrentViewport); +} + ImGuiViewport* ImGui::Viewport(ImGuiID id, ImGuiViewportFlags flags, const ImVec2& os_desktop_pos, const ImVec2& size) { ImGuiContext& g = *GImGui; @@ -6318,6 +6333,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // VIEWPORT UpdateWindowViewport(window, window_pos_set_by_api); + SetCurrentViewport(window->Viewport); flags = window->Flags; if (p_open != NULL && window->Viewport->PlatformRequestClose && !(window->Viewport->Flags & ImGuiViewportFlags_MainViewport)) { diff --git a/imgui.h b/imgui.h index abe755984bf6..54baff058e91 100644 --- a/imgui.h +++ b/imgui.h @@ -958,6 +958,10 @@ struct ImGuiPlatformInterface void (*SetWindowAlpha)(ImGuiViewport* viewport, float alpha); void (*RenderViewport)(ImGuiViewport* viewport); void (*SwapBuffers)(ImGuiViewport* viewport); + + // FIXME-DPI + void (*BeginViewport)(ImGuiViewport* viewport); // (Optional) Called during Begin() every time the viewport we are outputting into changes (viewport = next viewport) + void (*EndViewport)(ImGuiViewport* viewport); // (Optional) Called during Begin() every time the viewport we are outputting into changes (viewport = previous viewport) }; // (Optional) Setup required only if (io.ConfigFlags & ImGuiConfigFlags_EnableMultiViewport) is enabled @@ -966,9 +970,8 @@ struct ImGuiRendererInterface void (*CreateViewport)(ImGuiViewport* viewport); void (*DestroyViewport)(ImGuiViewport* viewport); void (*ResizeViewport)(ImGuiViewport* viewport, int w, int h); - void (*RenderViewport)(ImGuiViewport* viewport); // Setup render output, clear targets, call Renderer_RenderDrawData - void (*RenderDrawData)(ImDrawData* draw_data); // Render a ImDrawList (collection of ImDrawList) for the area covering (io.DisplayPos) to (io.DisplayPos + io.DisplaySize) - void (*SwapBuffers)(ImGuiViewport* viewport); // Call Present/SwapBuffers + void (*RenderViewport)(ImGuiViewport* viewport); // Setup render output, clear targets, call Renderer_RenderDrawData + void (*SwapBuffers)(ImGuiViewport* viewport); // Call Present/SwapBuffers }; // You may modify the ImGui::GetStyle() main instance during initialization and before NewFrame(). diff --git a/imgui_internal.h b/imgui_internal.h index 11089ee5303a..56f20b0f9bcd 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -654,6 +654,7 @@ struct ImGuiContext // Viewports ImVectorViewports; + ImGuiViewport* CurrentViewport; ImGuiViewport* MouseViewport; ImGuiViewport* MouseLastViewport; ImGuiViewport* MouseLastHoveredViewport; @@ -783,6 +784,7 @@ struct ImGuiContext NextTreeNodeOpenVal = false; NextTreeNodeOpenCond = 0; + CurrentViewport = NULL; MouseViewport = NULL; MouseLastViewport = MouseLastHoveredViewport = NULL; From 25fd9d6132a7968a6b19ad4b1488bb21562fd47c Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 7 Mar 2018 10:50:30 +0100 Subject: [PATCH 059/828] Viewport: Various sanity fixes. Popup always inherit viewport from their parent for now. (#1542) --- imgui.cpp | 51 ++++++++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 0f1f9e5df9fe..1f40aa8762d1 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4479,9 +4479,9 @@ static void ImGui::ResizeViewportTranslateWindows(int viewport_idx_min, int view static void ImGui::ResizeViewport(ImGuiViewport* viewport, const ImVec2& size) { - ImGuiContext& g = *GImGui; // We defer translating windows to the beginning of the frame. // Our viewport system already works with fully overlapped viewports, it's only certain user interactions that don't and they can't be performed while resizing. + ImGuiContext& g = *GImGui; viewport->Size = size; if (viewport == g.Viewports[0]) { @@ -4492,6 +4492,8 @@ static void ImGui::ResizeViewport(ImGuiViewport* viewport, const ImVec2& size) void ImGui::SetCurrentViewport(ImGuiViewport* viewport) { + // Notify platform interface of viewport changes + // FIXME-DPI: This is only currently used for experimenting with handling of multiple DPI ImGuiContext& g = *GImGui; if (g.CurrentViewport == viewport) return; @@ -5902,6 +5904,7 @@ static void ImGui::UpdateWindowViewport(ImGuiWindow* window, bool window_pos_set { ImGuiContext& g = *GImGui; ImGuiWindowFlags flags = window->Flags; + (void)window_pos_set_by_api; // Restore main viewport if multi viewports are not supported by the back-end ImGuiViewport* main_viewport = g.Viewports[0]; @@ -5917,16 +5920,16 @@ static void ImGui::UpdateWindowViewport(ImGuiWindow* window, bool window_pos_set bool created_viewport = false; - ImGuiViewport* prev_viewport = window->Viewport; if (g.NextWindowData.ViewportCond) { - window->Viewport = prev_viewport = FindViewportByID(g.NextWindowData.ViewportId); + // Code explicitly request a viewport + window->Viewport = FindViewportByID(g.NextWindowData.ViewportId); window->ViewportId = g.NextWindowData.ViewportId; // Store ID even if Viewport isn't resolved. } - else if (flags & ImGuiWindowFlags_ChildWindow)// || (flags & ImGuiWindowFlags_Popup)) + else if (flags & ImGuiWindowFlags_ChildWindow) { IM_ASSERT(window->ParentWindow); - window->Viewport = prev_viewport = window->ParentWindow->Viewport; + window->Viewport = window->ParentWindow->Viewport; } else if (window_follow_mouse_viewport && IsMousePosValid()) { @@ -5938,7 +5941,7 @@ static void ImGui::UpdateWindowViewport(ImGuiWindow* window, bool window_pos_set ImGuiViewportFlags viewport_flags = ImGuiViewportFlags_NoDecoration | ImGuiViewportFlags_NoFocusOnAppearing | ImGuiViewportFlags_NoInputs; ImGuiViewport* viewport = Viewport(window->ID, viewport_flags, os_desktop_pos, window->Size); window->Flags |= ImGuiWindowFlags_FullViewport | ImGuiWindowFlags_NoBringToFrontOnFocus; - window->Viewport = prev_viewport = viewport; + window->Viewport = viewport; created_viewport = true; // Preserve relative mouse position so docking title bar test stays valid mid-frame (since it isn't latched) @@ -5950,20 +5953,24 @@ static void ImGui::UpdateWindowViewport(ImGuiWindow* window, bool window_pos_set // This is so we don't require of the multi-viewport windowing back-end to preserve mouse buttons after a window closure, making it easier to implement them. bool preserve_temporary_viewport = g.MovingWindow && g.MovingWindow->RootWindow == window && (window->Viewport->Flags & ImGuiWindowFlags_FullViewport); if (!preserve_temporary_viewport) - window->Viewport = prev_viewport = g.MouseViewport; + window->Viewport = g.MouseViewport; } } else if (g.NavWindow != NULL && g.NavWindow != window && (flags & ImGuiWindowFlags_Tooltip)) { - window->Viewport = prev_viewport = g.NavWindow->Viewport; + window->Viewport = g.NavWindow->Viewport; } - - // Appearing popups grabs the viewport from their parent window + + // Appearing popups reset their viewport so they can inherit again const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFrames == 1); - if ((flags & ImGuiWindowFlags_Popup) && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize) + if ((flags & ImGuiWindowFlags_Popup) && window_just_appearing_after_hidden_for_resize) + window->Viewport = NULL; + + // By default inherit from parent window + if (window->Viewport == NULL && window->ParentWindow) window->Viewport = window->ParentWindow->Viewport; - // If we stored a viewport id (= window that hasn't been activated yet), try to restore the viewport based on saved 'window->ViewportOsDesktopPos' + // Restore a viewport id (= window that hasn't been activated yet), try to restore the viewport based on saved 'window->ViewportOsDesktopPos' if (window->Viewport == NULL && window->ViewportId != 0) { window->Viewport = FindViewportByID(window->ViewportId); @@ -5973,7 +5980,7 @@ static void ImGui::UpdateWindowViewport(ImGuiWindow* window, bool window_pos_set { ImGuiViewport* viewport = Viewport(window->ID, ImGuiViewportFlags_NoDecoration, window->ViewportOsDesktopPos, window->Size); window->Flags |= ImGuiWindowFlags_FullViewport | ImGuiWindowFlags_NoBringToFrontOnFocus; - window->Viewport = prev_viewport = viewport; + window->Viewport = viewport; created_viewport = true; } else @@ -5983,13 +5990,11 @@ static void ImGui::UpdateWindowViewport(ImGuiWindow* window, bool window_pos_set } } - if (window->Viewport == NULL && window->ParentWindow) - window->Viewport = window->ParentWindow->Viewport; - // Fallback to default viewport if (window->Viewport == NULL) - window->Viewport = g.Viewports[0]; + window->Viewport = main_viewport; + // When we own the viewport update its size if (window->ID == window->Viewport->ID && !created_viewport) { window->Viewport->Flags |= ImGuiViewportFlags_NoDecoration; @@ -5999,19 +6004,13 @@ static void ImGui::UpdateWindowViewport(ImGuiWindow* window, bool window_pos_set } // Disable rounding for the window - if (window->Viewport != g.Viewports[0]) + if (window->Viewport != main_viewport) window->WindowRounding = 0.0f; if (window->Flags & ImGuiWindowFlags_FullViewport) SetWindowPos(window, window->Viewport->Pos, ImGuiCond_Always); window->ViewportId = window->Viewport->ID; - window->Viewport->LastFrameActive = g.FrameCount; - - // On unexpected viewport changes, we try to preserve the same position in OS space. - if (prev_viewport != window->Viewport && prev_viewport != NULL && window->WasActive) - if (!(flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip) && !window_pos_set_by_api) - SetWindowViewportTranslateToPreservePlatformPos(window, prev_viewport, window->Viewport); } struct ImGuiResizeGripDef @@ -6320,7 +6319,9 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) UpdateWindowViewport(window, window_pos_set_by_api); SetCurrentViewport(window->Viewport); + window->Viewport->LastFrameActive = g.FrameCount; flags = window->Flags; + if (p_open != NULL && window->Viewport->PlatformRequestClose && !(window->Viewport->Flags & ImGuiViewportFlags_MainViewport)) { window->Viewport->PlatformRequestClose = false; @@ -11882,7 +11883,7 @@ bool ImGui::BeginMenu(const char* label, bool enabled) if (menu_is_open) { SetNextWindowPos(popup_pos, ImGuiCond_Always); - ImGuiWindowFlags flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings | ((window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) ? ImGuiWindowFlags_ChildMenu|ImGuiWindowFlags_ChildWindow : ImGuiWindowFlags_ChildMenu); + ImGuiWindowFlags flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings | ((window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) ? ImGuiWindowFlags_ChildMenu|ImGuiWindowFlags_ChildWindow : ImGuiWindowFlags_ChildMenu); menu_is_open = BeginPopupEx(id, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display) } From 21ff03978a1cd1b85edf87f5abd3f6faf6cc088d Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 7 Mar 2018 11:36:37 +0100 Subject: [PATCH 060/828] Viewport: Modal display their darkneing/whitening layer over all viewports. (#1542) --- imgui.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 1f40aa8762d1..1e496def60a9 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6515,9 +6515,14 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) else PushClipRect(viewport_rect.Min, viewport_rect.Max, true); - // Draw modal window background (darkens what is behind them) - if ((flags & ImGuiWindowFlags_Modal) != 0 && window == GetFrontMostModalRootWindow()) + // Draw modal window background (darkens what is behind them, all viewports) + if ((flags & ImGuiWindowFlags_Modal) != 0 && window == GetFrontMostModalRootWindow() && window->HiddenFrames <= 0) + { window->DrawList->AddRectFilled(viewport_rect.Min, viewport_rect.Max, GetColorU32(ImGuiCol_ModalWindowDarkening, g.ModalWindowDarkeningRatio)); + for (int viewport_n = 0; viewport_n < g.Viewports.Size; viewport_n++) + if (g.Viewports[viewport_n] != window->Viewport) + g.OverlayDrawList.AddRectFilled(g.Viewports[viewport_n]->Pos, g.Viewports[viewport_n]->Pos + g.Viewports[viewport_n]->Size, GetColorU32(ImGuiCol_ModalWindowDarkening, g.ModalWindowDarkeningRatio)); + } // Draw navigation selection/windowing rectangle background if (g.NavWindowingTarget == window) From f318f2d5ea1b334cc0e1883034bb07de6db1eda4 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 7 Mar 2018 11:50:56 +0100 Subject: [PATCH 061/828] Examples: Added Makefile for SDL+OpenGL2 example. (#1668) --- examples/sdl_opengl2_example/Makefile | 66 +++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 examples/sdl_opengl2_example/Makefile diff --git a/examples/sdl_opengl2_example/Makefile b/examples/sdl_opengl2_example/Makefile new file mode 100644 index 000000000000..dc4d69f1ac81 --- /dev/null +++ b/examples/sdl_opengl2_example/Makefile @@ -0,0 +1,66 @@ +# +# Cross Platform Makefile +# Compatible with MSYS2/MINGW, Ubuntu 14.04.1 and Mac OS X +# +# You will need SDL2 (http://www.libsdl.org): +# Linux: +# apt-get install libsdl2-dev +# Mac OS X: +# brew install sdl2 +# MSYS2: +# pacman -S mingw-w64-i686-SDL +# + +#CXX = g++ +#CXX = clang++ + +EXE = sdl_opengl2_example +SOURCES = main.cpp imgui_impl_sdl_gl2.cpp +SOURCES += ../../imgui.cpp ../../imgui_demo.cpp ../../imgui_draw.cpp +OBJS = $(addsuffix .o, $(basename $(notdir $(SOURCES)))) + +UNAME_S := $(shell uname -s) + + +ifeq ($(UNAME_S), Linux) #LINUX + ECHO_MESSAGE = "Linux" + LIBS = -lGL -ldl `sdl2-config --libs` + + CXXFLAGS = -I../../ `sdl2-config --cflags` + CXXFLAGS += -Wall -Wformat + CFLAGS = $(CXXFLAGS) +endif + +ifeq ($(UNAME_S), Darwin) #APPLE + ECHO_MESSAGE = "Mac OS X" + LIBS = -framework OpenGL -framework Cocoa -framework IOKit -framework CoreVideo `sdl2-config --libs` + + CXXFLAGS = -I../../ -I/usr/local/include `sdl2-config --cflags` + CXXFLAGS += -Wall -Wformat + CFLAGS = $(CXXFLAGS) +endif + +ifeq ($(findstring MINGW,$(UNAME_S)),MINGW) + ECHO_MESSAGE = "Windows" + LIBS = -lgdi32 -lopengl32 -limm32 `pkg-config --static --libs sdl2` + + CXXFLAGS = -I../../ `pkg-config --cflags sdl2` + CXXFLAGS += -Wall -Wformat + CFLAGS = $(CXXFLAGS) +endif + + +%.o:%.cpp + $(CXX) $(CXXFLAGS) -c -o $@ $< + +%.o:../../%.cpp + $(CXX) $(CXXFLAGS) -c -o $@ $< + +all: $(EXE) + @echo Build complete for $(ECHO_MESSAGE) + +$(EXE): $(OBJS) + $(CXX) -o $@ $^ $(CXXFLAGS) $(LIBS) + +clean: + rm -f $(EXE) $(OBJS) From 19b92751b9054760698a9279b2a674ade676985f Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 7 Mar 2018 12:05:47 +0100 Subject: [PATCH 062/828] Examples: Updated Makefile and batch files to work with new examples. Using lowercase "gl.h" as it seems this is what Linux wants. --- examples/directx10_example/build_win32.bat | 2 +- examples/directx11_example/build_win32.bat | 2 +- examples/directx12_example/build_win32.bat | 2 +- examples/directx9_example/build_win32.bat | 2 +- examples/imgui_impl_opengl2.cpp | 2 +- examples/opengl2_example/Makefile | 5 ++++- examples/opengl2_example/build_win32.bat | 2 +- examples/opengl3_example/Makefile | 5 ++++- examples/opengl3_example/build_win32.bat | 2 +- examples/sdl_opengl2_example/Makefile | 5 ++++- examples/sdl_opengl2_example/build_win32.bat | 2 +- examples/sdl_opengl3_example/Makefile | 5 ++++- examples/sdl_opengl3_example/build_win32.bat | 2 +- examples/vulkan_example/build_win32.bat | 4 ++-- examples/vulkan_example/build_win64.bat | 4 ++-- 15 files changed, 29 insertions(+), 17 deletions(-) diff --git a/examples/directx10_example/build_win32.bat b/examples/directx10_example/build_win32.bat index 2b30786b516b..e7f4bb2cf2b7 100644 --- a/examples/directx10_example/build_win32.bat +++ b/examples/directx10_example/build_win32.bat @@ -1,4 +1,4 @@ @REM Build for Visual Studio compiler. Run your copy of vcvars32.bat or vcvarsall.bat to setup command-line compiler. mkdir Debug -cl /nologo /Zi /MD /I ..\.. /I "%WindowsSdkDir%Include\um" /I "%WindowsSdkDir%Include\shared" /I "%DXSDK_DIR%Include" /D UNICODE /D _UNICODE *.cpp ..\..\*.cpp /FeDebug/directx10_example.exe /FoDebug/ /link /LIBPATH:"%DXSDK_DIR%/Lib/x86" d3d10.lib d3dcompiler.lib +cl /nologo /Zi /MD /I ..\.. /I "%WindowsSdkDir%Include\um" /I "%WindowsSdkDir%Include\shared" /I "%DXSDK_DIR%Include" /D UNICODE /D _UNICODE *.cpp ..\imgui_impl_win32.cpp ..\imgui_impl_dx10.cpp ..\..\*.cpp /FeDebug/directx10_example.exe /FoDebug/ /link /LIBPATH:"%DXSDK_DIR%/Lib/x86" d3d10.lib d3dcompiler.lib diff --git a/examples/directx11_example/build_win32.bat b/examples/directx11_example/build_win32.bat index 2d1c40f80c0c..b448a5798147 100644 --- a/examples/directx11_example/build_win32.bat +++ b/examples/directx11_example/build_win32.bat @@ -1,4 +1,4 @@ @REM Build for Visual Studio compiler. Run your copy of vcvars32.bat or vcvarsall.bat to setup command-line compiler. mkdir Debug -cl /nologo /Zi /MD /I ..\.. /I "%WindowsSdkDir%Include\um" /I "%WindowsSdkDir%Include\shared" /I "%DXSDK_DIR%Include" /D UNICODE /D _UNICODE *.cpp ..\..\*.cpp /FeDebug/directx11_example.exe /FoDebug/ /link /LIBPATH:"%DXSDK_DIR%/Lib/x86" d3d11.lib d3dcompiler.lib +cl /nologo /Zi /MD /I ..\.. /I "%WindowsSdkDir%Include\um" /I "%WindowsSdkDir%Include\shared" /I "%DXSDK_DIR%Include" /D UNICODE /D _UNICODE *.cpp ..\imgui_impl_win32.cpp ..\imgui_impl_dx11.cpp ..\..\*.cpp /FeDebug/directx11_example.exe /FoDebug/ /link /LIBPATH:"%DXSDK_DIR%/Lib/x86" d3d11.lib d3dcompiler.lib diff --git a/examples/directx12_example/build_win32.bat b/examples/directx12_example/build_win32.bat index 0daf68b1cc15..04fbd5d01691 100644 --- a/examples/directx12_example/build_win32.bat +++ b/examples/directx12_example/build_win32.bat @@ -1,4 +1,4 @@ @REM Build for Visual Studio compiler. Run your copy of vcvars32.bat or vcvarsall.bat to setup command-line compiler. mkdir Debug -cl /nologo /Zi /MD /I ..\.. /I "%WindowsSdkDir%Include\um" /I "%WindowsSdkDir%Include\shared" /D UNICODE /D _UNICODE *.cpp ..\..\*.cpp /FeDebug/directx12_example.exe /FoDebug/ /link d3d12.lib d3dcompiler.lib dxgi.lib +cl /nologo /Zi /MD /I ..\.. /I "%WindowsSdkDir%Include\um" /I "%WindowsSdkDir%Include\shared" /D UNICODE /D _UNICODE *.cpp ..\imgui_impl_win32.cpp ..\imgui_impl_dx12.cpp ..\..\*.cpp /FeDebug/directx12_example.exe /FoDebug/ /link d3d12.lib d3dcompiler.lib dxgi.lib diff --git a/examples/directx9_example/build_win32.bat b/examples/directx9_example/build_win32.bat index c3647d4c1313..e8edc27d2252 100644 --- a/examples/directx9_example/build_win32.bat +++ b/examples/directx9_example/build_win32.bat @@ -1,3 +1,3 @@ @REM Build for Visual Studio compiler. Run your copy of vcvars32.bat or vcvarsall.bat to setup command-line compiler. mkdir Debug -cl /nologo /Zi /MD /I ..\.. /I "%DXSDK_DIR%/Include" /D UNICODE /D _UNICODE *.cpp ..\..\*.cpp /FeDebug/directx9_example.exe /FoDebug/ /link /LIBPATH:"%DXSDK_DIR%/Lib/x86" d3d9.lib +cl /nologo /Zi /MD /I ..\.. /I "%DXSDK_DIR%/Include" /D UNICODE /D _UNICODE *.cpp ..\imgui_impl_win32.cpp ..\imgui_impl_dx9.cpp ..\..\*.cpp /FeDebug/directx9_example.exe /FoDebug/ /link /LIBPATH:"%DXSDK_DIR%/Lib/x86" d3d9.lib diff --git a/examples/imgui_impl_opengl2.cpp b/examples/imgui_impl_opengl2.cpp index 3d47702bd231..88b3a6c90abb 100644 --- a/examples/imgui_impl_opengl2.cpp +++ b/examples/imgui_impl_opengl2.cpp @@ -30,7 +30,7 @@ #if defined(__APPLE__) #include #else -#include +#include #endif // OpenGL Data diff --git a/examples/opengl2_example/Makefile b/examples/opengl2_example/Makefile index aa7dbf47fd2b..cf126b734048 100644 --- a/examples/opengl2_example/Makefile +++ b/examples/opengl2_example/Makefile @@ -15,7 +15,7 @@ #CXX = clang++ EXE = opengl2_example -SOURCES = main.cpp imgui_impl_glfw_gl2.cpp +SOURCES = main.cpp ../imgui_impl_glfw.cpp ../imgui_impl_opengl2.cpp SOURCES += ../../imgui.cpp ../../imgui_demo.cpp ../../imgui_draw.cpp OBJS = $(addsuffix .o, $(basename $(notdir $(SOURCES)))) @@ -55,6 +55,9 @@ endif %.o:%.cpp $(CXX) $(CXXFLAGS) -c -o $@ $< +%.o:../%.cpp + $(CXX) $(CXXFLAGS) -c -o $@ $< + %.o:../../%.cpp $(CXX) $(CXXFLAGS) -c -o $@ $< diff --git a/examples/opengl2_example/build_win32.bat b/examples/opengl2_example/build_win32.bat index 28e6752fd176..fba5030666a9 100644 --- a/examples/opengl2_example/build_win32.bat +++ b/examples/opengl2_example/build_win32.bat @@ -1,3 +1,3 @@ @REM Build for Visual Studio compiler. Run your copy of vcvars32.bat or vcvarsall.bat to setup command-line compiler. mkdir Debug -cl /nologo /Zi /MD /I ..\.. /I ..\libs\glfw\include *.cpp ..\..\*.cpp /FeDebug/opengl2_example.exe /FoDebug/ /link /LIBPATH:..\libs\glfw\lib-vc2010-32 glfw3.lib opengl32.lib gdi32.lib shell32.lib +cl /nologo /Zi /MD /I ..\.. /I ..\libs\glfw\include *.cpp ..\imgui_impl_glfw.cpp ..\imgui_impl_opengl2.cpp ..\..\*.cpp /FeDebug/opengl2_example.exe /FoDebug/ /link /LIBPATH:..\libs\glfw\lib-vc2010-32 glfw3.lib opengl32.lib gdi32.lib shell32.lib diff --git a/examples/opengl3_example/Makefile b/examples/opengl3_example/Makefile index 008f2ca75d44..313ba09ba100 100644 --- a/examples/opengl3_example/Makefile +++ b/examples/opengl3_example/Makefile @@ -15,7 +15,7 @@ #CXX = clang++ EXE = opengl3_example -SOURCES = main.cpp imgui_impl_glfw_gl3.cpp +SOURCES = main.cpp ../imgui_impl_glfw.cpp ../imgui_impl_opengl3.cpp SOURCES += ../../imgui.cpp ../../imgui_demo.cpp ../../imgui_draw.cpp SOURCES += ../libs/gl3w/GL/gl3w.c OBJS = $(addsuffix .o, $(basename $(notdir $(SOURCES)))) @@ -56,6 +56,9 @@ endif %.o:%.cpp $(CXX) $(CXXFLAGS) -c -o $@ $< +%.o:../%.cpp + $(CXX) $(CXXFLAGS) -c -o $@ $< + %.o:../../%.cpp $(CXX) $(CXXFLAGS) -c -o $@ $< diff --git a/examples/opengl3_example/build_win32.bat b/examples/opengl3_example/build_win32.bat index 931159185527..f6f021689297 100644 --- a/examples/opengl3_example/build_win32.bat +++ b/examples/opengl3_example/build_win32.bat @@ -1,3 +1,3 @@ @REM Build for Visual Studio compiler. Run your copy of vcvars32.bat or vcvarsall.bat to setup command-line compiler. mkdir Debug -cl /nologo /Zi /MD /I ..\.. /I ..\libs\glfw\include /I ..\libs\gl3w *.cpp ..\..\imgui*.cpp ..\libs\gl3w\GL\gl3w.c /FeDebug/opengl_example3.exe /FoDebug/ /link /LIBPATH:..\libs\glfw\lib-vc2010-32 glfw3.lib opengl32.lib gdi32.lib shell32.lib +cl /nologo /Zi /MD /I ..\.. /I ..\libs\glfw\include /I ..\libs\gl3w *.cpp ..\imgui_impl_glfw.cpp ..\imgui_impl_opengl3.cpp ..\..\imgui*.cpp ..\libs\gl3w\GL\gl3w.c /FeDebug/opengl_example3.exe /FoDebug/ /link /LIBPATH:..\libs\glfw\lib-vc2010-32 glfw3.lib opengl32.lib gdi32.lib shell32.lib diff --git a/examples/sdl_opengl2_example/Makefile b/examples/sdl_opengl2_example/Makefile index dc4d69f1ac81..1fa4a91d795b 100644 --- a/examples/sdl_opengl2_example/Makefile +++ b/examples/sdl_opengl2_example/Makefile @@ -15,7 +15,7 @@ #CXX = clang++ EXE = sdl_opengl2_example -SOURCES = main.cpp imgui_impl_sdl_gl2.cpp +SOURCES = main.cpp ../imgui_impl_sdl2.cpp ../imgui_impl_opengl2.cpp SOURCES += ../../imgui.cpp ../../imgui_demo.cpp ../../imgui_draw.cpp OBJS = $(addsuffix .o, $(basename $(notdir $(SOURCES)))) @@ -53,6 +53,9 @@ endif %.o:%.cpp $(CXX) $(CXXFLAGS) -c -o $@ $< +%.o:../%.cpp + $(CXX) $(CXXFLAGS) -c -o $@ $< + %.o:../../%.cpp $(CXX) $(CXXFLAGS) -c -o $@ $< diff --git a/examples/sdl_opengl2_example/build_win32.bat b/examples/sdl_opengl2_example/build_win32.bat index 6fe8cb9a7fcf..7844dc1ff2fd 100644 --- a/examples/sdl_opengl2_example/build_win32.bat +++ b/examples/sdl_opengl2_example/build_win32.bat @@ -1,3 +1,3 @@ @REM Build for Visual Studio compiler. Run your copy of vcvars32.bat or vcvarsall.bat to setup command-line compiler. mkdir Debug -cl /nologo /Zi /MD /I ..\.. /I ..\libs\gl3w /I %SDL2_DIR%\include main.cpp imgui_impl_sdl_gl2.cpp ..\..\imgui*.cpp ..\libs\gl3w\GL\gl3w.c /FeDebug/sdl_opengl2_example.exe /FoDebug/ /link /libpath:%SDL2_DIR%\lib\x86 SDL2.lib SDL2main.lib opengl32.lib /subsystem:console +cl /nologo /Zi /MD /I ..\.. /I ..\libs\gl3w /I %SDL2_DIR%\include *.cpp ..\imgui_impl_sdl2.cpp ..\imgui_impl_opengl2.cpp ..\..\imgui*.cpp ..\libs\gl3w\GL\gl3w.c /FeDebug/sdl_opengl2_example.exe /FoDebug/ /link /libpath:%SDL2_DIR%\lib\x86 SDL2.lib SDL2main.lib opengl32.lib /subsystem:console diff --git a/examples/sdl_opengl3_example/Makefile b/examples/sdl_opengl3_example/Makefile index 67f0f03b524e..b37d3336d5d3 100644 --- a/examples/sdl_opengl3_example/Makefile +++ b/examples/sdl_opengl3_example/Makefile @@ -15,7 +15,7 @@ #CXX = clang++ EXE = sdl_opengl3_example -SOURCES = main.cpp imgui_impl_sdl_gl3.cpp +SOURCES = main.cpp ../imgui_impl_sdl2.cpp ../imgui_impl_opengl3.cpp SOURCES += ../../imgui.cpp ../../imgui_demo.cpp ../../imgui_draw.cpp SOURCES += ../libs/gl3w/GL/gl3w.c OBJS = $(addsuffix .o, $(basename $(notdir $(SOURCES)))) @@ -54,6 +54,9 @@ endif %.o:%.cpp $(CXX) $(CXXFLAGS) -c -o $@ $< +%.o:../%.cpp + $(CXX) $(CXXFLAGS) -c -o $@ $< + %.o:../../%.cpp $(CXX) $(CXXFLAGS) -c -o $@ $< diff --git a/examples/sdl_opengl3_example/build_win32.bat b/examples/sdl_opengl3_example/build_win32.bat index aec9584e49df..c6c87c4b0162 100644 --- a/examples/sdl_opengl3_example/build_win32.bat +++ b/examples/sdl_opengl3_example/build_win32.bat @@ -1,3 +1,3 @@ @REM Build for Visual Studio compiler. Run your copy of vcvars32.bat or vcvarsall.bat to setup command-line compiler. mkdir Debug -cl /nologo /Zi /MD /I ..\.. /I ..\libs\gl3w /I %SDL2_DIR%\include main.cpp imgui_impl_sdl_gl3.cpp ..\..\imgui*.cpp ..\libs\gl3w\GL\gl3w.c /FeDebug/sdl_opengl3_example.exe /FoDebug/ /link /libpath:%SDL2_DIR%\lib\x86 SDL2.lib SDL2main.lib opengl32.lib /subsystem:console +cl /nologo /Zi /MD /I ..\.. /I ..\libs\gl3w /I %SDL2_DIR%\include *.cpp ..\imgui_impl_sdl2.cpp ..\imgui_impl_opengl3.cpp ..\..\imgui*.cpp ..\libs\gl3w\GL\gl3w.c /FeDebug/sdl_opengl3_example.exe /FoDebug/ /link /libpath:%SDL2_DIR%\lib\x86 SDL2.lib SDL2main.lib opengl32.lib /subsystem:console diff --git a/examples/vulkan_example/build_win32.bat b/examples/vulkan_example/build_win32.bat index bacb6394396d..c09ac34ef65f 100644 --- a/examples/vulkan_example/build_win32.bat +++ b/examples/vulkan_example/build_win32.bat @@ -1,7 +1,7 @@ @REM Build for Visual Studio compiler. Run your copy of vcvars32.bat or vcvarsall.bat to setup command-line compiler. mkdir Debug -cl /nologo /Zi /MD /I ..\.. /I ..\libs\glfw\include /I %VULKAN_SDK%\include *.cpp ..\..\*.cpp /FeDebug/vulkan_example.exe /FoDebug/ /link /LIBPATH:..\libs\glfw\lib-vc2010-32 /libpath:%VULKAN_SDK%\lib32 glfw3.lib opengl32.lib gdi32.lib shell32.lib vulkan-1.lib +cl /nologo /Zi /MD /I ..\.. /I ..\libs\glfw\include /I %VULKAN_SDK%\include *.cpp ..\imgui_impl_glfw.cpp ..\imgui_impl_vulkan.cpp ..\..\*.cpp /FeDebug/vulkan_example.exe /FoDebug/ /link /LIBPATH:..\libs\glfw\lib-vc2010-32 /libpath:%VULKAN_SDK%\lib32 glfw3.lib opengl32.lib gdi32.lib shell32.lib vulkan-1.lib mkdir Release -cl /nologo /Zi /MD /Ox /Oi /I ..\.. /I ..\libs\glfw\include /I %VULKAN_SDK%\include *.cpp ..\..\*.cpp /FeRelease/vulkan_example.exe /FoRelease/ /link /LIBPATH:..\libs\glfw\lib-vc2010-32 /libpath:%VULKAN_SDK%\lib32 glfw3.lib opengl32.lib gdi32.lib shell32.lib vulkan-1.lib +cl /nologo /Zi /MD /Ox /Oi /I ..\.. /I ..\libs\glfw\include /I %VULKAN_SDK%\include *.cpp ..\imgui_impl_glfw.cpp ..\imgui_impl_vulkan.cpp ..\..\*.cpp /FeRelease/vulkan_example.exe /FoRelease/ /link /LIBPATH:..\libs\glfw\lib-vc2010-32 /libpath:%VULKAN_SDK%\lib32 glfw3.lib opengl32.lib gdi32.lib shell32.lib vulkan-1.lib diff --git a/examples/vulkan_example/build_win64.bat b/examples/vulkan_example/build_win64.bat index 71e557b19dc1..f7b50247843b 100644 --- a/examples/vulkan_example/build_win64.bat +++ b/examples/vulkan_example/build_win64.bat @@ -1,7 +1,7 @@ @REM Build for Visual Studio compiler. Run your copy of amd64/vcvars32.bat to setup 64-bit command-line compiler. mkdir Debug -cl /nologo /Zi /MD /I ..\.. /I ..\libs\glfw\include /I %VULKAN_SDK%\include *.cpp ..\..\*.cpp /FeDebug/vulkan_example.exe /FoDebug/ /link /LIBPATH:..\libs\glfw\lib-vc2010-64 /libpath:%VULKAN_SDK%\lib glfw3.lib opengl32.lib gdi32.lib shell32.lib vulkan-1.lib +cl /nologo /Zi /MD /I ..\.. /I ..\libs\glfw\include /I %VULKAN_SDK%\include *.cpp ..\imgui_impl_glfw.cpp ..\imgui_impl_vulkan.cpp ..\..\*.cpp /FeDebug/vulkan_example.exe /FoDebug/ /link /LIBPATH:..\libs\glfw\lib-vc2010-64 /libpath:%VULKAN_SDK%\lib glfw3.lib opengl32.lib gdi32.lib shell32.lib vulkan-1.lib mkdir Release -cl /nologo /Zi /MD /Ox /Oi /I ..\.. /I ..\libs\glfw\include /I %VULKAN_SDK%\include *.cpp ..\..\*.cpp /FeRelease/vulkan_example.exe /FoRelease/ /link /LIBPATH:..\libs\glfw\lib-vc2010-64 /libpath:%VULKAN_SDK%\lib glfw3.lib opengl32.lib gdi32.lib shell32.lib vulkan-1.lib +cl /nologo /Zi /MD /Ox /Oi /I ..\.. /I ..\libs\glfw\include /I %VULKAN_SDK%\include *.cpp ..\imgui_impl_glfw.cpp ..\imgui_impl_vulkan.cpp ..\..\*.cpp /FeRelease/vulkan_example.exe /FoRelease/ /link /LIBPATH:..\libs\glfw\lib-vc2010-64 /libpath:%VULKAN_SDK%\lib glfw3.lib opengl32.lib gdi32.lib shell32.lib vulkan-1.lib From 52c78820aac177018b4118176747055ca9e4a275 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 7 Mar 2018 12:19:19 +0100 Subject: [PATCH 063/828] Examples: SDL: Fix for pre 2.0.4. --- examples/imgui_impl_sdl2.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/examples/imgui_impl_sdl2.cpp b/examples/imgui_impl_sdl2.cpp index 0d6f8490f697..f2409f8f14b1 100644 --- a/examples/imgui_impl_sdl2.cpp +++ b/examples/imgui_impl_sdl2.cpp @@ -35,6 +35,7 @@ #include // Data +static SDL_Window* g_Window = NULL; static Uint64 g_Time = 0; static bool g_MousePressed[3] = { false, false, false }; static SDL_Cursor* g_MouseCursors[ImGuiMouseCursor_Count_] = { 0 }; @@ -100,6 +101,8 @@ bool ImGui_ImplSDL2_ProcessEvent(SDL_Event* event) bool ImGui_ImplSDL2_Init(SDL_Window* window, void* sdl_gl_context) { + g_Window = window; + // Keyboard mapping. ImGui will use those indices to peek into the io.KeyDown[] array. ImGuiIO& io = ImGui::GetIO(); io.KeyMap[ImGuiKey_Tab] = SDL_SCANCODE_TAB; @@ -141,8 +144,6 @@ bool ImGui_ImplSDL2_Init(SDL_Window* window, void* sdl_gl_context) SDL_VERSION(&wmInfo.version); SDL_GetWindowWMInfo(window, &wmInfo); io.ImeWindowHandle = wmInfo.info.win.window; -#else - (void)window; #endif // Our mouse update function expect PlatformHandle to be filled for the main viewport @@ -157,6 +158,7 @@ bool ImGui_ImplSDL2_Init(SDL_Window* window, void* sdl_gl_context) void ImGui_ImplSDL2_Shutdown() { ImGui_ImplSDL2_ShutdownPlatformInterface(); + g_Window = NULL; // Destroy SDL mouse cursors for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_Count_; cursor_n++) @@ -179,6 +181,7 @@ static void ImGui_ImplSDL2_UpdateMouse() io.MouseDown[2] = g_MousePressed[2] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_MIDDLE)) != 0; g_MousePressed[0] = g_MousePressed[1] = g_MousePressed[2] = false; +#if SDL_VERSION_ATLEAST(2,0,4) SDL_Window* focused_window = SDL_GetKeyboardFocus(); if (focused_window) { @@ -198,9 +201,11 @@ static void ImGui_ImplSDL2_UpdateMouse() // We already retrieve global mouse position, SDL_CaptureMouse() also let the OS know our drag outside boundaries shouldn't trigger, e.g.: OS window resize cursor // The function is only supported from SDL 2.0.4 (released Jan 2016) -#if (SDL_MAJOR_VERSION >= 2) && (SDL_MINOR_VERSION >= 0) && (SDL_PATCHLEVEL >= 4) bool any_mouse_button_down = ImGui::IsAnyMouseDown(); SDL_CaptureMouse(any_mouse_button_down ? SDL_TRUE : SDL_FALSE); +#else + if (SDL_GetWindowFlags(g_Window) & SDL_WINDOW_INPUT_FOCUS) + io.MousePos = ImVec2((float)mx, (float)my); #endif // Update OS/hardware mouse cursor if imgui isn't drawing a software cursor From 9dcc07422ee7dd4f660c4a507431e6f28a4742f4 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 7 Mar 2018 12:35:26 +0100 Subject: [PATCH 064/828] Viewport: Reorganized viewport enable flags. Both user + platform + renderer need to enable a flag. (#1542) --- examples/directx10_example/main.cpp | 2 +- examples/directx11_example/main.cpp | 2 +- examples/directx12_example/main.cpp | 2 +- examples/imgui_impl_dx10.cpp | 3 ++- examples/imgui_impl_dx11.cpp | 3 ++- examples/imgui_impl_dx12.cpp | 3 ++- examples/imgui_impl_glfw.cpp | 3 ++- examples/imgui_impl_opengl3.cpp | 5 ++++- examples/imgui_impl_sdl2.cpp | 6 +++-- examples/imgui_impl_vulkan.cpp | 3 ++- examples/imgui_impl_win32.cpp | 3 ++- examples/opengl3_example/main.cpp | 2 +- examples/sdl_opengl3_example/main.cpp | 2 +- examples/sdl_vulkan_example/main.cpp | 2 +- examples/vulkan_example/main.cpp | 2 +- imgui.cpp | 32 +++++++++++++++++---------- imgui.h | 16 ++++++++------ 17 files changed, 56 insertions(+), 35 deletions(-) diff --git a/examples/directx10_example/main.cpp b/examples/directx10_example/main.cpp index ab6a0fdb3d8b..2e3f5dd35268 100644 --- a/examples/directx10_example/main.cpp +++ b/examples/directx10_example/main.cpp @@ -115,7 +115,7 @@ int main(int, char**) // Setup ImGui binding ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - io.ConfigFlags |= ImGuiConfigFlags_MultiViewports; + io.ConfigFlags |= ImGuiConfigFlags_EnableViewports; //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls ImGui_ImplWin32_Init(hwnd); diff --git a/examples/directx11_example/main.cpp b/examples/directx11_example/main.cpp index 8c0a98573dbf..ad2e675c1ebe 100644 --- a/examples/directx11_example/main.cpp +++ b/examples/directx11_example/main.cpp @@ -118,7 +118,7 @@ int main(int, char**) // Setup ImGui binding ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - io.ConfigFlags |= ImGuiConfigFlags_MultiViewports; + io.ConfigFlags |= ImGuiConfigFlags_EnableViewports; //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls ImGui_ImplWin32_Init(hwnd); diff --git a/examples/directx12_example/main.cpp b/examples/directx12_example/main.cpp index 1e2d81578cae..20957f0129b2 100644 --- a/examples/directx12_example/main.cpp +++ b/examples/directx12_example/main.cpp @@ -289,7 +289,7 @@ int main(int, char**) // Setup ImGui binding ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - io.ConfigFlags |= ImGuiConfigFlags_MultiViewports; + io.ConfigFlags |= ImGuiConfigFlags_EnableViewports; //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls ImGui_ImplWin32_Init(hwnd); diff --git a/examples/imgui_impl_dx10.cpp b/examples/imgui_impl_dx10.cpp index 014c4c37dfd0..fd41922f6cf2 100644 --- a/examples/imgui_impl_dx10.cpp +++ b/examples/imgui_impl_dx10.cpp @@ -479,7 +479,8 @@ bool ImGui_ImplDX10_Init(ID3D10Device* device) ImGuiIO& io = ImGui::GetIO(); g_pd3dDevice = device; g_pFactory = pFactory; - if (io.ConfigFlags & ImGuiConfigFlags_MultiViewports) + io.ConfigFlags |= ImGuiConfigFlags_RendererHasViewports; + if (io.ConfigFlags & ImGuiConfigFlags_EnableViewports) ImGui_ImplDX10_InitPlatformInterface(); return true; } diff --git a/examples/imgui_impl_dx11.cpp b/examples/imgui_impl_dx11.cpp index 56fc7f6aece6..42aed6957e27 100644 --- a/examples/imgui_impl_dx11.cpp +++ b/examples/imgui_impl_dx11.cpp @@ -487,7 +487,8 @@ bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_co g_pd3dDevice = device; g_pd3dDeviceContext = device_context; g_pFactory = pFactory; - if (io.ConfigFlags & ImGuiConfigFlags_MultiViewports) + io.ConfigFlags |= ImGuiConfigFlags_RendererHasViewports; + if (io.ConfigFlags & ImGuiConfigFlags_EnableViewports) ImGui_ImplDX11_InitPlatformInterface(); return true; } diff --git a/examples/imgui_impl_dx12.cpp b/examples/imgui_impl_dx12.cpp index 6de48c627e26..d0d05d6051c2 100644 --- a/examples/imgui_impl_dx12.cpp +++ b/examples/imgui_impl_dx12.cpp @@ -605,7 +605,8 @@ bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FO } ImGuiIO& io = ImGui::GetIO(); - if (io.ConfigFlags & ImGuiConfigFlags_MultiViewports) + io.ConfigFlags |= ImGuiConfigFlags_RendererHasViewports; // FIXME-VIEWPORT: Actually unfinshed.. + if (io.ConfigFlags & ImGuiConfigFlags_EnableViewports) ImGui_ImplDX12_InitPlatformInterface(); return true; diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index 2e28a52a0f73..bcb7ff88837e 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -153,7 +153,8 @@ bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks) // Our mouse update function expect PlatformHandle to be filled for the main viewport ImGuiViewport* main_viewport = ImGui::GetMainViewport(); main_viewport->PlatformHandle = (void*)g_Window; - if (io.ConfigFlags & ImGuiConfigFlags_MultiViewports) + io.ConfigFlags |= ImGuiConfigFlags_PlatformHasViewports; + if (io.ConfigFlags & ImGuiConfigFlags_EnableViewports) ImGui_ImplGlfw_InitPlatformInterface(); g_ClientApi = GlfwClientApi_OpenGL; diff --git a/examples/imgui_impl_opengl3.cpp b/examples/imgui_impl_opengl3.cpp index 935290c950d5..1136856cbbc2 100644 --- a/examples/imgui_impl_opengl3.cpp +++ b/examples/imgui_impl_opengl3.cpp @@ -45,7 +45,10 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version) strcpy(g_GlslVersion, glsl_version); strcat(g_GlslVersion, "\n"); - ImGui_ImplOpenGL3_InitPlatformInterface(); + ImGuiIO& io = ImGui::GetIO(); + io.ConfigFlags |= ImGuiConfigFlags_RendererHasViewports; + if (io.ConfigFlags & ImGuiConfigFlags_EnableViewports) + ImGui_ImplOpenGL3_InitPlatformInterface(); return true; } diff --git a/examples/imgui_impl_sdl2.cpp b/examples/imgui_impl_sdl2.cpp index f2409f8f14b1..b1c757db9d19 100644 --- a/examples/imgui_impl_sdl2.cpp +++ b/examples/imgui_impl_sdl2.cpp @@ -149,8 +149,11 @@ bool ImGui_ImplSDL2_Init(SDL_Window* window, void* sdl_gl_context) // Our mouse update function expect PlatformHandle to be filled for the main viewport ImGuiViewport* main_viewport = ImGui::GetMainViewport(); main_viewport->PlatformHandle = (void*)window; - if (io.ConfigFlags & ImGuiConfigFlags_MultiViewports) +#if SDL_VERSION_ATLEAST(2,0,4) + io.ConfigFlags |= ImGuiConfigFlags_PlatformHasViewports; + if (io.ConfigFlags & ImGuiConfigFlags_EnableViewports) ImGui_ImplSDL2_InitPlatformInterface(window, sdl_gl_context); +#endif return true; } @@ -172,7 +175,6 @@ static void ImGui_ImplSDL2_UpdateMouse() io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); io.MousePosViewport = 0; io.MouseHoveredViewport = 0; - io.ConfigFlags &= ~ImGuiConfigFlags_PlatformHasMouseHoveredViewport; // FIXME-VIEWPORT: We can't get this info properly with SDL, capture is messing up with SDL_WINDOW_MOUSE_FOCUS report and we'd need to handle _NoInputs int mx, my; Uint32 mouse_buttons = SDL_GetMouseState(&mx, &my); diff --git a/examples/imgui_impl_vulkan.cpp b/examples/imgui_impl_vulkan.cpp index 6f3c782f0f14..a013e38404e6 100644 --- a/examples/imgui_impl_vulkan.cpp +++ b/examples/imgui_impl_vulkan.cpp @@ -703,7 +703,8 @@ bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info, VkRenderPass rend ImGuiIO& io = ImGui::GetIO(); ImGui_ImplVulkan_CreateDeviceObjects(); - if (io.ConfigFlags & ImGuiConfigFlags_MultiViewports) + io.ConfigFlags |= ImGuiConfigFlags_RendererHasViewports; + if (io.ConfigFlags & ImGuiConfigFlags_EnableViewports) ImGui_ImplVulkan_InitPlatformInterface(); return true; diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index 061f75d92f7b..7537aa8e4fd3 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -71,7 +71,8 @@ bool ImGui_ImplWin32_Init(void* hwnd) ImGuiViewport* main_viewport = ImGui::GetMainViewport(); main_viewport->PlatformHandle = (void*)g_hWnd; - if (io.ConfigFlags & ImGuiConfigFlags_MultiViewports) + io.ConfigFlags |= ImGuiConfigFlags_PlatformHasViewports; + if (io.ConfigFlags & ImGuiConfigFlags_EnableViewports) ImGui_ImplWin32_InitPlatformInterface(); return true; diff --git a/examples/opengl3_example/main.cpp b/examples/opengl3_example/main.cpp index 381954266165..197865594da5 100644 --- a/examples/opengl3_example/main.cpp +++ b/examples/opengl3_example/main.cpp @@ -35,7 +35,7 @@ int main(int, char**) // Setup ImGui binding ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - io.ConfigFlags |= ImGuiConfigFlags_MultiViewports; + io.ConfigFlags |= ImGuiConfigFlags_EnableViewports; //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls diff --git a/examples/sdl_opengl3_example/main.cpp b/examples/sdl_opengl3_example/main.cpp index 0b9f94854468..f1fdf7a81894 100644 --- a/examples/sdl_opengl3_example/main.cpp +++ b/examples/sdl_opengl3_example/main.cpp @@ -37,7 +37,7 @@ int main(int, char**) // Setup ImGui binding ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - io.ConfigFlags |= ImGuiConfigFlags_MultiViewports; + io.ConfigFlags |= ImGuiConfigFlags_EnableViewports; //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls ImGui_ImplSDL2_Init(window, gl_context); diff --git a/examples/sdl_vulkan_example/main.cpp b/examples/sdl_vulkan_example/main.cpp index a453d5582a1d..cea829c5d4b3 100644 --- a/examples/sdl_vulkan_example/main.cpp +++ b/examples/sdl_vulkan_example/main.cpp @@ -344,7 +344,7 @@ int main(int, char**) // Setup ImGui binding ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - io.ConfigFlags |= ImGuiConfigFlags_MultiViewports; + io.ConfigFlags |= ImGuiConfigFlags_EnableViewports; //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls ImGui_ImplVulkan_InitInfo init_info = {}; diff --git a/examples/vulkan_example/main.cpp b/examples/vulkan_example/main.cpp index 9c2ec795e03b..872b1c39a81d 100644 --- a/examples/vulkan_example/main.cpp +++ b/examples/vulkan_example/main.cpp @@ -353,7 +353,7 @@ int main(int, char**) // Setup ImGui binding ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - io.ConfigFlags |= ImGuiConfigFlags_MultiViewports; + io.ConfigFlags |= ImGuiConfigFlags_EnableViewports; //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls ImGui_ImplVulkan_InitInfo init_info = {}; diff --git a/imgui.cpp b/imgui.cpp index 1e496def60a9..eaf8d7bd5ac2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3246,7 +3246,7 @@ static void ImGui::UpdateMovingWindowDropViewport(ImGuiWindow* window) // On release we either drop window over an existing viewport or create a new one // (We convert position from one viewport space to another, which is unnecessary at the moment but allows us to have viewport overlapping in term of imgui position) ImGuiContext& g = *GImGui; - if (!(g.IO.ConfigFlags & ImGuiConfigFlags_MultiViewports)) + if (!(g.IO.ConfigFlags & ImGuiConfigFlags_EnableViewports)) return; ImRect mouse_viewport_rect = g.MouseViewport->GetRect(); @@ -3386,11 +3386,11 @@ static void ImGui::UpdateViewports() ImGuiViewport* main_viewport = g.Viewports[0]; IM_ASSERT(main_viewport->ID == IMGUI_VIEWPORT_DEFAULT_ID); ImVec2 main_viewport_os_desktop_pos = ImVec2(0.0f, 0.0f); - if ((g.IO.ConfigFlags & ImGuiConfigFlags_MultiViewports)) + if ((g.IO.ConfigFlags & ImGuiConfigFlags_EnableViewports)) main_viewport_os_desktop_pos = g.IO.PlatformInterface.GetWindowPos(main_viewport); Viewport(IMGUI_VIEWPORT_DEFAULT_ID, ImGuiViewportFlags_MainViewport, main_viewport_os_desktop_pos, g.IO.DisplaySize); - if (!(g.IO.ConfigFlags & ImGuiConfigFlags_MultiViewports)) + if (!(g.IO.ConfigFlags & ImGuiConfigFlags_EnableViewports)) { g.MouseViewport = g.MouseLastViewport = main_viewport; return; @@ -3541,15 +3541,23 @@ void ImGui::NewFrame() if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation."); - if (g.IO.ConfigFlags & ImGuiConfigFlags_MultiViewports) + if (g.IO.ConfigFlags & ImGuiConfigFlags_EnableViewports) { + if ((g.IO.ConfigFlags & ImGuiConfigFlags_PlatformHasViewports) && (g.IO.ConfigFlags & ImGuiConfigFlags_RendererHasViewports)) + { #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - IM_ASSERT(g.IO.RenderDrawListsFn == NULL); // Call ImGui::Render() then pass ImGui::GetDrawData() yourself to your render function! + IM_ASSERT(g.IO.RenderDrawListsFn == NULL); // Call ImGui::Render() then pass ImGui::GetDrawData() yourself to your render function! #endif - IM_ASSERT(g.IO.PlatformInterface.CreateViewport != NULL); - IM_ASSERT(g.IO.PlatformInterface.DestroyViewport != NULL); - //IM_ASSERT(g.IO.PlatformInterface.RenderViewport != NULL || g.IO.RendererInterface.RenderViewport != NULL); // Missing rendering function - IM_ASSERT(g.Viewports[0]->PlatformUserData != NULL); // Platform init function didn't setup main viewport + IM_ASSERT(g.IO.PlatformInterface.CreateViewport != NULL); + IM_ASSERT(g.IO.PlatformInterface.DestroyViewport != NULL); + //IM_ASSERT(g.IO.PlatformInterface.RenderViewport != NULL || g.IO.RendererInterface.RenderViewport != NULL); // Missing rendering function + IM_ASSERT(g.Viewports[0]->PlatformUserData != NULL); // Platform init function didn't setup main viewport + } + else + { + // Disable feature, our back-ends do not support it + g.IO.ConfigFlags &= ~ImGuiConfigFlags_EnableViewports; + } } // Load settings on first frame @@ -4354,7 +4362,7 @@ void ImGui::EndFrame() g.FrameCountEnded = g.FrameCount; - if (g.IO.ConfigFlags & ImGuiConfigFlags_MultiViewports) + if (g.IO.ConfigFlags & ImGuiConfigFlags_EnableViewports) UpdatePlatformWindows(); } @@ -4418,7 +4426,7 @@ void ImGui::Render() void ImGui::RenderAdditionalViewports() { ImGuiContext& g = *GImGui; - if (g.IO.ConfigFlags & ImGuiConfigFlags_MultiViewports) + if (g.IO.ConfigFlags & ImGuiConfigFlags_EnableViewports) RenderPlatformWindows(); } @@ -5908,7 +5916,7 @@ static void ImGui::UpdateWindowViewport(ImGuiWindow* window, bool window_pos_set // Restore main viewport if multi viewports are not supported by the back-end ImGuiViewport* main_viewport = g.Viewports[0]; - if (!(g.IO.ConfigFlags & ImGuiConfigFlags_MultiViewports)) + if (!(g.IO.ConfigFlags & ImGuiConfigFlags_EnableViewports)) { window->Viewport = main_viewport; window->ViewportId = main_viewport->ID; diff --git a/imgui.h b/imgui.h index 41b53642f8aa..7b4a53970f70 100644 --- a/imgui.h +++ b/imgui.h @@ -772,18 +772,20 @@ enum ImGuiNavInput_ enum ImGuiConfigFlags_ { // Navigation - ImGuiConfigFlags_NavEnableKeyboard = 1 << 0, // Master keyboard navigation enable flag. NewFrame() will automatically fill io.NavInputs[] based on io.KeyDown[]. - ImGuiConfigFlags_NavEnableGamepad = 1 << 1, // Master gamepad navigation enable flag. This is mostly to instruct your imgui back-end to fill io.NavInputs[]. + ImGuiConfigFlags_NavEnableKeyboard = 1 << 0, // Keyboard navigation enable flag. NewFrame() will automatically fill io.NavInputs[] based on io.KeyDown[]. + ImGuiConfigFlags_NavEnableGamepad = 1 << 1, // Gamepad navigation enable flag. This is mostly to instruct your imgui back-end to fill io.NavInputs[]. ImGuiConfigFlags_NavMoveMouse = 1 << 2, // Request navigation to allow moving the mouse cursor. May be useful on TV/console systems where moving a virtual mouse is awkward. Will update io.MousePos and set io.WantMoveMouse=true. If enabled you MUST honor io.WantMoveMouse requests in your binding, otherwise ImGui will react as if the mouse is jumping around back and forth. ImGuiConfigFlags_NavNoCaptureKeyboard = 1 << 3, // Do not set the io.WantCaptureKeyboard flag with io.NavActive is set. // [BETA] Viewports - ImGuiConfigFlags_MultiViewports = 1 << 4, // User enable multiple viewports (require io.PlatformInterface + io.RendererInterface) - ImGuiConfigFlags_PlatformHasMouseHoveredViewport = 1 << 5, // Back-end knows how to set io.MouseHoveredViewport to the viewport directly under the mouse _IGNORING_ viewports with the ImGuiViewportFlags_NoInputs flag and _REGARDLESS_ of whether another viewport is focused and may have mouse capture. This info is not easy to provide correctly with most high-level engines. - ImGuiConfigFlags_PlatformHasWantMoveMouseSupport = 1 << 6, // Back-end honors io.WantMoveMouse request by updating the OS mouse cursor position (currently only used by ImGuiConfigFlags_NavMoveMouse feature, will be useful for widgets teleporting/wrapping the cursor) - ImGuiConfigFlags_PlatformHasWindowAlpha = 1 << 7, // Back-end can have transparent windows + ImGuiConfigFlags_EnableViewports = 1 << 4, // Viewport enable flags (require both ImGuiConfigFlags_PlatformHasViewports + ImGuiConfigFlags_RendererHasViewports set by the respective back-ends) + ImGuiConfigFlags_PlatformHasViewports = 1 << 5, // Back-end Platform supports multiple viewports + ImGuiConfigFlags_PlatformHasMouseHoveredViewport = 1 << 6, // Back-end Platform supports setting io.MouseHoveredViewport to the viewport directly under the mouse _IGNORING_ viewports with the ImGuiViewportFlags_NoInputs flag and _REGARDLESS_ of whether another viewport is focused and may be capturing the mouse. This information is _NOT EASY_ to provide correctly with most high-level engines. Don't see this without studying how the examples/ back-end handle it. + ImGuiConfigFlags_PlatformHasWantMoveMouseSupport = 1 << 7, // Back-end Platform supports io.WantMoveMouse request by updating the OS mouse cursor position (currently only used by ImGuiConfigFlags_NavMoveMouse feature, will be useful for widgets teleporting/wrapping the cursor) + ImGuiConfigFlags_PlatformHasWindowAlpha = 1 << 8, // Back-end Platform supports transparent windows + ImGuiConfigFlags_RendererHasViewports = 1 << 9, // Back-end Renderer supports multiple viewports - // Platform Info (strictly for the user/application) + // Platform Info (free of use, for user/application convenience) ImGuiConfigFlags_IsSRGB = 1 << 10, // Back-end is SRGB-aware (Storage flag to allow your back-end to communicate to shared widgets. Not used by core ImGui) ImGuiConfigFlags_IsTouchScreen = 1 << 11, // Back-end is using a touch screen instead of a mouse (Storage flag to allow your back-end to communicate to shared widgets. Not used by core ImGui) ImGuiConfigFlags_IsOnScreenKeyboard = 1 << 12 // Back-end uses an on-screen keyboard when io.WantTextInput is set. From d8719cf59b3d2907de76cb9e6629fe970fedf93b Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 7 Mar 2018 14:46:53 +0100 Subject: [PATCH 065/828] Fixed warnings. --- examples/imgui_impl_sdl2.cpp | 14 +++++++------- imgui.cpp | 6 ------ 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/examples/imgui_impl_sdl2.cpp b/examples/imgui_impl_sdl2.cpp index b1c757db9d19..7cd47f633f91 100644 --- a/examples/imgui_impl_sdl2.cpp +++ b/examples/imgui_impl_sdl2.cpp @@ -27,7 +27,6 @@ #include "imgui.h" #include "imgui_impl_sdl2.h" - #include "imgui_internal.h" // FIXME-PLATFORM // SDL @@ -149,11 +148,14 @@ bool ImGui_ImplSDL2_Init(SDL_Window* window, void* sdl_gl_context) // Our mouse update function expect PlatformHandle to be filled for the main viewport ImGuiViewport* main_viewport = ImGui::GetMainViewport(); main_viewport->PlatformHandle = (void*)window; + + // We need SDL_CaptureMouse(), SDL_GetGlobalMouseState() from SDL 2.0.4+ to support multiple viewports. + // We left the call to ImGui_ImplSDL2_InitPlatformInterface() outside of #ifdef to avoid unused-function warnings. #if SDL_VERSION_ATLEAST(2,0,4) io.ConfigFlags |= ImGuiConfigFlags_PlatformHasViewports; - if (io.ConfigFlags & ImGuiConfigFlags_EnableViewports) - ImGui_ImplSDL2_InitPlatformInterface(window, sdl_gl_context); #endif + if ((io.ConfigFlags & ImGuiConfigFlags_EnableViewports) && (io.ConfigFlags & ImGuiConfigFlags_PlatformHasViewports)) + ImGui_ImplSDL2_InitPlatformInterface(window, sdl_gl_context); return true; } @@ -252,9 +254,7 @@ void ImGui_ImplSDL2_NewFrame(SDL_Window* window) // Platform Windows // -------------------------------------------------------------------------------------------------------- -#include "imgui_internal.h" - -#define SDL_HAS_WINDOW_OPACITY SDL_VERSION_ATLEAST(2,0,5) +#define SDL_HAS_WINDOW_OPACITY SDL_VERSION_ATLEAST(2,0,5) struct ImGuiPlatformDataSDL2 { @@ -331,8 +331,8 @@ static void ImGui_ImplSDL2_ShowWindow(ImGuiViewport* viewport) return; } } - #endif + SDL_ShowWindow(data->Window); } diff --git a/imgui.cpp b/imgui.cpp index eaf8d7bd5ac2..bfc85340ebdf 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5194,12 +5194,6 @@ ImVec2 ImGui::GetItemRectSize() return window->DC.LastItemRect.GetSize(); } -static ImRect GetViewportRect() -{ - ImGuiContext& g = *GImGui; - return ImRect(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y); -} - // Not exposed publicly as BeginTooltip() because bool parameters are evil. Let's see if other needs arise first. void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, bool override_previous_tooltip) { From 9ea16e344aca09e6df74121927c18c2355bfca56 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 7 Mar 2018 16:32:46 +0100 Subject: [PATCH 066/828] Viewport: Disable extra viewports from hosting other windows. (#1542) --- examples/vulkan_example/main.cpp | 1 - imgui.cpp | 4 ++-- imgui_internal.h | 7 ++++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/vulkan_example/main.cpp b/examples/vulkan_example/main.cpp index 872b1c39a81d..115227f7aa50 100644 --- a/examples/vulkan_example/main.cpp +++ b/examples/vulkan_example/main.cpp @@ -392,7 +392,6 @@ int main(int, char**) VkCommandPool command_pool = wd->Frames[wd->FrameIndex].CommandPool; VkCommandBuffer command_buffer = wd->Frames[wd->FrameIndex].CommandBuffer; - VkResult err; err = vkResetCommandPool(g_Device, command_pool, 0); check_vk_result(err); VkCommandBufferBeginInfo begin_info = {}; diff --git a/imgui.cpp b/imgui.cpp index bfc85340ebdf..810c42f99a67 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3252,7 +3252,7 @@ static void ImGui::UpdateMovingWindowDropViewport(ImGuiWindow* window) ImRect mouse_viewport_rect = g.MouseViewport->GetRect(); ImVec2 window_pos_in_mouse_viewport = ConvertOsDesktopPosToViewportPos(ConvertViewportPosToOsDesktopPos(window->Pos, window->Viewport), g.MouseViewport); ImRect window_rect_in_mouse_viewport = ImRect(window_pos_in_mouse_viewport, window_pos_in_mouse_viewport + window->Size); - if (mouse_viewport_rect.Contains(window_rect_in_mouse_viewport)) + if ((g.MouseViewport->Flags & ImGuiViewportFlags_HostOtherWindows) && mouse_viewport_rect.Contains(window_rect_in_mouse_viewport)) { // Drop on an existing viewport ImGuiViewport* old_viewport = window->Viewport; @@ -3388,7 +3388,7 @@ static void ImGui::UpdateViewports() ImVec2 main_viewport_os_desktop_pos = ImVec2(0.0f, 0.0f); if ((g.IO.ConfigFlags & ImGuiConfigFlags_EnableViewports)) main_viewport_os_desktop_pos = g.IO.PlatformInterface.GetWindowPos(main_viewport); - Viewport(IMGUI_VIEWPORT_DEFAULT_ID, ImGuiViewportFlags_MainViewport, main_viewport_os_desktop_pos, g.IO.DisplaySize); + Viewport(IMGUI_VIEWPORT_DEFAULT_ID, ImGuiViewportFlags_MainViewport | ImGuiViewportFlags_HostOtherWindows, main_viewport_os_desktop_pos, g.IO.DisplaySize); if (!(g.IO.ConfigFlags & ImGuiConfigFlags_EnableViewports)) { diff --git a/imgui_internal.h b/imgui_internal.h index 28f2daea9208..38ded77f1062 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -517,9 +517,10 @@ struct ImDrawDataBuilder enum ImGuiViewportFlags_ { ImGuiViewportFlags_MainViewport = 1 << 0, - ImGuiViewportFlags_NoDecoration = 1 << 1, // Platform Window: Disable platform title bar, borders, etc. - ImGuiViewportFlags_NoFocusOnAppearing = 1 << 2, // Platform Window: Don't take focus when created. - ImGuiViewportFlags_NoInputs = 1 << 3 // Platform Window: Make mouse pass through so we can drag this window while peaking behind it. + ImGuiViewportFlags_HostOtherWindows = 1 << 1, + ImGuiViewportFlags_NoDecoration = 1 << 2, // Platform Window: Disable platform title bar, borders, etc. + ImGuiViewportFlags_NoFocusOnAppearing = 1 << 3, // Platform Window: Don't take focus when created. + ImGuiViewportFlags_NoInputs = 1 << 4 // Platform Window: Make mouse pass through so we can drag this window while peaking behind it. }; struct ImGuiViewport From aa3fe81c870695c037161eae1e0b08a834b5dcf6 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 8 Mar 2018 18:46:30 +0100 Subject: [PATCH 067/828] Examples: DPI: Hacked in a quick compile-and-run-everywhere call to SetProcessDpiAwareness(), will need to revisit. --- examples/directx11_example/main.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/examples/directx11_example/main.cpp b/examples/directx11_example/main.cpp index ad2e675c1ebe..81e85a3524f0 100644 --- a/examples/directx11_example/main.cpp +++ b/examples/directx11_example/main.cpp @@ -96,8 +96,28 @@ LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) return DefWindowProc(hWnd, msg, wParam, lParam); } +// FIXME-DPI: For now we just want to call SetProcessDpiAwareness(PROCESS_PER_MONITOR_AWARE) without requiring SDK 8.1 or 10 +void SetupDpiAwareness() +{ + typedef enum PROCESS_DPI_AWARENESS + { + PROCESS_DPI_UNAWARE = 0, + PROCESS_SYSTEM_DPI_AWARE = 1, + PROCESS_PER_MONITOR_DPI_AWARE = 2 + } PROCESS_DPI_AWARENESS; + if (HINSTANCE shcore_dll = ::LoadLibraryA("shcore.dll")) + { + typedef HRESULT(WINAPI * PFN_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS); + if (PFN_SetProcessDpiAwareness SetProcessDpiAwarenessFn = (PFN_SetProcessDpiAwareness)::GetProcAddress(shcore_dll, "SetProcessDpiAwareness")) + SetProcessDpiAwarenessFn(PROCESS_PER_MONITOR_DPI_AWARE); + ::FreeLibrary(shcore_dll); + } +} + int main(int, char**) { + SetupDpiAwareness(); + // Create application window WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, WndProc, 0L, 0L, GetModuleHandle(NULL), NULL, NULL, NULL, NULL, _T("ImGui Example"), NULL }; RegisterClassEx(&wc); From 4d46383100145703fe3243a609cb67a6eb816452 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 8 Mar 2018 20:10:40 +0100 Subject: [PATCH 068/828] Viewport: Coupling non-main viewport to one window. Viewport name derived from the window. Added ImGuiConfigFlags_PlatformNoTaskBar, off by default (aka re-established task-bars by default for now). (#1542) --- examples/directx10_example/main.cpp | 1 + examples/directx11_example/main.cpp | 1 + examples/imgui_impl_glfw.cpp | 4 +-- examples/imgui_impl_sdl2.cpp | 3 ++- examples/imgui_impl_win32.cpp | 9 ++++--- examples/opengl3_example/main.cpp | 1 + examples/sdl_opengl3_example/main.cpp | 1 + examples/sdl_vulkan_example/main.cpp | 1 + examples/vulkan_example/main.cpp | 1 + imgui.cpp | 36 ++++++++++++++++++--------- imgui.h | 17 +++++++------ imgui_internal.h | 10 +++++--- 12 files changed, 55 insertions(+), 30 deletions(-) diff --git a/examples/directx10_example/main.cpp b/examples/directx10_example/main.cpp index 2e3f5dd35268..811cda708569 100644 --- a/examples/directx10_example/main.cpp +++ b/examples/directx10_example/main.cpp @@ -116,6 +116,7 @@ int main(int, char**) ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_EnableViewports; + io.ConfigFlags |= ImGuiConfigFlags_PlatformNoTaskBar; //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls ImGui_ImplWin32_Init(hwnd); diff --git a/examples/directx11_example/main.cpp b/examples/directx11_example/main.cpp index 81e85a3524f0..d333f61cbb84 100644 --- a/examples/directx11_example/main.cpp +++ b/examples/directx11_example/main.cpp @@ -139,6 +139,7 @@ int main(int, char**) ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_EnableViewports; + io.ConfigFlags |= ImGuiConfigFlags_PlatformNoTaskBar; //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls ImGui_ImplWin32_Init(hwnd); diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index bcb7ff88837e..6b520ba26961 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -317,7 +317,6 @@ static void ImGui_ImplGlfw_CreateViewport(ImGuiViewport* viewport) data->Window = glfwCreateWindow((int)viewport->Size.x, (int)viewport->Size.y, "No Title Yet", NULL, share_window); data->WindowOwned = true; viewport->PlatformHandle = (void*)data->Window; - viewport->Name = NULL; ImGui_ImplGlfw_InstallCallbacks(data->Window); } @@ -358,7 +357,8 @@ static void ImGui_ImplGlfw_ShowWindow(ImGuiViewport* viewport) #if defined(_WIN32) // GLFW hack: Hide icon from task bar HWND hwnd = glfwGetWin32Window(data->Window); - if (viewport->Flags & ImGuiViewportFlags_NoDecoration) + ImGuiIO& io = ImGui::GetIO(); + if (io.ConfigFlags & ImGuiConfigFlags_PlatformNoTaskBar) { LONG ex_style = ::GetWindowLong(hwnd, GWL_EXSTYLE); ex_style &= ~WS_EX_APPWINDOW; diff --git a/examples/imgui_impl_sdl2.cpp b/examples/imgui_impl_sdl2.cpp index 7cd47f633f91..28a06d89aecb 100644 --- a/examples/imgui_impl_sdl2.cpp +++ b/examples/imgui_impl_sdl2.cpp @@ -316,7 +316,8 @@ static void ImGui_ImplSDL2_ShowWindow(ImGuiViewport* viewport) // SDL hack: Hide icon from task bar // Note: SDL 2.0.6+ has a SDL_WINDOW_SKIP_TASKBAR flag which is supported under Windows but the way it create the window breaks our seamless transition. - if (viewport->Flags & ImGuiViewportFlags_NoDecoration) + ImGuiIO& io = ImGui::GetIO(); + if (io.ConfigFlags & ImGuiConfigFlags_PlatformNoTaskBar) { LONG ex_style = ::GetWindowLong(hwnd, GWL_EXSTYLE); ex_style &= ~WS_EX_APPWINDOW; diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index 7537aa8e4fd3..1c82cb710149 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -291,15 +291,18 @@ static void ImGui_ImplWin32_CreateViewport(ImGuiViewport* viewport) ImGuiPlatformDataWin32* data = IM_NEW(ImGuiPlatformDataWin32)(); viewport->PlatformUserData = data; - if (viewport->Flags & ImGuiViewportFlags_NoDecoration) + ImGuiIO& io = ImGui::GetIO(); + bool no_decoration = (viewport->Flags & ImGuiViewportFlags_NoDecoration) != 0; + bool no_task_bar = (io.ConfigFlags & ImGuiConfigFlags_PlatformNoTaskBar) != 0; + if (no_decoration) { data->DwStyle = WS_POPUP; - data->DwExStyle = 0; + data->DwExStyle = no_task_bar ? WS_EX_TOOLWINDOW : WS_EX_APPWINDOW; } else { data->DwStyle = WS_OVERLAPPEDWINDOW; - data->DwExStyle = WS_EX_TOOLWINDOW; + data->DwExStyle = no_task_bar ? WS_EX_TOOLWINDOW : WS_EX_APPWINDOW; } // Create window diff --git a/examples/opengl3_example/main.cpp b/examples/opengl3_example/main.cpp index 197865594da5..0f943a9aece6 100644 --- a/examples/opengl3_example/main.cpp +++ b/examples/opengl3_example/main.cpp @@ -36,6 +36,7 @@ int main(int, char**) ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_EnableViewports; + io.ConfigFlags |= ImGuiConfigFlags_PlatformNoTaskBar; //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls diff --git a/examples/sdl_opengl3_example/main.cpp b/examples/sdl_opengl3_example/main.cpp index f1fdf7a81894..99dc3b956f84 100644 --- a/examples/sdl_opengl3_example/main.cpp +++ b/examples/sdl_opengl3_example/main.cpp @@ -38,6 +38,7 @@ int main(int, char**) ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_EnableViewports; + io.ConfigFlags |= ImGuiConfigFlags_PlatformNoTaskBar; //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls ImGui_ImplSDL2_Init(window, gl_context); diff --git a/examples/sdl_vulkan_example/main.cpp b/examples/sdl_vulkan_example/main.cpp index cea829c5d4b3..d896b8391ef3 100644 --- a/examples/sdl_vulkan_example/main.cpp +++ b/examples/sdl_vulkan_example/main.cpp @@ -345,6 +345,7 @@ int main(int, char**) ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_EnableViewports; + io.ConfigFlags |= ImGuiConfigFlags_PlatformNoTaskBar; //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls ImGui_ImplVulkan_InitInfo init_info = {}; diff --git a/examples/vulkan_example/main.cpp b/examples/vulkan_example/main.cpp index 115227f7aa50..fbec7a6deea3 100644 --- a/examples/vulkan_example/main.cpp +++ b/examples/vulkan_example/main.cpp @@ -354,6 +354,7 @@ int main(int, char**) ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_EnableViewports; + io.ConfigFlags |= ImGuiConfigFlags_PlatformNoTaskBar; //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls ImGui_ImplVulkan_InitInfo init_info = {}; diff --git a/imgui.cpp b/imgui.cpp index 810c42f99a67..f5949408f4e0 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3269,7 +3269,7 @@ static void ImGui::UpdateMovingWindowDropViewport(ImGuiWindow* window) { // Create new viewport ImVec2 os_pos = ConvertViewportPosToOsDesktopPos(window->Pos, window->Viewport); - ImGuiViewport* viewport = Viewport(window->ID, 0, os_pos, window->Size); + ImGuiViewport* viewport = Viewport(window, window->ID, 0, os_pos, window->Size); SetWindowViewportTranslateToPreservePlatformPos(window, window->Viewport, viewport); } } @@ -3388,7 +3388,7 @@ static void ImGui::UpdateViewports() ImVec2 main_viewport_os_desktop_pos = ImVec2(0.0f, 0.0f); if ((g.IO.ConfigFlags & ImGuiConfigFlags_EnableViewports)) main_viewport_os_desktop_pos = g.IO.PlatformInterface.GetWindowPos(main_viewport); - Viewport(IMGUI_VIEWPORT_DEFAULT_ID, ImGuiViewportFlags_MainViewport | ImGuiViewportFlags_HostOtherWindows, main_viewport_os_desktop_pos, g.IO.DisplaySize); + Viewport(NULL, IMGUI_VIEWPORT_DEFAULT_ID, ImGuiViewportFlags_MainViewport | ImGuiViewportFlags_HostOtherWindows, main_viewport_os_desktop_pos, g.IO.DisplaySize); if (!(g.IO.ConfigFlags & ImGuiConfigFlags_EnableViewports)) { @@ -3454,6 +3454,7 @@ static void UpdatePlatformWindows() ImGuiViewport* viewport = g.Viewports[i]; if ((viewport->Flags & ImGuiViewportFlags_MainViewport) || (viewport->LastFrameActive < g.FrameCount)) continue; + IM_ASSERT(viewport->Window != NULL); viewport->PlatformRequestClose = false; // FIXME-PLATFORM @@ -3466,13 +3467,17 @@ static void UpdatePlatformWindows() g.IO.PlatformInterface.SetWindowPos(viewport, viewport->PlatformOsDesktopPos); g.IO.PlatformInterface.SetWindowSize(viewport, viewport->Size); - char name[20]; - sprintf(name, "Viewport_%08X", viewport->ID); - if (viewport->Name == NULL || strcmp(viewport->Name, name) != 0) + // Update title bar + const char* title_begin = viewport->Window->Name; + const char* title_end = ImGui::FindRenderedTextEnd(title_begin); + const ImGuiID title_hash = ImHash(title_begin, (int)(title_end - title_begin)); + if (viewport->LastNameHash != title_hash ) { - g.IO.PlatformInterface.SetWindowTitle(viewport, name); - ImGui::MemFree(viewport->Name); - viewport->Name = ImStrdup(name); + viewport->LastNameHash = title_hash; + char* title_displayed = ImStrdup(viewport->Window->Name); + title_displayed[title_end - title_begin] = 0; + g.IO.PlatformInterface.SetWindowTitle(viewport, title_displayed); + ImGui::MemFree(title_displayed); } if (is_new_window) @@ -4512,7 +4517,7 @@ void ImGui::SetCurrentViewport(ImGuiViewport* viewport) g.IO.PlatformInterface.BeginViewport(g.CurrentViewport); } -ImGuiViewport* ImGui::Viewport(ImGuiID id, ImGuiViewportFlags flags, const ImVec2& os_desktop_pos, const ImVec2& size) +ImGuiViewport* ImGui::Viewport(ImGuiWindow* window, ImGuiID id, ImGuiViewportFlags flags, const ImVec2& os_desktop_pos, const ImVec2& size) { ImGuiContext& g = *GImGui; IM_ASSERT(id != 0); @@ -4532,6 +4537,7 @@ ImGuiViewport* ImGui::Viewport(ImGuiID id, ImGuiViewportFlags flags, const ImVec } IM_ASSERT(viewport->Pos.y == 0.0f); + viewport->Window = window; viewport->Flags = flags; viewport->PlatformOsDesktopPos = os_desktop_pos; viewport->LastFrameActive = g.FrameCount; @@ -5940,8 +5946,8 @@ static void ImGui::UpdateWindowViewport(ImGuiWindow* window, bool window_pos_set { // Create an undecorated, temporary OS/platform window ImVec2 os_desktop_pos = ConvertViewportPosToOsDesktopPos(g.IO.MousePos - g.ActiveIdClickOffset, g.MouseViewport); - ImGuiViewportFlags viewport_flags = ImGuiViewportFlags_NoDecoration | ImGuiViewportFlags_NoFocusOnAppearing | ImGuiViewportFlags_NoInputs; - ImGuiViewport* viewport = Viewport(window->ID, viewport_flags, os_desktop_pos, window->Size); + ImGuiViewportFlags viewport_flags = ImGuiViewportFlags_NoDecoration | ImGuiViewportFlags_NoFocusOnAppearing | ImGuiViewportFlags_NoInputs; + ImGuiViewport* viewport = Viewport(window, window->ID, viewport_flags, os_desktop_pos, window->Size); window->Flags |= ImGuiWindowFlags_FullViewport | ImGuiWindowFlags_NoBringToFrontOnFocus; window->Viewport = viewport; created_viewport = true; @@ -5980,7 +5986,7 @@ static void ImGui::UpdateWindowViewport(ImGuiWindow* window, bool window_pos_set { if (window->ViewportOsDesktopPos.x != FLT_MAX && window->ViewportOsDesktopPos.y != FLT_MAX) { - ImGuiViewport* viewport = Viewport(window->ID, ImGuiViewportFlags_NoDecoration, window->ViewportOsDesktopPos, window->Size); + ImGuiViewport* viewport = Viewport(window, window->ID, ImGuiViewportFlags_NoDecoration, window->ViewportOsDesktopPos, window->Size); window->Flags |= ImGuiWindowFlags_FullViewport | ImGuiWindowFlags_NoBringToFrontOnFocus; window->Viewport = viewport; created_viewport = true; @@ -6005,6 +6011,10 @@ static void ImGui::UpdateWindowViewport(ImGuiWindow* window, bool window_pos_set window->Flags |= ImGuiWindowFlags_FullViewport | ImGuiWindowFlags_NoBringToFrontOnFocus; } + // If the OS window has a title bar, hide our imgui title bar + if ((window->Flags & ImGuiWindowFlags_FullViewport) && !(window->Viewport->Flags & ImGuiViewportFlags_NoDecoration)) + window->Flags |= ImGuiWindowFlags_NoTitleBar; + // Disable rounding for the window if (window->Viewport != main_viewport) window->WindowRounding = 0.0f; @@ -13933,6 +13943,8 @@ void ImGui::ShowMetricsWindow(bool* p_open) if (ImGui::TreeNode((void*)(intptr_t)viewport->ID, "Viewport #%d, ID: 0x%08X, DrawLists: %d, Size: (%.0f,%.0f)", i, viewport->ID, draw_data->Layers[0].Size, viewport->Size.x, viewport->Size.y)) { ImGui::BulletText("Pos: (%.0f,%.0f)", viewport->Pos.x, viewport->Pos.y); + ImGui::BulletText("Flags: 0x%04X", viewport->Flags); + ImGui::BulletText("PlatformOsDesktopPos: (%.0f,%.0f)", viewport->PlatformOsDesktopPos.x, viewport->PlatformOsDesktopPos.y); for (int draw_list_i = 0; draw_list_i < viewport->DrawDataBuilder.Layers[0].Size; draw_list_i++) Funcs::NodeDrawList(NULL, viewport->DrawDataBuilder.Layers[0][draw_list_i], "DrawList"); ImGui::TreePop(); diff --git a/imgui.h b/imgui.h index 7b4a53970f70..a31a7b1fe0d9 100644 --- a/imgui.h +++ b/imgui.h @@ -779,16 +779,17 @@ enum ImGuiConfigFlags_ // [BETA] Viewports ImGuiConfigFlags_EnableViewports = 1 << 4, // Viewport enable flags (require both ImGuiConfigFlags_PlatformHasViewports + ImGuiConfigFlags_RendererHasViewports set by the respective back-ends) - ImGuiConfigFlags_PlatformHasViewports = 1 << 5, // Back-end Platform supports multiple viewports - ImGuiConfigFlags_PlatformHasMouseHoveredViewport = 1 << 6, // Back-end Platform supports setting io.MouseHoveredViewport to the viewport directly under the mouse _IGNORING_ viewports with the ImGuiViewportFlags_NoInputs flag and _REGARDLESS_ of whether another viewport is focused and may be capturing the mouse. This information is _NOT EASY_ to provide correctly with most high-level engines. Don't see this without studying how the examples/ back-end handle it. - ImGuiConfigFlags_PlatformHasWantMoveMouseSupport = 1 << 7, // Back-end Platform supports io.WantMoveMouse request by updating the OS mouse cursor position (currently only used by ImGuiConfigFlags_NavMoveMouse feature, will be useful for widgets teleporting/wrapping the cursor) - ImGuiConfigFlags_PlatformHasWindowAlpha = 1 << 8, // Back-end Platform supports transparent windows - ImGuiConfigFlags_RendererHasViewports = 1 << 9, // Back-end Renderer supports multiple viewports + ImGuiConfigFlags_PlatformNoTaskBar = 1 << 5, + ImGuiConfigFlags_PlatformHasViewports = 1 << 6, // Back-end Platform supports multiple viewports + ImGuiConfigFlags_PlatformHasMouseHoveredViewport = 1 << 7, // Back-end Platform supports setting io.MouseHoveredViewport to the viewport directly under the mouse _IGNORING_ viewports with the ImGuiViewportFlags_NoInputs flag and _REGARDLESS_ of whether another viewport is focused and may be capturing the mouse. This information is _NOT EASY_ to provide correctly with most high-level engines. Don't see this without studying how the examples/ back-end handle it. + ImGuiConfigFlags_PlatformHasWantMoveMouseSupport = 1 << 8, // Back-end Platform supports io.WantMoveMouse request by updating the OS mouse cursor position (currently only used by ImGuiConfigFlags_NavMoveMouse feature, will be useful for widgets teleporting/wrapping the cursor) + ImGuiConfigFlags_PlatformHasWindowAlpha = 1 << 9, // Back-end Platform supports transparent windows + ImGuiConfigFlags_RendererHasViewports = 1 << 10, // Back-end Renderer supports multiple viewports // Platform Info (free of use, for user/application convenience) - ImGuiConfigFlags_IsSRGB = 1 << 10, // Back-end is SRGB-aware (Storage flag to allow your back-end to communicate to shared widgets. Not used by core ImGui) - ImGuiConfigFlags_IsTouchScreen = 1 << 11, // Back-end is using a touch screen instead of a mouse (Storage flag to allow your back-end to communicate to shared widgets. Not used by core ImGui) - ImGuiConfigFlags_IsOnScreenKeyboard = 1 << 12 // Back-end uses an on-screen keyboard when io.WantTextInput is set. + ImGuiConfigFlags_IsSRGB = 1 << 20, // Back-end is SRGB-aware (Storage flag to allow your back-end to communicate to shared widgets. Not used by core ImGui) + ImGuiConfigFlags_IsTouchScreen = 1 << 21, // Back-end is using a touch screen instead of a mouse (Storage flag to allow your back-end to communicate to shared widgets. Not used by core ImGui) + ImGuiConfigFlags_IsOnScreenKeyboard = 1 << 22 // Back-end uses an on-screen keyboard when io.WantTextInput is set. }; // Enumeration for PushStyleColor() / PopStyleColor() diff --git a/imgui_internal.h b/imgui_internal.h index 38ded77f1062..3dc1b434dbe8 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -530,7 +530,8 @@ struct ImGuiViewport ImGuiViewportFlags Flags; int LastFrameActive; int LastFrameAsRefViewport; // Last frame number this viewport was io.MouseViewportRef - char* Name; // Name (OPTIONAL) + ImGuiID LastNameHash; + ImGuiWindow* Window; ImVec2 Pos; // Position in imgui virtual space (Pos.y == 0.0) ImVec2 Size; ImDrawData DrawData; @@ -538,14 +539,15 @@ struct ImGuiViewport // [Optional] OS/Platform Layer data. This is to allow the creation/manipulate of multiple OS/Platform windows. Not all back-ends will allow this. ImVec2 PlatformOsDesktopPos; // Position in OS desktop/native space + float PlatformDpiScale; // FIXME-DPI: Unused void* PlatformUserData; // void* to hold custom data structure for the platform (e.g. windowing info, render context) void* PlatformHandle; // void* for FindViewportByPlatformHandle(). (e.g. HWND, GlfwWindow*) bool PlatformRequestClose; // Platform window requested closure bool PlatformRequestResize; // Platform window requested resize void* RendererUserData; // void* to hold custom data structure for the renderer (e.g. framebuffer) - ImGuiViewport(ImGuiID id, int idx) { ID = id; Idx = idx; Flags = 0; LastFrameActive = LastFrameAsRefViewport = -1; Name = NULL; PlatformUserData = PlatformHandle = NULL; PlatformRequestClose = PlatformRequestResize = false; RendererUserData = NULL; } - ~ImGuiViewport() { IM_ASSERT(PlatformUserData == NULL && RendererUserData == NULL); if (Name) ImGui::MemFree(Name); } + ImGuiViewport(ImGuiID id, int idx) { ID = id; Idx = idx; Flags = 0; LastFrameActive = LastFrameAsRefViewport = -1; LastNameHash = 0; Window = NULL; PlatformDpiScale = 1.0f; PlatformUserData = PlatformHandle = NULL; PlatformRequestClose = PlatformRequestResize = false; RendererUserData = NULL; } + ~ImGuiViewport() { IM_ASSERT(PlatformUserData == NULL && RendererUserData == NULL); } ImRect GetRect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } float GetNextX() const { const float SPACING = 4.0f; return Pos.x + Size.x + SPACING; } }; @@ -1084,7 +1086,7 @@ namespace ImGui IMGUI_API void Shutdown(ImGuiContext* context); // Since 1.60 this is a _private_ function. You can call DestroyContext() to destroy the context created by CreateContext(). // Viewports - IMGUI_API ImGuiViewport* Viewport(ImGuiID id, ImGuiViewportFlags flags, const ImVec2& os_desktop_pos, const ImVec2& size); // os_desktop_pos allows imgui to reposition windows relative to each order when moving from one viewport to the other. + IMGUI_API ImGuiViewport* Viewport(ImGuiWindow* window, ImGuiID id, ImGuiViewportFlags flags, const ImVec2& os_desktop_pos, const ImVec2& size); // os_desktop_pos allows imgui to reposition windows relative to each order when moving from one viewport to the other. inline ImVector&GetViewports() { return GImGui->Viewports; } inline ImGuiViewport* GetMainViewport() { return GImGui->Viewports[0]; } IMGUI_API ImGuiViewport* FindViewportByID(ImGuiID id); From 1eb89d7e3b6960edbbb0276581a6f26617e717f4 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 8 Mar 2018 20:32:34 +0100 Subject: [PATCH 069/828] Viewport: Changed signature of ResizeViewport to be consistent with other similar functions. --- examples/imgui_impl_dx10.cpp | 4 ++-- examples/imgui_impl_dx11.cpp | 4 ++-- examples/imgui_impl_vulkan.cpp | 5 ++--- examples/imgui_impl_win32.cpp | 2 +- imgui.h | 2 +- 5 files changed, 8 insertions(+), 9 deletions(-) diff --git a/examples/imgui_impl_dx10.cpp b/examples/imgui_impl_dx10.cpp index fd41922f6cf2..980f3342a88f 100644 --- a/examples/imgui_impl_dx10.cpp +++ b/examples/imgui_impl_dx10.cpp @@ -566,7 +566,7 @@ static void ImGui_ImplDX10_DestroyViewport(ImGuiViewport* viewport) viewport->RendererUserData = NULL; } -static void ImGui_ImplDX10_ResizeViewport(ImGuiViewport* viewport, int w, int h) +static void ImGui_ImplDX10_ResizeViewport(ImGuiViewport* viewport, ImVec2 size) { ImGuiPlatformDataDx10* data = (ImGuiPlatformDataDx10*)viewport->RendererUserData; if (data->RTView) @@ -577,7 +577,7 @@ static void ImGui_ImplDX10_ResizeViewport(ImGuiViewport* viewport, int w, int h) if (data->SwapChain) { ID3D10Texture2D* pBackBuffer = NULL; - data->SwapChain->ResizeBuffers(0, w, h, DXGI_FORMAT_UNKNOWN, 0); + data->SwapChain->ResizeBuffers(0, (UINT)size.x, (UINT)size.y, DXGI_FORMAT_UNKNOWN, 0); data->SwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer)); g_pd3dDevice->CreateRenderTargetView(pBackBuffer, NULL, &data->RTView); pBackBuffer->Release(); diff --git a/examples/imgui_impl_dx11.cpp b/examples/imgui_impl_dx11.cpp index 42aed6957e27..719025a0b84d 100644 --- a/examples/imgui_impl_dx11.cpp +++ b/examples/imgui_impl_dx11.cpp @@ -574,7 +574,7 @@ static void ImGui_ImplDX11_DestroyViewport(ImGuiViewport* viewport) viewport->RendererUserData = NULL; } -static void ImGui_ImplDX11_ResizeViewport(ImGuiViewport* viewport, int w, int h) +static void ImGui_ImplDX11_ResizeViewport(ImGuiViewport* viewport, ImVec2 size) { ImGuiPlatformDataDx11* data = (ImGuiPlatformDataDx11*)viewport->RendererUserData; if (data->RTView) @@ -585,7 +585,7 @@ static void ImGui_ImplDX11_ResizeViewport(ImGuiViewport* viewport, int w, int h) if (data->SwapChain) { ID3D11Texture2D* pBackBuffer = NULL; - data->SwapChain->ResizeBuffers(0, w, h, DXGI_FORMAT_UNKNOWN, 0); + data->SwapChain->ResizeBuffers(0, (UINT)size.x, (UINT)size.y, DXGI_FORMAT_UNKNOWN, 0); data->SwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer)); g_pd3dDevice->CreateRenderTargetView(pBackBuffer, NULL, &data->RTView); pBackBuffer->Release(); diff --git a/examples/imgui_impl_vulkan.cpp b/examples/imgui_impl_vulkan.cpp index a013e38404e6..839d737cc064 100644 --- a/examples/imgui_impl_vulkan.cpp +++ b/examples/imgui_impl_vulkan.cpp @@ -1055,13 +1055,12 @@ static void ImGui_ImplVulkan_DestroyViewport(ImGuiViewport* viewport) viewport->RendererUserData = NULL; } -static void ImGui_ImplVulkan_ResizeViewport(ImGuiViewport* viewport, int w, int h) +static void ImGui_ImplVulkan_ResizeViewport(ImGuiViewport* viewport, ImVec2 size) { ImGuiPlatformDataVulkan* data = (ImGuiPlatformDataVulkan*)viewport->RendererUserData; //... (void)data; - (void)w; - (void)h; + (void)size; } static void ImGui_ImplVulkan_RenderViewport(ImGuiViewport* viewport) diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index 1c82cb710149..c02bfad9f31d 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -418,7 +418,7 @@ static LRESULT CALLBACK ImGui_ImplWin32_WndProcHandler_PlatformWindow(HWND hWnd, if (!data->ExternalResize) viewport->PlatformRequestResize = true; if (io.RendererInterface.ResizeViewport) - io.RendererInterface.ResizeViewport(viewport, (int)LOWORD(lParam), (int)HIWORD(lParam)); + io.RendererInterface.ResizeViewport(viewport, ImVec2((float)LOWORD(lParam), (float)HIWORD(lParam))); break; } } diff --git a/imgui.h b/imgui.h index a31a7b1fe0d9..cff3325281ac 100644 --- a/imgui.h +++ b/imgui.h @@ -973,7 +973,7 @@ struct ImGuiRendererInterface { void (*CreateViewport)(ImGuiViewport* viewport); void (*DestroyViewport)(ImGuiViewport* viewport); - void (*ResizeViewport)(ImGuiViewport* viewport, int w, int h); + void (*ResizeViewport)(ImGuiViewport* viewport, ImVec2 size); void (*RenderViewport)(ImGuiViewport* viewport); // Setup render output, clear targets, call Renderer_RenderDrawData void (*SwapBuffers)(ImGuiViewport* viewport); // Call Present/SwapBuffers }; From 43f375b2f2df5aae3a29784dd08ffd1f364a06dc Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 26 Feb 2018 16:35:18 +0100 Subject: [PATCH 070/828] Viewport,Platform: Added GetWindowDpiScale() platform interface, changes of scale are reflected by positioning and resizing windows in their given viewport. (#1542) --- imgui.cpp | 44 +++++++++++++++++++++++++++++++++++++++++--- imgui.h | 1 + imgui_internal.h | 3 ++- 3 files changed, 44 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index f5949408f4e0..562b5f93e25a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3347,13 +3347,13 @@ static void ImGui::UpdateViewports() const ImVec2 mouse_os_pos = ConvertViewportPosToOsDesktopPos(g.IO.MousePos, viewport_ref); g.CurrentViewport = NULL; - for (int n = 1; n < g.Viewports.Size; n++) + for (int n = 0; n < g.Viewports.Size; n++) { // Erase unused viewports ImGuiViewport* viewport = g.Viewports[n]; IM_ASSERT(viewport->Idx == n); - IM_ASSERT(!(viewport->Flags & ImGuiViewportFlags_MainViewport)); - if (viewport->LastFrameActive < g.FrameCount - 2) + + if (n > 0 && viewport->LastFrameActive < g.FrameCount - 2) { // Translate windows like if we were resizing the viewport to be zero-width ResizeViewportTranslateWindows(n + 1, g.Viewports.Size, viewport->Pos.x - viewport->GetNextX(), -1, viewport); @@ -3380,6 +3380,19 @@ static void ImGui::UpdateViewports() if (dx != 0.0f) ResizeViewportTranslateWindows(viewport->Idx + 1, g.Viewports.Size, dx, 0, NULL); } + + // Update DPI Scale + float new_dpi_scale; + if (g.IO.PlatformInterface.GetWindowDpiScale) + new_dpi_scale = g.IO.PlatformInterface.GetWindowDpiScale(viewport); + else + new_dpi_scale = (viewport->PlatformDpiScale != 0.0f) ? viewport->PlatformDpiScale : 1.0f; + if (viewport->PlatformDpiScale != 0.0f && new_dpi_scale != viewport->PlatformDpiScale) + { + float scale_factor = new_dpi_scale / viewport->PlatformDpiScale; + ScaleWindowsInViewport(viewport, scale_factor); + } + viewport->PlatformDpiScale = new_dpi_scale; } // Update main viewport with current size (and OS window position, if known) @@ -13769,6 +13782,31 @@ static void RenderViewportThumbnail(ImDrawList* draw_list, const ImRect& bb, con draw_list->AddRect(bb.Min, bb.Max, ImGui::GetColorU32(ImGuiCol_Border)); } +static void ScaleWindow(ImGuiWindow* window, float scale) +{ + ImVec2 origin = window->Viewport->Pos; + window->Pos = window->PosFloat = ImFloor((window->PosFloat - origin) * scale + origin); + window->Size = ImFloor(window->Size * scale); + window->SizeFull = ImFloor(window->SizeFull * scale); + window->SizeContents = ImFloor(window->SizeContents * scale); +} + +// Scale all windows (position, size). Use when e.g. changing DPI. (This is a lossy operation!) +void ImGui::ScaleWindowsInViewport(ImGuiViewport* viewport, float scale) +{ + if (viewport->Window) + { + ScaleWindow(viewport->Window, scale); + } + else + { + ImGuiContext& g = *GImGui; + for (int i = 0; i != g.Windows.Size; i++) + if (g.Windows[i]->Viewport == viewport) + ScaleWindow(g.Windows[i], scale); + } +} + void ImGui::ShowViewportThumbnails() { ImGuiContext& g = *GImGui; diff --git a/imgui.h b/imgui.h index cff3325281ac..529c1a0fc474 100644 --- a/imgui.h +++ b/imgui.h @@ -964,6 +964,7 @@ struct ImGuiPlatformInterface void (*SwapBuffers)(ImGuiViewport* viewport); // FIXME-DPI + float (*GetWindowDpiScale)(ImGuiViewport* viewport); // (Optional) void (*BeginViewport)(ImGuiViewport* viewport); // (Optional) Called during Begin() every time the viewport we are outputting into changes (viewport = next viewport) void (*EndViewport)(ImGuiViewport* viewport); // (Optional) Called during Begin() every time the viewport we are outputting into changes (viewport = previous viewport) }; diff --git a/imgui_internal.h b/imgui_internal.h index 3dc1b434dbe8..17f7c7efd06f 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -546,7 +546,7 @@ struct ImGuiViewport bool PlatformRequestResize; // Platform window requested resize void* RendererUserData; // void* to hold custom data structure for the renderer (e.g. framebuffer) - ImGuiViewport(ImGuiID id, int idx) { ID = id; Idx = idx; Flags = 0; LastFrameActive = LastFrameAsRefViewport = -1; LastNameHash = 0; Window = NULL; PlatformDpiScale = 1.0f; PlatformUserData = PlatformHandle = NULL; PlatformRequestClose = PlatformRequestResize = false; RendererUserData = NULL; } + ImGuiViewport(ImGuiID id, int idx) { ID = id; Idx = idx; Flags = 0; LastFrameActive = LastFrameAsRefViewport = -1; LastNameHash = 0; Window = NULL; PlatformDpiScale = 0.0f; PlatformUserData = PlatformHandle = NULL; PlatformRequestClose = PlatformRequestResize = false; RendererUserData = NULL; } ~ImGuiViewport() { IM_ASSERT(PlatformUserData == NULL && RendererUserData == NULL); } ImRect GetRect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } float GetNextX() const { const float SPACING = 4.0f; return Pos.x + Size.x + SPACING; } @@ -1092,6 +1092,7 @@ namespace ImGui IMGUI_API ImGuiViewport* FindViewportByID(ImGuiID id); IMGUI_API ImGuiViewport* FindViewportByPlatformHandle(void* platform_handle); IMGUI_API void SetNextWindowViewport(ImGuiID id); + IMGUI_API void ScaleWindowsInViewport(ImGuiViewport* viewport, float scale); IMGUI_API void ShowViewportThumbnails(); IMGUI_API void DestroyViewportsPlaformData(ImGuiContext* context); IMGUI_API void DestroyViewportsRendererData(ImGuiContext* context); From a4629b0b36f8ef1ac588a74b9eca5fb46701e23c Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 9 Mar 2018 15:37:16 +0100 Subject: [PATCH 071/828] Viewport, DPI: Select viewport before locking style sizes and before handling double-click-on-title-bar to collapse. (#1542, #1676) --- imgui.cpp | 29 +++++++++++++++-------------- imgui_internal.h | 2 +- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index d7048fcd88d3..3dac7cea9330 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6291,7 +6291,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if (g.NextWindowData.CollapsedCond) SetWindowCollapsed(window, g.NextWindowData.CollapsedVal, g.NextWindowData.CollapsedCond); if (g.NextWindowData.FocusCond) - SetWindowFocus(); + FocusWindow(window); if (window->Appearing) SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, false); @@ -6317,6 +6317,20 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->LastFrameActive = current_frame; window->IDStack.resize(1); + // VIEWPORT + // We need to do this before using any style/font sizes, as viewport with a different DPI will affect those sizes. + + UpdateWindowViewport(window, window_pos_set_by_api); + SetCurrentViewport(window->Viewport); + window->Viewport->LastFrameActive = g.FrameCount; + flags = window->Flags; + + if (p_open != NULL && window->Viewport->PlatformRequestClose && !(window->Viewport->Flags & ImGuiViewportFlags_MainViewport)) + { + window->Viewport->PlatformRequestClose = false; + *p_open = false; + } + // Lock window rounding, border size and padding for the frame (so that altering them doesn't cause inconsistencies) window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding; window->WindowBorderSize = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildBorderSize : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize; @@ -6342,19 +6356,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) } window->CollapseToggleWanted = false; - // VIEWPORT - - UpdateWindowViewport(window, window_pos_set_by_api); - SetCurrentViewport(window->Viewport); - window->Viewport->LastFrameActive = g.FrameCount; - flags = window->Flags; - - if (p_open != NULL && window->Viewport->PlatformRequestClose && !(window->Viewport->Flags & ImGuiViewportFlags_MainViewport)) - { - window->Viewport->PlatformRequestClose = false; - *p_open = false; - } - // SIZE // Update contents size from last frame for auto-fitting (unless explicitly specified) diff --git a/imgui_internal.h b/imgui_internal.h index 06bdbbc8edf4..4aa9a3b9950c 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1076,7 +1076,7 @@ namespace ImGui inline ImGuiWindow* GetCurrentWindowRead() { ImGuiContext& g = *GImGui; return g.CurrentWindow; } inline ImGuiWindow* GetCurrentWindow() { ImGuiContext& g = *GImGui; g.CurrentWindow->WriteAccessed = true; return g.CurrentWindow; } IMGUI_API ImGuiWindow* FindWindowByName(const char* name); - IMGUI_API void FocusWindow(ImGuiWindow* window); + IMGUI_API void FocusWindow(ImGuiWindow* window); // FIXME: Rename to SetWindowFocus() IMGUI_API void BringWindowToFront(ImGuiWindow* window); IMGUI_API void BringWindowToBack(ImGuiWindow* window); IMGUI_API bool IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent); From 648735a4cf48d28b10f0795f0584f2448a6fe71d Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 9 Mar 2018 15:49:12 +0100 Subject: [PATCH 072/828] Viewport, DPI: Don't activate current window until a viewport has been selected. At this point we'll have the proper size/scale for the current DPI + fix viewport callback when appending to an existing window more than once a frame. (#1542, #1676) --- imgui.cpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 3dac7cea9330..a17e409d9083 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4523,6 +4523,8 @@ void ImGui::SetCurrentViewport(ImGuiViewport* viewport) // Notify platform interface of viewport changes // FIXME-DPI: This is only currently used for experimenting with handling of multiple DPI ImGuiContext& g = *GImGui; + if (viewport) + viewport->LastFrameActive = g.FrameCount; if (g.CurrentViewport == viewport) return; if (g.CurrentViewport && g.IO.PlatformInterface.EndViewport) @@ -6030,10 +6032,6 @@ static void ImGui::UpdateWindowViewport(ImGuiWindow* window, bool window_pos_set if ((window->Flags & ImGuiWindowFlags_FullViewport) && !(window->Viewport->Flags & ImGuiViewportFlags_NoDecoration)) window->Flags |= ImGuiWindowFlags_NoTitleBar; - // Disable rounding for the window - if (window->Viewport != main_viewport) - window->WindowRounding = 0.0f; - if (window->Flags & ImGuiWindowFlags_FullViewport) SetWindowPos(window, window->Viewport->Pos, ImGuiCond_Always); @@ -6238,8 +6236,9 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow)); // Add to stack + // We intentionally set g.CurrentWindow to NULL to prevent usage until when the viewport is set, then will call SetCurrentWindow() g.CurrentWindowStack.push_back(window); - SetCurrentWindow(window); + g.CurrentWindow = NULL; CheckStacksSize(window, true); if (flags & ImGuiWindowFlags_Popup) { @@ -6322,7 +6321,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) UpdateWindowViewport(window, window_pos_set_by_api); SetCurrentViewport(window->Viewport); - window->Viewport->LastFrameActive = g.FrameCount; + SetCurrentWindow(window); flags = window->Flags; if (p_open != NULL && window->Viewport->PlatformRequestClose && !(window->Viewport->Flags & ImGuiViewportFlags_MainViewport)) @@ -6333,6 +6332,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Lock window rounding, border size and padding for the frame (so that altering them doesn't cause inconsistencies) window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding; + if (window->Viewport != GetMainViewport()) + window->WindowRounding = 0.0f; window->WindowBorderSize = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildBorderSize : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize; window->WindowPadding = style.WindowPadding; if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_Popup)) && window->WindowBorderSize == 0.0f) @@ -6786,6 +6787,11 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DC.LastItemStatusFlags = IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0; window->DC.LastItemRect = title_bar_rect; } + else + { + SetCurrentViewport(window->Viewport); + SetCurrentWindow(window); + } PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true); From 10030ff3ec16b7389e0e78bd5ad2fc1f211f6e50 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 9 Mar 2018 15:58:29 +0100 Subject: [PATCH 073/828] Viewport: Fix dropping back viewport not being moved to the front of the window list. (Now that viewport are more tighly coupled to a single root window) (#1542) --- imgui.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index a17e409d9083..f1e4ab10e4e1 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5959,13 +5959,14 @@ static void ImGui::UpdateWindowViewport(ImGuiWindow* window, bool window_pos_set else if (window_follow_mouse_viewport && IsMousePosValid()) { // Calculate mouse position in OS/platform coordinates - if (!window_is_mouse_tooltip && !GetViewportRect(window).Contains(window->Rect())) + ImGuiViewport* current_viewport = window->Viewport; + if (!window_is_mouse_tooltip && !current_viewport->GetRect().Contains(window->Rect())) { // Create an undecorated, temporary OS/platform window ImVec2 os_desktop_pos = ConvertViewportPosToOsDesktopPos(g.IO.MousePos - g.ActiveIdClickOffset, g.MouseViewport); ImGuiViewportFlags viewport_flags = ImGuiViewportFlags_NoDecoration | ImGuiViewportFlags_NoFocusOnAppearing | ImGuiViewportFlags_NoInputs; ImGuiViewport* viewport = Viewport(window, window->ID, viewport_flags, os_desktop_pos, window->Size); - window->Flags |= ImGuiWindowFlags_FullViewport | ImGuiWindowFlags_NoBringToFrontOnFocus; + window->Flags |= ImGuiWindowFlags_FullViewport; window->Viewport = viewport; created_viewport = true; @@ -6004,7 +6005,7 @@ static void ImGui::UpdateWindowViewport(ImGuiWindow* window, bool window_pos_set if (window->ViewportOsDesktopPos.x != FLT_MAX && window->ViewportOsDesktopPos.y != FLT_MAX) { ImGuiViewport* viewport = Viewport(window, window->ID, ImGuiViewportFlags_NoDecoration, window->ViewportOsDesktopPos, window->Size); - window->Flags |= ImGuiWindowFlags_FullViewport | ImGuiWindowFlags_NoBringToFrontOnFocus; + window->Flags |= ImGuiWindowFlags_FullViewport; window->Viewport = viewport; created_viewport = true; } @@ -6025,7 +6026,7 @@ static void ImGui::UpdateWindowViewport(ImGuiWindow* window, bool window_pos_set window->Viewport->Flags |= ImGuiViewportFlags_NoDecoration; window->Viewport->Size = window->Size; window->Viewport->PlatformOsDesktopPos = ConvertViewportPosToOsDesktopPos(window->Pos, window->Viewport); - window->Flags |= ImGuiWindowFlags_FullViewport | ImGuiWindowFlags_NoBringToFrontOnFocus; + window->Flags |= ImGuiWindowFlags_FullViewport; } // If the OS window has a title bar, hide our imgui title bar From a2fbcc9ad4e92f0ea11265c8539c86c0caa1b0c3 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 9 Mar 2018 19:02:52 +0100 Subject: [PATCH 074/828] Examples: DPI: Portable DPI related helpers in the _Win32 examples. Using one in examples's main.cpp, the GetDpiScale functions are not wired anywhere for now. (#1542, #1676) --- examples/directx10_example/main.cpp | 2 + examples/directx11_example/main.cpp | 20 +------- examples/imgui_impl_win32.cpp | 78 +++++++++++++++++++++++++++++ examples/imgui_impl_win32.h | 6 +++ imgui.cpp | 4 +- 5 files changed, 89 insertions(+), 21 deletions(-) diff --git a/examples/directx10_example/main.cpp b/examples/directx10_example/main.cpp index 811cda708569..fb034cd8c520 100644 --- a/examples/directx10_example/main.cpp +++ b/examples/directx10_example/main.cpp @@ -95,6 +95,8 @@ LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) int main(int, char**) { + ImGui_ImplWin32_EnableDpiAwareness(); + // Create application window WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, WndProc, 0L, 0L, GetModuleHandle(NULL), NULL, NULL, NULL, NULL, _T("ImGui Example"), NULL }; RegisterClassEx(&wc); diff --git a/examples/directx11_example/main.cpp b/examples/directx11_example/main.cpp index d333f61cbb84..3cec017d7fb8 100644 --- a/examples/directx11_example/main.cpp +++ b/examples/directx11_example/main.cpp @@ -96,27 +96,9 @@ LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) return DefWindowProc(hWnd, msg, wParam, lParam); } -// FIXME-DPI: For now we just want to call SetProcessDpiAwareness(PROCESS_PER_MONITOR_AWARE) without requiring SDK 8.1 or 10 -void SetupDpiAwareness() -{ - typedef enum PROCESS_DPI_AWARENESS - { - PROCESS_DPI_UNAWARE = 0, - PROCESS_SYSTEM_DPI_AWARE = 1, - PROCESS_PER_MONITOR_DPI_AWARE = 2 - } PROCESS_DPI_AWARENESS; - if (HINSTANCE shcore_dll = ::LoadLibraryA("shcore.dll")) - { - typedef HRESULT(WINAPI * PFN_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS); - if (PFN_SetProcessDpiAwareness SetProcessDpiAwarenessFn = (PFN_SetProcessDpiAwareness)::GetProcAddress(shcore_dll, "SetProcessDpiAwareness")) - SetProcessDpiAwarenessFn(PROCESS_PER_MONITOR_DPI_AWARE); - ::FreeLibrary(shcore_dll); - } -} - int main(int, char**) { - SetupDpiAwareness(); + ImGui_ImplWin32_EnableDpiAwareness(); // Create application window WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, WndProc, 0L, 0L, GetModuleHandle(NULL), NULL, NULL, NULL, NULL, _T("ImGui Example"), NULL }; diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index c02bfad9f31d..eeea68a8770b 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -271,6 +271,84 @@ IMGUI_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARAM wPa return 0; } +// -------------------------------------------------------------------------------------------------------- +// DPI handling +// Those in theory should be simple calls but Windows has multiple ways to handle DPI, and most of them +// require recent Windows versions at runtime or recent Windows SDK at compile-time. Neither we want to depend on. +// So we dynamically select and load those functions to avoid dependencies. This is the scheme successfully +// used by GLFW (from which we borrowed some of the code here) and other applications aiming to be portable. +//--------------------------------------------------------------------------------------------------------- +// FIXME-DPI: For now we just call SetProcessDpiAwareness(PROCESS_PER_MONITOR_AWARE) without requiring SDK 8.1 or 10. +// We may allow/aim calling the most-recent-available version, e.g. Windows 10 Creators Update has SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); +// At this point ImGui_ImplWin32_EnableDpiAwareness() is just a helper called by main.cpp, we don't call it ourselves. +//--------------------------------------------------------------------------------------------------------- + +static BOOL IsWindowsVersionOrGreater(WORD major, WORD minor, WORD sp) +{ + OSVERSIONINFOEXW osvi = { sizeof(osvi), major, minor, 0, 0,{ 0 }, sp }; + DWORD mask = VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR; + ULONGLONG cond = VerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL); + cond = VerSetConditionMask(cond, VER_MINORVERSION, VER_GREATER_EQUAL); + cond = VerSetConditionMask(cond, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL); + return VerifyVersionInfoW(&osvi, mask, cond); +} +#define IsWindows8Point1OrGreater() IsWindowsVersionOrGreater(HIBYTE(0x0602), LOBYTE(0x0602), 0) // _WIN32_WINNT_WINBLUE +#define IsWindows10OrGreater() IsWindowsVersionOrGreater(HIBYTE(0x0A00), LOBYTE(0x0A00), 0) // _WIN32_WINNT_WIN10 + +#ifndef DPI_ENUMS_DECLARED +typedef enum { PROCESS_DPI_UNAWARE = 0, PROCESS_SYSTEM_DPI_AWARE = 1, PROCESS_PER_MONITOR_DPI_AWARE = 2 } PROCESS_DPI_AWARENESS; +typedef enum { MDT_EFFECTIVE_DPI = 0, MDT_ANGULAR_DPI = 1, MDT_RAW_DPI = 2, MDT_DEFAULT = MDT_EFFECTIVE_DPI } MONITOR_DPI_TYPE; +#endif +typedef HRESULT(WINAPI * PFN_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS); // Shcore.lib+dll, Windows 8.1 +typedef HRESULT(WINAPI * PFN_GetDpiForMonitor)(HMONITOR, MONITOR_DPI_TYPE, UINT*, UINT*); // Shcore.lib+dll, Windows 8.1 + +void ImGui_ImplWin32_EnableDpiAwareness() +{ + if (IsWindows8Point1OrGreater()) + { + static HINSTANCE shcore_dll = ::LoadLibraryA("shcore.dll"); // Reference counted per-process + if (PFN_SetProcessDpiAwareness SetProcessDpiAwarenessFn = (PFN_SetProcessDpiAwareness)::GetProcAddress(shcore_dll, "SetProcessDpiAwareness")) + SetProcessDpiAwarenessFn(PROCESS_PER_MONITOR_DPI_AWARE); + } + else + { + SetProcessDPIAware(); + } +} + +float ImGui_ImplWin32_GetDpiScaleForMonitor(void* monitor) +{ + UINT xdpi = 96, ydpi = 96; + if (IsWindows8Point1OrGreater()) + { + static HINSTANCE shcore_dll = ::LoadLibraryA("shcore.dll"); // Reference counted per-process + if (PFN_GetDpiForMonitor GetDpiForMonitorFn = (PFN_GetDpiForMonitor)::GetProcAddress(shcore_dll, "GetDpiForMonitor")) + GetDpiForMonitorFn((HMONITOR)monitor, MDT_EFFECTIVE_DPI, &xdpi, &ydpi); + } + else + { + const HDC dc = ::GetDC(NULL); + xdpi = ::GetDeviceCaps(dc, LOGPIXELSX); + ydpi = ::GetDeviceCaps(dc, LOGPIXELSY); + ::ReleaseDC(NULL, dc); + } + IM_ASSERT(xdpi == ydpi); // Please contact me if you hit this assert! + return xdpi / 96.0f; +} + +float ImGui_ImplWin32_GetDpiScaleForHwnd(void* hwnd) +{ + HMONITOR monitor = ::MonitorFromWindow((HWND)hwnd, MONITOR_DEFAULTTONEAREST); + return ImGui_ImplWin32_GetDpiScaleForMonitor(monitor); +} + +float ImGui_ImplWin32_GetDpiScaleForRect(int x1, int y1, int x2, int y2) +{ + RECT viewport_rect = { (UINT)x1, (UINT)y1, (UINT)x2, (UINT)y2 }; + HMONITOR monitor = ::MonitorFromRect(&viewport_rect, MONITOR_DEFAULTTONEAREST); + return ImGui_ImplWin32_GetDpiScaleForMonitor(monitor); +} + // -------------------------------------------------------------------------------------------------------- // Platform Windows // -------------------------------------------------------------------------------------------------------- diff --git a/examples/imgui_impl_win32.h b/examples/imgui_impl_win32.h index a43f067d1cd5..2a04cb09f314 100644 --- a/examples/imgui_impl_win32.h +++ b/examples/imgui_impl_win32.h @@ -5,6 +5,12 @@ IMGUI_API bool ImGui_ImplWin32_Init(void* hwnd); IMGUI_API void ImGui_ImplWin32_Shutdown(); IMGUI_API void ImGui_ImplWin32_NewFrame(); +// DPI-related helpers (which run and compile without requiring 8.1 or 10, neither Windows version, neither associated SDK) +IMGUI_API void ImGui_ImplWin32_EnableDpiAwareness(); +IMGUI_API float ImGui_ImplWin32_GetDpiScaleForHwnd(void* hwnd); // HWND hwnd +IMGUI_API float ImGui_ImplWin32_GetDpiScaleForMonitor(void* monitor); // HMONITOR monitor +IMGUI_API float ImGui_ImplWin32_GetDpiScaleForRect(int x1, int y1, int x2, int y2); + // Handler for Win32 messages, update mouse/keyboard data. // You may or not need this for your implementation, but it can serve as reference for handling inputs. // Intentionally commented out to avoid dragging dependencies on types. You can copy the extern declaration in your code. diff --git a/imgui.cpp b/imgui.cpp index f1e4ab10e4e1..fefcfda9c170 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -13804,13 +13804,13 @@ static void ScaleWindow(ImGuiWindow* window, float scale) // Scale all windows (position, size). Use when e.g. changing DPI. (This is a lossy operation!) void ImGui::ScaleWindowsInViewport(ImGuiViewport* viewport, float scale) { + ImGuiContext& g = *GImGui; if (viewport->Window) { ScaleWindow(viewport->Window, scale); } else { - ImGuiContext& g = *GImGui; for (int i = 0; i != g.Windows.Size; i++) if (g.Windows[i]->Viewport == viewport) ScaleWindow(g.Windows[i], scale); @@ -13992,7 +13992,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) { ImGui::BulletText("Pos: (%.0f,%.0f)", viewport->Pos.x, viewport->Pos.y); ImGui::BulletText("Flags: 0x%04X", viewport->Flags); - ImGui::BulletText("PlatformOsDesktopPos: (%.0f,%.0f)", viewport->PlatformOsDesktopPos.x, viewport->PlatformOsDesktopPos.y); + ImGui::BulletText("PlatformOsDesktopPos: (%.0f,%.0f); DpiScale: %.0f%%", viewport->PlatformOsDesktopPos.x, viewport->PlatformOsDesktopPos.y, viewport->PlatformDpiScale * 100.0f); for (int draw_list_i = 0; draw_list_i < viewport->DrawDataBuilder.Layers[0].Size; draw_list_i++) Funcs::NodeDrawList(NULL, viewport->DrawDataBuilder.Layers[0][draw_list_i], "DrawList"); ImGui::TreePop(); From 5e63711084c4596ff0c6cdc73d041094250d95a4 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 9 Mar 2018 19:08:47 +0100 Subject: [PATCH 075/828] Viewport, DPI: Some early work on per-viewport DPI support. At the moment the easiest way is to replace fonts during the ChangedViewport callback, but down the line we should aim at handling some of it at ImFont level. (#1542, #1676) --- examples/directx11_example/main.cpp | 15 ++++++++++++ examples/imgui_impl_win32.cpp | 13 ++++++++++ imgui.cpp | 37 ++++++++++++++++++++--------- imgui.h | 18 +++++++------- imgui_internal.h | 9 +++---- 5 files changed, 69 insertions(+), 23 deletions(-) diff --git a/examples/directx11_example/main.cpp b/examples/directx11_example/main.cpp index 3cec017d7fb8..6e81b811b4d0 100644 --- a/examples/directx11_example/main.cpp +++ b/examples/directx11_example/main.cpp @@ -67,6 +67,10 @@ void CleanupDeviceD3D() if (g_pd3dDevice) { g_pd3dDevice->Release(); g_pd3dDevice = NULL; } } +#ifndef WM_DPICHANGED +#define WM_DPICHANGED 0x02E0 // From Windows SDK 8.1+ headers +#endif + extern LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { @@ -92,6 +96,15 @@ LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) case WM_DESTROY: PostQuitMessage(0); return 0; + case WM_DPICHANGED: + if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_EnableDpiScaleViewports) + { + //const int dpi = HIWORD(wParam); + //printf("WM_DPICHANGED to %d (%.0f%%)\n", dpi, (float)dpi / 96.0f * 100.0f); + const RECT* suggested_rect = (RECT*)lParam; + ::SetWindowPos(hWnd, NULL, suggested_rect->left, suggested_rect->top, suggested_rect->right - suggested_rect->left, suggested_rect->bottom - suggested_rect->top, SWP_NOZORDER | SWP_NOACTIVATE); + } + break; } return DefWindowProc(hWnd, msg, wParam, lParam); } @@ -121,6 +134,8 @@ int main(int, char**) ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_EnableViewports; + io.ConfigFlags |= ImGuiConfigFlags_EnableDpiScaleFonts; + io.ConfigFlags |= ImGuiConfigFlags_EnableDpiScaleViewports; io.ConfigFlags |= ImGuiConfigFlags_PlatformNoTaskBar; //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index eeea68a8770b..ef0d64493c6b 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -470,6 +470,18 @@ static void ImGui_ImplWin32_SetWindowTitle(ImGuiViewport* viewport, const char* ::SetWindowTextA(data->Hwnd, title); } +static float ImGui_ImplWin32_GetWindowDpiScale(ImGuiViewport* viewport) +{ + ImGuiPlatformDataWin32* data = (ImGuiPlatformDataWin32*)viewport->PlatformUserData; + if (data && data->Hwnd) + return ImGui_ImplWin32_GetDpiScaleForHwnd(data->Hwnd); + + // The first frame a viewport is created we don't have a window yet + return ImGui_ImplWin32_GetDpiScaleForRect( + (int)(viewport->PlatformOsDesktopPos.x), (int)(viewport->PlatformOsDesktopPos.y), + (int)(viewport->PlatformOsDesktopPos.x + viewport->Size.x), (int)(viewport->PlatformOsDesktopPos.y + viewport->Size.y)); +} + static LRESULT CALLBACK ImGui_ImplWin32_WndProcHandler_PlatformWindow(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam)) @@ -531,6 +543,7 @@ static void ImGui_ImplWin32_InitPlatformInterface() io.PlatformInterface.SetWindowSize = ImGui_ImplWin32_SetWindowSize; io.PlatformInterface.GetWindowSize = ImGui_ImplWin32_GetWindowSize; io.PlatformInterface.SetWindowTitle = ImGui_ImplWin32_SetWindowTitle; + io.PlatformInterface.GetWindowDpiScale = ImGui_ImplWin32_GetWindowDpiScale; // Register main window handle ImGuiViewport* main_viewport = ImGui::GetMainViewport(); diff --git a/imgui.cpp b/imgui.cpp index fefcfda9c170..eaf8df1d8943 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1944,7 +1944,7 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) LastFrameActive = -1; ItemWidthDefault = 0.0f; - FontWindowScale = 1.0f; + FontWindowScale = FontDpiScale = 1.0f; DrawList = IM_NEW(ImDrawList)(&context->DrawListSharedData); DrawList->_OwnerName = Name; @@ -3388,13 +3388,20 @@ static void ImGui::UpdateViewports() if (g.IO.PlatformInterface.GetWindowDpiScale) new_dpi_scale = g.IO.PlatformInterface.GetWindowDpiScale(viewport); else - new_dpi_scale = (viewport->PlatformDpiScale != 0.0f) ? viewport->PlatformDpiScale : 1.0f; - if (viewport->PlatformDpiScale != 0.0f && new_dpi_scale != viewport->PlatformDpiScale) + new_dpi_scale = (viewport->DpiScale != 0.0f) ? viewport->DpiScale : 1.0f; + if (viewport->DpiScale != 0.0f && new_dpi_scale != viewport->DpiScale) { - float scale_factor = new_dpi_scale / viewport->PlatformDpiScale; - ScaleWindowsInViewport(viewport, scale_factor); + float scale_factor = new_dpi_scale / viewport->DpiScale; + if (g.IO.ConfigFlags & ImGuiConfigFlags_EnableDpiScaleViewports) + ScaleWindowsInViewport(viewport, scale_factor); + //if (viewport == GetMainViewport()) + // g.IO.PlatformInterface.SetWindowSize(viewport, viewport->Size * scale_factor); + + // FIXME-DPI: We need to preserve our pivots + //if (g.MovingWindow) + // g.ActiveIdClickOffset = g.ActiveIdClickOffset * scale_factor; } - viewport->PlatformDpiScale = new_dpi_scale; + viewport->DpiScale = new_dpi_scale; } // Update main viewport with current size (and OS window position, if known) @@ -4527,11 +4534,9 @@ void ImGui::SetCurrentViewport(ImGuiViewport* viewport) viewport->LastFrameActive = g.FrameCount; if (g.CurrentViewport == viewport) return; - if (g.CurrentViewport && g.IO.PlatformInterface.EndViewport) - g.IO.PlatformInterface.EndViewport(g.CurrentViewport); g.CurrentViewport = viewport; - if (g.CurrentViewport && g.IO.PlatformInterface.BeginViewport) - g.IO.PlatformInterface.BeginViewport(g.CurrentViewport); + if (g.CurrentViewport && g.IO.PlatformInterface.ChangedViewport) + g.IO.PlatformInterface.ChangedViewport(g.CurrentViewport); } ImGuiViewport* ImGui::Viewport(ImGuiWindow* window, ImGuiID id, ImGuiViewportFlags flags, const ImVec2& os_desktop_pos, const ImVec2& size) @@ -4558,6 +4563,10 @@ ImGuiViewport* ImGui::Viewport(ImGuiWindow* window, ImGuiID id, ImGuiViewportFla viewport->Flags = flags; viewport->PlatformOsDesktopPos = os_desktop_pos; viewport->LastFrameActive = g.FrameCount; + + // Request an initial DpiScale before the OS platform window creation + if (g.IO.PlatformInterface.GetWindowDpiScale) + viewport->DpiScale = g.IO.PlatformInterface.GetWindowDpiScale(viewport); return viewport; } @@ -6322,6 +6331,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) UpdateWindowViewport(window, window_pos_set_by_api); SetCurrentViewport(window->Viewport); + window->FontDpiScale = (g.IO.ConfigFlags & ImGuiConfigFlags_EnableDpiScaleFonts) ? window->Viewport->DpiScale : 1.0f; SetCurrentWindow(window); flags = window->Flags; @@ -13805,6 +13815,11 @@ static void ScaleWindow(ImGuiWindow* window, float scale) void ImGui::ScaleWindowsInViewport(ImGuiViewport* viewport, float scale) { ImGuiContext& g = *GImGui; + if (g.IO.MousePosViewport == viewport->ID) + { + //g.IO.MousePos = g.IO.MousePosPrev = ImFloor((g.IO.MousePos - viewport->Pos) * scale) + viewport->Pos; + //g.IO.MouseDelta = ImVec2(0,0); + } if (viewport->Window) { ScaleWindow(viewport->Window, scale); @@ -13992,7 +14007,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) { ImGui::BulletText("Pos: (%.0f,%.0f)", viewport->Pos.x, viewport->Pos.y); ImGui::BulletText("Flags: 0x%04X", viewport->Flags); - ImGui::BulletText("PlatformOsDesktopPos: (%.0f,%.0f); DpiScale: %.0f%%", viewport->PlatformOsDesktopPos.x, viewport->PlatformOsDesktopPos.y, viewport->PlatformDpiScale * 100.0f); + ImGui::BulletText("PlatformOsDesktopPos: (%.0f,%.0f); DpiScale: %.0f%%", viewport->PlatformOsDesktopPos.x, viewport->PlatformOsDesktopPos.y, viewport->DpiScale * 100.0f); for (int draw_list_i = 0; draw_list_i < viewport->DrawDataBuilder.Layers[0].Size; draw_list_i++) Funcs::NodeDrawList(NULL, viewport->DrawDataBuilder.Layers[0][draw_list_i], "DrawList"); ImGui::TreePop(); diff --git a/imgui.h b/imgui.h index 1f32bdc198c2..04caa0cd9ee7 100644 --- a/imgui.h +++ b/imgui.h @@ -779,12 +779,15 @@ enum ImGuiConfigFlags_ // [BETA] Viewports ImGuiConfigFlags_EnableViewports = 1 << 4, // Viewport enable flags (require both ImGuiConfigFlags_PlatformHasViewports + ImGuiConfigFlags_RendererHasViewports set by the respective back-ends) - ImGuiConfigFlags_PlatformNoTaskBar = 1 << 5, - ImGuiConfigFlags_PlatformHasViewports = 1 << 6, // Back-end Platform supports multiple viewports - ImGuiConfigFlags_PlatformHasMouseHoveredViewport = 1 << 7, // Back-end Platform supports setting io.MouseHoveredViewport to the viewport directly under the mouse _IGNORING_ viewports with the ImGuiViewportFlags_NoInputs flag and _REGARDLESS_ of whether another viewport is focused and may be capturing the mouse. This information is _NOT EASY_ to provide correctly with most high-level engines. Don't see this without studying how the examples/ back-end handle it. - ImGuiConfigFlags_PlatformHasWantMoveMouseSupport = 1 << 8, // Back-end Platform supports io.WantMoveMouse request by updating the OS mouse cursor position (currently only used by ImGuiConfigFlags_NavMoveMouse feature, will be useful for widgets teleporting/wrapping the cursor) - ImGuiConfigFlags_PlatformHasWindowAlpha = 1 << 9, // Back-end Platform supports transparent windows - ImGuiConfigFlags_RendererHasViewports = 1 << 10, // Back-end Renderer supports multiple viewports + ImGuiConfigFlags_EnableDpiScaleViewports = 1 << 5, + ImGuiConfigFlags_EnableDpiScaleFonts = 1 << 6, + + ImGuiConfigFlags_PlatformNoTaskBar = 1 << 10, + ImGuiConfigFlags_PlatformHasViewports = 1 << 11, // Back-end Platform supports multiple viewports + ImGuiConfigFlags_PlatformHasMouseHoveredViewport = 1 << 12, // Back-end Platform supports setting io.MouseHoveredViewport to the viewport directly under the mouse _IGNORING_ viewports with the ImGuiViewportFlags_NoInputs flag and _REGARDLESS_ of whether another viewport is focused and may be capturing the mouse. This information is _NOT EASY_ to provide correctly with most high-level engines. Don't see this without studying how the examples/ back-end handle it. + ImGuiConfigFlags_PlatformHasWantMoveMouseSupport = 1 << 13, // Back-end Platform supports io.WantMoveMouse request by updating the OS mouse cursor position (currently only used by ImGuiConfigFlags_NavMoveMouse feature, will be useful for widgets teleporting/wrapping the cursor) + ImGuiConfigFlags_PlatformHasWindowAlpha = 1 << 14, // Back-end Platform supports transparent windows + ImGuiConfigFlags_RendererHasViewports = 1 << 15, // Back-end Renderer supports multiple viewports // Platform Info (free of use, for user/application convenience) ImGuiConfigFlags_IsSRGB = 1 << 20, // Back-end is SRGB-aware (Storage flag to allow your back-end to communicate to shared widgets. Not used by core ImGui) @@ -965,8 +968,7 @@ struct ImGuiPlatformInterface // FIXME-DPI float (*GetWindowDpiScale)(ImGuiViewport* viewport); // (Optional) - void (*BeginViewport)(ImGuiViewport* viewport); // (Optional) Called during Begin() every time the viewport we are outputting into changes (viewport = next viewport) - void (*EndViewport)(ImGuiViewport* viewport); // (Optional) Called during Begin() every time the viewport we are outputting into changes (viewport = previous viewport) + void (*ChangedViewport)(ImGuiViewport* viewport); // (Optional) Called during Begin() every time the viewport we are outputting into changes (viewport = next viewport) }; // (Optional) Setup required only if (io.ConfigFlags & ImGuiConfigFlags_EnableMultiViewport) is enabled diff --git a/imgui_internal.h b/imgui_internal.h index 4aa9a3b9950c..56a8a1bd0bc5 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -534,19 +534,19 @@ struct ImGuiViewport ImGuiWindow* Window; ImVec2 Pos; // Position in imgui virtual space (Pos.y == 0.0) ImVec2 Size; + float DpiScale; ImDrawData DrawData; ImDrawDataBuilder DrawDataBuilder; // [Optional] OS/Platform Layer data. This is to allow the creation/manipulate of multiple OS/Platform windows. Not all back-ends will allow this. ImVec2 PlatformOsDesktopPos; // Position in OS desktop/native space - float PlatformDpiScale; // FIXME-DPI: Unused void* PlatformUserData; // void* to hold custom data structure for the platform (e.g. windowing info, render context) void* PlatformHandle; // void* for FindViewportByPlatformHandle(). (e.g. HWND, GlfwWindow*) bool PlatformRequestClose; // Platform window requested closure bool PlatformRequestResize; // Platform window requested resize void* RendererUserData; // void* to hold custom data structure for the renderer (e.g. framebuffer) - ImGuiViewport(ImGuiID id, int idx) { ID = id; Idx = idx; Flags = 0; LastFrameActive = LastFrameAsRefViewport = -1; LastNameHash = 0; Window = NULL; PlatformDpiScale = 0.0f; PlatformUserData = PlatformHandle = NULL; PlatformRequestClose = PlatformRequestResize = false; RendererUserData = NULL; } + ImGuiViewport(ImGuiID id, int idx) { ID = id; Idx = idx; Flags = 0; LastFrameActive = LastFrameAsRefViewport = -1; LastNameHash = 0; Window = NULL; DpiScale = 0.0f; PlatformUserData = PlatformHandle = NULL; PlatformRequestClose = PlatformRequestResize = false; RendererUserData = NULL; } ~ImGuiViewport() { IM_ASSERT(PlatformUserData == NULL && RendererUserData == NULL); } ImRect GetRect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } float GetNextX() const { const float SPACING = 4.0f; return Pos.x + Size.x + SPACING; } @@ -1010,7 +1010,8 @@ struct IMGUI_API ImGuiWindow ImGuiMenuColumns MenuColumns; // Simplified columns storage for menu items ImGuiStorage StateStorage; ImVector ColumnsStorage; - float FontWindowScale; // Scale multiplier per-window + float FontWindowScale; // User scale multiplier per-window + float FontDpiScale; ImDrawList* DrawList; ImGuiWindow* ParentWindow; // If we are a child _or_ popup window, this is pointing to our parent. Otherwise NULL. ImGuiWindow* RootWindow; // Point to ourself or first ancestor that is not a child window. @@ -1042,7 +1043,7 @@ struct IMGUI_API ImGuiWindow // We don't use g.FontSize because the window may be != g.CurrentWidow. ImRect Rect() const { return ImRect(Pos.x, Pos.y, Pos.x+Size.x, Pos.y+Size.y); } - float CalcFontSize() const { return GImGui->FontBaseSize * FontWindowScale; } + float CalcFontSize() const { return GImGui->FontBaseSize * FontWindowScale * FontDpiScale; } float TitleBarHeight() const { return (Flags & ImGuiWindowFlags_NoTitleBar) ? 0.0f : CalcFontSize() + GImGui->Style.FramePadding.y * 2.0f; } ImRect TitleBarRect() const { return ImRect(Pos, ImVec2(Pos.x + SizeFull.x, Pos.y + TitleBarHeight())); } float MenuBarHeight() const { return (Flags & ImGuiWindowFlags_MenuBar) ? CalcFontSize() + GImGui->Style.FramePadding.y * 2.0f : 0.0f; } From 1e81a53e753883bc39d1f5237a1f6acd07e01733 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 12 Mar 2018 16:22:24 +0100 Subject: [PATCH 076/828] Examples: DPI: Minor warning fix. (#1676) --- examples/imgui_impl_win32.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index ef0d64493c6b..f8c40355dbc2 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -344,7 +344,7 @@ float ImGui_ImplWin32_GetDpiScaleForHwnd(void* hwnd) float ImGui_ImplWin32_GetDpiScaleForRect(int x1, int y1, int x2, int y2) { - RECT viewport_rect = { (UINT)x1, (UINT)y1, (UINT)x2, (UINT)y2 }; + RECT viewport_rect = { (LONG)x1, (LONG)y1, (LONG)x2, (LONG)y2 }; HMONITOR monitor = ::MonitorFromRect(&viewport_rect, MONITOR_DEFAULTTONEAREST); return ImGui_ImplWin32_GetDpiScaleForMonitor(monitor); } From cdeef65b052c47303781cc695b30fdf890dfbfa7 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 12 Mar 2018 17:37:28 +0100 Subject: [PATCH 077/828] Examples: Vulkan: Passing Queue, QueueFamily to binding. Fixed scissor (fixed in master). ImGui_ImplVulkanH_DestroyWindowData() waits for device to be idle. (#1042) --- examples/imgui_impl_vulkan.cpp | 17 +++++++++++++---- examples/imgui_impl_vulkan.h | 2 ++ examples/sdl_vulkan_example/main.cpp | 7 +++++-- examples/vulkan_example/main.cpp | 7 +++++-- 4 files changed, 25 insertions(+), 8 deletions(-) diff --git a/examples/imgui_impl_vulkan.cpp b/examples/imgui_impl_vulkan.cpp index 839d737cc064..8864c3d5c3da 100644 --- a/examples/imgui_impl_vulkan.cpp +++ b/examples/imgui_impl_vulkan.cpp @@ -29,6 +29,8 @@ static const VkAllocationCallbacks* g_Allocator = NULL; static VkPhysicalDevice g_PhysicalDevice = VK_NULL_HANDLE; static VkInstance g_Instance = VK_NULL_HANDLE; static VkDevice g_Device = VK_NULL_HANDLE; +static uint32_t g_QueueFamily = (uint32_t)-1; +static VkQueue g_Queue = VK_NULL_HANDLE; static VkPipelineCache g_PipelineCache = VK_NULL_HANDLE; static VkDescriptorPool g_DescriptorPool = VK_NULL_HANDLE; static VkRenderPass g_RenderPass = VK_NULL_HANDLE; @@ -298,7 +300,7 @@ void ImGui_ImplVulkan_RenderDrawData(VkCommandBuffer command_buffer, ImDrawData* // Apply scissor/clipping rectangle // FIXME: We could clamp width/height based on clamped min/max values. VkRect2D scissor; - scissor.offset.x = (int32_t)(pcmd->ClipRect.x - io.DisplayPos.x) > 0 ? (int32_t)(pcmd->ClipRect.x - io.DisplayPos.y) : 0; + scissor.offset.x = (int32_t)(pcmd->ClipRect.x - io.DisplayPos.x) > 0 ? (int32_t)(pcmd->ClipRect.x - io.DisplayPos.x) : 0; scissor.offset.y = (int32_t)(pcmd->ClipRect.y - io.DisplayPos.y) > 0 ? (int32_t)(pcmd->ClipRect.y - io.DisplayPos.y) : 0; scissor.extent.width = (uint32_t)(pcmd->ClipRect.z - pcmd->ClipRect.x); scissor.extent.height = (uint32_t)(pcmd->ClipRect.w - pcmd->ClipRect.y + 1); // FIXME: Why +1 here? @@ -624,7 +626,7 @@ bool ImGui_ImplVulkan_CreateDeviceObjects() VkDynamicState dynamic_states[2] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; VkPipelineDynamicStateCreateInfo dynamic_state = {}; dynamic_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; - dynamic_state.dynamicStateCount = 2; + dynamic_state.dynamicStateCount = (uint32_t)IM_ARRAYSIZE(dynamic_states); dynamic_state.pDynamicStates = dynamic_states; VkGraphicsPipelineCreateInfo info = {}; @@ -692,13 +694,19 @@ bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info, VkRenderPass rend IM_ASSERT(info->Instance != NULL); IM_ASSERT(info->PhysicalDevice != NULL); IM_ASSERT(info->Device != NULL); + IM_ASSERT(info->Queue != NULL); + IM_ASSERT(info->DescriptorPool != NULL); + IM_ASSERT(render_pass != NULL); - g_Allocator = info->Allocator; + g_Instance = info->Instance; g_PhysicalDevice = info->PhysicalDevice; g_Device = info->Device; + g_QueueFamily = info->QueueFamily; + g_Queue = info->Queue; g_RenderPass = render_pass; g_PipelineCache = info->PipelineCache; g_DescriptorPool = info->DescriptorPool; + g_Allocator = info->Allocator; g_CheckVkResultFn = info->CheckVkResultFn; ImGuiIO& io = ImGui::GetIO(); @@ -999,6 +1007,7 @@ void ImGui_ImplVulkanH_CreateWindowDataSwapChainAndFramebuffer(VkPhysicalDevice void ImGui_ImplVulkanH_DestroyWindowData(VkInstance instance, VkDevice device, ImGui_ImplVulkan_WindowData* wd, const VkAllocationCallbacks* allocator) { + vkDeviceWaitIdle(device); for (int i = 0; i < IMGUI_VK_QUEUED_FRAMES; i++) { ImGui_ImplVulkan_FrameData* fd = &wd->Frames[i]; @@ -1027,7 +1036,7 @@ void ImGui_ImplVulkanH_DestroyWindowData(VkInstance instance, VkDevice device, I struct ImGuiPlatformDataVulkan { - ImGui_ImplVulkan_WindowData Wd; + ImGui_ImplVulkan_WindowData WindowData; ImGuiPlatformDataVulkan() { } ~ImGuiPlatformDataVulkan() { } diff --git a/examples/imgui_impl_vulkan.h b/examples/imgui_impl_vulkan.h index 50a223d1734f..afabbb74e9a1 100644 --- a/examples/imgui_impl_vulkan.h +++ b/examples/imgui_impl_vulkan.h @@ -18,6 +18,8 @@ struct ImGui_ImplVulkan_InitInfo VkInstance Instance; VkPhysicalDevice PhysicalDevice; VkDevice Device; + uint32_t QueueFamily; + VkQueue Queue; VkPipelineCache PipelineCache; VkDescriptorPool DescriptorPool; const VkAllocationCallbacks* Allocator; diff --git a/examples/sdl_vulkan_example/main.cpp b/examples/sdl_vulkan_example/main.cpp index d896b8391ef3..7ed0f997d46e 100644 --- a/examples/sdl_vulkan_example/main.cpp +++ b/examples/sdl_vulkan_example/main.cpp @@ -38,8 +38,8 @@ static void check_vk_result(VkResult err) #ifdef IMGUI_VULKAN_DEBUG_REPORT static VKAPI_ATTR VkBool32 VKAPI_CALL debug_report(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char* pLayerPrefix, const char* pMessage, void* pUserData) { - (void)flags; (void)object; (void)location; (void)messageCode; (void)pUserData; (void)pLayerPrefix; // Unused arguemnts - printf("[vulkan] ObjectType: %i\nMessage: %s\n\n", objectType, pMessage); + (void)flags; (void)object; (void)location; (void)messageCode; (void)pUserData; (void)pLayerPrefix; // Unused arguments + fprintf(stderr, "[vulkan] ObjectType: %i\nMessage: %s\n\n", objectType, pMessage); return VK_FALSE; } #endif // IMGUI_VULKAN_DEBUG_REPORT @@ -348,10 +348,13 @@ int main(int, char**) io.ConfigFlags |= ImGuiConfigFlags_PlatformNoTaskBar; //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + // Setup Vulkan binding ImGui_ImplVulkan_InitInfo init_info = {}; init_info.Instance = g_Instance; init_info.PhysicalDevice = g_PhysicalDevice; init_info.Device = g_Device; + init_info.QueueFamily = g_QueueFamily; + init_info.Queue = g_Queue; init_info.PipelineCache = g_PipelineCache; init_info.DescriptorPool = g_DescriptorPool; init_info.Allocator = g_Allocator; diff --git a/examples/vulkan_example/main.cpp b/examples/vulkan_example/main.cpp index fbec7a6deea3..3e39cb06c5a4 100644 --- a/examples/vulkan_example/main.cpp +++ b/examples/vulkan_example/main.cpp @@ -41,8 +41,8 @@ static void check_vk_result(VkResult err) #ifdef IMGUI_VULKAN_DEBUG_REPORT static VKAPI_ATTR VkBool32 VKAPI_CALL debug_report(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char* pLayerPrefix, const char* pMessage, void* pUserData) { - (void)flags; (void)object; (void)location; (void)messageCode; (void)pUserData; (void)pLayerPrefix; // Unused arguemnts - printf("[vulkan] ObjectType: %i\nMessage: %s\n\n", objectType, pMessage); + (void)flags; (void)object; (void)location; (void)messageCode; (void)pUserData; (void)pLayerPrefix; // Unused arguments + fprintf(stderr, "[vulkan] ObjectType: %i\nMessage: %s\n\n", objectType, pMessage); return VK_FALSE; } #endif // IMGUI_VULKAN_DEBUG_REPORT @@ -357,10 +357,13 @@ int main(int, char**) io.ConfigFlags |= ImGuiConfigFlags_PlatformNoTaskBar; //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + // Setup Vulkan binding ImGui_ImplVulkan_InitInfo init_info = {}; init_info.Instance = g_Instance; init_info.PhysicalDevice = g_PhysicalDevice; init_info.Device = g_Device; + init_info.QueueFamily = g_QueueFamily; + init_info.Queue = g_Queue; init_info.PipelineCache = g_PipelineCache; init_info.DescriptorPool = g_DescriptorPool; init_info.Allocator = g_Allocator; From b88492746e6afbb216c8e10908679b7e968207d7 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 12 Mar 2018 18:43:25 +0100 Subject: [PATCH 078/828] Examples, Viewport: Vulkan: Experiment (broken) multi-viewport support, merging code from ParticlePeter branches. (#1542, #1042) --- examples/imgui_impl_glfw.cpp | 3 + examples/imgui_impl_vulkan.cpp | 173 ++++++++++++++++++++++----- examples/imgui_impl_vulkan.h | 4 +- examples/sdl_vulkan_example/main.cpp | 2 +- examples/vulkan_example/main.cpp | 169 +++++++++++++------------- imgui.h | 3 + 6 files changed, 243 insertions(+), 111 deletions(-) diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index 6b520ba26961..b9cf06e0df38 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -452,6 +452,9 @@ static void ImGui_ImplGlfw_InitPlatformInterface() io.PlatformInterface.RenderViewport = ImGui_ImplGlfw_RenderViewport; io.PlatformInterface.SwapBuffers = ImGui_ImplGlfw_SwapBuffers; + // We let the user set up the link to glfwCreateWindowSurface() here, so this binding can work with old GLFW and without Vulkan headers + io.PlatformInterface.CreateVkSurface = NULL; + // Register main window handle ImGuiViewport* main_viewport = ImGui::GetMainViewport(); ImGuiPlatformDataGlfw* data = IM_NEW(ImGuiPlatformDataGlfw)(); diff --git a/examples/imgui_impl_vulkan.cpp b/examples/imgui_impl_vulkan.cpp index 8864c3d5c3da..a117600c8061 100644 --- a/examples/imgui_impl_vulkan.cpp +++ b/examples/imgui_impl_vulkan.cpp @@ -55,7 +55,7 @@ struct FrameDataForRender VkBuffer IndexBuffer; }; static int g_FrameIndex = 0; -static FrameDataForRender g_FramesDataBuffers[IMGUI_VK_QUEUED_FRAMES]; +static FrameDataForRender g_FramesDataBuffers[IMGUI_VK_QUEUED_FRAMES] = {}; // Font data static VkSampler g_FontSampler = VK_NULL_HANDLE; @@ -204,6 +204,7 @@ void ImGui_ImplVulkan_RenderDrawData(VkCommandBuffer command_buffer, ImDrawData* return; FrameDataForRender* fd = &g_FramesDataBuffers[g_FrameIndex]; + g_FrameIndex = (g_FrameIndex + 1) % IMGUI_VK_QUEUED_FRAMES; // Create the Vertex and Index buffers: size_t vertex_size = draw_data->TotalVtxCount * sizeof(ImDrawVert); @@ -713,7 +714,10 @@ bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info, VkRenderPass rend ImGui_ImplVulkan_CreateDeviceObjects(); io.ConfigFlags |= ImGuiConfigFlags_RendererHasViewports; if (io.ConfigFlags & ImGuiConfigFlags_EnableViewports) + { + IM_ASSERT(io.PlatformInterface.CreateVkSurface != NULL); ImGui_ImplVulkan_InitPlatformInterface(); + } return true; } @@ -728,12 +732,6 @@ void ImGui_ImplVulkan_NewFrame() { } -void ImGui_ImplVulkan_Render(VkCommandBuffer command_buffer) -{ - ImGui_ImplVulkan_RenderDrawData(command_buffer, ImGui::GetDrawData()); - g_FrameIndex = (g_FrameIndex + 1) % IMGUI_VK_QUEUED_FRAMES; -} - //------------------------------------------------------------------------- // Miscellaneous Vulkan Helpers // (Those are currently not strictly needed by the binding, but will be once if we support multi-viewports) @@ -747,7 +745,7 @@ ImGui_ImplVulkan_FrameData::ImGui_ImplVulkan_FrameData() CommandPool = VK_NULL_HANDLE; CommandBuffer = VK_NULL_HANDLE; Fence = VK_NULL_HANDLE; - PresentCompleteSemaphore = VK_NULL_HANDLE; + ImageAcquiredSemaphore = VK_NULL_HANDLE; RenderCompleteSemaphore = VK_NULL_HANDLE; } @@ -867,7 +865,7 @@ void ImGui_ImplVulkanH_CreateWindowDataCommandBuffers(VkPhysicalDevice physical_ { VkSemaphoreCreateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - err = vkCreateSemaphore(device, &info, allocator, &fd->PresentCompleteSemaphore); + err = vkCreateSemaphore(device, &info, allocator, &fd->ImageAcquiredSemaphore); check_vk_result(err); err = vkCreateSemaphore(device, &info, allocator, &fd->RenderCompleteSemaphore); check_vk_result(err); @@ -875,8 +873,22 @@ void ImGui_ImplVulkanH_CreateWindowDataCommandBuffers(VkPhysicalDevice physical_ } } +int ImGui_ImplVulkanH_GetMinImageCountFromPresentMode(VkPresentModeKHR present_mode) +{ + if (present_mode == VK_PRESENT_MODE_MAILBOX_KHR) + return 3; + if (present_mode == VK_PRESENT_MODE_FIFO_KHR || present_mode == VK_PRESENT_MODE_FIFO_RELAXED_KHR) + return 2; + if (present_mode == VK_PRESENT_MODE_IMMEDIATE_KHR) + return 1; + IM_ASSERT(0); + return 1; +} + void ImGui_ImplVulkanH_CreateWindowDataSwapChainAndFramebuffer(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkan_WindowData* wd, const VkAllocationCallbacks* allocator, int w, int h) { + uint32_t min_image_count = 2; // FIXME: this should become a function parameter + VkResult err; VkSwapchainKHR old_swapchain = wd->Swapchain; err = vkDeviceWaitIdle(device); @@ -893,12 +905,17 @@ void ImGui_ImplVulkanH_CreateWindowDataSwapChainAndFramebuffer(VkPhysicalDevice wd->BackBufferCount = 0; if (wd->RenderPass) vkDestroyRenderPass(device, wd->RenderPass, allocator); + + // If min image count was not specified, request different count of images dependent on selected present mode + if (min_image_count == 0) + min_image_count = ImGui_ImplVulkanH_GetMinImageCountFromPresentMode(wd->PresentMode); // Create Swapchain { VkSwapchainCreateInfoKHR info = {}; info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; info.surface = wd->Surface; + info.minImageCount = min_image_count; info.imageFormat = wd->SurfaceFormat.format; info.imageColorSpace = wd->SurfaceFormat.colorSpace; info.imageArrayLayers = 1; @@ -912,10 +929,10 @@ void ImGui_ImplVulkanH_CreateWindowDataSwapChainAndFramebuffer(VkPhysicalDevice VkSurfaceCapabilitiesKHR cap; err = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, wd->Surface, &cap); check_vk_result(err); - if (cap.maxImageCount > 0) - info.minImageCount = (cap.minImageCount + 2 < cap.maxImageCount) ? (cap.minImageCount + 2) : cap.maxImageCount; - else - info.minImageCount = cap.minImageCount + 2; + if (info.minImageCount < cap.minImageCount) + info.minImageCount = cap.minImageCount; + else if (info.minImageCount > cap.maxImageCount) + info.minImageCount = cap.maxImageCount; if (cap.currentExtent.width == 0xffffffff) { @@ -1007,14 +1024,16 @@ void ImGui_ImplVulkanH_CreateWindowDataSwapChainAndFramebuffer(VkPhysicalDevice void ImGui_ImplVulkanH_DestroyWindowData(VkInstance instance, VkDevice device, ImGui_ImplVulkan_WindowData* wd, const VkAllocationCallbacks* allocator) { - vkDeviceWaitIdle(device); + vkDeviceWaitIdle(device); // FIXME: We could wait on the Queue if we had the queue in wd-> (otherwise VulkanH functions can't use globals) + //vkQueueWaitIdle(g_Queue); + for (int i = 0; i < IMGUI_VK_QUEUED_FRAMES; i++) { ImGui_ImplVulkan_FrameData* fd = &wd->Frames[i]; vkDestroyFence(device, fd->Fence, allocator); vkFreeCommandBuffers(device, fd->CommandPool, 1, &fd->CommandBuffer); vkDestroyCommandPool(device, fd->CommandPool, allocator); - vkDestroySemaphore(device, fd->PresentCompleteSemaphore, allocator); + vkDestroySemaphore(device, fd->ImageAcquiredSemaphore, allocator); vkDestroySemaphore(device, fd->RenderCompleteSemaphore, allocator); } for (uint32_t i = 0; i < wd->BackBufferCount; i++) @@ -1046,19 +1065,41 @@ static void ImGui_ImplVulkan_CreateViewport(ImGuiViewport* viewport) { ImGuiPlatformDataVulkan* data = IM_NEW(ImGuiPlatformDataVulkan)(); viewport->RendererUserData = data; + ImGui_ImplVulkan_WindowData* wd = &data->WindowData; - // FIXME-PLATFORM - //HWND hwnd = (HWND)viewport->PlatformHandle; - //IM_ASSERT(hwnd != 0); + // Create surface + ImGuiIO& io = ImGui::GetIO(); + VkResult err = (VkResult)io.PlatformInterface.CreateVkSurface(viewport->PlatformHandle, (ImU64)g_Instance, (const void*)g_Allocator, (ImU64*)&wd->Surface); + check_vk_result(err); + + // Check for WSI support + VkBool32 res; + vkGetPhysicalDeviceSurfaceSupportKHR(g_PhysicalDevice, g_QueueFamily, wd->Surface, &res); + if (res != VK_TRUE) + { + fprintf(stderr, "Error no WSI support on physical device 0\n"); + exit(-1); + } + + // Get Surface Format + const VkFormat requestSurfaceImageFormat[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM }; + const VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; + wd->SurfaceFormat = ImGui_ImplVulkanH_SelectSurfaceFormat(g_PhysicalDevice, wd->Surface, requestSurfaceImageFormat, (size_t)IM_ARRAYSIZE(requestSurfaceImageFormat), requestSurfaceColorSpace); - //... + // Get Present Mode + VkPresentModeKHR present_mode = VK_PRESENT_MODE_IMMEDIATE_KHR; + wd->PresentMode = ImGui_ImplVulkanH_SelectPresentMode(g_PhysicalDevice, wd->Surface, &present_mode, 1); + + // Create SwapChain, RenderPass, Framebuffer, etc. + ImGui_ImplVulkanH_CreateWindowDataCommandBuffers(g_PhysicalDevice, g_Device, g_QueueFamily, wd, g_Allocator); + ImGui_ImplVulkanH_CreateWindowDataSwapChainAndFramebuffer(g_PhysicalDevice, g_Device, wd, g_Allocator, (int)viewport->Size.x, (int)viewport->Size.y); } static void ImGui_ImplVulkan_DestroyViewport(ImGuiViewport* viewport) { if (ImGuiPlatformDataVulkan* data = (ImGuiPlatformDataVulkan*)viewport->RendererUserData) { - //... + ImGui_ImplVulkanH_DestroyWindowData(g_Instance, g_Device, &data->WindowData, g_Allocator); IM_DELETE(data); } viewport->RendererUserData = NULL; @@ -1067,32 +1108,107 @@ static void ImGui_ImplVulkan_DestroyViewport(ImGuiViewport* viewport) static void ImGui_ImplVulkan_ResizeViewport(ImGuiViewport* viewport, ImVec2 size) { ImGuiPlatformDataVulkan* data = (ImGuiPlatformDataVulkan*)viewport->RendererUserData; - //... - (void)data; - (void)size; + ImGui_ImplVulkan_WindowData* wd = &data->WindowData; + ImGui_ImplVulkanH_CreateWindowDataSwapChainAndFramebuffer(g_PhysicalDevice, g_Device, wd, g_Allocator, (int)size.x, (int)size.y); } static void ImGui_ImplVulkan_RenderViewport(ImGuiViewport* viewport) { ImGuiPlatformDataVulkan* data = (ImGuiPlatformDataVulkan*)viewport->RendererUserData; + ImGui_ImplVulkan_WindowData* wd = &data->WindowData; + VkResult err; + ImVec4 clear_color = ImGui::GetStyle().Colors[ImGuiCol_WindowBg]; // FIXME-PLATFORM clear_color.w = 1.0f; - (void)data; - // clear - // call ImGui_ImplVulkan_RenderDrawData(&viewport->DrawData) + { + ImGui_ImplVulkan_FrameData* fd = &wd->Frames[wd->FrameIndex]; + for (;;) + { + err = vkWaitForFences(g_Device, 1, &fd->Fence, VK_TRUE, 100); + if (err == VK_SUCCESS) break; + if (err == VK_TIMEOUT) continue; + check_vk_result(err); + } + { + err = vkAcquireNextImageKHR(g_Device, wd->Swapchain, UINT64_MAX, fd->ImageAcquiredSemaphore, VK_NULL_HANDLE, &fd->BackbufferIndex); + check_vk_result(err); + } + { + err = vkResetCommandPool(g_Device, fd->CommandPool, 0); + check_vk_result(err); + VkCommandBufferBeginInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + err = vkBeginCommandBuffer(fd->CommandBuffer, &info); + check_vk_result(err); + } + { + VkRenderPassBeginInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + info.renderPass = wd->RenderPass; + info.framebuffer = wd->Framebuffer[fd->BackbufferIndex]; + info.renderArea.extent.width = wd->Width; + info.renderArea.extent.height = wd->Height; + info.clearValueCount = 1; + info.pClearValues = &wd->ClearValue; + vkCmdBeginRenderPass(fd->CommandBuffer, &info, VK_SUBPASS_CONTENTS_INLINE); + } + } + + memcpy(&wd->ClearValue.color.float32[0], &clear_color, 4 * sizeof(float)); + ImGui_ImplVulkan_RenderDrawData(wd->Frames[wd->FrameIndex].CommandBuffer, &viewport->DrawData); + + { + ImGui_ImplVulkan_FrameData* fd = &wd->Frames[wd->FrameIndex]; + vkCmdEndRenderPass(fd->CommandBuffer); + { + VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + VkSubmitInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + info.waitSemaphoreCount = 1; + info.pWaitSemaphores = &fd->ImageAcquiredSemaphore; + info.pWaitDstStageMask = &wait_stage; + info.commandBufferCount = 1; + info.pCommandBuffers = &fd->CommandBuffer; + info.signalSemaphoreCount = 1; + info.pSignalSemaphores = &fd->RenderCompleteSemaphore; + + err = vkEndCommandBuffer(fd->CommandBuffer); + check_vk_result(err); + err = vkResetFences(g_Device, 1, &fd->Fence); + check_vk_result(err); + err = vkQueueSubmit(g_Queue, 1, &info, fd->Fence); + check_vk_result(err); + } + } } static void ImGui_ImplVulkan_SwapBuffers(ImGuiViewport* viewport) { ImGuiPlatformDataVulkan* data = (ImGuiPlatformDataVulkan*)viewport->RendererUserData; - (void)data; - //... + ImGui_ImplVulkan_WindowData* wd = &data->WindowData; + + VkResult err; + uint32_t PresentIndex = wd->FrameIndex; + + ImGui_ImplVulkan_FrameData* fd = &wd->Frames[PresentIndex]; + VkPresentInfoKHR info = {}; + info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + info.waitSemaphoreCount = 1; + info.pWaitSemaphores = &fd->RenderCompleteSemaphore; + info.swapchainCount = 1; + info.pSwapchains = &wd->Swapchain; + info.pImageIndices = &fd->BackbufferIndex; + err = vkQueuePresentKHR(g_Queue, &info); + check_vk_result(err); + wd->FrameIndex = (wd->FrameIndex + 1) % IMGUI_VK_QUEUED_FRAMES; } void ImGui_ImplVulkan_InitPlatformInterface() { ImGuiIO& io = ImGui::GetIO(); + IM_ASSERT(io.PlatformInterface.CreateVkSurface != NULL); io.RendererInterface.CreateViewport = ImGui_ImplVulkan_CreateViewport; io.RendererInterface.DestroyViewport = ImGui_ImplVulkan_DestroyViewport; io.RendererInterface.ResizeViewport = ImGui_ImplVulkan_ResizeViewport; @@ -1102,6 +1218,7 @@ void ImGui_ImplVulkan_InitPlatformInterface() void ImGui_ImplVulkan_ShutdownPlatformInterface() { + ImGui::DestroyViewportsRendererData(ImGui::GetCurrentContext()); ImGuiIO& io = ImGui::GetIO(); memset(&io.RendererInterface, 0, sizeof(io.RendererInterface)); } diff --git a/examples/imgui_impl_vulkan.h b/examples/imgui_impl_vulkan.h index afabbb74e9a1..f9ef133ead19 100644 --- a/examples/imgui_impl_vulkan.h +++ b/examples/imgui_impl_vulkan.h @@ -29,7 +29,6 @@ struct ImGui_ImplVulkan_InitInfo IMGUI_API bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info, VkRenderPass render_pass); IMGUI_API void ImGui_ImplVulkan_Shutdown(); IMGUI_API void ImGui_ImplVulkan_NewFrame(); -IMGUI_API void ImGui_ImplVulkan_Render(VkCommandBuffer command_buffer); IMGUI_API void ImGui_ImplVulkan_RenderDrawData(VkCommandBuffer command_buffer, ImDrawData* draw_data); // Called by Init/NewFrame/Shutdown @@ -55,6 +54,7 @@ IMGUI_API void ImGui_ImplVulkanH_CreateWindowDataSwapChainAndFr IMGUI_API void ImGui_ImplVulkanH_DestroyWindowData(VkInstance instance, VkDevice device, ImGui_ImplVulkan_WindowData* wd, const VkAllocationCallbacks* allocator); IMGUI_API VkSurfaceFormatKHR ImGui_ImplVulkanH_SelectSurfaceFormat(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkFormat* request_formats, int request_formats_count, VkColorSpaceKHR request_color_space); IMGUI_API VkPresentModeKHR ImGui_ImplVulkanH_SelectPresentMode(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkPresentModeKHR* request_modes, int request_modes_count); +IMGUI_API int ImGui_ImplVulkanH_GetMinImageCountFromPresentMode(VkPresentModeKHR present_mode); struct ImGui_ImplVulkan_FrameData { @@ -62,7 +62,7 @@ struct ImGui_ImplVulkan_FrameData VkCommandPool CommandPool; VkCommandBuffer CommandBuffer; VkFence Fence; - VkSemaphore PresentCompleteSemaphore; + VkSemaphore ImageAcquiredSemaphore; VkSemaphore RenderCompleteSemaphore; IMGUI_API ImGui_ImplVulkan_FrameData(); diff --git a/examples/sdl_vulkan_example/main.cpp b/examples/sdl_vulkan_example/main.cpp index 7ed0f997d46e..5c4e600889f3 100644 --- a/examples/sdl_vulkan_example/main.cpp +++ b/examples/sdl_vulkan_example/main.cpp @@ -479,7 +479,7 @@ int main(int, char**) ImGui::Render(); memcpy(&wd->ClearValue.color.float32[0], &clear_color, 4 * sizeof(float)); FrameBegin(wd); - ImGui_ImplVulkan_Render(wd->Frames[wd->FrameIndex].CommandBuffer); + ImGui_ImplVulkan_RenderDrawData(wd->Frames[wd->FrameIndex].CommandBuffer, ImGui::GetDrawData()); FrameEnd(wd); ImGui::RenderAdditionalViewports(); diff --git a/examples/vulkan_example/main.cpp b/examples/vulkan_example/main.cpp index cd4e3e7264a8..4029c651b9e1 100644 --- a/examples/vulkan_example/main.cpp +++ b/examples/vulkan_example/main.cpp @@ -224,83 +224,79 @@ static void CleanupVulkan() vkDestroyInstance(g_Instance, g_Allocator); } -static void FrameBegin(ImGui_ImplVulkan_WindowData* wd) +static void FrameRender(ImGui_ImplVulkan_WindowData* wd) { - ImGui_ImplVulkan_FrameData* fd = &wd->Frames[wd->FrameIndex]; - VkResult err; - for (;;) - { - err = vkWaitForFences(g_Device, 1, &fd->Fence, VK_TRUE, 100); - if (err == VK_SUCCESS) break; - if (err == VK_TIMEOUT) continue; - check_vk_result(err); - } - { - err = vkAcquireNextImageKHR(g_Device, wd->Swapchain, UINT64_MAX, fd->PresentCompleteSemaphore, VK_NULL_HANDLE, &fd->BackbufferIndex); - check_vk_result(err); - } - { - err = vkResetCommandPool(g_Device, fd->CommandPool, 0); - check_vk_result(err); - VkCommandBufferBeginInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - err = vkBeginCommandBuffer(fd->CommandBuffer, &info); - check_vk_result(err); - } - { - VkRenderPassBeginInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; - info.renderPass = wd->RenderPass; - info.framebuffer = wd->Framebuffer[fd->BackbufferIndex]; - info.renderArea.extent.width = wd->Width; - info.renderArea.extent.height = wd->Height; - info.clearValueCount = 1; - info.pClearValues = &wd->ClearValue; - vkCmdBeginRenderPass(fd->CommandBuffer, &info, VK_SUBPASS_CONTENTS_INLINE); - } -} + VkResult err; + + VkSemaphore& image_acquired_semaphore = wd->Frames[wd->FrameIndex].ImageAcquiredSemaphore; + err = vkAcquireNextImageKHR(g_Device, wd->Swapchain, UINT64_MAX, image_acquired_semaphore, VK_NULL_HANDLE, &wd->FrameIndex); + check_vk_result(err); -static void FrameEnd(ImGui_ImplVulkan_WindowData* wd) -{ ImGui_ImplVulkan_FrameData* fd = &wd->Frames[wd->FrameIndex]; - VkResult err; - vkCmdEndRenderPass(fd->CommandBuffer); { - VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - VkSubmitInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - info.waitSemaphoreCount = 1; - info.pWaitSemaphores = &fd->PresentCompleteSemaphore; - info.pWaitDstStageMask = &wait_stage; - info.commandBufferCount = 1; - info.pCommandBuffers = &fd->CommandBuffer; - info.signalSemaphoreCount = 1; - info.pSignalSemaphores = &fd->RenderCompleteSemaphore; - - err = vkEndCommandBuffer(fd->CommandBuffer); - check_vk_result(err); - err = vkResetFences(g_Device, 1, &fd->Fence); - check_vk_result(err); - err = vkQueueSubmit(g_Queue, 1, &info, fd->Fence); - check_vk_result(err); - } + err = vkWaitForFences(g_Device, 1, &fd->Fence, VK_TRUE, UINT64_MAX); // wait indefinitely instead of periodically checking + check_vk_result(err); + + err = vkResetFences(g_Device, 1, &fd->Fence); + check_vk_result(err); + } + { + err = vkResetCommandPool(g_Device, fd->CommandPool, 0); + check_vk_result(err); + VkCommandBufferBeginInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + err = vkBeginCommandBuffer(fd->CommandBuffer, &info); + check_vk_result(err); + } + { + VkRenderPassBeginInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + info.renderPass = wd->RenderPass; + info.framebuffer = wd->Framebuffer[wd->FrameIndex]; + info.renderArea.extent.width = wd->Width; + info.renderArea.extent.height = wd->Height; + info.clearValueCount = 1; + info.pClearValues = &wd->ClearValue; + vkCmdBeginRenderPass(fd->CommandBuffer, &info, VK_SUBPASS_CONTENTS_INLINE); + } + + // Record Imgui Draw Data and draw funcs into command buffer + ImGui_ImplVulkan_RenderDrawData(fd->CommandBuffer, ImGui::GetDrawData()); + + // Submit command buffer + vkCmdEndRenderPass(fd->CommandBuffer); + { + VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + VkSubmitInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + info.waitSemaphoreCount = 1; + info.pWaitSemaphores = &image_acquired_semaphore; + info.pWaitDstStageMask = &wait_stage; + info.commandBufferCount = 1; + info.pCommandBuffers = &fd->CommandBuffer; + info.signalSemaphoreCount = 1; + info.pSignalSemaphores = &fd->RenderCompleteSemaphore; + + err = vkEndCommandBuffer(fd->CommandBuffer); + check_vk_result(err); + err = vkQueueSubmit(g_Queue, 1, &info, fd->Fence); + check_vk_result(err); + } } static void FramePresent(ImGui_ImplVulkan_WindowData* wd) { - VkResult err; - ImGui_ImplVulkan_FrameData* fd = &wd->Frames[wd->FrameIndex]; - VkPresentInfoKHR info = {}; - info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; - info.waitSemaphoreCount = 1; - info.pWaitSemaphores = &fd->RenderCompleteSemaphore; - info.swapchainCount = 1; - info.pSwapchains = &wd->Swapchain; - info.pImageIndices = &fd->BackbufferIndex; - err = vkQueuePresentKHR(g_Queue, &info); - check_vk_result(err); + VkPresentInfoKHR info = {}; + info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + info.waitSemaphoreCount = 1; + info.pWaitSemaphores = &fd->RenderCompleteSemaphore; + info.swapchainCount = 1; + info.pSwapchains = &wd->Swapchain; + info.pImageIndices = &wd->FrameIndex; + VkResult err = vkQueuePresentKHR(g_Queue, &info); + check_vk_result(err); } static void glfw_error_callback(int error, const char* description) @@ -312,12 +308,23 @@ static void glfw_resize_callback(GLFWwindow*, int w, int h) { g_ResizeWanted = true; g_ResizeWidth = w; - g_ResizeHeight = h; + g_ResizeHeight = h; +} + +static int glfw_create_vk_surface(void* platform_handle, ImU64 vk_instance, const void* vk_allocator, ImU64* out_vk_surface) +{ + GLFWwindow* window = (GLFWwindow*)platform_handle; + VkInstance instance = (VkInstance)vk_instance; + const VkAllocationCallbacks* allocator = (const VkAllocationCallbacks*)vk_allocator; + VkSurfaceKHR* surface = (VkSurfaceKHR*)out_vk_surface; + VkResult err = glfwCreateWindowSurface(instance, window, allocator, surface); + check_vk_result(err); + return (int)err; } int main(int, char**) { - // Setup window + // Setup window glfwSetErrorCallback(glfw_error_callback); if (!glfwInit()) return 1; @@ -354,6 +361,10 @@ int main(int, char**) io.ConfigFlags |= ImGuiConfigFlags_PlatformNoTaskBar; //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + // Setup GLFW binding + ImGui_ImplGlfw_InitForVulkan(window, true); + io.PlatformInterface.CreateVkSurface = glfw_create_vk_surface; + // Setup Vulkan binding ImGui_ImplVulkan_InitInfo init_info = {}; init_info.Instance = g_Instance; @@ -365,7 +376,6 @@ int main(int, char**) init_info.DescriptorPool = g_DescriptorPool; init_info.Allocator = g_Allocator; init_info.CheckVkResultFn = check_vk_result; - ImGui_ImplGlfw_InitForVulkan(window, true); ImGui_ImplVulkan_Init(&init_info, wd->RenderPass); // Setup style @@ -430,9 +440,12 @@ int main(int, char**) // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. glfwPollEvents(); - if (g_ResizeWanted) - ImGui_ImplVulkanH_CreateWindowDataSwapChainAndFramebuffer(g_PhysicalDevice, g_Device, &g_WindowData, g_Allocator, g_ResizeWidth, g_ResizeHeight); - g_ResizeWanted = false; + if (g_ResizeWanted) + { + ImGui_ImplVulkanH_CreateWindowDataSwapChainAndFramebuffer(g_PhysicalDevice, g_Device, &g_WindowData, g_Allocator, g_ResizeWidth, g_ResizeHeight); + g_ResizeWanted = false; + } + ImGui_ImplVulkan_NewFrame(); ImGui_ImplGlfw_NewFrame(); @@ -443,7 +456,7 @@ int main(int, char**) static int counter = 0; ImGui::Text("Hello, world!"); // Display some text (you can use a format string too) ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f - ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color + ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color (nb: you could use (float*)&wd->ClearValue instead) ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our windows open/close state ImGui::Checkbox("Another Window", &show_another_window); @@ -476,13 +489,9 @@ int main(int, char**) // Rendering ImGui::Render(); memcpy(&wd->ClearValue.color.float32[0], &clear_color, 4 * sizeof(float)); - FrameBegin(wd); - ImGui_ImplVulkan_Render(wd->Frames[wd->FrameIndex].CommandBuffer); - FrameEnd(wd); + FrameRender(wd); ImGui::RenderAdditionalViewports(); FramePresent(wd); - - wd->FrameIndex = (wd->FrameIndex + 1) % IMGUI_VK_QUEUED_FRAMES; } // Cleanup diff --git a/imgui.h b/imgui.h index 7e56bd01f171..0bbdfb65386d 100644 --- a/imgui.h +++ b/imgui.h @@ -966,6 +966,9 @@ struct ImGuiPlatformInterface void (*RenderViewport)(ImGuiViewport* viewport); void (*SwapBuffers)(ImGuiViewport* viewport); + // FIXME-VIEWPORT: Experimenting with back-end abstraction. This probably shouldn't stay as is. + int (*CreateVkSurface)(void* platform_handle, ImU64 vk_instance, const void* vk_allocator, ImU64* out_vk_surface); + // FIXME-DPI float (*GetWindowDpiScale)(ImGuiViewport* viewport); // (Optional) void (*ChangedViewport)(ImGuiViewport* viewport); // (Optional) Called during Begin() every time the viewport we are outputting into changes (viewport = next viewport) From 63714740312c4887056d80cce1ff6ee267494282 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 12 Mar 2018 23:11:53 +0100 Subject: [PATCH 079/828] Viewport: Removed unnecessary function hoop. --- imgui.cpp | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index be244e9a08d1..911457a26907 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3514,12 +3514,14 @@ static void UpdatePlatformWindows() } } -static void RenderPlatformWindows() +void ImGui::RenderAdditionalViewports() { - // Render ImGuiContext& g = *GImGui; ImVec2 backup_display_pos = g.IO.DisplayPos; ImVec2 backup_display_size = g.IO.DisplaySize; + if (!(g.IO.ConfigFlags & ImGuiConfigFlags_EnableViewports)) + return; + for (int i = 0; i < g.Viewports.Size; i++) { ImGuiViewport* viewport = g.Viewports[i]; @@ -4452,13 +4454,6 @@ void ImGui::Render() #endif } -void ImGui::RenderAdditionalViewports() -{ - ImGuiContext& g = *GImGui; - if (g.IO.ConfigFlags & ImGuiConfigFlags_EnableViewports) - RenderPlatformWindows(); -} - ImGuiViewport* ImGui::FindViewportByID(ImGuiID id) { ImGuiContext& g = *GImGui; From c50198debe4978853f3f8ed6e84a7b2685991c06 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 12 Mar 2018 23:15:40 +0100 Subject: [PATCH 080/828] Viewport: Obsoleted io.DisplayPos (which was a Viewport branch thing), added ImDrawData::DisplayPos, DisplaySize (#1542) wip --- examples/imgui_impl_dx10.cpp | 20 +++++++------- examples/imgui_impl_dx11.cpp | 20 +++++++------- examples/imgui_impl_opengl2.cpp | 9 ++++--- examples/imgui_impl_opengl3.cpp | 21 ++++++++------- examples/imgui_impl_vulkan.cpp | 17 ++++++------ imgui.cpp | 48 +++++++++++---------------------- imgui.h | 9 ++++--- imgui_demo.cpp | 2 +- 8 files changed, 67 insertions(+), 79 deletions(-) diff --git a/examples/imgui_impl_dx10.cpp b/examples/imgui_impl_dx10.cpp index 980f3342a88f..db9090152694 100644 --- a/examples/imgui_impl_dx10.cpp +++ b/examples/imgui_impl_dx10.cpp @@ -12,7 +12,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) // 2018-XX-XX: Platform: Added support for multiple windows via the ImGuiRendererInterface. -// 2018-XX-XX: DirectX10: Offset projection matrix and clipping rectangle by io.DisplayPos (which will be non-zero for multi-viewport applications). +// 2018-XX-XX: DirectX10: Offset projection matrix and clipping rectangle by draw_data->DisplayPos (which will be non-zero for multi-viewport applications). // 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplDX10_RenderDrawData() in the .h file so you can call it yourself. // 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. // 2016-05-07: DirectX10: Disabling depth-write. @@ -105,17 +105,16 @@ void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data) g_pIB->Unmap(); // Setup orthographic projection matrix into our constant buffer - // Our visible imgui space lies from io.DisplayPos (top left) to io.DisplayPos+io.DisplaySize (bottom right). io.DisplayPos is typically (0,0) for single viewport applications. - ImGuiIO& io = ImGui::GetIO(); + // Our visible imgui space lies from draw_data->DisplayPps (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayMin is typically (0,0) for single viewport apps. { void* mapped_resource; if (g_pVertexConstantBuffer->Map(D3D10_MAP_WRITE_DISCARD, 0, &mapped_resource) != S_OK) return; VERTEX_CONSTANT_BUFFER* constant_buffer = (VERTEX_CONSTANT_BUFFER*)mapped_resource; - float L = io.DisplayPos.x; - float R = io.DisplayPos.x + io.DisplaySize.x; - float T = io.DisplayPos.y; - float B = io.DisplayPos.y + io.DisplaySize.y; + float L = draw_data->DisplayPos.x; + float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x; + float T = draw_data->DisplayPos.y; + float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y; float mvp[4][4] = { { 2.0f/(R-L), 0.0f, 0.0f, 0.0f }, @@ -169,8 +168,8 @@ void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data) // Setup viewport D3D10_VIEWPORT vp; memset(&vp, 0, sizeof(D3D10_VIEWPORT)); - vp.Width = (UINT)io.DisplaySize.x; - vp.Height = (UINT)io.DisplaySize.y; + vp.Width = (UINT)draw_data->DisplaySize.x; + vp.Height = (UINT)draw_data->DisplaySize.y; vp.MinDepth = 0.0f; vp.MaxDepth = 1.0f; vp.TopLeftX = vp.TopLeftY = 0; @@ -197,6 +196,7 @@ void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data) // Render command lists int vtx_offset = 0; int idx_offset = 0; + ImVec2 display_pos = draw_data->DisplayPos; for (int n = 0; n < draw_data->CmdListsCount; n++) { const ImDrawList* cmd_list = draw_data->CmdLists[n]; @@ -211,7 +211,7 @@ void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data) else { // Apply scissor/clipping rectangle - const ImVec4 clip_rect = ImVec4(pcmd->ClipRect.x - io.DisplayPos.x, pcmd->ClipRect.y - io.DisplayPos.y, pcmd->ClipRect.z - io.DisplayPos.x, pcmd->ClipRect.w - io.DisplayPos.y); + const ImVec4 clip_rect = ImVec4(pcmd->ClipRect.x - display_pos.x, pcmd->ClipRect.y - display_pos.y, pcmd->ClipRect.z - display_pos.x, pcmd->ClipRect.w - display_pos.y); const D3D10_RECT clip_rect_dx = { (LONG)clip_rect.x, (LONG)clip_rect.y, (LONG)clip_rect.z, (LONG)clip_rect.w }; ctx->RSSetScissorRects(1, &clip_rect_dx); diff --git a/examples/imgui_impl_dx11.cpp b/examples/imgui_impl_dx11.cpp index 719025a0b84d..52952bc068d5 100644 --- a/examples/imgui_impl_dx11.cpp +++ b/examples/imgui_impl_dx11.cpp @@ -12,7 +12,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) // 2018-XX-XX: Platform: Added support for multiple windows via the ImGuiRendererInterface. -// 2018-XX-XX: DirectX11: Offset projection matrix and clipping rectangle by io.DisplayPos (which will be non-zero for multi-viewport applications). +// 2018-XX-XX: DirectX11: Offset projection matrix and clipping rectangle by draw_data->DisplayPos (which will be non-zero for multi-viewport applications). // 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplDX11_RenderDrawData() in the .h file so you can call it yourself. // 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. // 2016-05-07: DirectX11: Disabling depth-write. @@ -107,17 +107,16 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) ctx->Unmap(g_pIB, 0); // Setup orthographic projection matrix into our constant buffer - // Our visible imgui space lies from io.DisplayPos (top left) to io.DisplayPos+io.DisplaySize (bottom right). io.DisplayPos is typically (0,0) for single viewport applications. - ImGuiIO& io = ImGui::GetIO(); + // Our visible imgui space lies from draw_data->DisplayPps (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayMin is typically (0,0) for single viewport apps. { D3D11_MAPPED_SUBRESOURCE mapped_resource; if (ctx->Map(g_pVertexConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped_resource) != S_OK) return; VERTEX_CONSTANT_BUFFER* constant_buffer = (VERTEX_CONSTANT_BUFFER*)mapped_resource.pData; - float L = io.DisplayPos.x; - float R = io.DisplayPos.x + io.DisplaySize.x; - float T = io.DisplayPos.y; - float B = io.DisplayPos.y + io.DisplaySize.y; + float L = draw_data->DisplayPos.x; + float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x; + float T = draw_data->DisplayPos.y; + float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y; float mvp[4][4] = { { 2.0f/(R-L), 0.0f, 0.0f, 0.0f }, @@ -174,8 +173,8 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) // Setup viewport D3D11_VIEWPORT vp; memset(&vp, 0, sizeof(D3D11_VIEWPORT)); - vp.Width = io.DisplaySize.x; - vp.Height = io.DisplaySize.y; + vp.Width = draw_data->DisplaySize.x; + vp.Height = draw_data->DisplaySize.y; vp.MinDepth = 0.0f; vp.MaxDepth = 1.0f; vp.TopLeftX = vp.TopLeftY = 0; @@ -202,6 +201,7 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) // Render command lists int vtx_offset = 0; int idx_offset = 0; + ImVec2 display_pos = draw_data->DisplayPos; for (int n = 0; n < draw_data->CmdListsCount; n++) { const ImDrawList* cmd_list = draw_data->CmdLists[n]; @@ -216,7 +216,7 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) else { // Apply scissor/clipping rectangle - const ImVec4 clip_rect = ImVec4(pcmd->ClipRect.x - io.DisplayPos.x, pcmd->ClipRect.y - io.DisplayPos.y, pcmd->ClipRect.z - io.DisplayPos.x, pcmd->ClipRect.w - io.DisplayPos.y); + const ImVec4 clip_rect = ImVec4(pcmd->ClipRect.x - display_pos.x, pcmd->ClipRect.y - display_pos.y, pcmd->ClipRect.z - display_pos.x, pcmd->ClipRect.w - display_pos.y); const D3D11_RECT clip_rect_dx = { (LONG)clip_rect.x, (LONG)clip_rect.y, (LONG)clip_rect.z, (LONG)clip_rect.w }; ctx->RSSetScissorRects(1, &clip_rect_dx); diff --git a/examples/imgui_impl_opengl2.cpp b/examples/imgui_impl_opengl2.cpp index 88b3a6c90abb..ae1c234d1ac7 100644 --- a/examples/imgui_impl_opengl2.cpp +++ b/examples/imgui_impl_opengl2.cpp @@ -11,7 +11,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2018-XX-XX: OpenGL: Offset projection matrix and clipping rectangle by io.DisplayPos (which will be non-zero for multi-viewport applications). +// 2018-XX-XX: OpenGL: Offset projection matrix and clipping rectangle by draw_data->DisplayPos (which will be non-zero for multi-viewport applications). // 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplGlfwGL2_RenderDrawData() in the .h file so you can call it yourself. // 2017-09-01: OpenGL: Save and restore current polygon mode. // 2016-09-10: OpenGL: Uploading font texture as RGBA32 to increase compatibility with users shaders (not ideal). @@ -86,17 +86,18 @@ void ImGui_ImplOpenGL2_RenderDrawData(ImDrawData* draw_data) //glUseProgram(0); // You may want this if using this code in an OpenGL 3+ context where shaders may be bound // Setup viewport, orthographic projection matrix - // Our visible imgui space lies from io.DisplayPos (top left) to io.DisplayPos+io.DisplaySize (bottom right). io.DisplayPos is typically (0,0) for single viewport applications. + // Our visible imgui space lies from draw_data->DisplayPps (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayMin is typically (0,0) for single viewport apps. glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); - glOrtho(io.DisplayPos.x, io.DisplayPos.x + io.DisplaySize.x, io.DisplayPos.y + io.DisplaySize.y, io.DisplayPos.y, -1.0f, +1.0f); + glOrtho(draw_data->DisplayPos.x, draw_data->DisplayPos.x + draw_data->DisplaySize.x, draw_data->DisplayPos.y + draw_data->DisplaySize.y, draw_data->DisplayPos.y, -1.0f, +1.0f); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); // Render command lists + ImVec2 display_pos = draw_data->DisplayPos; for (int n = 0; n < draw_data->CmdListsCount; n++) { const ImDrawList* cmd_list = draw_data->CmdLists[n]; @@ -116,7 +117,7 @@ void ImGui_ImplOpenGL2_RenderDrawData(ImDrawData* draw_data) } else { - ImVec4 clip_rect = ImVec4(pcmd->ClipRect.x - io.DisplayPos.x, pcmd->ClipRect.y - io.DisplayPos.y, pcmd->ClipRect.z - io.DisplayPos.x, pcmd->ClipRect.w - io.DisplayPos.y); + ImVec4 clip_rect = ImVec4(pcmd->ClipRect.x - display_pos.x, pcmd->ClipRect.y - display_pos.y, pcmd->ClipRect.z - display_pos.x, pcmd->ClipRect.w - display_pos.y); if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f) { // Apply scissor/clipping rectangle diff --git a/examples/imgui_impl_opengl3.cpp b/examples/imgui_impl_opengl3.cpp index 1136856cbbc2..e851f0e22f62 100644 --- a/examples/imgui_impl_opengl3.cpp +++ b/examples/imgui_impl_opengl3.cpp @@ -6,7 +6,7 @@ // (minor and older changes stripped away, please see git history for details) // 2018-03-06: OpenGL: Added const char* glsl_version parameter to ImGui_ImplOpenGL3_Init() so user can override the GLSL version e.g. "#version 150". // 2018-02-23: OpenGL: Create the VAO in the render function so the setup can more easily be used with multiple shared GL context. -// 2018-XX-XX: OpenGL: Offset projection matrix and clipping rectangle by io.DisplayPos (which will be non-zero for multi-viewport applications). +// 2018-XX-XX: OpenGL: Offset projection matrix and clipping rectangle by draw_data->DisplayPos (which will be non-zero for multi-viewport applications). // 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplSdlGL3_RenderDrawData() in the .h file so you can call it yourself. // 2018-01-07: OpenGL: Changed GLSL shader version from 330 to 150. // 2017-09-01: OpenGL: Save and restore current bound sampler. Save and restore current polygon mode. @@ -71,9 +71,9 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) { // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) ImGuiIO& io = ImGui::GetIO(); - int fb_width = (int)(io.DisplaySize.x * io.DisplayFramebufferScale.x); - int fb_height = (int)(io.DisplaySize.y * io.DisplayFramebufferScale.y); - if (fb_width == 0 || fb_height == 0) + int fb_width = (int)(draw_data->DisplaySize.x * io.DisplayFramebufferScale.x); + int fb_height = (int)(draw_data->DisplaySize.y * io.DisplayFramebufferScale.y); + if (fb_width <= 0 || fb_height <= 0) return; draw_data->ScaleClipRects(io.DisplayFramebufferScale); @@ -110,12 +110,12 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); // Setup viewport, orthographic projection matrix - // Our visible imgui space lies from io.DisplayPos (top left) to io.DisplayPos+io.DisplaySize (bottom right). io.DisplayPos is typically (0,0) for single viewport applications. + // Our visible imgui space lies from draw_data->DisplayPps (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayMin is typically (0,0) for single viewport apps. glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height); - float L = io.DisplayPos.x; - float R = io.DisplayPos.x + io.DisplaySize.x; - float T = io.DisplayPos.y; - float B = io.DisplayPos.y + io.DisplaySize.y; + float L = draw_data->DisplayPos.x; + float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x; + float T = draw_data->DisplayPos.y; + float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y; const float ortho_projection[4][4] = { { 2.0f/(R-L), 0.0f, 0.0f, 0.0f }, @@ -142,6 +142,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) glVertexAttribPointer(g_AttribLocationColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, col)); // Draw + ImVec2 display_pos = draw_data->DisplayPos; for (int n = 0; n < draw_data->CmdListsCount; n++) { const ImDrawList* cmd_list = draw_data->CmdLists[n]; @@ -163,7 +164,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) } else { - ImVec4 clip_rect = ImVec4(pcmd->ClipRect.x - io.DisplayPos.x, pcmd->ClipRect.y - io.DisplayPos.y, pcmd->ClipRect.z - io.DisplayPos.x, pcmd->ClipRect.w - io.DisplayPos.y); + ImVec4 clip_rect = ImVec4(pcmd->ClipRect.x - display_pos.x, pcmd->ClipRect.y - display_pos.y, pcmd->ClipRect.z - display_pos.x, pcmd->ClipRect.w - display_pos.y); if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f) { // Apply scissor/clipping rectangle diff --git a/examples/imgui_impl_vulkan.cpp b/examples/imgui_impl_vulkan.cpp index a117600c8061..28f07413355e 100644 --- a/examples/imgui_impl_vulkan.cpp +++ b/examples/imgui_impl_vulkan.cpp @@ -13,7 +13,7 @@ // (minor and older changes stripped away, please see git history for details) // 2018-03-03: Vulkan: Various refactor, created a couple of ImGui_ImplVulkanH_XXX helper that the example can use and that viewport support will use. // 2018-03-01: Vulkan: Renamed ImGui_ImplVulkan_Init_Info to ImGui_ImplVulkan_InitInfo and fields to match more closely Vulkan terminology. -// 2018-02-18: Vulkan: Offset projection matrix and clipping rectangle by io.DisplayPos (which will be non-zero for multi-viewport applications). +// 2018-02-18: Vulkan: Offset projection matrix and clipping rectangle by draw_data->DisplayPos (which will be non-zero for multi-viewport applications). // 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback, ImGui_ImplVulkan_Render() calls ImGui_ImplVulkan_RenderDrawData() itself. // 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. // 2017-05-15: Vulkan: Fix scissor offset being negative. Fix new Vulkan validation warnings. Set required depth member for buffer image copy. @@ -271,14 +271,14 @@ void ImGui_ImplVulkan_RenderDrawData(VkCommandBuffer command_buffer, ImDrawData* } // Setup scale and translation: - // (Our visible imgui space lies from io.DisplayPos (top left) to io.DisplayPos+io.DisplaySize (bottom right). io.DisplayPos is typically (0,0) for single viewport applications.) + // Our visible imgui space lies from draw_data->DisplayPps (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayMin is typically (0,0) for single viewport apps. { float scale[2]; - scale[0] = 2.0f / io.DisplaySize.x; - scale[1] = 2.0f / io.DisplaySize.y; + scale[0] = 2.0f / draw_data->DisplaySize.x; + scale[1] = 2.0f / draw_data->DisplaySize.y; float translate[2]; - translate[0] = -1.0f - io.DisplayPos.x * scale[0]; - translate[1] = -1.0f - io.DisplayPos.y * scale[1]; + translate[0] = -1.0f - draw_data->DisplayPos.x * scale[0]; + translate[1] = -1.0f - draw_data->DisplayPos.y * scale[1]; vkCmdPushConstants(command_buffer, g_PipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, sizeof(float) * 0, sizeof(float) * 2, scale); vkCmdPushConstants(command_buffer, g_PipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, sizeof(float) * 2, sizeof(float) * 2, translate); } @@ -286,6 +286,7 @@ void ImGui_ImplVulkan_RenderDrawData(VkCommandBuffer command_buffer, ImDrawData* // Render the command lists: int vtx_offset = 0; int idx_offset = 0; + ImVec2 display_pos = draw_data->DisplayPos; for (int n = 0; n < draw_data->CmdListsCount; n++) { const ImDrawList* cmd_list = draw_data->CmdLists[n]; @@ -301,8 +302,8 @@ void ImGui_ImplVulkan_RenderDrawData(VkCommandBuffer command_buffer, ImDrawData* // Apply scissor/clipping rectangle // FIXME: We could clamp width/height based on clamped min/max values. VkRect2D scissor; - scissor.offset.x = (int32_t)(pcmd->ClipRect.x - io.DisplayPos.x) > 0 ? (int32_t)(pcmd->ClipRect.x - io.DisplayPos.x) : 0; - scissor.offset.y = (int32_t)(pcmd->ClipRect.y - io.DisplayPos.y) > 0 ? (int32_t)(pcmd->ClipRect.y - io.DisplayPos.y) : 0; + scissor.offset.x = (int32_t)(pcmd->ClipRect.x - display_pos.x) > 0 ? (int32_t)(pcmd->ClipRect.x - display_pos.x) : 0; + scissor.offset.y = (int32_t)(pcmd->ClipRect.y - display_pos.y) > 0 ? (int32_t)(pcmd->ClipRect.y - display_pos.y) : 0; scissor.extent.width = (uint32_t)(pcmd->ClipRect.z - pcmd->ClipRect.x); scissor.extent.height = (uint32_t)(pcmd->ClipRect.w - pcmd->ClipRect.y + 1); // FIXME: Why +1 here? vkCmdSetScissor(command_buffer, 0, 1, &scissor); diff --git a/imgui.cpp b/imgui.cpp index 911457a26907..59196740506d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -254,7 +254,7 @@ - 2018/03/12 (1.60) - Removed ImGuiCol_CloseButton, ImGuiCol_CloseButtonActive, ImGuiCol_CloseButtonHovered as the closing cross uses regular button colors now. - 2018/03/08 (1.60) - Changed ImFont::DisplayOffset.y to default to 0 instead of +1. Fixed rounding of Ascent/Descent to match TrueType renderer. If you were adding or subtracting to ImFont::DisplayOffset check if your fonts are correctly aligned vertically. - 2018/03/03 (1.60) - Renamed ImGuiStyleVar_Count_ to ImGuiStyleVar_COUNT and ImGuiMouseCursor_Count_ to ImGuiMouseCursor_COUNT for consistency with other public enums. - - 2018/02/27 (1.XX) - removed io.DisplayVisibleMin, io.DisplayVisibleMax settings (it was used to clip within the DisplayPos..DisplayPos+Size range, I don't know of anyone using it) + - 2018/02/27 (1.XX) - removed io.DisplayVisibleMin, io.DisplayVisibleMax settings (it was used to clip within the DisplayMin..DisplayMax range, I don't know of anyone using it) - 2018/02/18 (1.60) - BeginDragDropSource(): temporarily removed the optional mouse_button=0 parameter because it is not really usable in many situations at the moment. - 2018/02/16 (1.60) - obsoleted the io.RenderDrawListsFn callback, you can call your graphics engine render function after ImGui::Render(). Use ImGui::GetDrawData() to retrieve the ImDrawData* to display. - 2018/02/07 (1.60) - reorganized context handling to be more explicit, @@ -745,7 +745,6 @@ static void UpdateViewports(); static void UpdateWindowViewport(ImGuiWindow* window, bool window_pos_set_by_api); static void SetCurrentViewport(ImGuiViewport* viewport); static void SetWindowViewportTranslateToPreservePlatformPos(ImGuiWindow* window, ImGuiViewport* old_viewport, ImGuiViewport* new_viewport); -static void ResizeViewport(ImGuiViewport* viewport, const ImVec2& size); static void ResizeViewportTranslateWindows(int viewport_idx_min, int viewport_idx_max, float pos_x_delta, int idx_delta, ImGuiViewport* viewport_to_erase); } @@ -3517,8 +3516,6 @@ static void UpdatePlatformWindows() void ImGui::RenderAdditionalViewports() { ImGuiContext& g = *GImGui; - ImVec2 backup_display_pos = g.IO.DisplayPos; - ImVec2 backup_display_size = g.IO.DisplaySize; if (!(g.IO.ConfigFlags & ImGuiConfigFlags_EnableViewports)) return; @@ -3527,15 +3524,11 @@ void ImGui::RenderAdditionalViewports() ImGuiViewport* viewport = g.Viewports[i]; if ((viewport->Flags & ImGuiViewportFlags_MainViewport) || (viewport->LastFrameActive < g.FrameCount)) continue; - g.IO.DisplayPos = viewport->Pos; // For legacy reason our render functions are viewport agnostic so we pass our coordinates via the IO structure - g.IO.DisplaySize = viewport->Size; if (g.IO.PlatformInterface.RenderViewport) g.IO.PlatformInterface.RenderViewport(viewport); if (g.IO.RendererInterface.RenderViewport) g.IO.RendererInterface.RenderViewport(viewport); } - g.IO.DisplayPos = backup_display_pos; - g.IO.DisplaySize = backup_display_size; // Swap for (int i = 0; i < g.Viewports.Size; i++) @@ -4273,16 +4266,18 @@ void ImDrawDataBuilder::FlattenIntoSingleLayer() } } -static void SetupDrawData(ImVector* draw_lists, ImDrawData* out_draw_data) +static void SetupViewportDrawData(ImGuiViewport* viewport, ImVector* draw_lists) { - out_draw_data->Valid = true; - out_draw_data->CmdLists = (draw_lists->Size > 0) ? draw_lists->Data : NULL; - out_draw_data->CmdListsCount = draw_lists->Size; - out_draw_data->TotalVtxCount = out_draw_data->TotalIdxCount = 0; + viewport->DrawData.Valid = true; + viewport->DrawData.CmdLists = (draw_lists->Size > 0) ? draw_lists->Data : NULL; + viewport->DrawData.CmdListsCount = draw_lists->Size; + viewport->DrawData.TotalVtxCount = viewport->DrawData.TotalIdxCount = 0; + viewport->DrawData.DisplayPos = viewport->Pos; + viewport->DrawData.DisplaySize = viewport->Size; for (int n = 0; n < draw_lists->Size; n++) { - out_draw_data->TotalVtxCount += draw_lists->Data[n]->VtxBuffer.Size; - out_draw_data->TotalIdxCount += draw_lists->Data[n]->IdxBuffer.Size; + viewport->DrawData.TotalVtxCount += draw_lists->Data[n]->VtxBuffer.Size; + viewport->DrawData.TotalIdxCount += draw_lists->Data[n]->IdxBuffer.Size; } } @@ -4442,7 +4437,7 @@ void ImGui::Render() ImGuiViewport* viewport = g.Viewports[n]; viewport->DrawDataBuilder.FlattenIntoSingleLayer(); AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[0], &g.OverlayDrawList); - SetupDrawData(&viewport->DrawDataBuilder.Layers[0], &viewport->DrawData); + SetupViewportDrawData(viewport, &viewport->DrawDataBuilder.Layers[0]); g.IO.MetricsRenderVertices += viewport->DrawData.TotalVtxCount; g.IO.MetricsRenderIndices += viewport->DrawData.TotalIdxCount; } @@ -4509,19 +4504,6 @@ static void ImGui::ResizeViewportTranslateWindows(int viewport_idx_min, int view } } -static void ImGui::ResizeViewport(ImGuiViewport* viewport, const ImVec2& size) -{ - // We defer translating windows to the beginning of the frame. - // Our viewport system already works with fully overlapped viewports, it's only certain user interactions that don't and they can't be performed while resizing. - ImGuiContext& g = *GImGui; - viewport->Size = size; - if (viewport == g.Viewports[0]) - { - g.IO.DisplayPos = viewport->Pos; - g.IO.DisplaySize = viewport->Size; - } -} - void ImGui::SetCurrentViewport(ImGuiViewport* viewport) { // Notify platform interface of viewport changes @@ -4544,7 +4526,9 @@ ImGuiViewport* ImGui::Viewport(ImGuiWindow* window, ImGuiID id, ImGuiViewportFla ImGuiViewport* viewport = FindViewportByID(id); if (viewport) { - ResizeViewport(viewport, size); + // We defer translating windows to the beginning of the frame. + // Our viewport system already works with fully overlapped viewports, it's only certain user interactions that don't and they can't be performed while resizing. + viewport->Size = size; } else { @@ -6537,7 +6521,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if (flags & ImGuiWindowFlags_FullViewport) if (window->Size.x != window->Viewport->Size.x || window->Size.y != window->Viewport->Size.y) { - ResizeViewport(window->Viewport, window->SizeFull); + window->Viewport->Size = window->SizeFull; viewport_rect = GetViewportRect(window); } @@ -11706,7 +11690,7 @@ bool ImGui::MenuItem(const char* label, const char* shortcut, bool* p_selected, bool ImGui::BeginMainMenuBar() { ImGuiContext& g = *GImGui; - SetNextWindowPos(g.IO.DisplayPos); + SetNextWindowPos(ImVec2(0.0f, 0.0f)); SetNextWindowSize(ImVec2(g.IO.DisplaySize.x, g.FontBaseSize + g.Style.FramePadding.y * 2.0f)); PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0,0)); diff --git a/imgui.h b/imgui.h index cdb901079be5..b670c9fa1a82 100644 --- a/imgui.h +++ b/imgui.h @@ -1104,7 +1104,6 @@ struct ImGuiIO // Output - Retrieve after calling NewFrame() //------------------------------------------------------------------ - ImVec2 DisplayPos; // Generally ImVec2(0,0). When using multiple viewports this can be repositioned by imgui. The display area goes from DisplayPos to DisplayPos+DisplaySize. bool WantCaptureMouse; // When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application. This is set by ImGui when it wants to use your mouse (e.g. unclicked mouse is hovering a window, or a widget is active). bool WantCaptureKeyboard; // When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. This is set by ImGui when it wants to use your keyboard inputs. bool WantTextInput; // Mobile/console: when io.WantTextInput is true, you may display an on-screen keyboard. This is set by ImGui when it wants textual keyboard input to happen (e.g. when a InputText widget is active). @@ -1161,7 +1160,7 @@ namespace ImGui bool Begin(const char* name, bool* p_open, const ImVec2& size_on_first_use, float bg_alpha_override = -1.0f, ImGuiWindowFlags flags = 0); // Use SetNextWindowSize(size, ImGuiCond_FirstUseEver) + SetNextWindowBgAlpha() instead. static inline bool IsRootWindowOrAnyChildHovered() { return IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows); } static inline void AlignFirstTextHeightToWidgets() { AlignTextToFramePadding(); } - static inline void SetNextWindowPosCenter(ImGuiCond c=0) { ImGuiIO& io = GetIO(); SetNextWindowPos(ImVec2(io.DisplayPos.x + io.DisplaySize.x * 0.5f, io.DisplayPos.y + io.DisplaySize.y * 0.5f), c, ImVec2(0.5f, 0.5f)); } // FIXME-VIEWPORT: Select viewport based on mouse position + static inline void SetNextWindowPosCenter(ImGuiCond c=0) { ImGuiIO& io = GetIO(); SetNextWindowPos(ImVec2(io.DisplaySize.x * 0.5f, io.DisplaySize.y * 0.5f), c, ImVec2(0.5f, 0.5f)); } // FIXME-VIEWPORT: Select viewport based on mouse position // OBSOLETED in 1.51 (between Jun 2017 and Aug 2017) static inline bool IsItemHoveredRect() { return IsItemHovered(ImGuiHoveredFlags_RectOnly); } static inline bool IsPosHoveringAnyWindow(const ImVec2&) { IM_ASSERT(0); return false; } // This was misleading and partly broken. You probably want to use the ImGui::GetIO().WantCaptureMouse flag instead. @@ -1660,11 +1659,13 @@ struct ImDrawData int CmdListsCount; int TotalVtxCount; // For convenience, sum of all cmd_lists vtx_buffer.Size int TotalIdxCount; // For convenience, sum of all cmd_lists idx_buffer.Size + ImVec2 DisplayPos; // Virtual upper-left position of the viewport to render (== ImVec2(0,0) for the main viewport == upper-left of the orthogonal projection matrix to use) + ImVec2 DisplaySize; // Size of the viewport to render (== io.DisplaySize for the main viewport) (DisplayPos + DisplaySize == lower-right of the orthogonal projection matrix to use) // Functions ImDrawData() { Clear(); } - void Clear() { Valid = false; CmdLists = NULL; CmdListsCount = TotalVtxCount = TotalIdxCount = 0; } // Draw lists are owned by the ImGuiContext and only pointed to here. - IMGUI_API void DeIndexAllBuffers(); // For backward compatibility or convenience: convert all buffers from indexed to de-indexed, in case you cannot render indexed. Note: this is slow and most likely a waste of resources. Always prefer indexed rendering! + void Clear() { Valid = false; CmdLists = NULL; CmdListsCount = TotalVtxCount = TotalIdxCount = 0; DisplayPos = DisplaySize = ImVec2(0.f, 0.f); } // Draw lists are owned by the ImGuiContext and only pointed to here. + IMGUI_API void DeIndexAllBuffers(); // Convenience: convert all buffers from indexed to non-indexed, in case you cannot render indexed. Note: this is slow and most likely a waste of resources. Always prefer indexed rendering! IMGUI_API void ScaleClipRects(const ImVec2& sc); // Helper to scale the ClipRect field of each ImDrawCmd. Use if your final output buffer is at a different scale than ImGui expects, or if there is a difference between your window resolution and framebuffer resolution. }; diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 0bd7a5d5ed8a..a12ee666778e 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2433,7 +2433,7 @@ static void ShowExampleAppFixedOverlay(bool* p_open) const float DISTANCE = 10.0f; static int corner = 0; ImGuiIO& io = ImGui::GetIO(); - ImVec2 window_pos = ImVec2(io.DisplayPos.x + ((corner & 1) ? io.DisplaySize.x - DISTANCE : DISTANCE), io.DisplayPos.y + ((corner & 2) ? io.DisplaySize.y - DISTANCE : DISTANCE)); + ImVec2 window_pos = ImVec2((corner & 1) ? io.DisplaySize.x - DISTANCE : DISTANCE, (corner & 2) ? io.DisplaySize.y - DISTANCE : DISTANCE); ImVec2 window_pos_pivot = ImVec2((corner & 1) ? 1.0f : 0.0f, (corner & 2) ? 1.0f : 0.0f); ImGui::SetNextWindowPos(window_pos, ImGuiCond_Always, window_pos_pivot); ImGui::SetNextWindowBgAlpha(0.3f); // Transparent background From 921bb92eece950788cea8d01a84542dd1f7e898c Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 13 Mar 2018 12:16:44 +0100 Subject: [PATCH 081/828] Viewport: Fixed software mouse cursor from appearing in all multiple lists simultaneously. (#1542) --- imgui.cpp | 2 ++ imgui.h | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 59196740506d..f3f948ed4439 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4422,12 +4422,14 @@ void ImGui::Render() const ImVec2 pos = g.IO.MousePos - offset; const ImTextureID tex_id = g.IO.Fonts->TexID; const float sc = g.Style.MouseCursorScale; + g.OverlayDrawList.PushClipRect(g.MouseViewport->Pos, g.MouseViewport->Pos + g.MouseViewport->Size); g.OverlayDrawList.PushTextureID(tex_id); g.OverlayDrawList.AddImage(tex_id, pos + ImVec2(1,0)*sc, pos+ImVec2(1,0)*sc + size*sc, uv[2], uv[3], IM_COL32(0,0,0,48)); // Shadow g.OverlayDrawList.AddImage(tex_id, pos + ImVec2(2,0)*sc, pos+ImVec2(2,0)*sc + size*sc, uv[2], uv[3], IM_COL32(0,0,0,48)); // Shadow g.OverlayDrawList.AddImage(tex_id, pos, pos + size*sc, uv[2], uv[3], IM_COL32(0,0,0,255)); // Black border g.OverlayDrawList.AddImage(tex_id, pos, pos + size*sc, uv[0], uv[1], IM_COL32(255,255,255,255)); // White fill g.OverlayDrawList.PopTextureID(); + g.OverlayDrawList.PopClipRect(); } // Setup ImDrawData structures for end-user diff --git a/imgui.h b/imgui.h index b670c9fa1a82..f1fc7ea4f807 100644 --- a/imgui.h +++ b/imgui.h @@ -492,7 +492,7 @@ namespace ImGui IMGUI_API bool IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max); // test if rectangle (in screen space) is visible / not clipped. to perform coarse clipping on user's side. IMGUI_API float GetTime(); IMGUI_API int GetFrameCount(); - IMGUI_API ImDrawList* GetOverlayDrawList(); // this draw list will be the last rendered one, useful to quickly draw overlays shapes/text + IMGUI_API ImDrawList* GetOverlayDrawList(); // this draw list will be the last rendered one, it covers all viewports. useful to quickly draw overlays shapes/text IMGUI_API ImDrawListSharedData* GetDrawListSharedData(); IMGUI_API const char* GetStyleColorName(ImGuiCol idx); IMGUI_API ImVec2 CalcTextSize(const char* text, const char* text_end = NULL, bool hide_text_after_double_hash = false, float wrap_width = -1.0f); From 98b66a5fc9cd7e01cfca3b3ec44f77944bfe40c3 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 13 Mar 2018 21:45:09 +0100 Subject: [PATCH 082/828] Examples: Using draw_data->DisplaySize, followup to c50198debe4978853f3f8ed6e84a7b2685991c06. Fix Vulkan secondary viewport rendering. SDL+Vulkan: Matched changes. Fix vcprojs. (#1542, #1042) --- examples/imgui_impl_dx9.cpp | 12 +-- examples/imgui_impl_opengl2.cpp | 4 +- examples/imgui_impl_vulkan.cpp | 4 +- examples/sdl_opengl2_example/main.cpp | 2 +- examples/sdl_vulkan_example/main.cpp | 74 +++++++------------ .../sdl_vulkan_example.vcxproj | 2 +- .../vulkan_example/vulkan_example.vcxproj | 2 +- 7 files changed, 39 insertions(+), 61 deletions(-) diff --git a/examples/imgui_impl_dx9.cpp b/examples/imgui_impl_dx9.cpp index 8be032d51cdb..931e5148a92e 100644 --- a/examples/imgui_impl_dx9.cpp +++ b/examples/imgui_impl_dx9.cpp @@ -42,8 +42,7 @@ struct CUSTOMVERTEX void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data) { // Avoid rendering when minimized - ImGuiIO& io = ImGui::GetIO(); - if (io.DisplaySize.x <= 0.0f || io.DisplaySize.y <= 0.0f) + if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f) return; // Create and grow buffers if needed @@ -101,8 +100,8 @@ void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data) // Setup viewport D3DVIEWPORT9 vp; vp.X = vp.Y = 0; - vp.Width = (DWORD)io.DisplaySize.x; - vp.Height = (DWORD)io.DisplaySize.y; + vp.Width = (DWORD)draw_data->DisplaySize.x; + vp.Height = (DWORD)draw_data->DisplaySize.y; vp.MinZ = 0.0f; vp.MaxZ = 1.0f; g_pd3dDevice->SetViewport(&vp); @@ -131,7 +130,10 @@ void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data) // Setup orthographic projection matrix // Being agnostic of whether or can be used, we aren't relying on D3DXMatrixIdentity()/D3DXMatrixOrthoOffCenterLH() or DirectX::XMMatrixIdentity()/DirectX::XMMatrixOrthographicOffCenterLH() { - const float L = 0.5f, R = io.DisplaySize.x+0.5f, T = 0.5f, B = io.DisplaySize.y+0.5f; + float L = draw_data->DisplayPos.x + 0.5f; + float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x + 0.5f; + float T = draw_data->DisplayPos.y + 0.5f; + float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y + 0.5f; D3DMATRIX mat_identity = { { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f } }; D3DMATRIX mat_projection = { diff --git a/examples/imgui_impl_opengl2.cpp b/examples/imgui_impl_opengl2.cpp index ae1c234d1ac7..cf763efd4c3b 100644 --- a/examples/imgui_impl_opengl2.cpp +++ b/examples/imgui_impl_opengl2.cpp @@ -60,8 +60,8 @@ void ImGui_ImplOpenGL2_RenderDrawData(ImDrawData* draw_data) { // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) ImGuiIO& io = ImGui::GetIO(); - int fb_width = (int)(io.DisplaySize.x * io.DisplayFramebufferScale.x); - int fb_height = (int)(io.DisplaySize.y * io.DisplayFramebufferScale.y); + int fb_width = (int)(draw_data->DisplaySize.x * io.DisplayFramebufferScale.x); + int fb_height = (int)(draw_data->DisplaySize.y * io.DisplayFramebufferScale.y); if (fb_width == 0 || fb_height == 0) return; draw_data->ScaleClipRects(io.DisplayFramebufferScale); diff --git a/examples/imgui_impl_vulkan.cpp b/examples/imgui_impl_vulkan.cpp index 28f07413355e..11d8e8de679f 100644 --- a/examples/imgui_impl_vulkan.cpp +++ b/examples/imgui_impl_vulkan.cpp @@ -263,8 +263,8 @@ void ImGui_ImplVulkan_RenderDrawData(VkCommandBuffer command_buffer, ImDrawData* VkViewport viewport; viewport.x = 0; viewport.y = 0; - viewport.width = io.DisplaySize.x; - viewport.height = io.DisplaySize.y; + viewport.width = draw_data->DisplaySize.x; + viewport.height = draw_data->DisplaySize.y; viewport.minDepth = 0.0f; viewport.maxDepth = 1.0f; vkCmdSetViewport(command_buffer, 0, 1, &viewport); diff --git a/examples/sdl_opengl2_example/main.cpp b/examples/sdl_opengl2_example/main.cpp index 05b358c853bc..1b5c503489b0 100644 --- a/examples/sdl_opengl2_example/main.cpp +++ b/examples/sdl_opengl2_example/main.cpp @@ -121,7 +121,7 @@ int main(int, char**) } // Rendering - glViewport(0, 0, (int)ImGui::GetIO().DisplaySize.x, (int)ImGui::GetIO().DisplaySize.y); + glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y); glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w); glClear(GL_COLOR_BUFFER_BIT); //glUseProgram(0); // You may want this if using this code in an OpenGL 3+ context where shaders may be bound diff --git a/examples/sdl_vulkan_example/main.cpp b/examples/sdl_vulkan_example/main.cpp index 5c4e600889f3..08db99ce12c2 100644 --- a/examples/sdl_vulkan_example/main.cpp +++ b/examples/sdl_vulkan_example/main.cpp @@ -9,7 +9,6 @@ #include #include -// FIXME-VULKAN: Resizing with IMGUI_UNLIMITED_FRAME_RATE triggers errors from the validation layer. #define IMGUI_UNLIMITED_FRAME_RATE #ifdef _DEBUG #define IMGUI_VULKAN_DEBUG_REPORT @@ -220,19 +219,20 @@ static void CleanupVulkan() vkDestroyInstance(g_Instance, g_Allocator); } -static void FrameBegin(ImGui_ImplVulkan_WindowData* wd) +static void FrameRender(ImGui_ImplVulkan_WindowData* wd) { + VkResult err; + + VkSemaphore& image_acquired_semaphore = wd->Frames[wd->FrameIndex].ImageAcquiredSemaphore; + err = vkAcquireNextImageKHR(g_Device, wd->Swapchain, UINT64_MAX, image_acquired_semaphore, VK_NULL_HANDLE, &wd->FrameIndex); + check_vk_result(err); + ImGui_ImplVulkan_FrameData* fd = &wd->Frames[wd->FrameIndex]; - VkResult err; - for (;;) { - err = vkWaitForFences(g_Device, 1, &fd->Fence, VK_TRUE, 100); - if (err == VK_SUCCESS) break; - if (err == VK_TIMEOUT) continue; + err = vkWaitForFences(g_Device, 1, &fd->Fence, VK_TRUE, UINT64_MAX); // wait indefinitely instead of periodically checking check_vk_result(err); - } - { - err = vkAcquireNextImageKHR(g_Device, wd->Swapchain, UINT64_MAX, fd->PresentCompleteSemaphore, VK_NULL_HANDLE, &fd->BackbufferIndex); + + err = vkResetFences(g_Device, 1, &fd->Fence); check_vk_result(err); } { @@ -248,26 +248,25 @@ static void FrameBegin(ImGui_ImplVulkan_WindowData* wd) VkRenderPassBeginInfo info = {}; info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; info.renderPass = wd->RenderPass; - info.framebuffer = wd->Framebuffer[fd->BackbufferIndex]; + info.framebuffer = wd->Framebuffer[wd->FrameIndex]; info.renderArea.extent.width = wd->Width; info.renderArea.extent.height = wd->Height; info.clearValueCount = 1; info.pClearValues = &wd->ClearValue; vkCmdBeginRenderPass(fd->CommandBuffer, &info, VK_SUBPASS_CONTENTS_INLINE); } -} -static void FrameEnd(ImGui_ImplVulkan_WindowData* wd) -{ - ImGui_ImplVulkan_FrameData* fd = &wd->Frames[wd->FrameIndex]; - VkResult err; + // Record Imgui Draw Data and draw funcs into command buffer + ImGui_ImplVulkan_RenderDrawData(fd->CommandBuffer, ImGui::GetDrawData()); + + // Submit command buffer vkCmdEndRenderPass(fd->CommandBuffer); { VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; VkSubmitInfo info = {}; info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; info.waitSemaphoreCount = 1; - info.pWaitSemaphores = &fd->PresentCompleteSemaphore; + info.pWaitSemaphores = &image_acquired_semaphore; info.pWaitDstStageMask = &wait_stage; info.commandBufferCount = 1; info.pCommandBuffers = &fd->CommandBuffer; @@ -276,8 +275,6 @@ static void FrameEnd(ImGui_ImplVulkan_WindowData* wd) err = vkEndCommandBuffer(fd->CommandBuffer); check_vk_result(err); - err = vkResetFences(g_Device, 1, &fd->Fence); - check_vk_result(err); err = vkQueueSubmit(g_Queue, 1, &info, fd->Fence); check_vk_result(err); } @@ -285,23 +282,15 @@ static void FrameEnd(ImGui_ImplVulkan_WindowData* wd) static void FramePresent(ImGui_ImplVulkan_WindowData* wd) { - VkResult err; - // If IMGUI_UNLIMITED_FRAME_RATE is defined we present the latest but one frame. Otherwise we present the latest rendered frame -#ifdef IMGUI_UNLIMITED_FRAME_RATE - uint32_t PresentIndex = (wd->FrameIndex + IMGUI_VK_QUEUED_FRAMES - 1) % IMGUI_VK_QUEUED_FRAMES; -#else - uint32_t PresentIndex = wd->FrameIndex; -#endif // IMGUI_UNLIMITED_FRAME_RATE - - ImGui_ImplVulkan_FrameData* fd = &wd->Frames[PresentIndex]; + ImGui_ImplVulkan_FrameData* fd = &wd->Frames[wd->FrameIndex]; VkPresentInfoKHR info = {}; info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; info.waitSemaphoreCount = 1; info.pWaitSemaphores = &fd->RenderCompleteSemaphore; info.swapchainCount = 1; info.pSwapchains = &wd->Swapchain; - info.pImageIndices = &fd->BackbufferIndex; - err = vkQueuePresentKHR(g_Queue, &info); + info.pImageIndices = &wd->FrameIndex; + VkResult err = vkQueuePresentKHR(g_Queue, &info); check_vk_result(err); } @@ -329,6 +318,7 @@ int main(int, char**) // Create Window Surface VkSurfaceKHR surface; + VkResult err; if (SDL_Vulkan_CreateSurface(window, g_Instance, &surface) == 0) { printf("Failed to create Vulkan surface.\n"); @@ -348,6 +338,9 @@ int main(int, char**) io.ConfigFlags |= ImGuiConfigFlags_PlatformNoTaskBar; //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + // Setup SDL binding + ImGui_ImplSDL2_Init(window, NULL); + // Setup Vulkan binding ImGui_ImplVulkan_InitInfo init_info = {}; init_info.Instance = g_Instance; @@ -359,7 +352,6 @@ int main(int, char**) init_info.DescriptorPool = g_DescriptorPool; init_info.Allocator = g_Allocator; init_info.CheckVkResultFn = check_vk_result; - ImGui_ImplSDL2_Init(window, NULL); ImGui_ImplVulkan_Init(&init_info, wd->RenderPass); // Setup style @@ -387,7 +379,6 @@ int main(int, char**) VkCommandPool command_pool = wd->Frames[wd->FrameIndex].CommandPool; VkCommandBuffer command_buffer = wd->Frames[wd->FrameIndex].CommandBuffer; - VkResult err; err = vkResetCommandPool(g_Device, command_pool, 0); check_vk_result(err); VkCommandBufferBeginInfo begin_info = {}; @@ -416,8 +407,6 @@ int main(int, char**) bool show_another_window = false; ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); - bool swap_chain_has_at_least_one_image = false; - // Main loop bool done = false; while (!done) @@ -478,26 +467,13 @@ int main(int, char**) // Rendering ImGui::Render(); memcpy(&wd->ClearValue.color.float32[0], &clear_color, 4 * sizeof(float)); - FrameBegin(wd); - ImGui_ImplVulkan_RenderDrawData(wd->Frames[wd->FrameIndex].CommandBuffer, ImGui::GetDrawData()); - FrameEnd(wd); - + FrameRender(wd); ImGui::RenderAdditionalViewports(); - -#ifdef IMGUI_UNLIMITED_FRAME_RATE - // When IMGUI_UNLIMITED_FRAME_RATE is defined we render into latest image acquired from the swapchain but we display the image which was rendered before. - // Hence we must render once and increase the FrameIndex without presenting. - if (swap_chain_has_at_least_one_image) - FramePresent(wd); -#else FramePresent(wd); -#endif - swap_chain_has_at_least_one_image = true; - wd->FrameIndex = (wd->FrameIndex + 1) % IMGUI_VK_QUEUED_FRAMES; } // Cleanup - VkResult err = vkDeviceWaitIdle(g_Device); + err = vkDeviceWaitIdle(g_Device); check_vk_result(err); ImGui_ImplVulkan_Shutdown(); ImGui_ImplSDL2_Shutdown(); diff --git a/examples/sdl_vulkan_example/sdl_vulkan_example.vcxproj b/examples/sdl_vulkan_example/sdl_vulkan_example.vcxproj index f848f1ffc133..3f7733a80f58 100644 --- a/examples/sdl_vulkan_example/sdl_vulkan_example.vcxproj +++ b/examples/sdl_vulkan_example/sdl_vulkan_example.vcxproj @@ -20,7 +20,7 @@ {BAE3D0B5-9695-4EB1-AD0F-75890EB4A3B3} - opengl3_example + sdl_vulkan_example diff --git a/examples/vulkan_example/vulkan_example.vcxproj b/examples/vulkan_example/vulkan_example.vcxproj index 299b26f4f3c5..0523bb18d8bf 100644 --- a/examples/vulkan_example/vulkan_example.vcxproj +++ b/examples/vulkan_example/vulkan_example.vcxproj @@ -20,7 +20,7 @@ {57E2DF5A-6FC8-45BB-99DD-91A18C646E80} - opengl3_example + vulkan_example From d0e0b106f09aa48689ed76855b8b3c15c2aeb764 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 13 Mar 2018 23:16:15 +0100 Subject: [PATCH 083/828] Examples: SDL,GLFW,Vulkan: The Platform<>Renderer link is handled by SDL/GLFW platforms, both can compile without Vulkan headers, SDL+Vulkan is now on part with GLFW+Vulkan (aka broken the same way!). (#1542) --- examples/imgui_impl_glfw.cpp | 37 ++++++++++++++++++++++++++--- examples/imgui_impl_sdl2.cpp | 40 +++++++++++++++++++++++++------- examples/imgui_impl_vulkan.cpp | 3 +-- examples/vulkan_example/main.cpp | 12 ---------- imgui.h | 2 +- 5 files changed, 67 insertions(+), 27 deletions(-) diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index b9cf06e0df38..796b0904ffa4 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -34,6 +34,12 @@ #define GLFW_EXPOSE_NATIVE_WIN32 #include // for glfwGetWin32Window #endif +#ifdef GLFW_HOVERED +#define GLFW_HAS_GLFW_HOVERED 1 +#else +#define GLFW_HAS_GLFW_HOVERED 0 +#endif +#define GLFW_HAS_VULKAN (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ // Data enum GlfwClientApi @@ -437,6 +443,31 @@ static void ImGui_ImplGlfw_SwapBuffers(ImGuiViewport* viewport) glfwSwapBuffers(data->Window); } +// Vulkan support (the Vulkan renderer needs to call a platform-side support function to create the surface) +// Avoid including so we can build without it +#if GLFW_HAS_VULKAN +#ifndef VULKAN_H_ +#define VK_DEFINE_HANDLE(object) typedef struct object##_T* object; +#if defined(__LP64__) || defined(_WIN64) || defined(__x86_64__) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__) +#define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef struct object##_T *object; +#else +#define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef uint64_t object; +#endif +VK_DEFINE_HANDLE(VkInstance) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSurfaceKHR) +struct VkAllocationCallbacks; +enum VkResult { VK_RESULT_MAX_ENUM = 0x7FFFFFFF }; +#endif // VULKAN_H_ +extern "C" { extern GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance, GLFWwindow* window, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface); } +static int ImGui_ImplGlfw_CreateVkSurface(ImGuiViewport* viewport, ImU64 vk_instance, const void* vk_allocator, ImU64* out_vk_surface) +{ + ImGuiPlatformDataGlfw* data = (ImGuiPlatformDataGlfw*)viewport->PlatformUserData; + IM_ASSERT(g_ClientApi == GlfwClientApi_Vulkan); + VkResult err = glfwCreateWindowSurface((VkInstance)vk_instance, data->Window, (const VkAllocationCallbacks*)vk_allocator, (VkSurfaceKHR*)out_vk_surface); + return (int)err; +} +#endif // GLFW_HAS_VULKAN + static void ImGui_ImplGlfw_InitPlatformInterface() { // Register platform interface (will be coupled with a renderer interface) @@ -451,9 +482,9 @@ static void ImGui_ImplGlfw_InitPlatformInterface() io.PlatformInterface.SetWindowTitle = ImGui_ImplGlfw_SetWindowTitle; io.PlatformInterface.RenderViewport = ImGui_ImplGlfw_RenderViewport; io.PlatformInterface.SwapBuffers = ImGui_ImplGlfw_SwapBuffers; - - // We let the user set up the link to glfwCreateWindowSurface() here, so this binding can work with old GLFW and without Vulkan headers - io.PlatformInterface.CreateVkSurface = NULL; +#if GLFW_HAS_VULKAN + io.PlatformInterface.CreateVkSurface = ImGui_ImplGlfw_CreateVkSurface; +#endif // Register main window handle ImGuiViewport* main_viewport = ImGui::GetMainViewport(); diff --git a/examples/imgui_impl_sdl2.cpp b/examples/imgui_impl_sdl2.cpp index 28a06d89aecb..22b60aff4ffc 100644 --- a/examples/imgui_impl_sdl2.cpp +++ b/examples/imgui_impl_sdl2.cpp @@ -32,6 +32,9 @@ // SDL #include #include +#define SDL_HAS_CAPTURE_MOUSE SDL_VERSION_ATLEAST(2,0,4) +#define SDL_HAS_WINDOW_OPACITY SDL_VERSION_ATLEAST(2,0,5) +#define SDL_HAS_VULKAN SDL_VERSION_ATLEAST(2,0,6) // Data static SDL_Window* g_Window = NULL; @@ -151,7 +154,7 @@ bool ImGui_ImplSDL2_Init(SDL_Window* window, void* sdl_gl_context) // We need SDL_CaptureMouse(), SDL_GetGlobalMouseState() from SDL 2.0.4+ to support multiple viewports. // We left the call to ImGui_ImplSDL2_InitPlatformInterface() outside of #ifdef to avoid unused-function warnings. -#if SDL_VERSION_ATLEAST(2,0,4) +#if SDL_HAS_CAPTURE_MOUSE io.ConfigFlags |= ImGuiConfigFlags_PlatformHasViewports; #endif if ((io.ConfigFlags & ImGuiConfigFlags_EnableViewports) && (io.ConfigFlags & ImGuiConfigFlags_PlatformHasViewports)) @@ -185,7 +188,7 @@ static void ImGui_ImplSDL2_UpdateMouse() io.MouseDown[2] = g_MousePressed[2] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_MIDDLE)) != 0; g_MousePressed[0] = g_MousePressed[1] = g_MousePressed[2] = false; -#if SDL_VERSION_ATLEAST(2,0,4) +#if SDL_HAS_CAPTURE_MOUSE SDL_Window* focused_window = SDL_GetKeyboardFocus(); if (focused_window) { @@ -254,8 +257,6 @@ void ImGui_ImplSDL2_NewFrame(SDL_Window* window) // Platform Windows // -------------------------------------------------------------------------------------------------------- -#define SDL_HAS_WINDOW_OPACITY SDL_VERSION_ATLEAST(2,0,5) - struct ImGuiPlatformDataSDL2 { SDL_Window* Window; @@ -280,12 +281,13 @@ static void ImGui_ImplSDL2_CreateViewport(ImGuiViewport* viewport) // We don't enable SDL_WINDOW_RESIZABLE because it enforce windows decorations Uint32 sdl_flags = 0; - sdl_flags |= SDL_WINDOW_OPENGL; // FIXME-PLATFORM + sdl_flags |= main_viewport_data->GLContext ? SDL_WINDOW_OPENGL : SDL_WINDOW_VULKAN; sdl_flags |= SDL_WINDOW_HIDDEN; sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? SDL_WINDOW_BORDERLESS : 0; sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? 0 : SDL_WINDOW_RESIZABLE; data->Window = SDL_CreateWindow("No Title Yet", 0, 0, (int)viewport->Size.x, (int)viewport->Size.y, sdl_flags); - data->GLContext = SDL_GL_CreateContext(data->Window); + if (main_viewport_data->GLContext) + data->GLContext = SDL_GL_CreateContext(data->Window); viewport->PlatformHandle = (void*)data->Window; } @@ -374,15 +376,32 @@ static void ImGui_ImplSDL2_SetWindowTitle(ImGuiViewport* viewport, const char* t static void ImGui_ImplSDL2_RenderViewport(ImGuiViewport* viewport) { ImGuiPlatformDataSDL2* data = (ImGuiPlatformDataSDL2*)viewport->PlatformUserData; - SDL_GL_MakeCurrent(data->Window, data->GLContext); + if (data->GLContext) + SDL_GL_MakeCurrent(data->Window, data->GLContext); } static void ImGui_ImplSDL2_SwapBuffers(ImGuiViewport* viewport) { ImGuiPlatformDataSDL2* data = (ImGuiPlatformDataSDL2*)viewport->PlatformUserData; - SDL_GL_MakeCurrent(data->Window, data->GLContext); // FIXME-PLATFORM2 - SDL_GL_SwapWindow(data->Window); + if (data->GLContext) + { + SDL_GL_MakeCurrent(data->Window, data->GLContext); // FIXME-PLATFORM2 + SDL_GL_SwapWindow(data->Window); + } +} + +// Vulkan support (the Vulkan renderer needs to call a platform-side support function to create the surface) +// SDL is graceful enough to _not_ need so we can safely include this. +#if SDL_HAS_VULKAN +#include +static int ImGui_ImplSDL2_CreateVkSurface(ImGuiViewport* viewport, ImU64 vk_instance, const void* vk_allocator, ImU64* out_vk_surface) +{ + ImGuiPlatformDataSDL2* data = (ImGuiPlatformDataSDL2*)viewport->PlatformUserData; + (void)vk_allocator; + SDL_bool ret = SDL_Vulkan_CreateSurface(data->Window, (VkInstance)vk_instance, (VkSurfaceKHR*)out_vk_surface); + return ret ? 0 : 1; // ret ? VK_SUCCESS : VK_NOT_READY } +#endif // SDL_HAS_VULKAN static void ImGui_ImplSDL2_InitPlatformInterface(SDL_Window* window, void* sdl_gl_context) { @@ -398,6 +417,9 @@ static void ImGui_ImplSDL2_InitPlatformInterface(SDL_Window* window, void* sdl_g io.PlatformInterface.SetWindowTitle = ImGui_ImplSDL2_SetWindowTitle; io.PlatformInterface.RenderViewport = ImGui_ImplSDL2_RenderViewport; io.PlatformInterface.SwapBuffers = ImGui_ImplSDL2_SwapBuffers; +#if SDL_HAS_VULKAN + io.PlatformInterface.CreateVkSurface = ImGui_ImplSDL2_CreateVkSurface; +#endif io.ConfigFlags |= SDL_HAS_WINDOW_OPACITY ? ImGuiConfigFlags_PlatformHasWindowAlpha : 0; diff --git a/examples/imgui_impl_vulkan.cpp b/examples/imgui_impl_vulkan.cpp index 11d8e8de679f..c8283a18ef70 100644 --- a/examples/imgui_impl_vulkan.cpp +++ b/examples/imgui_impl_vulkan.cpp @@ -199,7 +199,6 @@ static void CreateOrResizeBuffer(VkBuffer& buffer, VkDeviceMemory& buffer_memory void ImGui_ImplVulkan_RenderDrawData(VkCommandBuffer command_buffer, ImDrawData* draw_data) { VkResult err; - ImGuiIO& io = ImGui::GetIO(); if (draw_data->TotalVtxCount == 0) return; @@ -1070,7 +1069,7 @@ static void ImGui_ImplVulkan_CreateViewport(ImGuiViewport* viewport) // Create surface ImGuiIO& io = ImGui::GetIO(); - VkResult err = (VkResult)io.PlatformInterface.CreateVkSurface(viewport->PlatformHandle, (ImU64)g_Instance, (const void*)g_Allocator, (ImU64*)&wd->Surface); + VkResult err = (VkResult)io.PlatformInterface.CreateVkSurface(viewport, (ImU64)g_Instance, (const void*)g_Allocator, (ImU64*)&wd->Surface); check_vk_result(err); // Check for WSI support diff --git a/examples/vulkan_example/main.cpp b/examples/vulkan_example/main.cpp index 4029c651b9e1..cfcc6605f848 100644 --- a/examples/vulkan_example/main.cpp +++ b/examples/vulkan_example/main.cpp @@ -311,17 +311,6 @@ static void glfw_resize_callback(GLFWwindow*, int w, int h) g_ResizeHeight = h; } -static int glfw_create_vk_surface(void* platform_handle, ImU64 vk_instance, const void* vk_allocator, ImU64* out_vk_surface) -{ - GLFWwindow* window = (GLFWwindow*)platform_handle; - VkInstance instance = (VkInstance)vk_instance; - const VkAllocationCallbacks* allocator = (const VkAllocationCallbacks*)vk_allocator; - VkSurfaceKHR* surface = (VkSurfaceKHR*)out_vk_surface; - VkResult err = glfwCreateWindowSurface(instance, window, allocator, surface); - check_vk_result(err); - return (int)err; -} - int main(int, char**) { // Setup window @@ -363,7 +352,6 @@ int main(int, char**) // Setup GLFW binding ImGui_ImplGlfw_InitForVulkan(window, true); - io.PlatformInterface.CreateVkSurface = glfw_create_vk_surface; // Setup Vulkan binding ImGui_ImplVulkan_InitInfo init_info = {}; diff --git a/imgui.h b/imgui.h index f1fc7ea4f807..65a66ff7c821 100644 --- a/imgui.h +++ b/imgui.h @@ -963,7 +963,7 @@ struct ImGuiPlatformInterface void (*SwapBuffers)(ImGuiViewport* viewport); // FIXME-VIEWPORT: Experimenting with back-end abstraction. This probably shouldn't stay as is. - int (*CreateVkSurface)(void* platform_handle, ImU64 vk_instance, const void* vk_allocator, ImU64* out_vk_surface); + int (*CreateVkSurface)(ImGuiViewport* viewport, ImU64 vk_instance, const void* vk_allocator, ImU64* out_vk_surface); // FIXME-DPI float (*GetWindowDpiScale)(ImGuiViewport* viewport); // (Optional) From 83ef61fa56bf843e39a99462cc5b0a2e2690c20f Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 14 Mar 2018 14:14:16 +0100 Subject: [PATCH 084/828] Examples: SDL: Fix for versions olders than 2.0.6 --- examples/imgui_impl_sdl2.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/examples/imgui_impl_sdl2.cpp b/examples/imgui_impl_sdl2.cpp index 22b60aff4ffc..233bb02138a2 100644 --- a/examples/imgui_impl_sdl2.cpp +++ b/examples/imgui_impl_sdl2.cpp @@ -35,6 +35,9 @@ #define SDL_HAS_CAPTURE_MOUSE SDL_VERSION_ATLEAST(2,0,4) #define SDL_HAS_WINDOW_OPACITY SDL_VERSION_ATLEAST(2,0,5) #define SDL_HAS_VULKAN SDL_VERSION_ATLEAST(2,0,6) +#if !SDL_HAS_VULKAN +static const Uint32 SDL_WINDOW_VULKAN = 0x10000000; +#endif // Data static SDL_Window* g_Window = NULL; From 207ad45983dda5701fe1c98fd1a81ea4b27b7af5 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 14 Mar 2018 17:50:11 +0100 Subject: [PATCH 085/828] Metrics: Displaying all draw lists of a window (if it uses layer 0 and has already been appended to earlier in the frame). --- imgui.cpp | 8 ++++---- imgui_internal.h | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index f3f948ed4439..e818f38e1950 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -13980,15 +13980,15 @@ void ImGui::ShowMetricsWindow(bool* p_open) for (int i = 0; i < g.Viewports.Size; i++) { ImGuiViewport* viewport = g.Viewports[i]; - ImDrawDataBuilder* draw_data = &viewport->DrawDataBuilder; // Because we avoid create an extra list when there's only one viewport ImGui::SetNextTreeNodeOpen(true, ImGuiCond_Once); - if (ImGui::TreeNode((void*)(intptr_t)viewport->ID, "Viewport #%d, ID: 0x%08X, DrawLists: %d, Size: (%.0f,%.0f)", i, viewport->ID, draw_data->Layers[0].Size, viewport->Size.x, viewport->Size.y)) + if (ImGui::TreeNode((void*)(intptr_t)viewport->ID, "Viewport #%d, ID: 0x%08X, DrawLists: %d, Size: (%.0f,%.0f)", i, viewport->ID, viewport->DrawDataBuilder.GetDrawListCount(), viewport->Size.x, viewport->Size.y)) { ImGui::BulletText("Pos: (%.0f,%.0f)", viewport->Pos.x, viewport->Pos.y); ImGui::BulletText("Flags: 0x%04X", viewport->Flags); ImGui::BulletText("PlatformOsDesktopPos: (%.0f,%.0f); DpiScale: %.0f%%", viewport->PlatformOsDesktopPos.x, viewport->PlatformOsDesktopPos.y, viewport->DpiScale * 100.0f); - for (int draw_list_i = 0; draw_list_i < viewport->DrawDataBuilder.Layers[0].Size; draw_list_i++) - Funcs::NodeDrawList(NULL, viewport->DrawDataBuilder.Layers[0][draw_list_i], "DrawList"); + for (int layer_i = 0; layer_i < IM_ARRAYSIZE(viewport->DrawDataBuilder.Layers); layer_i++) + for (int draw_list_i = 0; draw_list_i < viewport->DrawDataBuilder.Layers[layer_i].Size; draw_list_i++) + Funcs::NodeDrawList(NULL, viewport->DrawDataBuilder.Layers[layer_i][draw_list_i], "DrawList"); ImGui::TreePop(); } } diff --git a/imgui_internal.h b/imgui_internal.h index a327e458825c..123c42b2070e 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -502,6 +502,7 @@ struct ImDrawDataBuilder void Clear() { for (int n = 0; n < IM_ARRAYSIZE(Layers); n++) Layers[n].resize(0); } void ClearFreeMemory() { for (int n = 0; n < IM_ARRAYSIZE(Layers); n++) Layers[n].clear(); } + int GetDrawListCount() const { int count = 0; for (int n = 0; n < IM_ARRAYSIZE(Layers); n++) count += Layers[n].Size; return count; } IMGUI_API void FlattenIntoSingleLayer(); }; From 6e58a95a017a7c309685c5d94474fe639d038e68 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 15 Mar 2018 10:54:27 +0100 Subject: [PATCH 086/828] Viewport, Platform, Examples: Changes to resizing flow + restored support for Platform events affecting the ImGui windows (so Decorated windows are functional). (#1542, #1042) .. SDL: Added platform move/resize/close support. GLFW: Added platform move/resize support. Moved Close to use callback for consistency. Win32: Vulkan: Fixed resize support. Naming is WIP "PlatforrmRequestXXX" is too ambiguous. Basically we either have a ImGui->Platform flow or a Platform->ImGui flow. Working a bigger refactor now. --- examples/imgui_impl_glfw.cpp | 24 ++++++++++++++++--- examples/imgui_impl_sdl2.cpp | 22 ++++++++++++++--- examples/imgui_impl_vulkan.cpp | 5 ++-- examples/imgui_impl_win32.cpp | 23 +++++------------- imgui.cpp | 44 ++++++++++++++++++++++++++++------ imgui_internal.h | 6 +++-- 6 files changed, 90 insertions(+), 34 deletions(-) diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index 796b0904ffa4..2b470acdb020 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -310,6 +310,24 @@ struct ImGuiPlatformDataGlfw ~ImGuiPlatformDataGlfw() { IM_ASSERT(Window == NULL); } }; +static void ImGui_ImplGlfw_WindowCloseCallback(GLFWwindow* window) +{ + if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle(window)) + viewport->PlatformRequestClose = true; +} + +static void ImGui_ImplGlfw_WindowPosCallback(GLFWwindow* window, int, int) +{ + if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle(window)) + viewport->PlatformRequestMove = true; +} + +static void ImGui_ImplGlfw_WindowSizeCallback(GLFWwindow* window, int, int) +{ + if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle(window)) + viewport->PlatformRequestResize = true; +} + static void ImGui_ImplGlfw_CreateViewport(ImGuiViewport* viewport) { ImGuiPlatformDataGlfw* data = IM_NEW(ImGuiPlatformDataGlfw)(); @@ -324,6 +342,9 @@ static void ImGui_ImplGlfw_CreateViewport(ImGuiViewport* viewport) data->WindowOwned = true; viewport->PlatformHandle = (void*)data->Window; ImGui_ImplGlfw_InstallCallbacks(data->Window); + glfwSetWindowCloseCallback(data->Window, ImGui_ImplGlfw_WindowCloseCallback); + glfwSetWindowPosCallback(data->Window, ImGui_ImplGlfw_WindowPosCallback); + glfwSetWindowSizeCallback(data->Window, ImGui_ImplGlfw_WindowSizeCallback); } static void ImGui_ImplGlfw_DestroyViewport(ImGuiViewport* viewport) @@ -431,9 +452,6 @@ static void ImGui_ImplGlfw_RenderViewport(ImGuiViewport* viewport) ImGuiPlatformDataGlfw* data = (ImGuiPlatformDataGlfw*)viewport->PlatformUserData; if (g_ClientApi == GlfwClientApi_OpenGL) glfwMakeContextCurrent(data->Window); - - if (glfwWindowShouldClose(data->Window)) - viewport->PlatformRequestClose = true; } static void ImGui_ImplGlfw_SwapBuffers(ImGuiViewport* viewport) diff --git a/examples/imgui_impl_sdl2.cpp b/examples/imgui_impl_sdl2.cpp index 233bb02138a2..496f2da269ce 100644 --- a/examples/imgui_impl_sdl2.cpp +++ b/examples/imgui_impl_sdl2.cpp @@ -100,6 +100,21 @@ bool ImGui_ImplSDL2_ProcessEvent(SDL_Event* event) io.KeySuper = ((SDL_GetModState() & KMOD_GUI) != 0); return true; } + // Multi-viewport support + case SDL_WINDOWEVENT: + Uint8 window_event = event->window.event; + if (window_event == SDL_WINDOWEVENT_CLOSE || window_event == SDL_WINDOWEVENT_MOVED || window_event == SDL_WINDOWEVENT_RESIZED) + if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle((void*)SDL_GetWindowFromID(event->window.windowID))) + { + if (window_event == SDL_WINDOWEVENT_CLOSE) + viewport->PlatformRequestClose = true; + if (window_event == SDL_WINDOWEVENT_MOVED) + viewport->PlatformRequestMove = true; + if (window_event == SDL_WINDOWEVENT_RESIZED) + viewport->PlatformRequestResize = true; + return true; + } + break; } return false; } @@ -288,7 +303,8 @@ static void ImGui_ImplSDL2_CreateViewport(ImGuiViewport* viewport) sdl_flags |= SDL_WINDOW_HIDDEN; sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? SDL_WINDOW_BORDERLESS : 0; sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? 0 : SDL_WINDOW_RESIZABLE; - data->Window = SDL_CreateWindow("No Title Yet", 0, 0, (int)viewport->Size.x, (int)viewport->Size.y, sdl_flags); + data->Window = SDL_CreateWindow("No Title Yet", + (int)viewport->PlatformOsDesktopPos.x, (int)viewport->PlatformOsDesktopPos.y, (int)viewport->Size.x, (int)viewport->Size.y, sdl_flags); if (main_viewport_data->GLContext) data->GLContext = SDL_GL_CreateContext(data->Window); viewport->PlatformHandle = (void*)data->Window; @@ -328,7 +344,7 @@ static void ImGui_ImplSDL2_ShowWindow(ImGuiViewport* viewport) ex_style &= ~WS_EX_APPWINDOW; ex_style |= WS_EX_TOOLWINDOW; ::SetWindowLong(hwnd, GWL_EXSTYLE, ex_style); - } + } // SDL hack: SDL always activate/focus windows :/ if (viewport->Flags & ImGuiViewportFlags_NoFocusOnAppearing) @@ -336,7 +352,7 @@ static void ImGui_ImplSDL2_ShowWindow(ImGuiViewport* viewport) ::ShowWindow(hwnd, SW_SHOWNA); return; } -} + } #endif SDL_ShowWindow(data->Window); diff --git a/examples/imgui_impl_vulkan.cpp b/examples/imgui_impl_vulkan.cpp index c8283a18ef70..e817ea1ff8c8 100644 --- a/examples/imgui_impl_vulkan.cpp +++ b/examples/imgui_impl_vulkan.cpp @@ -1108,8 +1108,9 @@ static void ImGui_ImplVulkan_DestroyViewport(ImGuiViewport* viewport) static void ImGui_ImplVulkan_ResizeViewport(ImGuiViewport* viewport, ImVec2 size) { ImGuiPlatformDataVulkan* data = (ImGuiPlatformDataVulkan*)viewport->RendererUserData; - ImGui_ImplVulkan_WindowData* wd = &data->WindowData; - ImGui_ImplVulkanH_CreateWindowDataSwapChainAndFramebuffer(g_PhysicalDevice, g_Device, wd, g_Allocator, (int)size.x, (int)size.y); + if (data == NULL) // This is NULL for the main viewport (which is left to the user/app to handle) + return; + ImGui_ImplVulkanH_CreateWindowDataSwapChainAndFramebuffer(g_PhysicalDevice, g_Device, &data->WindowData, g_Allocator, (int)size.x, (int)size.y); } static void ImGui_ImplVulkan_RenderViewport(ImGuiViewport* viewport) diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index f8c40355dbc2..7361e9af6f55 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -356,11 +356,10 @@ float ImGui_ImplWin32_GetDpiScaleForRect(int x1, int y1, int x2, int y2) struct ImGuiPlatformDataWin32 { HWND Hwnd; - bool ExternalResize; DWORD DwStyle; DWORD DwExStyle; - ImGuiPlatformDataWin32() { Hwnd = NULL; ExternalResize = false; DwStyle = DwExStyle = 0; } + ImGuiPlatformDataWin32() { Hwnd = NULL; DwStyle = DwExStyle = 0; } ~ImGuiPlatformDataWin32() { IM_ASSERT(Hwnd == NULL); } }; @@ -386,12 +385,11 @@ static void ImGui_ImplWin32_CreateViewport(ImGuiViewport* viewport) // Create window RECT rect = { (LONG)viewport->PlatformOsDesktopPos.x, (LONG)viewport->PlatformOsDesktopPos.y, (LONG)(viewport->PlatformOsDesktopPos.x + viewport->Size.x), (LONG)(viewport->PlatformOsDesktopPos.y + viewport->Size.y) }; ::AdjustWindowRectEx(&rect, data->DwStyle, FALSE, data->DwExStyle); - data->ExternalResize = true; data->Hwnd = ::CreateWindowExA( data->DwExStyle, "ImGui Platform", "No Title Yet", data->DwStyle, // Style, class name, window name rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, // Window area g_hWnd, NULL, ::GetModuleHandle(NULL), NULL); // Parent window, Menu, Instance, Param - data->ExternalResize = false; + viewport->PlatformRequestResize = false; viewport->PlatformHandle = data->Hwnd; } @@ -417,12 +415,10 @@ static void ImGui_ImplWin32_ShowWindow(ImGuiViewport* viewport) { ImGuiPlatformDataWin32* data = (ImGuiPlatformDataWin32*)viewport->PlatformUserData; IM_ASSERT(data->Hwnd != 0); - data->ExternalResize = true; if (viewport->Flags & ImGuiViewportFlags_NoFocusOnAppearing) ::ShowWindow(data->Hwnd, SW_SHOWNA); else ::ShowWindow(data->Hwnd, SW_SHOW); - data->ExternalResize = false; } static ImVec2 ImGui_ImplWin32_GetWindowPos(ImGuiViewport* viewport) @@ -456,11 +452,9 @@ static void ImGui_ImplWin32_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) { ImGuiPlatformDataWin32* data = (ImGuiPlatformDataWin32*)viewport->PlatformUserData; IM_ASSERT(data->Hwnd != 0); - data->ExternalResize = true; RECT rect = { 0, 0, (LONG)size.x, (LONG)size.y }; ::AdjustWindowRectEx(&rect, data->DwStyle, FALSE, data->DwExStyle); // Client to Screen ::SetWindowPos(data->Hwnd, NULL, 0, 0, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE); - data->ExternalResize = false; } static void ImGui_ImplWin32_SetWindowTitle(ImGuiViewport* viewport, const char* title) @@ -489,27 +483,22 @@ static LRESULT CALLBACK ImGui_ImplWin32_WndProcHandler_PlatformWindow(HWND hWnd, if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle((void*)hWnd)) { - ImGuiIO& io = ImGui::GetIO(); - ImGuiPlatformDataWin32* data = (ImGuiPlatformDataWin32*)viewport->PlatformUserData; switch (msg) { case WM_CLOSE: viewport->PlatformRequestClose = true; return 0; case WM_MOVE: - viewport->PlatformOsDesktopPos = ImVec2((float)(short)LOWORD(lParam), (float)(short)HIWORD(lParam)); + viewport->PlatformRequestMove = true; + break; + case WM_SIZE: + viewport->PlatformRequestResize = true; break; case WM_NCHITTEST: // Let mouse pass-through the window, this is used while e.g. dragging a window, we creates a temporary overlay but want the cursor to aim behind our overlay. if (viewport->Flags & ImGuiViewportFlags_NoInputs) return HTTRANSPARENT; break; - case WM_SIZE: - if (!data->ExternalResize) - viewport->PlatformRequestResize = true; - if (io.RendererInterface.ResizeViewport) - io.RendererInterface.ResizeViewport(viewport, ImVec2((float)LOWORD(lParam), (float)HIWORD(lParam))); - break; } } diff --git a/imgui.cpp b/imgui.cpp index e818f38e1950..001a86c6fe08 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3384,6 +3384,12 @@ static void ImGui::UpdateViewports() ResizeViewportTranslateWindows(viewport->Idx + 1, g.Viewports.Size, dx, 0, NULL); } + // Apply Platform Size to ImGui Size if requested + // We do it here instead of UpdatePlatformWindows() to allow the platform back-end to set PlatformRequestResize early + // (e.g. in their own message handler before NewFrame) and not have a frame of lag with it. + if (viewport->PlatformRequestResize) + viewport->Size = g.IO.PlatformInterface.GetWindowSize(viewport); + // Update DPI Scale float new_dpi_scale; if (g.IO.PlatformInterface.GetWindowDpiScale) @@ -3478,17 +3484,31 @@ static void UpdatePlatformWindows() if ((viewport->Flags & ImGuiViewportFlags_MainViewport) || (viewport->LastFrameActive < g.FrameCount)) continue; IM_ASSERT(viewport->Window != NULL); - viewport->PlatformRequestClose = false; - // FIXME-PLATFORM + if (viewport->PlatformRequestMove) + viewport->PlatformOsDesktopPos = g.IO.PlatformInterface.GetWindowPos(viewport); + bool is_new_window = viewport->PlatformHandle == NULL && viewport->PlatformUserData == NULL && viewport->RendererUserData == NULL; if (is_new_window && viewport->PlatformHandle == NULL && viewport->PlatformUserData == NULL) + { g.IO.PlatformInterface.CreateViewport(viewport); + } if (is_new_window && viewport->RendererUserData == NULL && g.IO.RendererInterface.CreateViewport != NULL) + { g.IO.RendererInterface.CreateViewport(viewport); + viewport->RendererLastSize = viewport->Size; + } + + // Update Pos/Size for Platform + if (!viewport->PlatformRequestMove) + g.IO.PlatformInterface.SetWindowPos(viewport, viewport->PlatformOsDesktopPos); + if (!viewport->PlatformRequestResize) + g.IO.PlatformInterface.SetWindowSize(viewport, viewport->Size); - g.IO.PlatformInterface.SetWindowPos(viewport, viewport->PlatformOsDesktopPos); - g.IO.PlatformInterface.SetWindowSize(viewport, viewport->Size); + // Update Size for Renderer + if (g.IO.RendererInterface.ResizeViewport && (viewport->RendererLastSize.x != viewport->Size.x || viewport->RendererLastSize.y != viewport->Size.y)) + g.IO.RendererInterface.ResizeViewport(viewport, viewport->Size); + viewport->RendererLastSize = viewport->Size; // Update title bar const char* title_begin = viewport->Window->Name; @@ -3503,13 +3523,18 @@ static void UpdatePlatformWindows() ImGui::MemFree(title_displayed); } + // Show window. On startup ensure platform window don't get focus. if (is_new_window) { - // On startup ensure platform window don't get focus. if (g.FrameCount < 2) viewport->Flags |= ImGuiViewportFlags_NoFocusOnAppearing; g.IO.PlatformInterface.ShowWindow(viewport); } + + // Clear request flags + viewport->PlatformRequestClose = false; + viewport->PlatformRequestMove = false; + viewport->PlatformRequestResize = false; } } @@ -6013,10 +6038,11 @@ static void ImGui::UpdateWindowViewport(ImGuiWindow* window, bool window_pos_set window->Viewport = main_viewport; // When we own the viewport update its size - if (window->ID == window->Viewport->ID && !created_viewport) + if (window == window->Viewport->Window && !created_viewport) { window->Viewport->Flags |= ImGuiViewportFlags_NoDecoration; - window->Viewport->Size = window->Size; + if (!window->Viewport->PlatformRequestResize) + window->Viewport->Size = window->Size; window->Viewport->PlatformOsDesktopPos = ConvertViewportPosToOsDesktopPos(window->Pos, window->Viewport); window->Flags |= ImGuiWindowFlags_FullViewport; } @@ -6026,7 +6052,11 @@ static void ImGui::UpdateWindowViewport(ImGuiWindow* window, bool window_pos_set window->Flags |= ImGuiWindowFlags_NoTitleBar; if (window->Flags & ImGuiWindowFlags_FullViewport) + { SetWindowPos(window, window->Viewport->Pos, ImGuiCond_Always); + if (window->Viewport->PlatformRequestResize) + SetWindowSize(window, window->Viewport->Size, ImGuiCond_Always); + } window->ViewportId = window->Viewport->ID; } diff --git a/imgui_internal.h b/imgui_internal.h index 123c42b2070e..1d49b9135fa2 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -535,10 +535,12 @@ struct ImGuiViewport void* PlatformUserData; // void* to hold custom data structure for the platform (e.g. windowing info, render context) void* PlatformHandle; // void* for FindViewportByPlatformHandle(). (e.g. HWND, GlfwWindow*) bool PlatformRequestClose; // Platform window requested closure - bool PlatformRequestResize; // Platform window requested resize + bool PlatformRequestMove; // Platform window requested move (e.g. window was moved using OS windowing facility) + bool PlatformRequestResize; // Platform window requested resize (e.g. window was resize using OS windowing facility) void* RendererUserData; // void* to hold custom data structure for the renderer (e.g. framebuffer) + ImVec2 RendererLastSize; - ImGuiViewport(ImGuiID id, int idx) { ID = id; Idx = idx; Flags = 0; LastFrameActive = LastFrameAsRefViewport = -1; LastNameHash = 0; Window = NULL; DpiScale = 0.0f; PlatformUserData = PlatformHandle = NULL; PlatformRequestClose = PlatformRequestResize = false; RendererUserData = NULL; } + ImGuiViewport(ImGuiID id, int idx) { ID = id; Idx = idx; Flags = 0; LastFrameActive = LastFrameAsRefViewport = -1; LastNameHash = 0; Window = NULL; DpiScale = 0.0f; PlatformUserData = PlatformHandle = NULL; PlatformRequestClose = PlatformRequestMove = PlatformRequestResize = false; RendererUserData = NULL; RendererLastSize = ImVec2(-1.0f,-1.0f); } ~ImGuiViewport() { IM_ASSERT(PlatformUserData == NULL && RendererUserData == NULL); } ImRect GetRect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } float GetNextX() const { const float SPACING = 4.0f; return Pos.x + Size.x + SPACING; } From 09d89439671c361dfea6d9c0ce4d40420e74960d Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 15 Mar 2018 16:42:21 +0100 Subject: [PATCH 087/828] Examples Refactor: GLFW: Explicit functions to init GLFW with OpenGL or Vulkan since we cannot read the api hints from glfw. --- examples/imgui_impl_glfw.cpp | 14 ++++++++------ examples/imgui_impl_glfw.h | 2 +- examples/opengl2_example/main.cpp | 2 +- examples/opengl3_example/main.cpp | 2 +- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index 2b470acdb020..7197bd5a34b9 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -111,7 +111,7 @@ void ImGui_ImplGlfw_InstallCallbacks(GLFWwindow* window) glfwSetCharCallback(window, ImGui_ImplGlfw_CharCallback); } -bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks) +static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, GlfwClientApi client_api) { g_Window = window; @@ -163,16 +163,18 @@ bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks) if (io.ConfigFlags & ImGuiConfigFlags_EnableViewports) ImGui_ImplGlfw_InitPlatformInterface(); - g_ClientApi = GlfwClientApi_OpenGL; + g_ClientApi = client_api; return true; } +bool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow* window, bool install_callbacks) +{ + return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_OpenGL); +} + bool ImGui_ImplGlfw_InitForVulkan(GLFWwindow* window, bool install_callbacks) { - if (!ImGui_ImplGlfw_Init(window, install_callbacks)) - return false; - g_ClientApi = GlfwClientApi_Vulkan; - return true; + return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_Vulkan); } void ImGui_ImplGlfw_Shutdown() diff --git a/examples/imgui_impl_glfw.h b/examples/imgui_impl_glfw.h index c19089d0330e..84ab937f8fc7 100644 --- a/examples/imgui_impl_glfw.h +++ b/examples/imgui_impl_glfw.h @@ -12,7 +12,7 @@ struct GLFWwindow; -IMGUI_API bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks); +IMGUI_API bool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow* window, bool install_callbacks); IMGUI_API bool ImGui_ImplGlfw_InitForVulkan(GLFWwindow* window, bool install_callbacks); IMGUI_API void ImGui_ImplGlfw_Shutdown(); IMGUI_API void ImGui_ImplGlfw_NewFrame(); diff --git a/examples/opengl2_example/main.cpp b/examples/opengl2_example/main.cpp index 7c5f8d772a87..2ba7a0542dee 100644 --- a/examples/opengl2_example/main.cpp +++ b/examples/opengl2_example/main.cpp @@ -31,7 +31,7 @@ int main(int, char**) ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls - ImGui_ImplGlfw_Init(window, true); + ImGui_ImplGlfw_InitForOpenGL(window, true); ImGui_ImplOpenGL2_Init(); // Setup style diff --git a/examples/opengl3_example/main.cpp b/examples/opengl3_example/main.cpp index 0f943a9aece6..69ec99a7e7a2 100644 --- a/examples/opengl3_example/main.cpp +++ b/examples/opengl3_example/main.cpp @@ -40,7 +40,7 @@ int main(int, char**) //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls - ImGui_ImplGlfw_Init(window, true); + ImGui_ImplGlfw_InitForOpenGL(window, true); ImGui_ImplOpenGL3_Init(); // Setup style From 8364d1ca6ca2b4f114f1f5e6de83f66e999df507 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 15 Mar 2018 17:52:53 +0100 Subject: [PATCH 088/828] Viewport: Removed back-end renderer code that implied we need to clear with WindowBg color. Added NoRendererClear flag. (#1542, #1042) --- examples/imgui_impl_dx10.cpp | 6 +++--- examples/imgui_impl_dx11.cpp | 6 +++--- examples/imgui_impl_dx12.cpp | 6 +++--- examples/imgui_impl_opengl3.cpp | 9 ++++++--- examples/imgui_impl_vulkan.cpp | 16 +++++++++------- examples/imgui_impl_vulkan.h | 1 + imgui.cpp | 6 ++++++ imgui_internal.h | 7 ++++--- 8 files changed, 35 insertions(+), 22 deletions(-) diff --git a/examples/imgui_impl_dx10.cpp b/examples/imgui_impl_dx10.cpp index db9090152694..4636e7da521c 100644 --- a/examples/imgui_impl_dx10.cpp +++ b/examples/imgui_impl_dx10.cpp @@ -587,10 +587,10 @@ static void ImGui_ImplDX10_ResizeViewport(ImGuiViewport* viewport, ImVec2 size) static void ImGui_ImplDX10_RenderViewport(ImGuiViewport* viewport) { ImGuiPlatformDataDx10* data = (ImGuiPlatformDataDx10*)viewport->RendererUserData; - ImVec4 clear_color = ImGui::GetStyle().Colors[ImGuiCol_WindowBg]; // FIXME-PLATFORM - clear_color.w = 1.0f; + ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); g_pd3dDevice->OMSetRenderTargets(1, &data->RTView, NULL); - g_pd3dDevice->ClearRenderTargetView(data->RTView, (float*)&clear_color); + if (!(viewport->Flags & ImGuiViewportFlags_NoRendererClear)) + g_pd3dDevice->ClearRenderTargetView(data->RTView, (float*)&clear_color); ImGui_ImplDX10_RenderDrawData(&viewport->DrawData); } diff --git a/examples/imgui_impl_dx11.cpp b/examples/imgui_impl_dx11.cpp index 52952bc068d5..09e32d80505c 100644 --- a/examples/imgui_impl_dx11.cpp +++ b/examples/imgui_impl_dx11.cpp @@ -595,10 +595,10 @@ static void ImGui_ImplDX11_ResizeViewport(ImGuiViewport* viewport, ImVec2 size) static void ImGui_ImplDX11_RenderViewport(ImGuiViewport* viewport) { ImGuiPlatformDataDx11* data = (ImGuiPlatformDataDx11*)viewport->RendererUserData; - ImVec4 clear_color = ImGui::GetStyle().Colors[ImGuiCol_WindowBg]; // FIXME-PLATFORM - clear_color.w = 1.0f; + ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); g_pd3dDeviceContext->OMSetRenderTargets(1, &data->RTView, NULL); - g_pd3dDeviceContext->ClearRenderTargetView(data->RTView, (float*)&clear_color); + if (!(viewport->Flags & ImGuiViewportFlags_NoRendererClear)) + g_pd3dDeviceContext->ClearRenderTargetView(data->RTView, (float*)&clear_color); ImGui_ImplDX11_RenderDrawData(&viewport->DrawData); } diff --git a/examples/imgui_impl_dx12.cpp b/examples/imgui_impl_dx12.cpp index d0d05d6051c2..dc7f1d75609e 100644 --- a/examples/imgui_impl_dx12.cpp +++ b/examples/imgui_impl_dx12.cpp @@ -734,10 +734,10 @@ static void ImGui_ImplDX12_RenderViewport(ImGuiViewport* viewport) IM_ASSERT(0); (void)data; /* - ImVec4 clear_color = ImGui::GetStyle().Colors[ImGuiCol_WindowBg]; // FIXME-PLATFORM - clear_color.w = 1.0f; + ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); g_pd3dDeviceContext->OMSetRenderTargets(1, &data->RTView, NULL); - g_pd3dDeviceContext->ClearRenderTargetView(data->RTView, (float*)&clear_color); + if (!(viewport->Flags & ImGuiViewportFlags_NoRendererClear)) + g_pd3dDeviceContext->ClearRenderTargetView(data->RTView, (float*)&clear_color); */ ImGui_ImplDX12_RenderDrawData(&viewport->DrawData); } diff --git a/examples/imgui_impl_opengl3.cpp b/examples/imgui_impl_opengl3.cpp index e851f0e22f62..42f2ccb6dbe9 100644 --- a/examples/imgui_impl_opengl3.cpp +++ b/examples/imgui_impl_opengl3.cpp @@ -331,9 +331,12 @@ void ImGui_ImplOpenGL3_DestroyDeviceObjects() static void ImGui_ImplOpenGL3_RenderViewport(ImGuiViewport* viewport) { - ImVec4 clear_color = ImGui::GetStyle().Colors[ImGuiCol_WindowBg]; - glClearColor(clear_color.x, clear_color.y, clear_color.z, 1.0f); // FIXME-PLATFORM - glClear(GL_COLOR_BUFFER_BIT); + if (!(viewport->Flags & ImGuiViewportFlags_NoRendererClear)) + { + ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); + glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w); + glClear(GL_COLOR_BUFFER_BIT); + } ImGui_ImplOpenGL3_RenderDrawData(&viewport->DrawData); } diff --git a/examples/imgui_impl_vulkan.cpp b/examples/imgui_impl_vulkan.cpp index e817ea1ff8c8..fe7af6ddf7c2 100644 --- a/examples/imgui_impl_vulkan.cpp +++ b/examples/imgui_impl_vulkan.cpp @@ -757,6 +757,7 @@ ImGui_ImplVulkan_WindowData::ImGui_ImplVulkan_WindowData() memset(&SurfaceFormat, 0, sizeof(SurfaceFormat)); PresentMode = VK_PRESENT_MODE_MAX_ENUM_KHR; RenderPass = VK_NULL_HANDLE; + ClearEnable = true; memset(&ClearValue, 0, sizeof(ClearValue)); BackBufferCount = 0; memset(&BackBuffer, 0, sizeof(BackBuffer)); @@ -959,7 +960,7 @@ void ImGui_ImplVulkanH_CreateWindowDataSwapChainAndFramebuffer(VkPhysicalDevice VkAttachmentDescription attachment = {}; attachment.format = wd->SurfaceFormat.format; attachment.samples = VK_SAMPLE_COUNT_1_BIT; - attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachment.loadOp = wd->ClearEnable ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_DONT_CARE; attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; @@ -1091,6 +1092,7 @@ static void ImGui_ImplVulkan_CreateViewport(ImGuiViewport* viewport) wd->PresentMode = ImGui_ImplVulkanH_SelectPresentMode(g_PhysicalDevice, wd->Surface, &present_mode, 1); // Create SwapChain, RenderPass, Framebuffer, etc. + wd->ClearEnable = (viewport->Flags & ImGuiViewportFlags_NoRendererClear) ? false : true; ImGui_ImplVulkanH_CreateWindowDataCommandBuffers(g_PhysicalDevice, g_Device, g_QueueFamily, wd, g_Allocator); ImGui_ImplVulkanH_CreateWindowDataSwapChainAndFramebuffer(g_PhysicalDevice, g_Device, wd, g_Allocator, (int)viewport->Size.x, (int)viewport->Size.y); } @@ -1110,6 +1112,7 @@ static void ImGui_ImplVulkan_ResizeViewport(ImGuiViewport* viewport, ImVec2 size ImGuiPlatformDataVulkan* data = (ImGuiPlatformDataVulkan*)viewport->RendererUserData; if (data == NULL) // This is NULL for the main viewport (which is left to the user/app to handle) return; + data->WindowData.ClearEnable = (viewport->Flags & ImGuiViewportFlags_NoRendererClear) ? false : true; ImGui_ImplVulkanH_CreateWindowDataSwapChainAndFramebuffer(g_PhysicalDevice, g_Device, &data->WindowData, g_Allocator, (int)size.x, (int)size.y); } @@ -1119,9 +1122,6 @@ static void ImGui_ImplVulkan_RenderViewport(ImGuiViewport* viewport) ImGui_ImplVulkan_WindowData* wd = &data->WindowData; VkResult err; - ImVec4 clear_color = ImGui::GetStyle().Colors[ImGuiCol_WindowBg]; // FIXME-PLATFORM - clear_color.w = 1.0f; - { ImGui_ImplVulkan_FrameData* fd = &wd->Frames[wd->FrameIndex]; for (;;) @@ -1145,19 +1145,21 @@ static void ImGui_ImplVulkan_RenderViewport(ImGuiViewport* viewport) check_vk_result(err); } { + ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); + memcpy(&wd->ClearValue.color.float32[0], &clear_color, 4 * sizeof(float)); + VkRenderPassBeginInfo info = {}; info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; info.renderPass = wd->RenderPass; info.framebuffer = wd->Framebuffer[fd->BackbufferIndex]; info.renderArea.extent.width = wd->Width; info.renderArea.extent.height = wd->Height; - info.clearValueCount = 1; - info.pClearValues = &wd->ClearValue; + info.clearValueCount = (viewport->Flags & ImGuiViewportFlags_NoRendererClear) ? 0 : 1; + info.pClearValues = (viewport->Flags & ImGuiViewportFlags_NoRendererClear) ? NULL : &wd->ClearValue; vkCmdBeginRenderPass(fd->CommandBuffer, &info, VK_SUBPASS_CONTENTS_INLINE); } } - memcpy(&wd->ClearValue.color.float32[0], &clear_color, 4 * sizeof(float)); ImGui_ImplVulkan_RenderDrawData(wd->Frames[wd->FrameIndex].CommandBuffer, &viewport->DrawData); { diff --git a/examples/imgui_impl_vulkan.h b/examples/imgui_impl_vulkan.h index f9ef133ead19..03d6ed3c0455 100644 --- a/examples/imgui_impl_vulkan.h +++ b/examples/imgui_impl_vulkan.h @@ -77,6 +77,7 @@ struct ImGui_ImplVulkan_WindowData VkSurfaceFormatKHR SurfaceFormat; VkPresentModeKHR PresentMode; VkRenderPass RenderPass; + bool ClearEnable; VkClearValue ClearValue; uint32_t BackBufferCount; VkImage BackBuffer[16]; diff --git a/imgui.cpp b/imgui.cpp index 001a86c6fe08..53e8698c606b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6053,6 +6053,10 @@ static void ImGui::UpdateWindowViewport(ImGuiWindow* window, bool window_pos_set if (window->Flags & ImGuiWindowFlags_FullViewport) { + // We currently have window fully covering a viewport and we disable WindowBg alpha, so clearing is not necessary + window->Viewport->Flags |= ImGuiViewportFlags_NoRendererClear; + + // Position SetWindowPos(window, window->Viewport->Pos, ImGuiCond_Always); if (window->Viewport->PlatformRequestResize) SetWindowSize(window, window->Viewport->Size, ImGuiCond_Always); @@ -6606,6 +6610,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) ImU32 bg_col = GetColorU32(GetWindowBgColorIdxFromFlags(flags)); if (g.NextWindowData.BgAlphaCond != 0) bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(g.NextWindowData.BgAlphaVal) << IM_COL32_A_SHIFT); + if (window->Flags & ImGuiWindowFlags_FullViewport) + bg_col = (bg_col | IM_COL32_A_MASK); window->DrawList->AddRectFilled(window->Pos+ImVec2(0,window->TitleBarHeight()), window->Pos+window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Bot); // Title bar diff --git a/imgui_internal.h b/imgui_internal.h index 1d49b9135fa2..6631fb89db85 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -510,9 +510,10 @@ enum ImGuiViewportFlags_ { ImGuiViewportFlags_MainViewport = 1 << 0, ImGuiViewportFlags_HostOtherWindows = 1 << 1, - ImGuiViewportFlags_NoDecoration = 1 << 2, // Platform Window: Disable platform title bar, borders, etc. - ImGuiViewportFlags_NoFocusOnAppearing = 1 << 3, // Platform Window: Don't take focus when created. - ImGuiViewportFlags_NoInputs = 1 << 4 // Platform Window: Make mouse pass through so we can drag this window while peaking behind it. + ImGuiViewportFlags_NoRendererClear = 1 << 2, // Platform Window: Renderer doesn't need to clear the framebuffer ahead. + ImGuiViewportFlags_NoDecoration = 1 << 3, // Platform Window: Disable platform title bar, borders, etc. + ImGuiViewportFlags_NoFocusOnAppearing = 1 << 4, // Platform Window: Don't take focus when created. + ImGuiViewportFlags_NoInputs = 1 << 5 // Platform Window: Make mouse pass through so we can drag this window while peaking behind it. }; struct ImGuiViewport From 2e1ac0f683db1d84e9a1a18c17e88b687c631781 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 15 Mar 2018 19:25:23 +0100 Subject: [PATCH 089/828] Viewport, Platform: SDL: Makes the CreateViewport function restore current GL context so in theory it is free from side-effect. That said, it seems like there is a bug in SDL because our CreateViewport (currently in Render(), not for long) have affect a jerky side-effect if SDL_GL_MakeCurrent() is called before Render(). (#1542) --- examples/imgui_impl_sdl2.cpp | 19 ++++++++++++++----- examples/sdl_opengl3_example/main.cpp | 2 +- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/examples/imgui_impl_sdl2.cpp b/examples/imgui_impl_sdl2.cpp index 496f2da269ce..cd9c42e615d3 100644 --- a/examples/imgui_impl_sdl2.cpp +++ b/examples/imgui_impl_sdl2.cpp @@ -294,19 +294,28 @@ static void ImGui_ImplSDL2_CreateViewport(ImGuiViewport* viewport) // FIXME-PLATFORM ImGuiViewport* main_viewport = ImGui::GetMainViewport(); ImGuiPlatformDataSDL2* main_viewport_data = (ImGuiPlatformDataSDL2*)main_viewport->PlatformUserData; - SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1); - SDL_GL_MakeCurrent(main_viewport_data->Window, main_viewport_data->GLContext); + + bool use_opengl = (main_viewport_data->GLContext != NULL); + SDL_GLContext backup_context = NULL; + if (use_opengl) + { + backup_context = SDL_GL_GetCurrentContext(); + SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1); + SDL_GL_MakeCurrent(main_viewport_data->Window, main_viewport_data->GLContext); + } // We don't enable SDL_WINDOW_RESIZABLE because it enforce windows decorations Uint32 sdl_flags = 0; - sdl_flags |= main_viewport_data->GLContext ? SDL_WINDOW_OPENGL : SDL_WINDOW_VULKAN; + sdl_flags |= use_opengl ? SDL_WINDOW_OPENGL : SDL_WINDOW_VULKAN; sdl_flags |= SDL_WINDOW_HIDDEN; sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? SDL_WINDOW_BORDERLESS : 0; sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? 0 : SDL_WINDOW_RESIZABLE; data->Window = SDL_CreateWindow("No Title Yet", (int)viewport->PlatformOsDesktopPos.x, (int)viewport->PlatformOsDesktopPos.y, (int)viewport->Size.x, (int)viewport->Size.y, sdl_flags); - if (main_viewport_data->GLContext) + if (use_opengl) data->GLContext = SDL_GL_CreateContext(data->Window); + if (use_opengl && backup_context) + SDL_GL_MakeCurrent(data->Window, backup_context); viewport->PlatformHandle = (void*)data->Window; } @@ -404,7 +413,7 @@ static void ImGui_ImplSDL2_SwapBuffers(ImGuiViewport* viewport) ImGuiPlatformDataSDL2* data = (ImGuiPlatformDataSDL2*)viewport->PlatformUserData; if (data->GLContext) { - SDL_GL_MakeCurrent(data->Window, data->GLContext); // FIXME-PLATFORM2 + SDL_GL_MakeCurrent(data->Window, data->GLContext); SDL_GL_SwapWindow(data->Window); } } diff --git a/examples/sdl_opengl3_example/main.cpp b/examples/sdl_opengl3_example/main.cpp index 99dc3b956f84..f1fffcaf66d1 100644 --- a/examples/sdl_opengl3_example/main.cpp +++ b/examples/sdl_opengl3_example/main.cpp @@ -125,11 +125,11 @@ int main(int, char**) } // Rendering + ImGui::Render(); SDL_GL_MakeCurrent(window, gl_context); glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y); glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w); glClear(GL_COLOR_BUFFER_BIT); - ImGui::Render(); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); ImGui::RenderAdditionalViewports(); From 1ab236d9df90f75f6d6781334cb81e3333c3183c Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 15 Mar 2018 20:02:50 +0100 Subject: [PATCH 090/828] Viewport, Platform: Internal renaming to use more consistent (OsDesktopPos > PlatformPos). (#1542) --- examples/imgui_impl_sdl2.cpp | 2 +- examples/imgui_impl_win32.cpp | 6 +-- imgui.cpp | 74 +++++++++++++++++------------------ imgui_internal.h | 10 ++--- 4 files changed, 46 insertions(+), 46 deletions(-) diff --git a/examples/imgui_impl_sdl2.cpp b/examples/imgui_impl_sdl2.cpp index cd9c42e615d3..8fe81f2accbe 100644 --- a/examples/imgui_impl_sdl2.cpp +++ b/examples/imgui_impl_sdl2.cpp @@ -311,7 +311,7 @@ static void ImGui_ImplSDL2_CreateViewport(ImGuiViewport* viewport) sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? SDL_WINDOW_BORDERLESS : 0; sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? 0 : SDL_WINDOW_RESIZABLE; data->Window = SDL_CreateWindow("No Title Yet", - (int)viewport->PlatformOsDesktopPos.x, (int)viewport->PlatformOsDesktopPos.y, (int)viewport->Size.x, (int)viewport->Size.y, sdl_flags); + (int)viewport->PlatformPos.x, (int)viewport->PlatformPos.y, (int)viewport->Size.x, (int)viewport->Size.y, sdl_flags); if (use_opengl) data->GLContext = SDL_GL_CreateContext(data->Window); if (use_opengl && backup_context) diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index 7361e9af6f55..70cb8bd92033 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -383,7 +383,7 @@ static void ImGui_ImplWin32_CreateViewport(ImGuiViewport* viewport) } // Create window - RECT rect = { (LONG)viewport->PlatformOsDesktopPos.x, (LONG)viewport->PlatformOsDesktopPos.y, (LONG)(viewport->PlatformOsDesktopPos.x + viewport->Size.x), (LONG)(viewport->PlatformOsDesktopPos.y + viewport->Size.y) }; + RECT rect = { (LONG)viewport->PlatformPos.x, (LONG)viewport->PlatformPos.y, (LONG)(viewport->PlatformPos.x + viewport->Size.x), (LONG)(viewport->PlatformPos.y + viewport->Size.y) }; ::AdjustWindowRectEx(&rect, data->DwStyle, FALSE, data->DwExStyle); data->Hwnd = ::CreateWindowExA( data->DwExStyle, "ImGui Platform", "No Title Yet", data->DwStyle, // Style, class name, window name @@ -472,8 +472,8 @@ static float ImGui_ImplWin32_GetWindowDpiScale(ImGuiViewport* viewport) // The first frame a viewport is created we don't have a window yet return ImGui_ImplWin32_GetDpiScaleForRect( - (int)(viewport->PlatformOsDesktopPos.x), (int)(viewport->PlatformOsDesktopPos.y), - (int)(viewport->PlatformOsDesktopPos.x + viewport->Size.x), (int)(viewport->PlatformOsDesktopPos.y + viewport->Size.y)); + (int)(viewport->PlatformPos.x), (int)(viewport->PlatformPos.y), + (int)(viewport->PlatformPos.x + viewport->Size.x), (int)(viewport->PlatformPos.y + viewport->Size.y)); } static LRESULT CALLBACK ImGui_ImplWin32_WndProcHandler_PlatformWindow(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) diff --git a/imgui.cpp b/imgui.cpp index 53e8698c606b..219f6f615cbd 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -739,8 +739,8 @@ static void FocusFrontMostActiveWindow(ImGuiWindow* ignore_window); // Viewport const ImGuiID IMGUI_VIEWPORT_DEFAULT_ID = 0x11111111; // Using a constant instead of e.g. ImHash("ViewportDefault", 0); so it's easier to spot in the debugger. The exact value doesn't matter. static inline ImRect GetViewportRect(ImGuiWindow* window) { return window->Viewport->GetRect(); } -static inline ImVec2 ConvertViewportPosToOsDesktopPos(const ImVec2& imgui_pos, ImGuiViewport* viewport) { return imgui_pos - viewport->Pos + viewport->PlatformOsDesktopPos; } -static inline ImVec2 ConvertOsDesktopPosToViewportPos(const ImVec2& os_pos, ImGuiViewport* viewport) { return os_pos - viewport->PlatformOsDesktopPos + viewport->Pos; } +static inline ImVec2 ConvertViewportPosToPlatformPos(const ImVec2& imgui_pos, ImGuiViewport* viewport) { return imgui_pos - viewport->Pos + viewport->PlatformPos; } +static inline ImVec2 ConvertPlatformPosToViewportPos(const ImVec2& platform_pos, ImGuiViewport* viewport) { return platform_pos - viewport->PlatformPos + viewport->Pos; } static void UpdateViewports(); static void UpdateWindowViewport(ImGuiWindow* window, bool window_pos_set_by_api); static void SetCurrentViewport(ImGuiViewport* viewport); @@ -1910,7 +1910,7 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) Flags = FlagsPreviousFrame = 0; Viewport = NULL; ViewportId = 0; - ViewportOsDesktopPos = ImVec2(FLT_MAX, FLT_MAX); + ViewportPlatformPos = ImVec2(FLT_MAX, FLT_MAX); PosFloat = Pos = ImVec2(0.0f, 0.0f); Size = SizeFull = ImVec2(0.0f, 0.0f); SizeContents = SizeContentsExplicit = ImVec2(0.0f, 0.0f); @@ -3253,7 +3253,7 @@ static void ImGui::UpdateMovingWindowDropViewport(ImGuiWindow* window) return; ImRect mouse_viewport_rect = g.MouseViewport->GetRect(); - ImVec2 window_pos_in_mouse_viewport = ConvertOsDesktopPosToViewportPos(ConvertViewportPosToOsDesktopPos(window->Pos, window->Viewport), g.MouseViewport); + ImVec2 window_pos_in_mouse_viewport = ConvertPlatformPosToViewportPos(ConvertViewportPosToPlatformPos(window->Pos, window->Viewport), g.MouseViewport); ImRect window_rect_in_mouse_viewport = ImRect(window_pos_in_mouse_viewport, window_pos_in_mouse_viewport + window->Size); if ((g.MouseViewport->Flags & ImGuiViewportFlags_HostOtherWindows) && mouse_viewport_rect.Contains(window_rect_in_mouse_viewport)) { @@ -3271,8 +3271,8 @@ static void ImGui::UpdateMovingWindowDropViewport(ImGuiWindow* window) else { // Create new viewport - ImVec2 os_pos = ConvertViewportPosToOsDesktopPos(window->Pos, window->Viewport); - ImGuiViewport* viewport = Viewport(window, window->ID, 0, os_pos, window->Size); + ImVec2 platform_pos = ConvertViewportPosToPlatformPos(window->Pos, window->Viewport); + ImGuiViewport* viewport = Viewport(window, window->ID, 0, platform_pos, window->Size); SetWindowViewportTranslateToPreservePlatformPos(window, window->Viewport, viewport); } } @@ -3326,15 +3326,15 @@ static void ImGui::UpdateMovingWindow() // If the back-end doesn't set MouseLastHoveredViewport or doesn't honor ImGuiViewportFlags_NoInputs, we do a search ourselves. // This search won't take account of the possibility that non-imgui windows may be in-between our dragged window and our target window. -static ImGuiViewport* FindViewportHoveredFromOsWindowStack(const ImVec2 mouse_os_pos) +static ImGuiViewport* FindViewportHoveredFromPlatformWindowStack(const ImVec2 mouse_platform_pos) { ImGuiContext& g = *GImGui; ImGuiViewport* best_candidate = NULL; for (int n = 0; n < g.Viewports.Size; n++) { ImGuiViewport* viewport = g.Viewports[n]; - ImRect os_rect = ImRect(viewport->PlatformOsDesktopPos, viewport->PlatformOsDesktopPos + viewport->Size); - if (!(viewport->Flags & ImGuiViewportFlags_NoInputs) && os_rect.Contains(mouse_os_pos)) + ImRect platform_rect = ImRect(viewport->PlatformPos, viewport->PlatformPos + viewport->Size); + if (!(viewport->Flags & ImGuiViewportFlags_NoInputs) && platform_rect.Contains(mouse_platform_pos)) if (best_candidate == NULL || best_candidate->LastFrameAsRefViewport < viewport->LastFrameAsRefViewport) best_candidate = viewport; } @@ -3347,7 +3347,7 @@ static void ImGui::UpdateViewports() // Mouse handling: latch the expected mouse OS position (if any) before processing viewport erasure ImGuiViewport* viewport_ref = g.IO.MousePosViewport ? FindViewportByID(g.IO.MousePosViewport) : g.Viewports[0]; - const ImVec2 mouse_os_pos = ConvertViewportPosToOsDesktopPos(g.IO.MousePos, viewport_ref); + const ImVec2 mouse_platform_pos = ConvertViewportPosToPlatformPos(g.IO.MousePos, viewport_ref); g.CurrentViewport = NULL; for (int n = 0; n < g.Viewports.Size; n++) @@ -3414,10 +3414,10 @@ static void ImGui::UpdateViewports() // Update main viewport with current size (and OS window position, if known) ImGuiViewport* main_viewport = g.Viewports[0]; IM_ASSERT(main_viewport->ID == IMGUI_VIEWPORT_DEFAULT_ID); - ImVec2 main_viewport_os_desktop_pos = ImVec2(0.0f, 0.0f); + ImVec2 main_viewport_platform_pos = ImVec2(0.0f, 0.0f); if ((g.IO.ConfigFlags & ImGuiConfigFlags_EnableViewports)) - main_viewport_os_desktop_pos = g.IO.PlatformInterface.GetWindowPos(main_viewport); - Viewport(NULL, IMGUI_VIEWPORT_DEFAULT_ID, ImGuiViewportFlags_MainViewport | ImGuiViewportFlags_HostOtherWindows, main_viewport_os_desktop_pos, g.IO.DisplaySize); + main_viewport_platform_pos = g.IO.PlatformInterface.GetWindowPos(main_viewport); + Viewport(NULL, IMGUI_VIEWPORT_DEFAULT_ID, ImGuiViewportFlags_MainViewport | ImGuiViewportFlags_HostOtherWindows, main_viewport_platform_pos, g.IO.DisplaySize); if (!(g.IO.ConfigFlags & ImGuiConfigFlags_EnableViewports)) { @@ -3434,14 +3434,14 @@ static void ImGui::UpdateViewports() { // Back-end failed at honoring its contract IM_ASSERT(0); - viewport_hovered = FindViewportHoveredFromOsWindowStack(mouse_os_pos); + viewport_hovered = FindViewportHoveredFromPlatformWindowStack(mouse_platform_pos); } } else { // If the back-end doesn't set MouseLastHoveredViewport or doesn't honor ImGuiViewportFlags_NoInputs, we do a search ourselves. // This search won't take account of the possibility that non-imgui windows may be in-between our dragged window and our target window. - viewport_hovered = FindViewportHoveredFromOsWindowStack(mouse_os_pos); + viewport_hovered = FindViewportHoveredFromPlatformWindowStack(mouse_platform_pos); } if (viewport_hovered != NULL) g.MouseLastHoveredViewport = viewport_hovered; @@ -3450,7 +3450,7 @@ static void ImGui::UpdateViewports() if (viewport_ref == NULL) { viewport_ref = main_viewport; - g.IO.MousePos = ConvertOsDesktopPosToViewportPos(mouse_os_pos, viewport_ref); + g.IO.MousePos = ConvertPlatformPosToViewportPos(mouse_platform_pos, viewport_ref); } g.MouseLastViewport = g.MouseViewport; @@ -3466,7 +3466,7 @@ static void ImGui::UpdateViewports() viewport_hovered = g.MouseLastHoveredViewport; if (viewport_hovered != NULL && viewport_hovered != g.MouseViewport && !(viewport_hovered->Flags & ImGuiViewportFlags_NoInputs)) { - g.IO.MousePos = ConvertOsDesktopPosToViewportPos(ConvertViewportPosToOsDesktopPos(g.IO.MousePos, g.MouseViewport), viewport_hovered); + g.IO.MousePos = ConvertPlatformPosToViewportPos(ConvertViewportPosToPlatformPos(g.IO.MousePos, g.MouseViewport), viewport_hovered); g.MouseViewport = viewport_hovered; } } @@ -3486,7 +3486,7 @@ static void UpdatePlatformWindows() IM_ASSERT(viewport->Window != NULL); if (viewport->PlatformRequestMove) - viewport->PlatformOsDesktopPos = g.IO.PlatformInterface.GetWindowPos(viewport); + viewport->PlatformPos = g.IO.PlatformInterface.GetWindowPos(viewport); bool is_new_window = viewport->PlatformHandle == NULL && viewport->PlatformUserData == NULL && viewport->RendererUserData == NULL; if (is_new_window && viewport->PlatformHandle == NULL && viewport->PlatformUserData == NULL) @@ -3501,7 +3501,7 @@ static void UpdatePlatformWindows() // Update Pos/Size for Platform if (!viewport->PlatformRequestMove) - g.IO.PlatformInterface.SetWindowPos(viewport, viewport->PlatformOsDesktopPos); + g.IO.PlatformInterface.SetWindowPos(viewport, viewport->PlatformPos); if (!viewport->PlatformRequestResize) g.IO.PlatformInterface.SetWindowSize(viewport, viewport->Size); @@ -3891,7 +3891,7 @@ static void SettingsHandlerWindow_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, if (sscanf(line, "Pos=%f,%f", &x, &y) == 2) { settings->Pos = ImVec2(x, y); } else if (sscanf(line, "Size=%f,%f", &x, &y) == 2) { settings->Size = ImMax(ImVec2(x, y), GImGui->Style.WindowMinSize); } else if (sscanf(line, "ViewportId=0x%08X", &u1) == 1) { settings->ViewportId = u1; } - else if (sscanf(line, "ViewportOsDesktopPos=%f,%f", &x, &y)==2) { settings->ViewportOsDesktopPos = ImVec2(x, y); } + else if (sscanf(line, "ViewportPlatformPos=%f,%f", &x, &y)==2) { settings->ViewportPlatformPos = ImVec2(x, y); } else if (sscanf(line, "Collapsed=%d", &i) == 1) { settings->Collapsed = (i != 0); } } @@ -3910,7 +3910,7 @@ static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSetting settings->Pos = window->Pos;// - window->Viewport->Pos; settings->Size = window->SizeFull; settings->ViewportId = window->ViewportId; - settings->ViewportOsDesktopPos = window->ViewportOsDesktopPos; + settings->ViewportPlatformPos = window->ViewportPlatformPos; settings->Collapsed = window->Collapsed; } @@ -3931,8 +3931,8 @@ static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSetting if (settings->ViewportId != 0 && settings->ViewportId != ImGui::IMGUI_VIEWPORT_DEFAULT_ID) { buf->appendf("ViewportId=0x%08X\n", settings->ViewportId); - if (settings->ViewportOsDesktopPos.x != FLT_MAX && settings->ViewportOsDesktopPos.y != FLT_MAX) - buf->appendf("ViewportOsDesktopPos=%d,%d\n", (int)settings->ViewportOsDesktopPos.x, (int)settings->ViewportOsDesktopPos.y); + if (settings->ViewportPlatformPos.x != FLT_MAX && settings->ViewportPlatformPos.y != FLT_MAX) + buf->appendf("ViewportPlatformPos=%d,%d\n", (int)settings->ViewportPlatformPos.x, (int)settings->ViewportPlatformPos.y); } buf->appendf("Collapsed=%d\n", settings->Collapsed); buf->appendf("\n"); @@ -4545,7 +4545,7 @@ void ImGui::SetCurrentViewport(ImGuiViewport* viewport) g.IO.PlatformInterface.ChangedViewport(g.CurrentViewport); } -ImGuiViewport* ImGui::Viewport(ImGuiWindow* window, ImGuiID id, ImGuiViewportFlags flags, const ImVec2& os_desktop_pos, const ImVec2& size) +ImGuiViewport* ImGui::Viewport(ImGuiWindow* window, ImGuiID id, ImGuiViewportFlags flags, const ImVec2& platform_pos, const ImVec2& size) { ImGuiContext& g = *GImGui; IM_ASSERT(id != 0); @@ -4569,7 +4569,7 @@ ImGuiViewport* ImGui::Viewport(ImGuiWindow* window, ImGuiID id, ImGuiViewportFla IM_ASSERT(viewport->Pos.y == 0.0f); viewport->Window = window; viewport->Flags = flags; - viewport->PlatformOsDesktopPos = os_desktop_pos; + viewport->PlatformPos = platform_pos; viewport->LastFrameActive = g.FrameCount; // Request an initial DpiScale before the OS platform window creation @@ -5779,8 +5779,8 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFl window->Collapsed = settings->Collapsed; if (settings->ViewportId) window->ViewportId = settings->ViewportId; - if (settings->ViewportOsDesktopPos.x != FLT_MAX && settings->ViewportOsDesktopPos.y != FLT_MAX) - window->ViewportOsDesktopPos = settings->ViewportOsDesktopPos; + if (settings->ViewportPlatformPos.x != FLT_MAX && settings->ViewportPlatformPos.y != FLT_MAX) + window->ViewportPlatformPos = settings->ViewportPlatformPos; if (ImLengthSqr(settings->Size) > 0.00001f) size = settings->Size; } @@ -5926,7 +5926,7 @@ static void ImGui::SetWindowViewportTranslateToPreservePlatformPos(ImGuiWindow* { if (prev_viewport == curr_viewport) return; - ImVec2 new_pos = ConvertOsDesktopPosToViewportPos(ConvertViewportPosToOsDesktopPos(window->PosFloat, prev_viewport), curr_viewport); + ImVec2 new_pos = ConvertPlatformPosToViewportPos(ConvertViewportPosToPlatformPos(window->PosFloat, prev_viewport), curr_viewport); if ((window->FlagsPreviousFrame ^ window->Flags) & ImGuiWindowFlags_NoTitleBar) { // As a convenience, automatically adjust for client rect difference for the common use case of toggling the imgui title-bar when we move our tools to a separate OS window @@ -5980,9 +5980,9 @@ static void ImGui::UpdateWindowViewport(ImGuiWindow* window, bool window_pos_set if (!window_is_mouse_tooltip && !current_viewport->GetRect().Contains(window->Rect())) { // Create an undecorated, temporary OS/platform window - ImVec2 os_desktop_pos = ConvertViewportPosToOsDesktopPos(g.IO.MousePos - g.ActiveIdClickOffset, g.MouseViewport); + ImVec2 platform_pos = ConvertViewportPosToPlatformPos(g.IO.MousePos - g.ActiveIdClickOffset, g.MouseViewport); ImGuiViewportFlags viewport_flags = ImGuiViewportFlags_NoDecoration | ImGuiViewportFlags_NoFocusOnAppearing | ImGuiViewportFlags_NoInputs; - ImGuiViewport* viewport = Viewport(window, window->ID, viewport_flags, os_desktop_pos, window->Size); + ImGuiViewport* viewport = Viewport(window, window->ID, viewport_flags, platform_pos, window->Size); window->Flags |= ImGuiWindowFlags_FullViewport; window->Viewport = viewport; created_viewport = true; @@ -6013,15 +6013,15 @@ static void ImGui::UpdateWindowViewport(ImGuiWindow* window, bool window_pos_set if (window->Viewport == NULL && window->ParentWindow) window->Viewport = window->ParentWindow->Viewport; - // Restore a viewport id (= window that hasn't been activated yet), try to restore the viewport based on saved 'window->ViewportOsDesktopPos' + // Restore a viewport id (= window that hasn't been activated yet), try to restore the viewport based on saved 'window->ViewportPlatformPos' if (window->Viewport == NULL && window->ViewportId != 0) { window->Viewport = FindViewportByID(window->ViewportId); if (window->Viewport == NULL) { - if (window->ViewportOsDesktopPos.x != FLT_MAX && window->ViewportOsDesktopPos.y != FLT_MAX) + if (window->ViewportPlatformPos.x != FLT_MAX && window->ViewportPlatformPos.y != FLT_MAX) { - ImGuiViewport* viewport = Viewport(window, window->ID, ImGuiViewportFlags_NoDecoration, window->ViewportOsDesktopPos, window->Size); + ImGuiViewport* viewport = Viewport(window, window->ID, ImGuiViewportFlags_NoDecoration, window->ViewportPlatformPos, window->Size); window->Flags |= ImGuiWindowFlags_FullViewport; window->Viewport = viewport; created_viewport = true; @@ -6043,7 +6043,7 @@ static void ImGui::UpdateWindowViewport(ImGuiWindow* window, bool window_pos_set window->Viewport->Flags |= ImGuiViewportFlags_NoDecoration; if (!window->Viewport->PlatformRequestResize) window->Viewport->Size = window->Size; - window->Viewport->PlatformOsDesktopPos = ConvertViewportPosToOsDesktopPos(window->Pos, window->Viewport); + window->Viewport->PlatformPos = ConvertViewportPosToPlatformPos(window->Pos, window->Viewport); window->Flags |= ImGuiWindowFlags_FullViewport; } @@ -6192,7 +6192,7 @@ static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au if (pos_target.x != FLT_MAX) { if (window->Flags & ImGuiWindowFlags_FullViewport) - window->Viewport->PlatformOsDesktopPos = ConvertViewportPosToOsDesktopPos(ImFloor(pos_target), window->Viewport); + window->Viewport->PlatformPos = ConvertViewportPosToPlatformPos(ImFloor(pos_target), window->Viewport); else window->Pos = window->PosFloat = ImFloor(pos_target); MarkIniSettingsDirty(window); @@ -6553,7 +6553,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) UpdateManualResize(window, size_auto_fit, &border_held, resize_grip_count, &resize_grip_col[0]); // When a window is marked as owning its viewport, we immediately update the viewport after a resize - window->ViewportOsDesktopPos = window->Viewport->PlatformOsDesktopPos; + window->ViewportPlatformPos = window->Viewport->PlatformPos; if (flags & ImGuiWindowFlags_FullViewport) if (window->Size.x != window->Viewport->Size.x || window->Size.y != window->Viewport->Size.y) { @@ -14020,8 +14020,8 @@ void ImGui::ShowMetricsWindow(bool* p_open) if (ImGui::TreeNode((void*)(intptr_t)viewport->ID, "Viewport #%d, ID: 0x%08X, DrawLists: %d, Size: (%.0f,%.0f)", i, viewport->ID, viewport->DrawDataBuilder.GetDrawListCount(), viewport->Size.x, viewport->Size.y)) { ImGui::BulletText("Pos: (%.0f,%.0f)", viewport->Pos.x, viewport->Pos.y); + ImGui::BulletText("PlatformPos: (%.0f,%.0f); DpiScale: %.0f%%", viewport->PlatformPos.x, viewport->PlatformPos.y, viewport->DpiScale * 100.0f); ImGui::BulletText("Flags: 0x%04X", viewport->Flags); - ImGui::BulletText("PlatformOsDesktopPos: (%.0f,%.0f); DpiScale: %.0f%%", viewport->PlatformOsDesktopPos.x, viewport->PlatformOsDesktopPos.y, viewport->DpiScale * 100.0f); for (int layer_i = 0; layer_i < IM_ARRAYSIZE(viewport->DrawDataBuilder.Layers); layer_i++) for (int draw_list_i = 0; draw_list_i < viewport->DrawDataBuilder.Layers[layer_i].Size; draw_list_i++) Funcs::NodeDrawList(NULL, viewport->DrawDataBuilder.Layers[layer_i][draw_list_i], "DrawList"); diff --git a/imgui_internal.h b/imgui_internal.h index 6631fb89db85..629d97e7a13d 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -408,11 +408,11 @@ struct ImGuiWindowSettings ImGuiID Id; ImVec2 Pos; ImVec2 Size; - ImVec2 ViewportOsDesktopPos; + ImVec2 ViewportPlatformPos; ImGuiID ViewportId; bool Collapsed; - ImGuiWindowSettings() { Name = NULL; Id = ViewportId = 0; Pos = Size = ImVec2(0,0); ViewportOsDesktopPos = ImVec2(FLT_MAX, FLT_MAX); Collapsed = false; } + ImGuiWindowSettings() { Name = NULL; Id = ViewportId = 0; Pos = Size = ImVec2(0,0); ViewportPlatformPos = ImVec2(FLT_MAX, FLT_MAX); Collapsed = false; } }; struct ImGuiSettingsHandler @@ -532,7 +532,7 @@ struct ImGuiViewport ImDrawDataBuilder DrawDataBuilder; // [Optional] OS/Platform Layer data. This is to allow the creation/manipulate of multiple OS/Platform windows. Not all back-ends will allow this. - ImVec2 PlatformOsDesktopPos; // Position in OS desktop/native space + ImVec2 PlatformPos; // Position in OS desktop/native space void* PlatformUserData; // void* to hold custom data structure for the platform (e.g. windowing info, render context) void* PlatformHandle; // void* for FindViewportByPlatformHandle(). (e.g. HWND, GlfwWindow*) bool PlatformRequestClose; // Platform window requested closure @@ -952,7 +952,7 @@ struct IMGUI_API ImGuiWindow ImGuiWindowFlags Flags, FlagsPreviousFrame; // See enum ImGuiWindowFlags_ ImGuiViewport* Viewport; // Always set in Begin(), only inactive windows may have a NULL value here ImGuiID ViewportId; // Inactive windows preserve their last viewport id (since the viewport may disappear with the window inactivity) - ImVec2 ViewportOsDesktopPos; + ImVec2 ViewportPlatformPos; ImVec2 PosFloat; ImVec2 Pos; // Position rounded-up to nearest pixel ImVec2 Size; // Current size (==SizeFull or collapsed title bar size) @@ -1082,7 +1082,7 @@ namespace ImGui IMGUI_API void Shutdown(ImGuiContext* context); // Since 1.60 this is a _private_ function. You can call DestroyContext() to destroy the context created by CreateContext(). // Viewports - IMGUI_API ImGuiViewport* Viewport(ImGuiWindow* window, ImGuiID id, ImGuiViewportFlags flags, const ImVec2& os_desktop_pos, const ImVec2& size); // os_desktop_pos allows imgui to reposition windows relative to each order when moving from one viewport to the other. + IMGUI_API ImGuiViewport* Viewport(ImGuiWindow* window, ImGuiID id, ImGuiViewportFlags flags, const ImVec2& platform_pos, const ImVec2& size); inline ImVector&GetViewports() { return GImGui->Viewports; } inline ImGuiViewport* GetMainViewport() { return GImGui->Viewports[0]; } IMGUI_API ImGuiViewport* FindViewportByID(ImGuiID id); From 46b61427e1f7c18e90fd43ebc1188ccd8958516a Mon Sep 17 00:00:00 2001 From: omar Date: Sun, 18 Mar 2018 18:44:57 +0100 Subject: [PATCH 091/828] Viewport, Platform: Refactored platform interface. Removed need to use imgui_internal.h in backends. Split viewport into public facing ImGuiViewport and internal structure. Exposing enough data to provide custom tweaked renderers. Renamed handlers, fixed lots of inconsistencies. (#1542, #1042) --- examples/directx10_example/main.cpp | 4 +- examples/directx11_example/main.cpp | 4 +- examples/directx12_example/main.cpp | 4 +- examples/imgui_impl_dx10.cpp | 35 ++- examples/imgui_impl_dx11.cpp | 42 ++-- examples/imgui_impl_dx12.cpp | 39 ++- examples/imgui_impl_glfw.cpp | 45 ++-- examples/imgui_impl_opengl3.cpp | 24 +- examples/imgui_impl_sdl2.cpp | 42 ++-- examples/imgui_impl_vulkan.cpp | 43 ++-- examples/imgui_impl_win32.cpp | 31 +-- examples/opengl2_example/main.cpp | 2 +- examples/opengl3_example/main.cpp | 6 +- examples/sdl_opengl3_example/main.cpp | 5 +- examples/sdl_vulkan_example/main.cpp | 6 +- examples/vulkan_example/main.cpp | 6 +- imgui.cpp | 331 ++++++++++++++------------ imgui.h | 131 +++++++--- imgui_internal.h | 70 ++---- 19 files changed, 459 insertions(+), 411 deletions(-) diff --git a/examples/directx10_example/main.cpp b/examples/directx10_example/main.cpp index fb034cd8c520..6166a60ffb35 100644 --- a/examples/directx10_example/main.cpp +++ b/examples/directx10_example/main.cpp @@ -208,7 +208,9 @@ int main(int, char**) ImGui::Render(); ImGui_ImplDX10_RenderDrawData(ImGui::GetDrawData()); - ImGui::RenderAdditionalViewports(); + // Update and Render additional Platform Windows + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindows(); g_pSwapChain->Present(1, 0); // Present with vsync //g_pSwapChain->Present(0, 0); // Present without vsync diff --git a/examples/directx11_example/main.cpp b/examples/directx11_example/main.cpp index 6e81b811b4d0..b5211009d8d8 100644 --- a/examples/directx11_example/main.cpp +++ b/examples/directx11_example/main.cpp @@ -226,7 +226,9 @@ int main(int, char**) ImGui::Render(); ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData()); - ImGui::RenderAdditionalViewports(); + // Update and Render additional Platform Windows + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindows(); g_pSwapChain->Present(1, 0); // Present with vsync //g_pSwapChain->Present(0, 0); // Present without vsync diff --git a/examples/directx12_example/main.cpp b/examples/directx12_example/main.cpp index 20957f0129b2..f3d5d17139a4 100644 --- a/examples/directx12_example/main.cpp +++ b/examples/directx12_example/main.cpp @@ -401,7 +401,9 @@ int main(int, char**) g_pd3dCommandQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*)&g_pd3dCommandList); - ImGui::RenderAdditionalViewports(); + // Update and Render additional Platform Windows + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindows(); g_pSwapChain->Present(1, 0); // Present with vsync //g_pSwapChain->Present(0, 0); // Present without vsync diff --git a/examples/imgui_impl_dx10.cpp b/examples/imgui_impl_dx10.cpp index 4636e7da521c..9a14b8ffd9f6 100644 --- a/examples/imgui_impl_dx10.cpp +++ b/examples/imgui_impl_dx10.cpp @@ -11,7 +11,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2018-XX-XX: Platform: Added support for multiple windows via the ImGuiRendererInterface. +// 2018-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. // 2018-XX-XX: DirectX10: Offset projection matrix and clipping rectangle by draw_data->DisplayPos (which will be non-zero for multi-viewport applications). // 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplDX10_RenderDrawData() in the .h file so you can call it yourself. // 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. @@ -498,12 +498,9 @@ void ImGui_ImplDX10_NewFrame() ImGui_ImplDX10_CreateDeviceObjects(); } - -// -------------------------------------------------------------------------------------------------------- -// Platform Windows -// -------------------------------------------------------------------------------------------------------- - -#include "imgui_internal.h" // ImGuiViewport +//-------------------------------------------------------------------------------------------------------- +// Platform Interface (Optional, for multi-viewport support) +//-------------------------------------------------------------------------------------------------------- struct ImGuiPlatformDataDx10 { @@ -514,7 +511,7 @@ struct ImGuiPlatformDataDx10 ~ImGuiPlatformDataDx10() { IM_ASSERT(SwapChain == NULL && RTView == NULL); } }; -static void ImGui_ImplDX10_CreateViewport(ImGuiViewport* viewport) +static void ImGui_ImplDX10_CreateWindow(ImGuiViewport* viewport) { ImGuiPlatformDataDx10* data = IM_NEW(ImGuiPlatformDataDx10)(); viewport->RendererUserData = data; @@ -551,7 +548,7 @@ static void ImGui_ImplDX10_CreateViewport(ImGuiViewport* viewport) } } -static void ImGui_ImplDX10_DestroyViewport(ImGuiViewport* viewport) +static void ImGui_ImplDX10_DestroyWindow(ImGuiViewport* viewport) { if (ImGuiPlatformDataDx10* data = (ImGuiPlatformDataDx10*)viewport->RendererUserData) { @@ -566,7 +563,7 @@ static void ImGui_ImplDX10_DestroyViewport(ImGuiViewport* viewport) viewport->RendererUserData = NULL; } -static void ImGui_ImplDX10_ResizeViewport(ImGuiViewport* viewport, ImVec2 size) +static void ImGui_ImplDX10_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) { ImGuiPlatformDataDx10* data = (ImGuiPlatformDataDx10*)viewport->RendererUserData; if (data->RTView) @@ -591,7 +588,7 @@ static void ImGui_ImplDX10_RenderViewport(ImGuiViewport* viewport) g_pd3dDevice->OMSetRenderTargets(1, &data->RTView, NULL); if (!(viewport->Flags & ImGuiViewportFlags_NoRendererClear)) g_pd3dDevice->ClearRenderTargetView(data->RTView, (float*)&clear_color); - ImGui_ImplDX10_RenderDrawData(&viewport->DrawData); + ImGui_ImplDX10_RenderDrawData(viewport->DrawData); } static void ImGui_ImplDX10_SwapBuffers(ImGuiViewport* viewport) @@ -602,18 +599,16 @@ static void ImGui_ImplDX10_SwapBuffers(ImGuiViewport* viewport) void ImGui_ImplDX10_InitPlatformInterface() { - ImGuiIO& io = ImGui::GetIO(); - io.RendererInterface.CreateViewport = ImGui_ImplDX10_CreateViewport; - io.RendererInterface.DestroyViewport = ImGui_ImplDX10_DestroyViewport; - io.RendererInterface.ResizeViewport = ImGui_ImplDX10_ResizeViewport; - io.RendererInterface.RenderViewport = ImGui_ImplDX10_RenderViewport; - io.RendererInterface.SwapBuffers = ImGui_ImplDX10_SwapBuffers; + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.Renderer_CreateWindow = ImGui_ImplDX10_CreateWindow; + platform_io.Renderer_DestroyWindow = ImGui_ImplDX10_DestroyWindow; + platform_io.Renderer_SetWindowSize = ImGui_ImplDX10_SetWindowSize; + platform_io.Renderer_RenderWindow = ImGui_ImplDX10_RenderViewport; + platform_io.Renderer_SwapBuffers = ImGui_ImplDX10_SwapBuffers; } void ImGui_ImplDX10_ShutdownPlatformInterface() { - ImGui::DestroyViewportsRendererData(ImGui::GetCurrentContext()); - ImGuiIO& io = ImGui::GetIO(); - memset(&io.RendererInterface, 0, sizeof(io.RendererInterface)); + ImGui::DestroyPlatformWindows(); } diff --git a/examples/imgui_impl_dx11.cpp b/examples/imgui_impl_dx11.cpp index 09e32d80505c..244fb6a80fad 100644 --- a/examples/imgui_impl_dx11.cpp +++ b/examples/imgui_impl_dx11.cpp @@ -11,7 +11,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2018-XX-XX: Platform: Added support for multiple windows via the ImGuiRendererInterface. +// 2018-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. // 2018-XX-XX: DirectX11: Offset projection matrix and clipping rectangle by draw_data->DisplayPos (which will be non-zero for multi-viewport applications). // 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplDX11_RenderDrawData() in the .h file so you can call it yourself. // 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. @@ -507,11 +507,9 @@ void ImGui_ImplDX11_NewFrame() ImGui_ImplDX11_CreateDeviceObjects(); } -// -------------------------------------------------------------------------------------------------------- -// Platform Windows -// -------------------------------------------------------------------------------------------------------- - -#include "imgui_internal.h" // ImGuiViewport +//-------------------------------------------------------------------------------------------------------- +// Platform Interface (Optional, for multi-viewport support) +//-------------------------------------------------------------------------------------------------------- struct ImGuiPlatformDataDx11 { @@ -522,12 +520,11 @@ struct ImGuiPlatformDataDx11 ~ImGuiPlatformDataDx11() { IM_ASSERT(SwapChain == NULL && RTView == NULL); } }; -static void ImGui_ImplDX11_CreateViewport(ImGuiViewport* viewport) +static void ImGui_ImplDX11_CreateWindow(ImGuiViewport* viewport) { ImGuiPlatformDataDx11* data = IM_NEW(ImGuiPlatformDataDx11)(); viewport->RendererUserData = data; - // FIXME-PLATFORM HWND hwnd = (HWND)viewport->PlatformHandle; IM_ASSERT(hwnd != 0); @@ -559,7 +556,7 @@ static void ImGui_ImplDX11_CreateViewport(ImGuiViewport* viewport) } } -static void ImGui_ImplDX11_DestroyViewport(ImGuiViewport* viewport) +static void ImGui_ImplDX11_DestroyWindow(ImGuiViewport* viewport) { if (ImGuiPlatformDataDx11* data = (ImGuiPlatformDataDx11*)viewport->RendererUserData) { @@ -574,7 +571,7 @@ static void ImGui_ImplDX11_DestroyViewport(ImGuiViewport* viewport) viewport->RendererUserData = NULL; } -static void ImGui_ImplDX11_ResizeViewport(ImGuiViewport* viewport, ImVec2 size) +static void ImGui_ImplDX11_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) { ImGuiPlatformDataDx11* data = (ImGuiPlatformDataDx11*)viewport->RendererUserData; if (data->RTView) @@ -592,14 +589,14 @@ static void ImGui_ImplDX11_ResizeViewport(ImGuiViewport* viewport, ImVec2 size) } } -static void ImGui_ImplDX11_RenderViewport(ImGuiViewport* viewport) +static void ImGui_ImplDX11_RenderWindow(ImGuiViewport* viewport) { ImGuiPlatformDataDx11* data = (ImGuiPlatformDataDx11*)viewport->RendererUserData; ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); g_pd3dDeviceContext->OMSetRenderTargets(1, &data->RTView, NULL); if (!(viewport->Flags & ImGuiViewportFlags_NoRendererClear)) g_pd3dDeviceContext->ClearRenderTargetView(data->RTView, (float*)&clear_color); - ImGui_ImplDX11_RenderDrawData(&viewport->DrawData); + ImGui_ImplDX11_RenderDrawData(viewport->DrawData); } static void ImGui_ImplDX11_SwapBuffers(ImGuiViewport* viewport) @@ -608,20 +605,17 @@ static void ImGui_ImplDX11_SwapBuffers(ImGuiViewport* viewport) data->SwapChain->Present(0, 0); // Present without vsync } -void ImGui_ImplDX11_InitPlatformInterface() +static void ImGui_ImplDX11_InitPlatformInterface() { - ImGuiIO& io = ImGui::GetIO(); - io.RendererInterface.CreateViewport = ImGui_ImplDX11_CreateViewport; - io.RendererInterface.DestroyViewport = ImGui_ImplDX11_DestroyViewport; - io.RendererInterface.ResizeViewport = ImGui_ImplDX11_ResizeViewport; - io.RendererInterface.RenderViewport = ImGui_ImplDX11_RenderViewport; - io.RendererInterface.SwapBuffers = ImGui_ImplDX11_SwapBuffers; + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.Renderer_CreateWindow = ImGui_ImplDX11_CreateWindow; + platform_io.Renderer_DestroyWindow = ImGui_ImplDX11_DestroyWindow; + platform_io.Renderer_SetWindowSize = ImGui_ImplDX11_SetWindowSize; + platform_io.Renderer_RenderWindow = ImGui_ImplDX11_RenderWindow; + platform_io.Renderer_SwapBuffers = ImGui_ImplDX11_SwapBuffers; } -void ImGui_ImplDX11_ShutdownPlatformInterface() +static void ImGui_ImplDX11_ShutdownPlatformInterface() { - ImGui::DestroyViewportsRendererData(ImGui::GetCurrentContext()); - ImGuiIO& io = ImGui::GetIO(); - memset(&io.RendererInterface, 0, sizeof(io.RendererInterface)); + ImGui::DestroyPlatformWindows(); } - diff --git a/examples/imgui_impl_dx12.cpp b/examples/imgui_impl_dx12.cpp index dc7f1d75609e..98190ebbe2b8 100644 --- a/examples/imgui_impl_dx12.cpp +++ b/examples/imgui_impl_dx12.cpp @@ -634,11 +634,9 @@ void ImGui_ImplDX12_NewFrame(ID3D12GraphicsCommandList* command_list) g_pd3dCommandList = command_list; } -// -------------------------------------------------------------------------------------------------------- -// Platform Windows -// -------------------------------------------------------------------------------------------------------- - -#include "imgui_internal.h" // ImGuiViewport +//-------------------------------------------------------------------------------------------------------- +// Platform Interface (Optional, for multi-viewport support) +//-------------------------------------------------------------------------------------------------------- struct ImGuiPlatformDataDx12 { @@ -648,7 +646,7 @@ struct ImGuiPlatformDataDx12 ~ImGuiPlatformDataDx12() { IM_ASSERT(SwapChain == NULL); } }; -static void ImGui_ImplDX12_CreateViewport(ImGuiViewport* viewport) +static void ImGui_ImplDX12_CreateWindow(ImGuiViewport* viewport) { ImGuiPlatformDataDx12* data = IM_NEW(ImGuiPlatformDataDx12)(); viewport->RendererUserData = data; @@ -688,7 +686,7 @@ static void ImGui_ImplDX12_CreateViewport(ImGuiViewport* viewport) */ } -static void ImGui_ImplDX12_DestroyViewport(ImGuiViewport* viewport) +static void ImGui_ImplDX12_DestroyWindow(ImGuiViewport* viewport) { if (ImGuiPlatformDataDx12* data = (ImGuiPlatformDataDx12*)viewport->RendererUserData) { @@ -706,11 +704,11 @@ static void ImGui_ImplDX12_DestroyViewport(ImGuiViewport* viewport) viewport->RendererUserData = NULL; } -static void ImGui_ImplDX12_ResizeViewport(ImGuiViewport* viewport, int w, int h) +static void ImGui_ImplDX12_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) { ImGuiPlatformDataDx12* data = (ImGuiPlatformDataDx12*)viewport->RendererUserData; IM_ASSERT(0); - (void)data; (void)w; (void)h; + (void)data; (void)size; /* if (data->RTView) { @@ -720,7 +718,7 @@ static void ImGui_ImplDX12_ResizeViewport(ImGuiViewport* viewport, int w, int h) if (data->SwapChain) { ID3D11Texture2D* pBackBuffer = NULL; - data->SwapChain->ResizeBuffers(0, w, h, DXGI_FORMAT_UNKNOWN, 0); + data->SwapChain->ResizeBuffers(0, (UINT)size.x, (UINT)size.y, DXGI_FORMAT_UNKNOWN, 0); data->SwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer)); g_pd3dDevice->CreateRenderTargetView(pBackBuffer, NULL, &data->RTView); pBackBuffer->Release(); @@ -728,7 +726,7 @@ static void ImGui_ImplDX12_ResizeViewport(ImGuiViewport* viewport, int w, int h) */ } -static void ImGui_ImplDX12_RenderViewport(ImGuiViewport* viewport) +static void ImGui_ImplDX12_RenderWindow(ImGuiViewport* viewport) { ImGuiPlatformDataDx12* data = (ImGuiPlatformDataDx12*)viewport->RendererUserData; IM_ASSERT(0); @@ -739,7 +737,7 @@ static void ImGui_ImplDX12_RenderViewport(ImGuiViewport* viewport) if (!(viewport->Flags & ImGuiViewportFlags_NoRendererClear)) g_pd3dDeviceContext->ClearRenderTargetView(data->RTView, (float*)&clear_color); */ - ImGui_ImplDX12_RenderDrawData(&viewport->DrawData); + ImGui_ImplDX12_RenderDrawData(viewport->DrawData); } static void ImGui_ImplDX12_SwapBuffers(ImGuiViewport* viewport) @@ -754,18 +752,15 @@ static void ImGui_ImplDX12_SwapBuffers(ImGuiViewport* viewport) void ImGui_ImplDX12_InitPlatformInterface() { - ImGuiIO& io = ImGui::GetIO(); - io.RendererInterface.CreateViewport = ImGui_ImplDX12_CreateViewport; - io.RendererInterface.DestroyViewport = ImGui_ImplDX12_DestroyViewport; - io.RendererInterface.ResizeViewport = ImGui_ImplDX12_ResizeViewport; - io.RendererInterface.RenderViewport = ImGui_ImplDX12_RenderViewport; - io.RendererInterface.SwapBuffers = ImGui_ImplDX12_SwapBuffers; + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.Renderer_CreateWindow = ImGui_ImplDX12_CreateWindow; + platform_io.Renderer_DestroyWindow = ImGui_ImplDX12_DestroyWindow; + platform_io.Renderer_SetWindowSize = ImGui_ImplDX12_SetWindowSize; + platform_io.Renderer_RenderWindow = ImGui_ImplDX12_RenderWindow; + platform_io.Renderer_SwapBuffers = ImGui_ImplDX12_SwapBuffers; } void ImGui_ImplDX12_ShutdownPlatformInterface() { - ImGui::DestroyViewportsRendererData(ImGui::GetCurrentContext()); - ImGuiIO& io = ImGui::GetIO(); - memset(&io.RendererInterface, 0, sizeof(io.RendererInterface)); + ImGui::DestroyPlatformWindows(); } - diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index 7197bd5a34b9..c761b8f955e9 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -12,7 +12,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2018-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformInterface +// 2018-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. // 2018-XX-XX: Inputs: Added support for mouse cursors, honoring ImGui::GetMouseCursor() value. // 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. // 2018-02-06: Inputs: Added mapping for ImGuiKey_Space. @@ -25,14 +25,13 @@ #include "imgui.h" #include "imgui_impl_glfw.h" -#include "imgui_internal.h" // FIXME-PLATFORM // GLFW #include #ifdef _WIN32 #undef APIENTRY #define GLFW_EXPOSE_NATIVE_WIN32 -#include // for glfwGetWin32Window +#include // for glfwGetWin32Window #endif #ifdef GLFW_HOVERED #define GLFW_HAS_GLFW_HOVERED 1 @@ -209,10 +208,10 @@ static void ImGui_ImplGlfw_UpdateMouse() g_MouseJustPressed[i] = false; } - const ImVector& viewports = ImGui::GetViewports(); - for (int n = 0; n < viewports.Size; n++) + ImGuiPlatformData* platform_data = ImGui::GetPlatformData(); + for (int n = 0; n < platform_data->Viewports.Size; n++) { - ImGuiViewport* viewport = viewports[n]; + ImGuiViewport* viewport = platform_data->Viewports[n]; GLFWwindow* window = (GLFWwindow*)viewport->PlatformHandle; IM_ASSERT(window != NULL); if (glfwGetWindowAttrib(window, GLFW_FOCUSED)) @@ -330,7 +329,7 @@ static void ImGui_ImplGlfw_WindowSizeCallback(GLFWwindow* window, int, int) viewport->PlatformRequestResize = true; } -static void ImGui_ImplGlfw_CreateViewport(ImGuiViewport* viewport) +static void ImGui_ImplGlfw_CreateWindow(ImGuiViewport* viewport) { ImGuiPlatformDataGlfw* data = IM_NEW(ImGuiPlatformDataGlfw)(); viewport->PlatformUserData = data; @@ -349,7 +348,7 @@ static void ImGui_ImplGlfw_CreateViewport(ImGuiViewport* viewport) glfwSetWindowSizeCallback(data->Window, ImGui_ImplGlfw_WindowSizeCallback); } -static void ImGui_ImplGlfw_DestroyViewport(ImGuiViewport* viewport) +static void ImGui_ImplGlfw_DestroyWindow(ImGuiViewport* viewport) { if (ImGuiPlatformDataGlfw* data = (ImGuiPlatformDataGlfw*)viewport->PlatformUserData) { @@ -449,7 +448,7 @@ static void ImGui_ImplGlfw_SetWindowTitle(ImGuiViewport* viewport, const char* t glfwSetWindowTitle(data->Window, title); } -static void ImGui_ImplGlfw_RenderViewport(ImGuiViewport* viewport) +static void ImGui_ImplGlfw_RenderWindow(ImGuiViewport* viewport) { ImGuiPlatformDataGlfw* data = (ImGuiPlatformDataGlfw*)viewport->PlatformUserData; if (g_ClientApi == GlfwClientApi_OpenGL) @@ -491,19 +490,19 @@ static int ImGui_ImplGlfw_CreateVkSurface(ImGuiViewport* viewport, ImU64 vk_inst static void ImGui_ImplGlfw_InitPlatformInterface() { // Register platform interface (will be coupled with a renderer interface) - ImGuiIO& io = ImGui::GetIO(); - io.PlatformInterface.CreateViewport = ImGui_ImplGlfw_CreateViewport; - io.PlatformInterface.DestroyViewport = ImGui_ImplGlfw_DestroyViewport; - io.PlatformInterface.ShowWindow = ImGui_ImplGlfw_ShowWindow; - io.PlatformInterface.SetWindowPos = ImGui_ImplGlfw_SetWindowPos; - io.PlatformInterface.GetWindowPos = ImGui_ImplGlfw_GetWindowPos; - io.PlatformInterface.SetWindowSize = ImGui_ImplGlfw_SetWindowSize; - io.PlatformInterface.GetWindowSize = ImGui_ImplGlfw_GetWindowSize; - io.PlatformInterface.SetWindowTitle = ImGui_ImplGlfw_SetWindowTitle; - io.PlatformInterface.RenderViewport = ImGui_ImplGlfw_RenderViewport; - io.PlatformInterface.SwapBuffers = ImGui_ImplGlfw_SwapBuffers; + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.Platform_CreateWindow = ImGui_ImplGlfw_CreateWindow; + platform_io.Platform_DestroyWindow = ImGui_ImplGlfw_DestroyWindow; + platform_io.Platform_ShowWindow = ImGui_ImplGlfw_ShowWindow; + platform_io.Platform_SetWindowPos = ImGui_ImplGlfw_SetWindowPos; + platform_io.Platform_GetWindowPos = ImGui_ImplGlfw_GetWindowPos; + platform_io.Platform_SetWindowSize = ImGui_ImplGlfw_SetWindowSize; + platform_io.Platform_GetWindowSize = ImGui_ImplGlfw_GetWindowSize; + platform_io.Platform_SetWindowTitle = ImGui_ImplGlfw_SetWindowTitle; + platform_io.Platform_RenderWindow = ImGui_ImplGlfw_RenderWindow; + platform_io.Platform_SwapBuffers = ImGui_ImplGlfw_SwapBuffers; #if GLFW_HAS_VULKAN - io.PlatformInterface.CreateVkSurface = ImGui_ImplGlfw_CreateVkSurface; + platform_io.Platform_CreateVkSurface = ImGui_ImplGlfw_CreateVkSurface; #endif // Register main window handle @@ -516,8 +515,4 @@ static void ImGui_ImplGlfw_InitPlatformInterface() static void ImGui_ImplGlfw_ShutdownPlatformInterface() { - ImGuiIO& io = ImGui::GetIO(); - ImGuiViewport* main_viewport = ImGui::GetMainViewport(); - main_viewport->PlatformHandle = NULL; - memset(&io.PlatformInterface, 0, sizeof(io.PlatformInterface)); } diff --git a/examples/imgui_impl_opengl3.cpp b/examples/imgui_impl_opengl3.cpp index 42f2ccb6dbe9..11083df8e82e 100644 --- a/examples/imgui_impl_opengl3.cpp +++ b/examples/imgui_impl_opengl3.cpp @@ -323,13 +323,11 @@ void ImGui_ImplOpenGL3_DestroyDeviceObjects() ImGui_ImplOpenGL3_DestroyFontsTexture(); } -// -------------------------------------------------------------------------------------------------------- -// Platform Windows -// -------------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------------- +// Platform Interface (Optional, for multi-viewport support) +//-------------------------------------------------------------------------------------------------------- -#include "imgui_internal.h" // ImGuiViewport - -static void ImGui_ImplOpenGL3_RenderViewport(ImGuiViewport* viewport) +static void ImGui_ImplOpenGL3_RenderWindow(ImGuiViewport* viewport) { if (!(viewport->Flags & ImGuiViewportFlags_NoRendererClear)) { @@ -337,18 +335,16 @@ static void ImGui_ImplOpenGL3_RenderViewport(ImGuiViewport* viewport) glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w); glClear(GL_COLOR_BUFFER_BIT); } - ImGui_ImplOpenGL3_RenderDrawData(&viewport->DrawData); + ImGui_ImplOpenGL3_RenderDrawData(viewport->DrawData); } -void ImGui_ImplOpenGL3_InitPlatformInterface() +static void ImGui_ImplOpenGL3_InitPlatformInterface() { - ImGuiIO& io = ImGui::GetIO(); - io.RendererInterface.RenderViewport = ImGui_ImplOpenGL3_RenderViewport; + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.Renderer_RenderWindow = ImGui_ImplOpenGL3_RenderWindow; } -void ImGui_ImplOpenGL3_ShutdownPlatformInterface() +static void ImGui_ImplOpenGL3_ShutdownPlatformInterface() { - ImGui::DestroyViewportsRendererData(ImGui::GetCurrentContext()); - ImGuiIO& io = ImGui::GetIO(); - memset(&io.RendererInterface, 0, sizeof(io.RendererInterface)); + ImGui::DestroyPlatformWindows(); } diff --git a/examples/imgui_impl_sdl2.cpp b/examples/imgui_impl_sdl2.cpp index 8fe81f2accbe..53a30dfdd31b 100644 --- a/examples/imgui_impl_sdl2.cpp +++ b/examples/imgui_impl_sdl2.cpp @@ -12,7 +12,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2018-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformInterface +// 2018-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. // 2018-XX-XX: Misc: ImGui_ImplSDL2_Init() now takes a SDL_GLContext parameter. // 2018-02-16: Inputs: Added support for mouse cursors, honoring ImGui::GetMouseCursor() value. // 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. @@ -27,7 +27,6 @@ #include "imgui.h" #include "imgui_impl_sdl2.h" -#include "imgui_internal.h" // FIXME-PLATFORM // SDL #include @@ -271,9 +270,9 @@ void ImGui_ImplSDL2_NewFrame(SDL_Window* window) ImGui::NewFrame(); } -// -------------------------------------------------------------------------------------------------------- -// Platform Windows -// -------------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------------- +// Platform Interface (Optional, for multi-viewport support) +//-------------------------------------------------------------------------------------------------------- struct ImGuiPlatformDataSDL2 { @@ -285,7 +284,7 @@ struct ImGuiPlatformDataSDL2 ~ImGuiPlatformDataSDL2() { IM_ASSERT(Window == NULL && GLContext == NULL); } }; -static void ImGui_ImplSDL2_CreateViewport(ImGuiViewport* viewport) +static void ImGui_ImplSDL2_CreateWindow(ImGuiViewport* viewport) { ImGuiPlatformDataSDL2* data = IM_NEW(ImGuiPlatformDataSDL2)(); viewport->PlatformUserData = data; @@ -319,7 +318,7 @@ static void ImGui_ImplSDL2_CreateViewport(ImGuiViewport* viewport) viewport->PlatformHandle = (void*)data->Window; } -static void ImGui_ImplSDL2_DestroyViewport(ImGuiViewport* viewport) +static void ImGui_ImplSDL2_DestroyWindow(ImGuiViewport* viewport) { if (ImGuiPlatformDataSDL2* data = (ImGuiPlatformDataSDL2*)viewport->PlatformUserData) { @@ -401,7 +400,7 @@ static void ImGui_ImplSDL2_SetWindowTitle(ImGuiViewport* viewport, const char* t SDL_SetWindowTitle(data->Window, title); } -static void ImGui_ImplSDL2_RenderViewport(ImGuiViewport* viewport) +static void ImGui_ImplSDL2_RenderWindow(ImGuiViewport* viewport) { ImGuiPlatformDataSDL2* data = (ImGuiPlatformDataSDL2*)viewport->PlatformUserData; if (data->GLContext) @@ -434,21 +433,22 @@ static int ImGui_ImplSDL2_CreateVkSurface(ImGuiViewport* viewport, ImU64 vk_inst static void ImGui_ImplSDL2_InitPlatformInterface(SDL_Window* window, void* sdl_gl_context) { // Register platform interface (will be coupled with a renderer interface) - ImGuiIO& io = ImGui::GetIO(); - io.PlatformInterface.CreateViewport = ImGui_ImplSDL2_CreateViewport; - io.PlatformInterface.DestroyViewport = ImGui_ImplSDL2_DestroyViewport; - io.PlatformInterface.ShowWindow = ImGui_ImplSDL2_ShowWindow; - io.PlatformInterface.SetWindowPos = ImGui_ImplSDL2_SetWindowPos; - io.PlatformInterface.GetWindowPos = ImGui_ImplSDL2_GetWindowPos; - io.PlatformInterface.SetWindowSize = ImGui_ImplSDL2_SetWindowSize; - io.PlatformInterface.GetWindowSize = ImGui_ImplSDL2_GetWindowSize; - io.PlatformInterface.SetWindowTitle = ImGui_ImplSDL2_SetWindowTitle; - io.PlatformInterface.RenderViewport = ImGui_ImplSDL2_RenderViewport; - io.PlatformInterface.SwapBuffers = ImGui_ImplSDL2_SwapBuffers; + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.Platform_CreateWindow = ImGui_ImplSDL2_CreateWindow; + platform_io.Platform_DestroyWindow = ImGui_ImplSDL2_DestroyWindow; + platform_io.Platform_ShowWindow = ImGui_ImplSDL2_ShowWindow; + platform_io.Platform_SetWindowPos = ImGui_ImplSDL2_SetWindowPos; + platform_io.Platform_GetWindowPos = ImGui_ImplSDL2_GetWindowPos; + platform_io.Platform_SetWindowSize = ImGui_ImplSDL2_SetWindowSize; + platform_io.Platform_GetWindowSize = ImGui_ImplSDL2_GetWindowSize; + platform_io.Platform_SetWindowTitle = ImGui_ImplSDL2_SetWindowTitle; + platform_io.Platform_RenderWindow = ImGui_ImplSDL2_RenderWindow; + platform_io.Platform_SwapBuffers = ImGui_ImplSDL2_SwapBuffers; #if SDL_HAS_VULKAN - io.PlatformInterface.CreateVkSurface = ImGui_ImplSDL2_CreateVkSurface; + platform_io.Platform_CreateVkSurface = ImGui_ImplSDL2_CreateVkSurface; #endif + ImGuiIO& io = ImGui::GetIO(); io.ConfigFlags |= SDL_HAS_WINDOW_OPACITY ? ImGuiConfigFlags_PlatformHasWindowAlpha : 0; // Register main window handle @@ -463,6 +463,4 @@ static void ImGui_ImplSDL2_InitPlatformInterface(SDL_Window* window, void* sdl_g static void ImGui_ImplSDL2_ShutdownPlatformInterface() { - ImGuiIO& io = ImGui::GetIO(); - memset(&io.PlatformInterface, 0, sizeof(io.PlatformInterface)); } diff --git a/examples/imgui_impl_vulkan.cpp b/examples/imgui_impl_vulkan.cpp index fe7af6ddf7c2..714f1dcf688a 100644 --- a/examples/imgui_impl_vulkan.cpp +++ b/examples/imgui_impl_vulkan.cpp @@ -23,6 +23,7 @@ #include "imgui.h" #include "imgui_impl_vulkan.h" +#include // Vulkan data static const VkAllocationCallbacks* g_Allocator = NULL; @@ -714,10 +715,7 @@ bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info, VkRenderPass rend ImGui_ImplVulkan_CreateDeviceObjects(); io.ConfigFlags |= ImGuiConfigFlags_RendererHasViewports; if (io.ConfigFlags & ImGuiConfigFlags_EnableViewports) - { - IM_ASSERT(io.PlatformInterface.CreateVkSurface != NULL); ImGui_ImplVulkan_InitPlatformInterface(); - } return true; } @@ -734,7 +732,6 @@ void ImGui_ImplVulkan_NewFrame() //------------------------------------------------------------------------- // Miscellaneous Vulkan Helpers -// (Those are currently not strictly needed by the binding, but will be once if we support multi-viewports) //------------------------------------------------------------------------- #include // malloc @@ -1049,11 +1046,10 @@ void ImGui_ImplVulkanH_DestroyWindowData(VkInstance instance, VkDevice device, I } //-------------------------------------------------------------------------------------------------------- -// Platform Windows (OPTIONAL/EXPERIMENTAL) +// Platform Interface (Optional, for multi-viewport support) +// FIXME-PLATFORM: Vulkan support unfinished //-------------------------------------------------------------------------------------------------------- -#include "imgui_internal.h" // ImGuiViewport - struct ImGuiPlatformDataVulkan { ImGui_ImplVulkan_WindowData WindowData; @@ -1062,15 +1058,15 @@ struct ImGuiPlatformDataVulkan ~ImGuiPlatformDataVulkan() { } }; -static void ImGui_ImplVulkan_CreateViewport(ImGuiViewport* viewport) +static void ImGui_ImplVulkan_CreateWindow(ImGuiViewport* viewport) { ImGuiPlatformDataVulkan* data = IM_NEW(ImGuiPlatformDataVulkan)(); viewport->RendererUserData = data; ImGui_ImplVulkan_WindowData* wd = &data->WindowData; // Create surface - ImGuiIO& io = ImGui::GetIO(); - VkResult err = (VkResult)io.PlatformInterface.CreateVkSurface(viewport, (ImU64)g_Instance, (const void*)g_Allocator, (ImU64*)&wd->Surface); + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + VkResult err = (VkResult)platform_io.Platform_CreateVkSurface(viewport, (ImU64)g_Instance, (const void*)g_Allocator, (ImU64*)&wd->Surface); check_vk_result(err); // Check for WSI support @@ -1097,7 +1093,7 @@ static void ImGui_ImplVulkan_CreateViewport(ImGuiViewport* viewport) ImGui_ImplVulkanH_CreateWindowDataSwapChainAndFramebuffer(g_PhysicalDevice, g_Device, wd, g_Allocator, (int)viewport->Size.x, (int)viewport->Size.y); } -static void ImGui_ImplVulkan_DestroyViewport(ImGuiViewport* viewport) +static void ImGui_ImplVulkan_DestroyWindow(ImGuiViewport* viewport) { if (ImGuiPlatformDataVulkan* data = (ImGuiPlatformDataVulkan*)viewport->RendererUserData) { @@ -1107,7 +1103,7 @@ static void ImGui_ImplVulkan_DestroyViewport(ImGuiViewport* viewport) viewport->RendererUserData = NULL; } -static void ImGui_ImplVulkan_ResizeViewport(ImGuiViewport* viewport, ImVec2 size) +static void ImGui_ImplVulkan_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) { ImGuiPlatformDataVulkan* data = (ImGuiPlatformDataVulkan*)viewport->RendererUserData; if (data == NULL) // This is NULL for the main viewport (which is left to the user/app to handle) @@ -1116,7 +1112,7 @@ static void ImGui_ImplVulkan_ResizeViewport(ImGuiViewport* viewport, ImVec2 size ImGui_ImplVulkanH_CreateWindowDataSwapChainAndFramebuffer(g_PhysicalDevice, g_Device, &data->WindowData, g_Allocator, (int)size.x, (int)size.y); } -static void ImGui_ImplVulkan_RenderViewport(ImGuiViewport* viewport) +static void ImGui_ImplVulkan_RenderWindow(ImGuiViewport* viewport) { ImGuiPlatformDataVulkan* data = (ImGuiPlatformDataVulkan*)viewport->RendererUserData; ImGui_ImplVulkan_WindowData* wd = &data->WindowData; @@ -1160,7 +1156,7 @@ static void ImGui_ImplVulkan_RenderViewport(ImGuiViewport* viewport) } } - ImGui_ImplVulkan_RenderDrawData(wd->Frames[wd->FrameIndex].CommandBuffer, &viewport->DrawData); + ImGui_ImplVulkan_RenderDrawData(wd->Frames[wd->FrameIndex].CommandBuffer, viewport->DrawData); { ImGui_ImplVulkan_FrameData* fd = &wd->Frames[wd->FrameIndex]; @@ -1210,18 +1206,17 @@ static void ImGui_ImplVulkan_SwapBuffers(ImGuiViewport* viewport) void ImGui_ImplVulkan_InitPlatformInterface() { - ImGuiIO& io = ImGui::GetIO(); - IM_ASSERT(io.PlatformInterface.CreateVkSurface != NULL); - io.RendererInterface.CreateViewport = ImGui_ImplVulkan_CreateViewport; - io.RendererInterface.DestroyViewport = ImGui_ImplVulkan_DestroyViewport; - io.RendererInterface.ResizeViewport = ImGui_ImplVulkan_ResizeViewport; - io.RendererInterface.RenderViewport = ImGui_ImplVulkan_RenderViewport; - io.RendererInterface.SwapBuffers = ImGui_ImplVulkan_SwapBuffers; + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_EnableViewports) + IM_ASSERT(platform_io.Platform_CreateVkSurface != NULL && "Platform needs to setup the CreateVkSurface handler."); + platform_io.Renderer_CreateWindow = ImGui_ImplVulkan_CreateWindow; + platform_io.Renderer_DestroyWindow = ImGui_ImplVulkan_DestroyWindow; + platform_io.Renderer_SetWindowSize = ImGui_ImplVulkan_SetWindowSize; + platform_io.Renderer_RenderWindow = ImGui_ImplVulkan_RenderWindow; + platform_io.Renderer_SwapBuffers = ImGui_ImplVulkan_SwapBuffers; } void ImGui_ImplVulkan_ShutdownPlatformInterface() { - ImGui::DestroyViewportsRendererData(ImGui::GetCurrentContext()); - ImGuiIO& io = ImGui::GetIO(); - memset(&io.RendererInterface, 0, sizeof(io.RendererInterface)); + ImGui::DestroyPlatformWindows(); } diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index 70cb8bd92033..744253eb0071 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -7,10 +7,8 @@ #include #include -#include "imgui_internal.h" // FIXME-PLATFORM - // CHANGELOG -// 2018-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformInterface +// 2018-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. // 2018-02-20: Inputs: Added support for mouse cursors (ImGui::GetMouseCursor() value and WM_SETCURSOR message handling). // 2018-02-06: Inputs: Added mapping for ImGuiKey_Space. // 2018-02-06: Inputs: Honoring the io.WantMoveMouse by repositioning the mouse (when using navigation and ImGuiConfigFlags_NavMoveMouse is set). @@ -363,7 +361,7 @@ struct ImGuiPlatformDataWin32 ~ImGuiPlatformDataWin32() { IM_ASSERT(Hwnd == NULL); } }; -static void ImGui_ImplWin32_CreateViewport(ImGuiViewport* viewport) +static void ImGui_ImplWin32_CreateWindow(ImGuiViewport* viewport) { ImGuiPlatformDataWin32* data = IM_NEW(ImGuiPlatformDataWin32)(); viewport->PlatformUserData = data; @@ -393,7 +391,7 @@ static void ImGui_ImplWin32_CreateViewport(ImGuiViewport* viewport) viewport->PlatformHandle = data->Hwnd; } -static void ImGui_ImplWin32_DestroyViewport(ImGuiViewport* viewport) +static void ImGui_ImplWin32_DestroyWindow(ImGuiViewport* viewport) { if (ImGuiPlatformDataWin32* data = (ImGuiPlatformDataWin32*)viewport->PlatformUserData) { @@ -523,16 +521,16 @@ static void ImGui_ImplWin32_InitPlatformInterface() ::RegisterClassEx(&wcex); // Register platform interface (will be coupled with a renderer interface) - ImGuiIO& io = ImGui::GetIO(); - io.PlatformInterface.CreateViewport = ImGui_ImplWin32_CreateViewport; - io.PlatformInterface.DestroyViewport = ImGui_ImplWin32_DestroyViewport; - io.PlatformInterface.ShowWindow = ImGui_ImplWin32_ShowWindow; - io.PlatformInterface.SetWindowPos = ImGui_ImplWin32_SetWindowPos; - io.PlatformInterface.GetWindowPos = ImGui_ImplWin32_GetWindowPos; - io.PlatformInterface.SetWindowSize = ImGui_ImplWin32_SetWindowSize; - io.PlatformInterface.GetWindowSize = ImGui_ImplWin32_GetWindowSize; - io.PlatformInterface.SetWindowTitle = ImGui_ImplWin32_SetWindowTitle; - io.PlatformInterface.GetWindowDpiScale = ImGui_ImplWin32_GetWindowDpiScale; + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.Platform_CreateWindow = ImGui_ImplWin32_CreateWindow; + platform_io.Platform_DestroyWindow = ImGui_ImplWin32_DestroyWindow; + platform_io.Platform_ShowWindow = ImGui_ImplWin32_ShowWindow; + platform_io.Platform_SetWindowPos = ImGui_ImplWin32_SetWindowPos; + platform_io.Platform_GetWindowPos = ImGui_ImplWin32_GetWindowPos; + platform_io.Platform_SetWindowSize = ImGui_ImplWin32_SetWindowSize; + platform_io.Platform_GetWindowSize = ImGui_ImplWin32_GetWindowSize; + platform_io.Platform_SetWindowTitle = ImGui_ImplWin32_SetWindowTitle; + platform_io.Platform_GetWindowDpiScale = ImGui_ImplWin32_GetWindowDpiScale; // Register main window handle ImGuiViewport* main_viewport = ImGui::GetMainViewport(); @@ -544,8 +542,5 @@ static void ImGui_ImplWin32_InitPlatformInterface() static void ImGui_ImplWin32_ShutdownPlatformInterface() { - ImGuiIO& io = ImGui::GetIO(); - memset(&io.PlatformInterface, 0, sizeof(io.PlatformInterface)); - ::UnregisterClass(_T("ImGui Platform"), ::GetModuleHandle(NULL)); } diff --git a/examples/opengl2_example/main.cpp b/examples/opengl2_example/main.cpp index 2ba7a0542dee..b8372c17afa1 100644 --- a/examples/opengl2_example/main.cpp +++ b/examples/opengl2_example/main.cpp @@ -106,13 +106,13 @@ int main(int, char**) } // Rendering + ImGui::Render(); int display_w, display_h; glfwGetFramebufferSize(window, &display_w, &display_h); glViewport(0, 0, display_w, display_h); glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w); glClear(GL_COLOR_BUFFER_BIT); //glUseProgram(0); // You may want this if using this code in an OpenGL 3+ context where shaders may be bound, but prefer using the GL3+ code. - ImGui::Render(); ImGui_ImplOpenGL2_RenderDrawData(ImGui::GetDrawData()); glfwSwapBuffers(window); } diff --git a/examples/opengl3_example/main.cpp b/examples/opengl3_example/main.cpp index 69ec99a7e7a2..b5c4f17bf834 100644 --- a/examples/opengl3_example/main.cpp +++ b/examples/opengl3_example/main.cpp @@ -115,16 +115,18 @@ int main(int, char**) } // Rendering + ImGui::Render(); int display_w, display_h; glfwMakeContextCurrent(window); glfwGetFramebufferSize(window, &display_w, &display_h); glViewport(0, 0, display_w, display_h); glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w); glClear(GL_COLOR_BUFFER_BIT); - ImGui::Render(); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); - ImGui::RenderAdditionalViewports(); + // Update and Render additional Platform Windows + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindows(); glfwMakeContextCurrent(window); glfwSwapBuffers(window); diff --git a/examples/sdl_opengl3_example/main.cpp b/examples/sdl_opengl3_example/main.cpp index f1fffcaf66d1..0e4c40c4ebcf 100644 --- a/examples/sdl_opengl3_example/main.cpp +++ b/examples/sdl_opengl3_example/main.cpp @@ -131,7 +131,10 @@ int main(int, char**) glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w); glClear(GL_COLOR_BUFFER_BIT); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); - ImGui::RenderAdditionalViewports(); + + // Update and Render additional Platform Windows + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindows(); SDL_GL_MakeCurrent(window, gl_context); SDL_GL_SwapWindow(window); diff --git a/examples/sdl_vulkan_example/main.cpp b/examples/sdl_vulkan_example/main.cpp index 08db99ce12c2..4ec3083b45ed 100644 --- a/examples/sdl_vulkan_example/main.cpp +++ b/examples/sdl_vulkan_example/main.cpp @@ -468,7 +468,11 @@ int main(int, char**) ImGui::Render(); memcpy(&wd->ClearValue.color.float32[0], &clear_color, 4 * sizeof(float)); FrameRender(wd); - ImGui::RenderAdditionalViewports(); + + // Update and Render additional Platform Windows + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindows(); + FramePresent(wd); } diff --git a/examples/vulkan_example/main.cpp b/examples/vulkan_example/main.cpp index cfcc6605f848..111613f7ae3b 100644 --- a/examples/vulkan_example/main.cpp +++ b/examples/vulkan_example/main.cpp @@ -478,7 +478,11 @@ int main(int, char**) ImGui::Render(); memcpy(&wd->ClearValue.color.float32[0], &clear_color, 4 * sizeof(float)); FrameRender(wd); - ImGui::RenderAdditionalViewports(); + + // Update and Render additional Platform Windows + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindows(); + FramePresent(wd); } diff --git a/imgui.cpp b/imgui.cpp index 219f6f615cbd..8310917e8a18 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -739,12 +739,13 @@ static void FocusFrontMostActiveWindow(ImGuiWindow* ignore_window); // Viewport const ImGuiID IMGUI_VIEWPORT_DEFAULT_ID = 0x11111111; // Using a constant instead of e.g. ImHash("ViewportDefault", 0); so it's easier to spot in the debugger. The exact value doesn't matter. static inline ImRect GetViewportRect(ImGuiWindow* window) { return window->Viewport->GetRect(); } -static inline ImVec2 ConvertViewportPosToPlatformPos(const ImVec2& imgui_pos, ImGuiViewport* viewport) { return imgui_pos - viewport->Pos + viewport->PlatformPos; } +static inline ImVec2 ConvertViewportPosToPlatformPos(const ImVec2& imgui_pos, ImGuiViewport* viewport) { return imgui_pos - viewport->Pos + viewport->PlatformPos; } static inline ImVec2 ConvertPlatformPosToViewportPos(const ImVec2& platform_pos, ImGuiViewport* viewport) { return platform_pos - viewport->PlatformPos + viewport->Pos; } +static ImGuiViewportP* Viewport(ImGuiWindow* window, ImGuiID id, ImGuiViewportFlags flags, const ImVec2& platform_pos, const ImVec2& size); static void UpdateViewports(); static void UpdateWindowViewport(ImGuiWindow* window, bool window_pos_set_by_api); -static void SetCurrentViewport(ImGuiViewport* viewport); -static void SetWindowViewportTranslateToPreservePlatformPos(ImGuiWindow* window, ImGuiViewport* old_viewport, ImGuiViewport* new_viewport); +static void SetCurrentViewport(ImGuiViewportP* viewport); +static void SetWindowViewportTranslateToPreservePlatformPos(ImGuiWindow* window, ImGuiViewportP* old_viewport, ImGuiViewportP* new_viewport); static void ResizeViewportTranslateWindows(int viewport_idx_min, int viewport_idx_max, float pos_x_delta, int idx_delta, ImGuiViewport* viewport_to_erase); } @@ -891,9 +892,6 @@ ImGuiIO::ImGuiIO() ImeSetInputScreenPosFn = ImeSetInputScreenPosFn_DefaultImpl; ImeWindowHandle = NULL; - memset(&PlatformInterface, 0, sizeof(PlatformInterface)); - memset(&RendererInterface, 0, sizeof(RendererInterface)); - #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS RenderDrawListsFn = NULL; #endif @@ -2659,6 +2657,12 @@ ImGuiIO& ImGui::GetIO() return GImGui->IO; } +ImGuiPlatformIO& ImGui::GetPlatformIO() +{ + IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() or ImGui::SetCurrentContext()?"); + return GImGui->PlatformIO; +} + ImGuiStyle& ImGui::GetStyle() { IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() or ImGui::SetCurrentContext()?"); @@ -2669,13 +2673,7 @@ ImGuiStyle& ImGui::GetStyle() ImDrawData* ImGui::GetDrawData() { ImGuiContext& g = *GImGui; - return g.Viewports[0]->DrawData.Valid ? &g.Viewports[0]->DrawData : NULL; -} - -ImDrawData* ImGui::GetDrawDataForViewport(ImGuiID viewport_id) -{ - ImGuiViewport* viewport = FindViewportByID(viewport_id); - return viewport && viewport->DrawData.Valid ? &viewport->DrawData : NULL; + return g.Viewports[0]->DrawDataP.Valid ? &g.Viewports[0]->DrawDataP : NULL; } float ImGui::GetTime() @@ -3255,10 +3253,10 @@ static void ImGui::UpdateMovingWindowDropViewport(ImGuiWindow* window) ImRect mouse_viewport_rect = g.MouseViewport->GetRect(); ImVec2 window_pos_in_mouse_viewport = ConvertPlatformPosToViewportPos(ConvertViewportPosToPlatformPos(window->Pos, window->Viewport), g.MouseViewport); ImRect window_rect_in_mouse_viewport = ImRect(window_pos_in_mouse_viewport, window_pos_in_mouse_viewport + window->Size); - if ((g.MouseViewport->Flags & ImGuiViewportFlags_HostOtherWindows) && mouse_viewport_rect.Contains(window_rect_in_mouse_viewport)) + if ((g.MouseViewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) && mouse_viewport_rect.Contains(window_rect_in_mouse_viewport)) { // Drop on an existing viewport - ImGuiViewport* old_viewport = window->Viewport; + ImGuiViewportP* old_viewport = window->Viewport; SetWindowViewportTranslateToPreservePlatformPos(window, window->Viewport, g.MouseViewport); // Our current scheme allow any window to land on a viewport, so when that viewport merges, move other windows as well @@ -3272,7 +3270,7 @@ static void ImGui::UpdateMovingWindowDropViewport(ImGuiWindow* window) { // Create new viewport ImVec2 platform_pos = ConvertViewportPosToPlatformPos(window->Pos, window->Viewport); - ImGuiViewport* viewport = Viewport(window, window->ID, 0, platform_pos, window->Size); + ImGuiViewportP* viewport = Viewport(window, window->ID, 0, platform_pos, window->Size); SetWindowViewportTranslateToPreservePlatformPos(window, window->Viewport, viewport); } } @@ -3326,13 +3324,13 @@ static void ImGui::UpdateMovingWindow() // If the back-end doesn't set MouseLastHoveredViewport or doesn't honor ImGuiViewportFlags_NoInputs, we do a search ourselves. // This search won't take account of the possibility that non-imgui windows may be in-between our dragged window and our target window. -static ImGuiViewport* FindViewportHoveredFromPlatformWindowStack(const ImVec2 mouse_platform_pos) +static ImGuiViewportP* FindViewportHoveredFromPlatformWindowStack(const ImVec2 mouse_platform_pos) { ImGuiContext& g = *GImGui; - ImGuiViewport* best_candidate = NULL; + ImGuiViewportP* best_candidate = NULL; for (int n = 0; n < g.Viewports.Size; n++) { - ImGuiViewport* viewport = g.Viewports[n]; + ImGuiViewportP* viewport = g.Viewports[n]; ImRect platform_rect = ImRect(viewport->PlatformPos, viewport->PlatformPos + viewport->Size); if (!(viewport->Flags & ImGuiViewportFlags_NoInputs) && platform_rect.Contains(mouse_platform_pos)) if (best_candidate == NULL || best_candidate->LastFrameAsRefViewport < viewport->LastFrameAsRefViewport) @@ -3344,33 +3342,31 @@ static ImGuiViewport* FindViewportHoveredFromPlatformWindowStack(const ImVec2 mo static void ImGui::UpdateViewports() { ImGuiContext& g = *GImGui; + IM_ASSERT(g.PlatformData.Viewports.Size <= g.Viewports.Size); // Mouse handling: latch the expected mouse OS position (if any) before processing viewport erasure - ImGuiViewport* viewport_ref = g.IO.MousePosViewport ? FindViewportByID(g.IO.MousePosViewport) : g.Viewports[0]; + ImGuiViewportP* viewport_ref = g.IO.MousePosViewport ? FindViewportByID(g.IO.MousePosViewport) : g.Viewports[0]; const ImVec2 mouse_platform_pos = ConvertViewportPosToPlatformPos(g.IO.MousePos, viewport_ref); g.CurrentViewport = NULL; for (int n = 0; n < g.Viewports.Size; n++) { // Erase unused viewports - ImGuiViewport* viewport = g.Viewports[n]; + ImGuiViewportP* viewport = g.Viewports[n]; IM_ASSERT(viewport->Idx == n); if (n > 0 && viewport->LastFrameActive < g.FrameCount - 2) { // Translate windows like if we were resizing the viewport to be zero-width ResizeViewportTranslateWindows(n + 1, g.Viewports.Size, viewport->Pos.x - viewport->GetNextX(), -1, viewport); - if (g.IO.RendererInterface.DestroyViewport) - g.IO.RendererInterface.DestroyViewport(viewport); - if (g.IO.PlatformInterface.DestroyViewport) - g.IO.PlatformInterface.DestroyViewport(viewport); - viewport->PlatformUserData = viewport->PlatformHandle = viewport->RendererUserData = NULL; g.Viewports.erase(g.Viewports.Data + n); // Destroy if (viewport == viewport_ref) viewport_ref = NULL; if (viewport == g.MouseViewport) g.MouseViewport = NULL; if (viewport == g.MouseLastHoveredViewport) g.MouseLastHoveredViewport = NULL; + IM_ASSERT(viewport->RendererUserData == NULL && viewport->PlatformUserData == NULL && viewport->PlatformHandle == NULL); + IM_ASSERT(g.PlatformData.Viewports.contains(viewport) == false); IM_DELETE(viewport); n--; continue; @@ -3384,16 +3380,18 @@ static void ImGui::UpdateViewports() ResizeViewportTranslateWindows(viewport->Idx + 1, g.Viewports.Size, dx, 0, NULL); } - // Apply Platform Size to ImGui Size if requested - // We do it here instead of UpdatePlatformWindows() to allow the platform back-end to set PlatformRequestResize early - // (e.g. in their own message handler before NewFrame) and not have a frame of lag with it. + // Apply Position and Size (from Platform Window to ImGui) if requested + // We do it here early in the frame instead of UpdatePlatformWindows() to allow the platform back-end to set PlatformRequestResize early + // (e.g. in their own message handler before NewFrame) and not have a frame of lag. + if (viewport->PlatformRequestMove) + viewport->PlatformPos = g.PlatformIO.Platform_GetWindowPos(viewport); if (viewport->PlatformRequestResize) - viewport->Size = g.IO.PlatformInterface.GetWindowSize(viewport); + viewport->Size = g.PlatformIO.Platform_GetWindowSize(viewport); // Update DPI Scale float new_dpi_scale; - if (g.IO.PlatformInterface.GetWindowDpiScale) - new_dpi_scale = g.IO.PlatformInterface.GetWindowDpiScale(viewport); + if (g.PlatformIO.Platform_GetWindowDpiScale) + new_dpi_scale = g.PlatformIO.Platform_GetWindowDpiScale(viewport); else new_dpi_scale = (viewport->DpiScale != 0.0f) ? viewport->DpiScale : 1.0f; if (viewport->DpiScale != 0.0f && new_dpi_scale != viewport->DpiScale) @@ -3402,7 +3400,7 @@ static void ImGui::UpdateViewports() if (g.IO.ConfigFlags & ImGuiConfigFlags_EnableDpiScaleViewports) ScaleWindowsInViewport(viewport, scale_factor); //if (viewport == GetMainViewport()) - // g.IO.PlatformInterface.SetWindowSize(viewport, viewport->Size * scale_factor); + // g.PlatformInterface.SetWindowSize(viewport, viewport->Size * scale_factor); // FIXME-DPI: We need to preserve our pivots //if (g.MovingWindow) @@ -3412,12 +3410,12 @@ static void ImGui::UpdateViewports() } // Update main viewport with current size (and OS window position, if known) - ImGuiViewport* main_viewport = g.Viewports[0]; + ImGuiViewportP* main_viewport = g.Viewports[0]; IM_ASSERT(main_viewport->ID == IMGUI_VIEWPORT_DEFAULT_ID); ImVec2 main_viewport_platform_pos = ImVec2(0.0f, 0.0f); if ((g.IO.ConfigFlags & ImGuiConfigFlags_EnableViewports)) - main_viewport_platform_pos = g.IO.PlatformInterface.GetWindowPos(main_viewport); - Viewport(NULL, IMGUI_VIEWPORT_DEFAULT_ID, ImGuiViewportFlags_MainViewport | ImGuiViewportFlags_HostOtherWindows, main_viewport_platform_pos, g.IO.DisplaySize); + main_viewport_platform_pos = g.PlatformIO.Platform_GetWindowPos(main_viewport); + Viewport(NULL, IMGUI_VIEWPORT_DEFAULT_ID, ImGuiViewportFlags_CanHostOtherWindows, main_viewport_platform_pos, g.IO.DisplaySize); if (!(g.IO.ConfigFlags & ImGuiConfigFlags_EnableViewports)) { @@ -3426,7 +3424,7 @@ static void ImGui::UpdateViewports() } // Mouse handling: decide on the actual mouse viewport for this frame between the active/focused viewport and the hovered viewport. - ImGuiViewport* viewport_hovered = NULL; + ImGuiViewportP* viewport_hovered = NULL; if (g.IO.ConfigFlags & ImGuiConfigFlags_PlatformHasMouseHoveredViewport) { viewport_hovered = g.IO.MouseHoveredViewport ? FindViewportByID(g.IO.MouseHoveredViewport) : NULL; @@ -3474,52 +3472,75 @@ static void ImGui::UpdateViewports() IM_ASSERT(g.MouseViewport != NULL); } -static void UpdatePlatformWindows() +ImGuiPlatformData* ImGui::GetPlatformData() +{ + return &GImGui->PlatformData; +} + +void ImGui::UpdatePlatformWindows() { - // Create/resize windows ImGuiContext& g = *GImGui; - for (int i = 0; i < g.Viewports.Size; i++) + IM_ASSERT(g.FrameCountEnded == g.FrameCount && "Forgot to call Render() or EndFrame() before UpdatePlatformWindows()?"); + IM_ASSERT(g.FrameCountPlatformEnded < g.FrameCount); + g.FrameCountPlatformEnded = g.FrameCount; + + g.PlatformData.MainViewport = g.Viewports[0]; + g.PlatformData.Viewports.resize(0); + g.PlatformData.Viewports.push_back(g.Viewports[0]); + if (!(g.IO.ConfigFlags & ImGuiConfigFlags_EnableViewports)) + return; + + // Create/resize/destroy platform windows and update the list that the user can process + for (int i = 1; i < g.Viewports.Size; i++) { - ImGuiViewport* viewport = g.Viewports[i]; - if ((viewport->Flags & ImGuiViewportFlags_MainViewport) || (viewport->LastFrameActive < g.FrameCount)) + ImGuiViewportP* viewport = g.Viewports[i]; + if (viewport->LastFrameActive < g.FrameCount) + { + if (viewport->LastFrameActive < g.FrameCount - 1) + { + if (g.PlatformIO.Renderer_DestroyWindow) + g.PlatformIO.Renderer_DestroyWindow(viewport); + if (g.PlatformIO.Platform_DestroyWindow) + g.PlatformIO.Platform_DestroyWindow(viewport); + IM_ASSERT(viewport->RendererUserData == NULL); + IM_ASSERT(viewport->PlatformUserData == NULL && viewport->PlatformHandle == NULL); + } continue; - IM_ASSERT(viewport->Window != NULL); + } - if (viewport->PlatformRequestMove) - viewport->PlatformPos = g.IO.PlatformInterface.GetWindowPos(viewport); + g.PlatformData.Viewports.push_back(viewport); + IM_ASSERT(viewport->Window != NULL); - bool is_new_window = viewport->PlatformHandle == NULL && viewport->PlatformUserData == NULL && viewport->RendererUserData == NULL; + bool is_new_window = (viewport->PlatformHandle == NULL && viewport->PlatformUserData == NULL && viewport->RendererUserData == NULL); if (is_new_window && viewport->PlatformHandle == NULL && viewport->PlatformUserData == NULL) + g.PlatformIO.Platform_CreateWindow(viewport); + if (is_new_window && viewport->RendererUserData == NULL && g.PlatformIO.Renderer_CreateWindow != NULL) { - g.IO.PlatformInterface.CreateViewport(viewport); - } - if (is_new_window && viewport->RendererUserData == NULL && g.IO.RendererInterface.CreateViewport != NULL) - { - g.IO.RendererInterface.CreateViewport(viewport); + g.PlatformIO.Renderer_CreateWindow(viewport); viewport->RendererLastSize = viewport->Size; } - // Update Pos/Size for Platform + // Apply Position and Size (from ImGui to Platform Window) if (!viewport->PlatformRequestMove) - g.IO.PlatformInterface.SetWindowPos(viewport, viewport->PlatformPos); + g.PlatformIO.Platform_SetWindowPos(viewport, viewport->PlatformPos); if (!viewport->PlatformRequestResize) - g.IO.PlatformInterface.SetWindowSize(viewport, viewport->Size); + g.PlatformIO.Platform_SetWindowSize(viewport, viewport->Size); // Update Size for Renderer - if (g.IO.RendererInterface.ResizeViewport && (viewport->RendererLastSize.x != viewport->Size.x || viewport->RendererLastSize.y != viewport->Size.y)) - g.IO.RendererInterface.ResizeViewport(viewport, viewport->Size); + if (g.PlatformIO.Renderer_SetWindowSize && (viewport->RendererLastSize.x != viewport->Size.x || viewport->RendererLastSize.y != viewport->Size.y)) + g.PlatformIO.Renderer_SetWindowSize(viewport, viewport->Size); viewport->RendererLastSize = viewport->Size; // Update title bar const char* title_begin = viewport->Window->Name; const char* title_end = ImGui::FindRenderedTextEnd(title_begin); const ImGuiID title_hash = ImHash(title_begin, (int)(title_end - title_begin)); - if (viewport->LastNameHash != title_hash ) + if (viewport->LastNameHash != title_hash) { viewport->LastNameHash = title_hash; char* title_displayed = ImStrdup(viewport->Window->Name); title_displayed[title_end - title_begin] = 0; - g.IO.PlatformInterface.SetWindowTitle(viewport, title_displayed); + g.PlatformIO.Platform_SetWindowTitle(viewport, title_displayed); ImGui::MemFree(title_displayed); } @@ -3528,43 +3549,47 @@ static void UpdatePlatformWindows() { if (g.FrameCount < 2) viewport->Flags |= ImGuiViewportFlags_NoFocusOnAppearing; - g.IO.PlatformInterface.ShowWindow(viewport); + g.PlatformIO.Platform_ShowWindow(viewport); } // Clear request flags - viewport->PlatformRequestClose = false; - viewport->PlatformRequestMove = false; - viewport->PlatformRequestResize = false; + viewport->PlatformRequestClose = viewport->PlatformRequestMove = viewport->PlatformRequestResize = false; } } -void ImGui::RenderAdditionalViewports() +// This is a default/basic function for performing the rendering/swap of multiple platform windows. +// Custom renderers may prefer to not call this function at all, and instead iterate the platform data and handle rendering/sync themselves. +// The Render/Swap functions stored in ImGuiPlatformInterface are merely here to allow for this helper to exist, but you can do it yourself: +// +// ImGuiPlatformData* data = ImGui::GetPlatformData(); +// for (int i = 1; i < data->Viewports.Size; i++) +// MyRenderFunction(data->Viewports[i], my_args); +// for (int i = 1; i < data->Viewports.Size; i++) +// MySwapBufferFunction(data->Viewports[i], my_args); +// +// Note how we intentionally skip the main viewport (index 0) which is generally rendered as part of our main application. +void ImGui::RenderPlatformWindows() { - ImGuiContext& g = *GImGui; - if (!(g.IO.ConfigFlags & ImGuiConfigFlags_EnableViewports)) + if (!(ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_EnableViewports)) return; - - for (int i = 0; i < g.Viewports.Size; i++) + + ImGuiPlatformData* data = ImGui::GetPlatformData(); + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + for (int i = 1; i < data->Viewports.Size; i++) { - ImGuiViewport* viewport = g.Viewports[i]; - if ((viewport->Flags & ImGuiViewportFlags_MainViewport) || (viewport->LastFrameActive < g.FrameCount)) - continue; - if (g.IO.PlatformInterface.RenderViewport) - g.IO.PlatformInterface.RenderViewport(viewport); - if (g.IO.RendererInterface.RenderViewport) - g.IO.RendererInterface.RenderViewport(viewport); + ImGuiViewport* viewport = data->Viewports[i]; + if (platform_io.Platform_RenderWindow) + platform_io.Platform_RenderWindow(viewport); + if (platform_io.Renderer_RenderWindow) + platform_io.Renderer_RenderWindow(viewport); } - - // Swap - for (int i = 0; i < g.Viewports.Size; i++) + for (int i = 1; i < data->Viewports.Size; i++) { - ImGuiViewport* viewport = g.Viewports[i]; - if ((viewport->Flags & ImGuiViewportFlags_MainViewport) || (viewport->LastFrameActive < g.FrameCount)) - continue; - if (g.IO.PlatformInterface.SwapBuffers) - g.IO.PlatformInterface.SwapBuffers(viewport); - if (g.IO.RendererInterface.SwapBuffers) - g.IO.RendererInterface.SwapBuffers(viewport); + ImGuiViewport* viewport = data->Viewports[i]; + if (platform_io.Platform_SwapBuffers) + platform_io.Platform_SwapBuffers(viewport); + if (platform_io.Renderer_SwapBuffers) + platform_io.Renderer_SwapBuffers(viewport); } } @@ -3574,7 +3599,7 @@ void ImGui::NewFrame() ImGuiContext& g = *GImGui; // Check user data - // (We pass an error message in the assert expression as a trick to get it visible to programmers who are not using a debugger, as most assert handlers display their argument) + // (We pass an error message in the assert expression to make it visible to programmers who are not using a debugger, as most assert handlers display their argument) IM_ASSERT(g.Initialized); IM_ASSERT(g.IO.DeltaTime >= 0.0f && "Need a positive DeltaTime (zero is tolerated but will cause some timing issues)"); IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f && "Invalid DisplaySize value"); @@ -3586,21 +3611,22 @@ void ImGui::NewFrame() for (int n = 0; n < ImGuiKey_COUNT; n++) IM_ASSERT(g.IO.KeyMap[n] >= -1 && g.IO.KeyMap[n] < IM_ARRAYSIZE(g.IO.KeysDown) && "io.KeyMap[] contains an out of bound value (need to be 0..512, or -1 for unmapped key)"); - // Do a simple check for required key mapping (we intentionally do NOT check all keys to not pressure user into setting up everything, but Space is required and was super recently added in 1.60 WIP) + // Perform simple check for required key mapping (we intentionally do NOT check all keys to not pressure user into setting up everything, but Space is required and was only recently added in 1.60 WIP) if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation."); + // Perform simple checks for multi-viewport and platform windows support if (g.IO.ConfigFlags & ImGuiConfigFlags_EnableViewports) { if ((g.IO.ConfigFlags & ImGuiConfigFlags_PlatformHasViewports) && (g.IO.ConfigFlags & ImGuiConfigFlags_RendererHasViewports)) { + IM_ASSERT(g.FrameCount == 0 || g.FrameCountPlatformEnded == g.FrameCount && "Forgot to call UpdatePlatformWindows() at the end of the previous frame?"); + IM_ASSERT(g.PlatformIO.Platform_CreateWindow != NULL && "Platform init didn't install handlers?"); + IM_ASSERT(g.PlatformIO.Platform_DestroyWindow != NULL && "Platform init didn't install handlers?"); + IM_ASSERT(g.Viewports[0]->PlatformUserData != NULL && "Platform init didn't setup main viewport."); #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS IM_ASSERT(g.IO.RenderDrawListsFn == NULL); // Call ImGui::Render() then pass ImGui::GetDrawData() yourself to your render function! #endif - IM_ASSERT(g.IO.PlatformInterface.CreateViewport != NULL); - IM_ASSERT(g.IO.PlatformInterface.DestroyViewport != NULL); - //IM_ASSERT(g.IO.PlatformInterface.RenderViewport != NULL || g.IO.RendererInterface.RenderViewport != NULL); // Missing rendering function - IM_ASSERT(g.Viewports[0]->PlatformUserData != NULL); // Platform init function didn't setup main viewport } else { @@ -3641,7 +3667,10 @@ void ImGui::NewFrame() // Mark rendering data as invalid to prevent user who may have a handle on it to use it for (int n = 0; n < g.Viewports.Size; n++) - g.Viewports[n]->DrawData.Clear(); + { + g.Viewports[n]->DrawData = NULL; + g.Viewports[n]->DrawDataP.Clear(); + } // Clear reference to active widget if the widget isn't alive anymore if (!g.HoveredIdPreviousFrame) @@ -3955,24 +3984,23 @@ void ImGui::Initialize(ImGuiContext* context) g.SettingsHandlers.push_front(ini_handler); // Create default viewport - ImGuiViewport* viewport = IM_NEW(ImGuiViewport)(IMGUI_VIEWPORT_DEFAULT_ID, 0); + ImGuiViewportP* viewport = IM_NEW(ImGuiViewportP)(); + viewport->ID = IMGUI_VIEWPORT_DEFAULT_ID; + viewport->Idx = 0; g.Viewports.push_back(viewport); g.Initialized = true; } -void ImGui::DestroyViewportsPlaformData(ImGuiContext* context) +void ImGui::DestroyPlatformWindows() { - if (context->IO.PlatformInterface.DestroyViewport) - for (int i = 0; i < context->Viewports.Size; i++) - context->IO.PlatformInterface.DestroyViewport(context->Viewports[i]); -} - -void ImGui::DestroyViewportsRendererData(ImGuiContext* context) -{ - if (context->IO.RendererInterface.DestroyViewport) - for (int i = 0; i < context->Viewports.Size; i++) - context->IO.RendererInterface.DestroyViewport(context->Viewports[i]); + ImGuiContext& g = *GImGui; + if (g.PlatformIO.Renderer_DestroyWindow) + for (int i = 0; i < g.Viewports.Size; i++) + g.PlatformIO.Renderer_DestroyWindow(g.Viewports[i]); + if (g.PlatformIO.Platform_DestroyWindow) + for (int i = 0; i < g.Viewports.Size; i++) + g.PlatformIO.Platform_DestroyWindow(g.Viewports[i]); } // This function is merely here to free heap allocations. @@ -3990,6 +4018,12 @@ void ImGui::Shutdown(ImGuiContext* context) SaveIniSettingsToDisk(g.IO.IniFilename); + // Destroy platform windows + ImGuiContext* backup_context = ImGui::GetCurrentContext(); + SetCurrentContext(context); + DestroyPlatformWindows(); + SetCurrentContext(backup_context); + // Clear everything else for (int i = 0; i < g.Windows.Size; i++) IM_DELETE(g.Windows[i]); @@ -4011,14 +4045,8 @@ void ImGui::Shutdown(ImGuiContext* context) g.OpenPopupStack.clear(); g.CurrentPopupStack.clear(); g.CurrentViewport = g.MouseViewport = g.MouseLastViewport = g.MouseLastHoveredViewport = NULL; - DestroyViewportsPlaformData(context); - DestroyViewportsRendererData(context); for (int i = 0; i < g.Viewports.Size; i++) - { - ImGuiViewport* viewport = g.Viewports[i]; - viewport->PlatformUserData = viewport->PlatformHandle = viewport->RendererUserData = NULL; - IM_DELETE(viewport); - } + IM_DELETE(g.Viewports[i]); g.Viewports.clear(); g.OverlayDrawList.ClearFreeMemory(); g.PrivateClipboard.clear(); @@ -4291,18 +4319,20 @@ void ImDrawDataBuilder::FlattenIntoSingleLayer() } } -static void SetupViewportDrawData(ImGuiViewport* viewport, ImVector* draw_lists) +static void SetupViewportDrawData(ImGuiViewportP* viewport, ImVector* draw_lists) { - viewport->DrawData.Valid = true; - viewport->DrawData.CmdLists = (draw_lists->Size > 0) ? draw_lists->Data : NULL; - viewport->DrawData.CmdListsCount = draw_lists->Size; - viewport->DrawData.TotalVtxCount = viewport->DrawData.TotalIdxCount = 0; - viewport->DrawData.DisplayPos = viewport->Pos; - viewport->DrawData.DisplaySize = viewport->Size; + ImDrawData* draw_data = &viewport->DrawDataP; + viewport->DrawData = draw_data; // Make publicly accessible + draw_data->Valid = true; + draw_data->CmdLists = (draw_lists->Size > 0) ? draw_lists->Data : NULL; + draw_data->CmdListsCount = draw_lists->Size; + draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0; + draw_data->DisplayPos = viewport->Pos; + draw_data->DisplaySize = viewport->Size; for (int n = 0; n < draw_lists->Size; n++) { - viewport->DrawData.TotalVtxCount += draw_lists->Data[n]->VtxBuffer.Size; - viewport->DrawData.TotalIdxCount += draw_lists->Data[n]->IdxBuffer.Size; + draw_data->TotalVtxCount += draw_lists->Data[n]->VtxBuffer.Size; + draw_data->TotalIdxCount += draw_lists->Data[n]->IdxBuffer.Size; } } @@ -4412,9 +4442,6 @@ void ImGui::EndFrame() memset(g.IO.NavInputs, 0, sizeof(g.IO.NavInputs)); g.FrameCountEnded = g.FrameCount; - - if (g.IO.ConfigFlags & ImGuiConfigFlags_EnableViewports) - UpdatePlatformWindows(); } void ImGui::Render() @@ -4461,22 +4488,28 @@ void ImGui::Render() g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = 0; for (int n = 0; n < g.Viewports.Size; n++) { - ImGuiViewport* viewport = g.Viewports[n]; + ImGuiViewportP* viewport = g.Viewports[n]; viewport->DrawDataBuilder.FlattenIntoSingleLayer(); AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[0], &g.OverlayDrawList); SetupViewportDrawData(viewport, &viewport->DrawDataBuilder.Layers[0]); - g.IO.MetricsRenderVertices += viewport->DrawData.TotalVtxCount; - g.IO.MetricsRenderIndices += viewport->DrawData.TotalIdxCount; + g.IO.MetricsRenderVertices += viewport->DrawData->TotalVtxCount; + g.IO.MetricsRenderIndices += viewport->DrawData->TotalIdxCount; } // Render. If user hasn't set a callback then they may retrieve the draw data via GetDrawData() #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - if (g.Viewports[0]->DrawData.CmdListsCount > 0 && g.IO.RenderDrawListsFn != NULL) - g.IO.RenderDrawListsFn(&g.Viewports[0]->DrawData); + if (g.Viewports[0]->DrawData->CmdListsCount > 0 && g.IO.RenderDrawListsFn != NULL) + g.IO.RenderDrawListsFn(g.Viewports[0]->DrawData); #endif } -ImGuiViewport* ImGui::FindViewportByID(ImGuiID id) +ImGuiViewport* ImGui::GetMainViewport() +{ + ImGuiContext& g = *GImGui; + return g.Viewports[0]; +} + +ImGuiViewportP* ImGui::FindViewportByID(ImGuiID id) { ImGuiContext& g = *GImGui; for (int n = 0; n < g.Viewports.Size; n++) @@ -4531,9 +4564,9 @@ static void ImGui::ResizeViewportTranslateWindows(int viewport_idx_min, int view } } -void ImGui::SetCurrentViewport(ImGuiViewport* viewport) +void ImGui::SetCurrentViewport(ImGuiViewportP* viewport) { - // Notify platform interface of viewport changes + // Notify platform layer of viewport changes // FIXME-DPI: This is only currently used for experimenting with handling of multiple DPI ImGuiContext& g = *GImGui; if (viewport) @@ -4541,16 +4574,16 @@ void ImGui::SetCurrentViewport(ImGuiViewport* viewport) if (g.CurrentViewport == viewport) return; g.CurrentViewport = viewport; - if (g.CurrentViewport && g.IO.PlatformInterface.ChangedViewport) - g.IO.PlatformInterface.ChangedViewport(g.CurrentViewport); + if (g.CurrentViewport && g.PlatformIO.Platform_OnChangedViewport) + g.PlatformIO.Platform_OnChangedViewport(g.CurrentViewport); } -ImGuiViewport* ImGui::Viewport(ImGuiWindow* window, ImGuiID id, ImGuiViewportFlags flags, const ImVec2& platform_pos, const ImVec2& size) +ImGuiViewportP* ImGui::Viewport(ImGuiWindow* window, ImGuiID id, ImGuiViewportFlags flags, const ImVec2& platform_pos, const ImVec2& size) { ImGuiContext& g = *GImGui; IM_ASSERT(id != 0); - ImGuiViewport* viewport = FindViewportByID(id); + ImGuiViewportP* viewport = (ImGuiViewportP*)FindViewportByID(id); if (viewport) { // We defer translating windows to the beginning of the frame. @@ -4560,7 +4593,9 @@ ImGuiViewport* ImGui::Viewport(ImGuiWindow* window, ImGuiID id, ImGuiViewportFla else { // New viewport - viewport = IM_NEW(ImGuiViewport)(id, g.Viewports.Size); + viewport = IM_NEW(ImGuiViewportP)(); + viewport->ID = id; + viewport->Idx = g.Viewports.Size; viewport->Pos = ImVec2(g.Viewports.back()->GetNextX(), 0.0f); viewport->Size = size; g.Viewports.push_back(viewport); @@ -4573,8 +4608,8 @@ ImGuiViewport* ImGui::Viewport(ImGuiWindow* window, ImGuiID id, ImGuiViewportFla viewport->LastFrameActive = g.FrameCount; // Request an initial DpiScale before the OS platform window creation - if (g.IO.PlatformInterface.GetWindowDpiScale) - viewport->DpiScale = g.IO.PlatformInterface.GetWindowDpiScale(viewport); + if (g.PlatformIO.Platform_GetWindowDpiScale) + viewport->DpiScale = g.PlatformIO.Platform_GetWindowDpiScale(viewport); return viewport; } @@ -5922,7 +5957,7 @@ static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& co *out_size = size_constrained; } -static void ImGui::SetWindowViewportTranslateToPreservePlatformPos(ImGuiWindow* window, ImGuiViewport* prev_viewport, ImGuiViewport* curr_viewport) +static void ImGui::SetWindowViewportTranslateToPreservePlatformPos(ImGuiWindow* window, ImGuiViewportP* prev_viewport, ImGuiViewportP* curr_viewport) { if (prev_viewport == curr_viewport) return; @@ -5948,8 +5983,8 @@ static void ImGui::UpdateWindowViewport(ImGuiWindow* window, bool window_pos_set ImGuiWindowFlags flags = window->Flags; (void)window_pos_set_by_api; - // Restore main viewport if multi viewports are not supported by the back-end - ImGuiViewport* main_viewport = g.Viewports[0]; + // Restore main viewport if multi-viewport is not supported by the back-end + ImGuiViewportP* main_viewport = g.Viewports[0]; if (!(g.IO.ConfigFlags & ImGuiConfigFlags_EnableViewports)) { window->Viewport = main_viewport; @@ -5976,13 +6011,13 @@ static void ImGui::UpdateWindowViewport(ImGuiWindow* window, bool window_pos_set else if (window_follow_mouse_viewport && IsMousePosValid()) { // Calculate mouse position in OS/platform coordinates - ImGuiViewport* current_viewport = window->Viewport; + ImGuiViewportP* current_viewport = window->Viewport; if (!window_is_mouse_tooltip && !current_viewport->GetRect().Contains(window->Rect())) { // Create an undecorated, temporary OS/platform window ImVec2 platform_pos = ConvertViewportPosToPlatformPos(g.IO.MousePos - g.ActiveIdClickOffset, g.MouseViewport); ImGuiViewportFlags viewport_flags = ImGuiViewportFlags_NoDecoration | ImGuiViewportFlags_NoFocusOnAppearing | ImGuiViewportFlags_NoInputs; - ImGuiViewport* viewport = Viewport(window, window->ID, viewport_flags, platform_pos, window->Size); + ImGuiViewportP* viewport = Viewport(window, window->ID, viewport_flags, platform_pos, window->Size); window->Flags |= ImGuiWindowFlags_FullViewport; window->Viewport = viewport; created_viewport = true; @@ -6021,7 +6056,7 @@ static void ImGui::UpdateWindowViewport(ImGuiWindow* window, bool window_pos_set { if (window->ViewportPlatformPos.x != FLT_MAX && window->ViewportPlatformPos.y != FLT_MAX) { - ImGuiViewport* viewport = Viewport(window, window->ID, ImGuiViewportFlags_NoDecoration, window->ViewportPlatformPos, window->Size); + ImGuiViewportP* viewport = Viewport(window, window->ID, ImGuiViewportFlags_NoDecoration, window->ViewportPlatformPos, window->Size); window->Flags |= ImGuiWindowFlags_FullViewport; window->Viewport = viewport; created_viewport = true; @@ -6352,7 +6387,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) SetCurrentWindow(window); flags = window->Flags; - if (p_open != NULL && window->Viewport->PlatformRequestClose && !(window->Viewport->Flags & ImGuiViewportFlags_MainViewport)) + if (p_open != NULL && window->Viewport->PlatformRequestClose && window->Viewport != GetMainViewport()) { window->Viewport->PlatformRequestClose = false; *p_open = false; @@ -6360,7 +6395,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Lock window rounding, border size and padding for the frame (so that altering them doesn't cause inconsistencies) window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding; - if (window->Viewport != GetMainViewport()) + if (window->Flags & ImGuiWindowFlags_FullViewport) window->WindowRounding = 0.0f; window->WindowBorderSize = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildBorderSize : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize; window->WindowPadding = style.WindowPadding; @@ -13827,7 +13862,7 @@ static void ScaleWindow(ImGuiWindow* window, float scale) } // Scale all windows (position, size). Use when e.g. changing DPI. (This is a lossy operation!) -void ImGui::ScaleWindowsInViewport(ImGuiViewport* viewport, float scale) +void ImGui::ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale) { ImGuiContext& g = *GImGui; if (g.IO.MousePosViewport == viewport->ID) @@ -13857,7 +13892,7 @@ void ImGui::ShowViewportThumbnails() ImVec2 p = window->DC.CursorPos; for (int n = 0; n < g.Viewports.Size; n++) { - ImGuiViewport* viewport = g.Viewports[n]; + ImGuiViewportP* viewport = g.Viewports[n]; if (n > 0) ImGui::SameLine(); ImRect bb(p + (viewport->Pos) * SCALE, p + (viewport->Pos + viewport->Size) * SCALE); @@ -14015,7 +14050,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::Unindent(ImGui::GetTreeNodeToLabelSpacing()); for (int i = 0; i < g.Viewports.Size; i++) { - ImGuiViewport* viewport = g.Viewports[i]; + ImGuiViewportP* viewport = g.Viewports[i]; ImGui::SetNextTreeNodeOpen(true, ImGuiCond_Once); if (ImGui::TreeNode((void*)(intptr_t)viewport->ID, "Viewport #%d, ID: 0x%08X, DrawLists: %d, Size: (%.0f,%.0f)", i, viewport->ID, viewport->DrawDataBuilder.GetDrawListCount(), viewport->Size.x, viewport->Size.y)) { diff --git a/imgui.h b/imgui.h index e28a0f19356d..a7415342a2c9 100644 --- a/imgui.h +++ b/imgui.h @@ -73,6 +73,8 @@ struct ImGuiSizeCallbackData; // Structure used to constraint window size struct ImGuiListClipper; // Helper to manually clip large list of items struct ImGuiPayload; // User data payload for drag and drop operations struct ImGuiViewport; // Viewport (generally ~1 per window to output to at the OS level. Need per-platform support to use multiple viewports) +struct ImGuiPlatformIO; // Multi-viewport support: interface for Platform/Renderer back-ends +struct ImGuiPlatformData; // Multi-viewport support: list of viewports to render struct ImGuiContext; // ImGui context (opaque) #ifndef ImTextureID @@ -103,6 +105,7 @@ typedef int ImGuiHoveredFlags; // flags: for IsItemHovered() etc. typedef int ImGuiInputTextFlags; // flags: for InputText*() // enum ImGuiInputTextFlags_ typedef int ImGuiSelectableFlags; // flags: for Selectable() // enum ImGuiSelectableFlags_ typedef int ImGuiTreeNodeFlags; // flags: for TreeNode*(),CollapsingHeader()// enum ImGuiTreeNodeFlags_ +typedef int ImGuiViewportFlags; // flags: for ImGuiViewport // enum ImGuiViewportFlags_ typedef int ImGuiWindowFlags; // flags: for Begin*() // enum ImGuiWindowFlags_ typedef int (*ImGuiTextEditCallback)(ImGuiTextEditCallbackData *data); typedef void (*ImGuiSizeCallback)(ImGuiSizeCallbackData* data); @@ -150,9 +153,7 @@ namespace ImGui IMGUI_API ImGuiStyle& GetStyle(); IMGUI_API void NewFrame(); // start a new ImGui frame, you can submit any command from this point until Render()/EndFrame(). IMGUI_API void Render(); // ends the ImGui frame, finalize the draw data. (Obsolete: optionally call io.RenderDrawListsFn if set. Nowadays, prefer calling your render function yourself.) - IMGUI_API void RenderAdditionalViewports(); IMGUI_API ImDrawData* GetDrawData(); // valid after Render() and until the next call to NewFrame(). this is what you have to render. (Obsolete: this used to be passed to your io.RenderDrawListsFn() function.) - IMGUI_API ImDrawData* GetDrawDataForViewport(ImGuiID viewport_id);// ImDrawData filtered to hold only the ImDrawList covering a given viewport. valid after Render() and until the next call to NewFrame() IMGUI_API void EndFrame(); // ends the ImGui frame. automatically called by Render(), so most likely don't need to ever call that yourself directly. If you don't need to render you may call EndFrame() but you'll have wasted CPU already. If you don't need to render, better to not create any imgui windows instead! // Demo, Debug, Information @@ -543,6 +544,15 @@ namespace ImGui IMGUI_API const char* GetClipboardText(); IMGUI_API void SetClipboardText(const char* text); + // (Optional) Platform interface for multi-viewport support + IMGUI_API ImGuiPlatformIO& GetPlatformIO(); // Platform/Renderer functions. + IMGUI_API ImGuiPlatformData* GetPlatformData(); // List of viewports. + IMGUI_API ImGuiViewport* GetMainViewport(); // GetPlatformData()->MainViewport + IMGUI_API void UpdatePlatformWindows(); // Call in main loop. Create/Destroy/Resize platform windows so there's one for each viewport + IMGUI_API void RenderPlatformWindows(); // Call in main loop. Call RenderWindow/SwapBuffers from the ImGuiPlatformIO structure. May be reimplemented by user. + IMGUI_API void DestroyPlatformWindows(); // Call in back-end shutdown. + IMGUI_API ImGuiViewport* FindViewportByPlatformHandle(void* platform_handle); + // Memory Utilities // All those functions are not reliant on the current context. // If you reload the contents of imgui.cpp at runtime, you may need to call SetCurrentContext() + SetAllocatorFunctions() again. @@ -958,39 +968,6 @@ enum ImGuiCond_ #endif }; -// (Optional) Setup required only if (io.ConfigFlags & ImGuiConfigFlags_EnableMultiViewport) is enabled -struct ImGuiPlatformInterface -{ - void (*CreateViewport)(ImGuiViewport* viewport); - void (*DestroyViewport)(ImGuiViewport* viewport); - void (*ShowWindow)(ImGuiViewport* viewport); - void (*SetWindowPos)(ImGuiViewport* viewport, ImVec2 pos); - ImVec2 (*GetWindowPos)(ImGuiViewport* viewport); - void (*SetWindowSize)(ImGuiViewport* viewport, ImVec2 size); - ImVec2 (*GetWindowSize)(ImGuiViewport* viewport); - void (*SetWindowTitle)(ImGuiViewport* viewport, const char* name); - void (*SetWindowAlpha)(ImGuiViewport* viewport, float alpha); - void (*RenderViewport)(ImGuiViewport* viewport); - void (*SwapBuffers)(ImGuiViewport* viewport); - - // FIXME-VIEWPORT: Experimenting with back-end abstraction. This probably shouldn't stay as is. - int (*CreateVkSurface)(ImGuiViewport* viewport, ImU64 vk_instance, const void* vk_allocator, ImU64* out_vk_surface); - - // FIXME-DPI - float (*GetWindowDpiScale)(ImGuiViewport* viewport); // (Optional) - void (*ChangedViewport)(ImGuiViewport* viewport); // (Optional) Called during Begin() every time the viewport we are outputting into changes (viewport = next viewport) -}; - -// (Optional) Setup required only if (io.ConfigFlags & ImGuiConfigFlags_EnableMultiViewport) is enabled -struct ImGuiRendererInterface -{ - void (*CreateViewport)(ImGuiViewport* viewport); - void (*DestroyViewport)(ImGuiViewport* viewport); - void (*ResizeViewport)(ImGuiViewport* viewport, ImVec2 size); - void (*RenderViewport)(ImGuiViewport* viewport); // Setup render output, clear targets, call Renderer_RenderDrawData - void (*SwapBuffers)(ImGuiViewport* viewport); // Call Present/SwapBuffers -}; - // You may modify the ImGui::GetStyle() main instance during initialization and before NewFrame(). // During the frame, use ImGui::PushStyleVar(ImGuiStyleVar_XXXX)/PopStyleVar() to alter the main style values, and ImGui::PushStyleColor(ImGuiCol_XXX)/PopStyleColor() for colors. struct ImGuiStyle @@ -1072,10 +1049,6 @@ struct ImGuiIO void (*SetClipboardTextFn)(void* user_data, const char* text); void* ClipboardUserData; - // Optional: platform interface to use multiple viewports - ImGuiPlatformInterface PlatformInterface; - ImGuiRendererInterface RendererInterface; - // Optional: notify OS Input Method Editor of the screen position of your cursor for text input position (e.g. when using Japanese/Chinese IME in Windows) // (default to use native imm32 api on Windows) void (*ImeSetInputScreenPosFn)(int x, int y); @@ -1883,6 +1856,86 @@ struct ImFont #endif }; +//----------------------------------------------------------------------------- +// [BETA] Platform interface for multi-viewport support +// - completely optional, for advanced users! +// - this is used for back-ends aiming to support the seamless creation of multiple viewport (= multiple Platform/OS windows) +// dear imgui manages the viewports, and the back-end create one Platform/OS windows for each secondary viewport. +// - if you are new to dear imgui and trying to integrate it into your engine, you should probably ignore this for now. +//----------------------------------------------------------------------------- + +// (Optional) Setup required only if (io.ConfigFlags & ImGuiConfigFlags_EnableViewports) is enabled +// This is designed so we can mix and match two imgui_impl_xxxx files, one for the Platform (~ Windowing), one for Renderer. +// Custom engine back-ends will often provide both Platform and Renderer interfaces and thus may not need to use all functions. +// Platform functions are typically called before their Renderer counterpart, apart from Destroy which are called the other way. +struct ImGuiPlatformIO +{ + // Platform (e.g. Win32, GLFW, SDL2) + void (*Platform_CreateWindow)(ImGuiViewport* vp); // Create a new platform window for the given viewport + void (*Platform_DestroyWindow)(ImGuiViewport* vp); + void (*Platform_ShowWindow)(ImGuiViewport* vp); // Newly created windows are initially hidden so we have a chance to call SetWindowPos/Size/Title on them. + void (*Platform_SetWindowPos)(ImGuiViewport* vp, ImVec2 pos); + ImVec2 (*Platform_GetWindowPos)(ImGuiViewport* vp); + void (*Platform_SetWindowSize)(ImGuiViewport* vp, ImVec2 size); + ImVec2 (*Platform_GetWindowSize)(ImGuiViewport* vp); + void (*Platform_SetWindowTitle)(ImGuiViewport* vp, const char* title); + void (*Platform_SetWindowAlpha)(ImGuiViewport* vp, float alpha); // (Optional) Setup window transparency + void (*Platform_RenderWindow)(ImGuiViewport* vp); // (Optional) Setup for render (platform side) + void (*Platform_SwapBuffers)(ImGuiViewport* vp); // (Optional) Call Present/SwapBuffers (platform side) + float (*Platform_GetWindowDpiScale)(ImGuiViewport* vp); // (Optional) DPI handling: Return DPI scale for this viewport. 1.0f = 96 DPI. (FIXME-DPI) + void (*Platform_OnChangedViewport)(ImGuiViewport* vp); // (Optional) DPI handling: Called during Begin() every time the viewport we are outputting into changes, so back-end has a chance to swap fonts to adjust style. + int (*Platform_CreateVkSurface)(ImGuiViewport* vp, ImU64 vk_inst, const void* vk_allocators, ImU64* out_vk_surface); // (Optional) For Renderer to call into Platform code + + // Renderer (e.g. DirectX, OpenGL3, Vulkan) + void (*Renderer_CreateWindow)(ImGuiViewport* vp); // Create swap chains, frame buffers etc. + void (*Renderer_DestroyWindow)(ImGuiViewport* vp); + void (*Renderer_SetWindowSize)(ImGuiViewport* vp, ImVec2 size); // Resize swap chain, frame buffers etc. + void (*Renderer_RenderWindow)(ImGuiViewport* vp); // (Optional) Clear targets, Render viewport->DrawData + void (*Renderer_SwapBuffers)(ImGuiViewport* vp); // (Optional) Call Present/SwapBuffers (renderer side) +}; + +// List of viewports to render as platform window (updated by ImGui::UpdatePlatformWindows) +struct ImGuiPlatformData +{ + // Viewports[0] is guaranteed to be _always_ the same as MainViewport. Following it are the secondary viewports. + // The main viewport is included in the list because it is more convenient for looping code. + ImGuiViewport* MainViewport; + ImVector Viewports; + + ImGuiPlatformData() { MainViewport = NULL; } +}; + +// Flags stored in ImGuiViewport::Flags, giving indications to the platform back-ends +enum ImGuiViewportFlags_ +{ + ImGuiViewportFlags_NoDecoration = 1 << 0, // Platform Window: Disable platform title bar, borders, etc. + ImGuiViewportFlags_NoFocusOnAppearing = 1 << 1, // Platform Window: Don't take focus when created. + ImGuiViewportFlags_NoInputs = 1 << 2, // Platform Window: Make mouse pass through so we can drag this window while peaking behind it. + ImGuiViewportFlags_NoRendererClear = 1 << 3 // Platform Window: Renderer doesn't need to clear the framebuffer ahead. +}; + +// The viewports created and managed by imgui. The role of the platform back-end is to create the platform/OS windows corresponding to each viewport. +struct ImGuiViewport +{ + ImGuiID ID; + ImGuiViewportFlags Flags; + ImVec2 Pos; // Position of viewport in imgui virtual space (all viewports Pos.y == 0.0f, main viewport Pos.x == 0.0f) + ImVec2 Size; // Size of viewport in pixel + float DpiScale; // 1.0f = 96 DPI = No extra scale + ImDrawData* DrawData; // The ImDrawData corresponding to this viewport. Valid after Render() and until the next call to NewFrame(). + + ImVec2 PlatformPos; // Position in OS desktop/native space + void* PlatformUserData; // void* to hold custom data structure for the platform (e.g. windowing info, render context) + void* PlatformHandle; // void* for FindViewportByPlatformHandle(). (e.g. HWND, GlfwWindow*, SDL_Window*) + bool PlatformRequestClose; // Platform window requested closure + bool PlatformRequestMove; // Platform window requested move (e.g. window was moved using OS windowing facility) + bool PlatformRequestResize; // Platform window requested resize (e.g. window was resize using OS windowing facility) + void* RendererUserData; // void* to hold custom data structure for the renderer (e.g. swap chain, frame-buffers etc.) + + ImGuiViewport() { ID = 0; Flags = 0; DpiScale = 0.0f; DrawData = NULL; PlatformUserData = PlatformHandle = NULL; PlatformRequestClose = PlatformRequestMove = PlatformRequestResize = false; RendererUserData = NULL; } + ~ImGuiViewport() { IM_ASSERT(PlatformUserData == NULL && RendererUserData == NULL); } +}; + #if defined(__clang__) #pragma clang diagnostic pop #endif diff --git a/imgui_internal.h b/imgui_internal.h index 629d97e7a13d..b605c7d5a46b 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -40,7 +40,6 @@ struct ImGuiMenuColumns; struct ImGuiDrawContext; struct ImGuiTextEditState; struct ImGuiPopupRef; -struct ImGuiViewport; struct ImGuiWindow; struct ImGuiWindowSettings; @@ -52,7 +51,6 @@ typedef int ImGuiNavHighlightFlags; // flags: for RenderNavHighlight() typedef int ImGuiNavDirSourceFlags; // flags: for GetNavInputAmount2d() // enum ImGuiNavDirSourceFlags_ typedef int ImGuiSeparatorFlags; // flags: for Separator() - internal // enum ImGuiSeparatorFlags_ typedef int ImGuiSliderFlags; // flags: for SliderBehavior() // enum ImGuiSliderFlags_ -typedef int ImGuiViewportFlags; // flags: for Viewport() // enum ImGuiViewportFlags_ //------------------------------------------------------------------------- // STB libraries @@ -506,45 +504,26 @@ struct ImDrawDataBuilder IMGUI_API void FlattenIntoSingleLayer(); }; -enum ImGuiViewportFlags_ +enum ImGuiViewportFlagsPrivate_ { - ImGuiViewportFlags_MainViewport = 1 << 0, - ImGuiViewportFlags_HostOtherWindows = 1 << 1, - ImGuiViewportFlags_NoRendererClear = 1 << 2, // Platform Window: Renderer doesn't need to clear the framebuffer ahead. - ImGuiViewportFlags_NoDecoration = 1 << 3, // Platform Window: Disable platform title bar, borders, etc. - ImGuiViewportFlags_NoFocusOnAppearing = 1 << 4, // Platform Window: Don't take focus when created. - ImGuiViewportFlags_NoInputs = 1 << 5 // Platform Window: Make mouse pass through so we can drag this window while peaking behind it. + ImGuiViewportFlags_CanHostOtherWindows = 1 << 10, // Normal viewports are associated to a single window. The main viewport can host multiple windows. }; -struct ImGuiViewport +// ImGuiViewport Private/Internals fields (cardinal sin: we are using inheritance!) +struct ImGuiViewportP : public ImGuiViewport { - ImGuiID ID; int Idx; - ImGuiViewportFlags Flags; - int LastFrameActive; - int LastFrameAsRefViewport; // Last frame number this viewport was io.MouseViewportRef + int LastFrameActive; // Last frame number this viewport was activated by a window + int LastFrameAsRefViewport; // Last frame number this viewport was io.MouseViewportRef ImGuiID LastNameHash; ImGuiWindow* Window; - ImVec2 Pos; // Position in imgui virtual space (Pos.y == 0.0) - ImVec2 Size; - float DpiScale; - ImDrawData DrawData; + ImDrawData DrawDataP; ImDrawDataBuilder DrawDataBuilder; - - // [Optional] OS/Platform Layer data. This is to allow the creation/manipulate of multiple OS/Platform windows. Not all back-ends will allow this. - ImVec2 PlatformPos; // Position in OS desktop/native space - void* PlatformUserData; // void* to hold custom data structure for the platform (e.g. windowing info, render context) - void* PlatformHandle; // void* for FindViewportByPlatformHandle(). (e.g. HWND, GlfwWindow*) - bool PlatformRequestClose; // Platform window requested closure - bool PlatformRequestMove; // Platform window requested move (e.g. window was moved using OS windowing facility) - bool PlatformRequestResize; // Platform window requested resize (e.g. window was resize using OS windowing facility) - void* RendererUserData; // void* to hold custom data structure for the renderer (e.g. framebuffer) ImVec2 RendererLastSize; - ImGuiViewport(ImGuiID id, int idx) { ID = id; Idx = idx; Flags = 0; LastFrameActive = LastFrameAsRefViewport = -1; LastNameHash = 0; Window = NULL; DpiScale = 0.0f; PlatformUserData = PlatformHandle = NULL; PlatformRequestClose = PlatformRequestMove = PlatformRequestResize = false; RendererUserData = NULL; RendererLastSize = ImVec2(-1.0f,-1.0f); } - ~ImGuiViewport() { IM_ASSERT(PlatformUserData == NULL && RendererUserData == NULL); } - ImRect GetRect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } - float GetNextX() const { const float SPACING = 4.0f; return Pos.x + Size.x + SPACING; } + ImGuiViewportP() { Idx = 1; LastFrameActive = LastFrameAsRefViewport = -1; LastNameHash = 0; Window = NULL; DrawData = NULL; RendererLastSize = ImVec2(-1.0f,-1.0f); } + ImRect GetRect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } + float GetNextX() const { const float SPACING = 4.0f; return Pos.x + Size.x + SPACING; } }; struct ImGuiNavMoveResult @@ -608,6 +587,7 @@ struct ImGuiContext bool Initialized; bool FontAtlasOwnedByContext; // Io.Fonts-> is owned by the ImGuiContext and will be destructed along with it. ImGuiIO IO; + ImGuiPlatformIO PlatformIO; ImGuiStyle Style; ImFont* Font; // (Shortcut) == FontStack.empty() ? IO.Font : FontStack.back() float FontSize; // (Shortcut) == FontBaseSize * g.CurrentWindow->FontWindowScale == window->FontSize(). Text height for current window. @@ -617,6 +597,7 @@ struct ImGuiContext float Time; int FrameCount; int FrameCountEnded; + int FrameCountPlatformEnded; int FrameCountRendered; ImVector Windows; ImVector WindowsSortBuffer; @@ -651,11 +632,12 @@ struct ImGuiContext ImGuiCond NextTreeNodeOpenCond; // Viewports - ImVectorViewports; - ImGuiViewport* CurrentViewport; - ImGuiViewport* MouseViewport; - ImGuiViewport* MouseLastViewport; - ImGuiViewport* MouseLastHoveredViewport; + ImVector Viewports; + ImGuiPlatformData PlatformData; // This is essentially the public facing version of the Viewports vector (it is updated in UpdatePlatformWindows and exclude the viewports about to be destroyed) + ImGuiViewportP* CurrentViewport; + ImGuiViewportP* MouseViewport; + ImGuiViewportP* MouseLastViewport; + ImGuiViewportP* MouseLastHoveredViewport; // Navigation data (for gamepad/keyboard) ImGuiWindow* NavWindow; // Focused window for navigation. Could be called 'FocusWindow' @@ -756,10 +738,11 @@ struct ImGuiContext FontSize = FontBaseSize = 0.0f; FontAtlasOwnedByContext = shared_font_atlas ? false : true; IO.Fonts = shared_font_atlas ? shared_font_atlas : IM_NEW(ImFontAtlas)(); + memset(&PlatformIO, 0, sizeof(PlatformIO)); Time = 0.0f; FrameCount = 0; - FrameCountEnded = FrameCountRendered = -1; + FrameCountEnded = FrameCountPlatformEnded = FrameCountRendered = -1; WindowsActiveCount = 0; CurrentWindow = NULL; HoveredWindow = NULL; @@ -950,7 +933,7 @@ struct IMGUI_API ImGuiWindow char* Name; ImGuiID ID; // == ImHash(Name) ImGuiWindowFlags Flags, FlagsPreviousFrame; // See enum ImGuiWindowFlags_ - ImGuiViewport* Viewport; // Always set in Begin(), only inactive windows may have a NULL value here + ImGuiViewportP* Viewport; // Always set in Begin(), only inactive windows may have a NULL value here ImGuiID ViewportId; // Inactive windows preserve their last viewport id (since the viewport may disappear with the window inactivity) ImVec2 ViewportPlatformPos; ImVec2 PosFloat; @@ -1082,17 +1065,12 @@ namespace ImGui IMGUI_API void Shutdown(ImGuiContext* context); // Since 1.60 this is a _private_ function. You can call DestroyContext() to destroy the context created by CreateContext(). // Viewports - IMGUI_API ImGuiViewport* Viewport(ImGuiWindow* window, ImGuiID id, ImGuiViewportFlags flags, const ImVec2& platform_pos, const ImVec2& size); - inline ImVector&GetViewports() { return GImGui->Viewports; } - inline ImGuiViewport* GetMainViewport() { return GImGui->Viewports[0]; } - IMGUI_API ImGuiViewport* FindViewportByID(ImGuiID id); - IMGUI_API ImGuiViewport* FindViewportByPlatformHandle(void* platform_handle); + IMGUI_API ImGuiViewportP* FindViewportByID(ImGuiID id); IMGUI_API void SetNextWindowViewport(ImGuiID id); - IMGUI_API void ScaleWindowsInViewport(ImGuiViewport* viewport, float scale); + IMGUI_API void ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale); IMGUI_API void ShowViewportThumbnails(); - IMGUI_API void DestroyViewportsPlaformData(ImGuiContext* context); - IMGUI_API void DestroyViewportsRendererData(ImGuiContext* context); + // Settings IMGUI_API void MarkIniSettingsDirty(); IMGUI_API ImGuiSettingsHandler* FindSettingsHandler(const char* type_name); IMGUI_API ImGuiWindowSettings* FindWindowSettings(ImGuiID id); From 0e090327504de971df65af565f45963316ac0ba5 Mon Sep 17 00:00:00 2001 From: omar Date: Sun, 18 Mar 2018 22:19:02 +0100 Subject: [PATCH 092/828] Viewport, Examples: Renamed types used by examples. (#1542) --- examples/imgui_impl_dx10.cpp | 16 ++++++++-------- examples/imgui_impl_dx11.cpp | 16 ++++++++-------- examples/imgui_impl_dx12.cpp | 16 ++++++++-------- examples/imgui_impl_glfw.cpp | 30 +++++++++++++++--------------- examples/imgui_impl_opengl3.cpp | 3 ++- examples/imgui_impl_sdl2.cpp | 32 ++++++++++++++++---------------- examples/imgui_impl_vulkan.cpp | 17 +++++++++-------- examples/imgui_impl_win32.cpp | 26 +++++++++++++------------- 8 files changed, 79 insertions(+), 77 deletions(-) diff --git a/examples/imgui_impl_dx10.cpp b/examples/imgui_impl_dx10.cpp index 9a14b8ffd9f6..eac0ccb8c27a 100644 --- a/examples/imgui_impl_dx10.cpp +++ b/examples/imgui_impl_dx10.cpp @@ -502,18 +502,18 @@ void ImGui_ImplDX10_NewFrame() // Platform Interface (Optional, for multi-viewport support) //-------------------------------------------------------------------------------------------------------- -struct ImGuiPlatformDataDx10 +struct ImGuiViewportDataDx10 { IDXGISwapChain* SwapChain; ID3D10RenderTargetView* RTView; - ImGuiPlatformDataDx10() { SwapChain = NULL; RTView = NULL; } - ~ImGuiPlatformDataDx10() { IM_ASSERT(SwapChain == NULL && RTView == NULL); } + ImGuiViewportDataDx10() { SwapChain = NULL; RTView = NULL; } + ~ImGuiViewportDataDx10() { IM_ASSERT(SwapChain == NULL && RTView == NULL); } }; static void ImGui_ImplDX10_CreateWindow(ImGuiViewport* viewport) { - ImGuiPlatformDataDx10* data = IM_NEW(ImGuiPlatformDataDx10)(); + ImGuiViewportDataDx10* data = IM_NEW(ImGuiViewportDataDx10)(); viewport->RendererUserData = data; // FIXME-PLATFORM @@ -550,7 +550,7 @@ static void ImGui_ImplDX10_CreateWindow(ImGuiViewport* viewport) static void ImGui_ImplDX10_DestroyWindow(ImGuiViewport* viewport) { - if (ImGuiPlatformDataDx10* data = (ImGuiPlatformDataDx10*)viewport->RendererUserData) + if (ImGuiViewportDataDx10* data = (ImGuiViewportDataDx10*)viewport->RendererUserData) { if (data->SwapChain) data->SwapChain->Release(); @@ -565,7 +565,7 @@ static void ImGui_ImplDX10_DestroyWindow(ImGuiViewport* viewport) static void ImGui_ImplDX10_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) { - ImGuiPlatformDataDx10* data = (ImGuiPlatformDataDx10*)viewport->RendererUserData; + ImGuiViewportDataDx10* data = (ImGuiViewportDataDx10*)viewport->RendererUserData; if (data->RTView) { data->RTView->Release(); @@ -583,7 +583,7 @@ static void ImGui_ImplDX10_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) static void ImGui_ImplDX10_RenderViewport(ImGuiViewport* viewport) { - ImGuiPlatformDataDx10* data = (ImGuiPlatformDataDx10*)viewport->RendererUserData; + ImGuiViewportDataDx10* data = (ImGuiViewportDataDx10*)viewport->RendererUserData; ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); g_pd3dDevice->OMSetRenderTargets(1, &data->RTView, NULL); if (!(viewport->Flags & ImGuiViewportFlags_NoRendererClear)) @@ -593,7 +593,7 @@ static void ImGui_ImplDX10_RenderViewport(ImGuiViewport* viewport) static void ImGui_ImplDX10_SwapBuffers(ImGuiViewport* viewport) { - ImGuiPlatformDataDx10* data = (ImGuiPlatformDataDx10*)viewport->RendererUserData; + ImGuiViewportDataDx10* data = (ImGuiViewportDataDx10*)viewport->RendererUserData; data->SwapChain->Present(0, 0); // Present without vsync } diff --git a/examples/imgui_impl_dx11.cpp b/examples/imgui_impl_dx11.cpp index 244fb6a80fad..1a7ecdfdaf9b 100644 --- a/examples/imgui_impl_dx11.cpp +++ b/examples/imgui_impl_dx11.cpp @@ -511,18 +511,18 @@ void ImGui_ImplDX11_NewFrame() // Platform Interface (Optional, for multi-viewport support) //-------------------------------------------------------------------------------------------------------- -struct ImGuiPlatformDataDx11 +struct ImGuiViewportDataDx11 { IDXGISwapChain* SwapChain; ID3D11RenderTargetView* RTView; - ImGuiPlatformDataDx11() { SwapChain = NULL; RTView = NULL; } - ~ImGuiPlatformDataDx11() { IM_ASSERT(SwapChain == NULL && RTView == NULL); } + ImGuiViewportDataDx11() { SwapChain = NULL; RTView = NULL; } + ~ImGuiViewportDataDx11() { IM_ASSERT(SwapChain == NULL && RTView == NULL); } }; static void ImGui_ImplDX11_CreateWindow(ImGuiViewport* viewport) { - ImGuiPlatformDataDx11* data = IM_NEW(ImGuiPlatformDataDx11)(); + ImGuiViewportDataDx11* data = IM_NEW(ImGuiViewportDataDx11)(); viewport->RendererUserData = data; HWND hwnd = (HWND)viewport->PlatformHandle; @@ -558,7 +558,7 @@ static void ImGui_ImplDX11_CreateWindow(ImGuiViewport* viewport) static void ImGui_ImplDX11_DestroyWindow(ImGuiViewport* viewport) { - if (ImGuiPlatformDataDx11* data = (ImGuiPlatformDataDx11*)viewport->RendererUserData) + if (ImGuiViewportDataDx11* data = (ImGuiViewportDataDx11*)viewport->RendererUserData) { if (data->SwapChain) data->SwapChain->Release(); @@ -573,7 +573,7 @@ static void ImGui_ImplDX11_DestroyWindow(ImGuiViewport* viewport) static void ImGui_ImplDX11_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) { - ImGuiPlatformDataDx11* data = (ImGuiPlatformDataDx11*)viewport->RendererUserData; + ImGuiViewportDataDx11* data = (ImGuiViewportDataDx11*)viewport->RendererUserData; if (data->RTView) { data->RTView->Release(); @@ -591,7 +591,7 @@ static void ImGui_ImplDX11_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) static void ImGui_ImplDX11_RenderWindow(ImGuiViewport* viewport) { - ImGuiPlatformDataDx11* data = (ImGuiPlatformDataDx11*)viewport->RendererUserData; + ImGuiViewportDataDx11* data = (ImGuiViewportDataDx11*)viewport->RendererUserData; ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); g_pd3dDeviceContext->OMSetRenderTargets(1, &data->RTView, NULL); if (!(viewport->Flags & ImGuiViewportFlags_NoRendererClear)) @@ -601,7 +601,7 @@ static void ImGui_ImplDX11_RenderWindow(ImGuiViewport* viewport) static void ImGui_ImplDX11_SwapBuffers(ImGuiViewport* viewport) { - ImGuiPlatformDataDx11* data = (ImGuiPlatformDataDx11*)viewport->RendererUserData; + ImGuiViewportDataDx11* data = (ImGuiViewportDataDx11*)viewport->RendererUserData; data->SwapChain->Present(0, 0); // Present without vsync } diff --git a/examples/imgui_impl_dx12.cpp b/examples/imgui_impl_dx12.cpp index 98190ebbe2b8..9eb5ac63f5d6 100644 --- a/examples/imgui_impl_dx12.cpp +++ b/examples/imgui_impl_dx12.cpp @@ -638,17 +638,17 @@ void ImGui_ImplDX12_NewFrame(ID3D12GraphicsCommandList* command_list) // Platform Interface (Optional, for multi-viewport support) //-------------------------------------------------------------------------------------------------------- -struct ImGuiPlatformDataDx12 +struct ImGuiViewportDataDx12 { IDXGISwapChain3* SwapChain; - ImGuiPlatformDataDx12() { SwapChain = NULL; } - ~ImGuiPlatformDataDx12() { IM_ASSERT(SwapChain == NULL); } + ImGuiViewportDataDx12() { SwapChain = NULL; } + ~ImGuiViewportDataDx12() { IM_ASSERT(SwapChain == NULL); } }; static void ImGui_ImplDX12_CreateWindow(ImGuiViewport* viewport) { - ImGuiPlatformDataDx12* data = IM_NEW(ImGuiPlatformDataDx12)(); + ImGuiViewportDataDx12* data = IM_NEW(ImGuiViewportDataDx12)(); viewport->RendererUserData = data; IM_ASSERT(0); @@ -688,7 +688,7 @@ static void ImGui_ImplDX12_CreateWindow(ImGuiViewport* viewport) static void ImGui_ImplDX12_DestroyWindow(ImGuiViewport* viewport) { - if (ImGuiPlatformDataDx12* data = (ImGuiPlatformDataDx12*)viewport->RendererUserData) + if (ImGuiViewportDataDx12* data = (ImGuiViewportDataDx12*)viewport->RendererUserData) { IM_ASSERT(0); /* @@ -706,7 +706,7 @@ static void ImGui_ImplDX12_DestroyWindow(ImGuiViewport* viewport) static void ImGui_ImplDX12_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) { - ImGuiPlatformDataDx12* data = (ImGuiPlatformDataDx12*)viewport->RendererUserData; + ImGuiViewportDataDx12* data = (ImGuiViewportDataDx12*)viewport->RendererUserData; IM_ASSERT(0); (void)data; (void)size; /* @@ -728,7 +728,7 @@ static void ImGui_ImplDX12_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) static void ImGui_ImplDX12_RenderWindow(ImGuiViewport* viewport) { - ImGuiPlatformDataDx12* data = (ImGuiPlatformDataDx12*)viewport->RendererUserData; + ImGuiViewportDataDx12* data = (ImGuiViewportDataDx12*)viewport->RendererUserData; IM_ASSERT(0); (void)data; /* @@ -742,7 +742,7 @@ static void ImGui_ImplDX12_RenderWindow(ImGuiViewport* viewport) static void ImGui_ImplDX12_SwapBuffers(ImGuiViewport* viewport) { - ImGuiPlatformDataDx12* data = (ImGuiPlatformDataDx12*)viewport->RendererUserData; + ImGuiViewportDataDx12* data = (ImGuiViewportDataDx12*)viewport->RendererUserData; IM_ASSERT(0); (void)data; /* diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index c761b8f955e9..d1f2e54b59ca 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -302,13 +302,13 @@ void ImGui_ImplGlfw_NewFrame() // Platform Windows // -------------------------------------------------------------------------------------------------------- -struct ImGuiPlatformDataGlfw +struct ImGuiViewportDataGlfw { GLFWwindow* Window; bool WindowOwned; - ImGuiPlatformDataGlfw() { Window = NULL; WindowOwned = false; } - ~ImGuiPlatformDataGlfw() { IM_ASSERT(Window == NULL); } + ImGuiViewportDataGlfw() { Window = NULL; WindowOwned = false; } + ~ImGuiViewportDataGlfw() { IM_ASSERT(Window == NULL); } }; static void ImGui_ImplGlfw_WindowCloseCallback(GLFWwindow* window) @@ -331,7 +331,7 @@ static void ImGui_ImplGlfw_WindowSizeCallback(GLFWwindow* window, int, int) static void ImGui_ImplGlfw_CreateWindow(ImGuiViewport* viewport) { - ImGuiPlatformDataGlfw* data = IM_NEW(ImGuiPlatformDataGlfw)(); + ImGuiViewportDataGlfw* data = IM_NEW(ImGuiViewportDataGlfw)(); viewport->PlatformUserData = data; // GLFW 3.2 unfortunately always set focus on glfwCreateWindow() if GLFW_VISIBLE is set, regardless of GLFW_FOCUSED @@ -350,7 +350,7 @@ static void ImGui_ImplGlfw_CreateWindow(ImGuiViewport* viewport) static void ImGui_ImplGlfw_DestroyWindow(ImGuiViewport* viewport) { - if (ImGuiPlatformDataGlfw* data = (ImGuiPlatformDataGlfw*)viewport->PlatformUserData) + if (ImGuiViewportDataGlfw* data = (ImGuiViewportDataGlfw*)viewport->PlatformUserData) { #if GLFW_HAS_GLFW_HOVERED HWND hwnd = glfwGetWin32Window(data->Window); @@ -380,7 +380,7 @@ static LRESULT CALLBACK WndProcNoInputs(HWND hWnd, UINT msg, WPARAM wParam, LPAR static void ImGui_ImplGlfw_ShowWindow(ImGuiViewport* viewport) { - ImGuiPlatformDataGlfw* data = (ImGuiPlatformDataGlfw*)viewport->PlatformUserData; + ImGuiViewportDataGlfw* data = (ImGuiViewportDataGlfw*)viewport->PlatformUserData; #if defined(_WIN32) // GLFW hack: Hide icon from task bar @@ -416,7 +416,7 @@ static void ImGui_ImplGlfw_ShowWindow(ImGuiViewport* viewport) static ImVec2 ImGui_ImplGlfw_GetWindowPos(ImGuiViewport* viewport) { - ImGuiPlatformDataGlfw* data = (ImGuiPlatformDataGlfw*)viewport->PlatformUserData; + ImGuiViewportDataGlfw* data = (ImGuiViewportDataGlfw*)viewport->PlatformUserData; int x = 0, y = 0; glfwGetWindowPos(data->Window, &x, &y); return ImVec2((float)x, (float)y); @@ -424,13 +424,13 @@ static ImVec2 ImGui_ImplGlfw_GetWindowPos(ImGuiViewport* viewport) static void ImGui_ImplGlfw_SetWindowPos(ImGuiViewport* viewport, ImVec2 pos) { - ImGuiPlatformDataGlfw* data = (ImGuiPlatformDataGlfw*)viewport->PlatformUserData; + ImGuiViewportDataGlfw* data = (ImGuiViewportDataGlfw*)viewport->PlatformUserData; glfwSetWindowPos(data->Window, (int)pos.x, (int)pos.y); } static ImVec2 ImGui_ImplGlfw_GetWindowSize(ImGuiViewport* viewport) { - ImGuiPlatformDataGlfw* data = (ImGuiPlatformDataGlfw*)viewport->PlatformUserData; + ImGuiViewportDataGlfw* data = (ImGuiViewportDataGlfw*)viewport->PlatformUserData; int w = 0, h = 0; glfwGetWindowSize(data->Window, &w, &h); return ImVec2((float)w, (float)h); @@ -438,26 +438,26 @@ static ImVec2 ImGui_ImplGlfw_GetWindowSize(ImGuiViewport* viewport) static void ImGui_ImplGlfw_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) { - ImGuiPlatformDataGlfw* data = (ImGuiPlatformDataGlfw*)viewport->PlatformUserData; + ImGuiViewportDataGlfw* data = (ImGuiViewportDataGlfw*)viewport->PlatformUserData; glfwSetWindowSize(data->Window, (int)size.x, (int)size.y); } static void ImGui_ImplGlfw_SetWindowTitle(ImGuiViewport* viewport, const char* title) { - ImGuiPlatformDataGlfw* data = (ImGuiPlatformDataGlfw*)viewport->PlatformUserData; + ImGuiViewportDataGlfw* data = (ImGuiViewportDataGlfw*)viewport->PlatformUserData; glfwSetWindowTitle(data->Window, title); } static void ImGui_ImplGlfw_RenderWindow(ImGuiViewport* viewport) { - ImGuiPlatformDataGlfw* data = (ImGuiPlatformDataGlfw*)viewport->PlatformUserData; + ImGuiViewportDataGlfw* data = (ImGuiViewportDataGlfw*)viewport->PlatformUserData; if (g_ClientApi == GlfwClientApi_OpenGL) glfwMakeContextCurrent(data->Window); } static void ImGui_ImplGlfw_SwapBuffers(ImGuiViewport* viewport) { - ImGuiPlatformDataGlfw* data = (ImGuiPlatformDataGlfw*)viewport->PlatformUserData; + ImGuiViewportDataGlfw* data = (ImGuiViewportDataGlfw*)viewport->PlatformUserData; if (g_ClientApi == GlfwClientApi_OpenGL) glfwSwapBuffers(data->Window); } @@ -480,7 +480,7 @@ enum VkResult { VK_RESULT_MAX_ENUM = 0x7FFFFFFF }; extern "C" { extern GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance, GLFWwindow* window, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface); } static int ImGui_ImplGlfw_CreateVkSurface(ImGuiViewport* viewport, ImU64 vk_instance, const void* vk_allocator, ImU64* out_vk_surface) { - ImGuiPlatformDataGlfw* data = (ImGuiPlatformDataGlfw*)viewport->PlatformUserData; + ImGuiViewportDataGlfw* data = (ImGuiViewportDataGlfw*)viewport->PlatformUserData; IM_ASSERT(g_ClientApi == GlfwClientApi_Vulkan); VkResult err = glfwCreateWindowSurface((VkInstance)vk_instance, data->Window, (const VkAllocationCallbacks*)vk_allocator, (VkSurfaceKHR*)out_vk_surface); return (int)err; @@ -507,7 +507,7 @@ static void ImGui_ImplGlfw_InitPlatformInterface() // Register main window handle ImGuiViewport* main_viewport = ImGui::GetMainViewport(); - ImGuiPlatformDataGlfw* data = IM_NEW(ImGuiPlatformDataGlfw)(); + ImGuiViewportDataGlfw* data = IM_NEW(ImGuiViewportDataGlfw)(); data->Window = g_Window; data->WindowOwned = false; main_viewport->PlatformUserData = data; diff --git a/examples/imgui_impl_opengl3.cpp b/examples/imgui_impl_opengl3.cpp index 11083df8e82e..f753e2b46f51 100644 --- a/examples/imgui_impl_opengl3.cpp +++ b/examples/imgui_impl_opengl3.cpp @@ -4,9 +4,10 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2018-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. +// 2018-XX-XX: OpenGL: Offset projection matrix and clipping rectangle by draw_data->DisplayPos (which will be non-zero for multi-viewport applications). // 2018-03-06: OpenGL: Added const char* glsl_version parameter to ImGui_ImplOpenGL3_Init() so user can override the GLSL version e.g. "#version 150". // 2018-02-23: OpenGL: Create the VAO in the render function so the setup can more easily be used with multiple shared GL context. -// 2018-XX-XX: OpenGL: Offset projection matrix and clipping rectangle by draw_data->DisplayPos (which will be non-zero for multi-viewport applications). // 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplSdlGL3_RenderDrawData() in the .h file so you can call it yourself. // 2018-01-07: OpenGL: Changed GLSL shader version from 330 to 150. // 2017-09-01: OpenGL: Save and restore current bound sampler. Save and restore current polygon mode. diff --git a/examples/imgui_impl_sdl2.cpp b/examples/imgui_impl_sdl2.cpp index 53a30dfdd31b..17d4ec03834c 100644 --- a/examples/imgui_impl_sdl2.cpp +++ b/examples/imgui_impl_sdl2.cpp @@ -274,25 +274,25 @@ void ImGui_ImplSDL2_NewFrame(SDL_Window* window) // Platform Interface (Optional, for multi-viewport support) //-------------------------------------------------------------------------------------------------------- -struct ImGuiPlatformDataSDL2 +struct ImGuiViewportDataSDL2 { SDL_Window* Window; Uint32 WindowID; SDL_GLContext GLContext; - ImGuiPlatformDataSDL2() { Window = NULL; WindowID = 0; GLContext = NULL; } - ~ImGuiPlatformDataSDL2() { IM_ASSERT(Window == NULL && GLContext == NULL); } + ImGuiViewportDataSDL2() { Window = NULL; WindowID = 0; GLContext = NULL; } + ~ImGuiViewportDataSDL2() { IM_ASSERT(Window == NULL && GLContext == NULL); } }; static void ImGui_ImplSDL2_CreateWindow(ImGuiViewport* viewport) { - ImGuiPlatformDataSDL2* data = IM_NEW(ImGuiPlatformDataSDL2)(); + ImGuiViewportDataSDL2* data = IM_NEW(ImGuiViewportDataSDL2)(); viewport->PlatformUserData = data; // Share GL resources with main context // FIXME-PLATFORM ImGuiViewport* main_viewport = ImGui::GetMainViewport(); - ImGuiPlatformDataSDL2* main_viewport_data = (ImGuiPlatformDataSDL2*)main_viewport->PlatformUserData; + ImGuiViewportDataSDL2* main_viewport_data = (ImGuiViewportDataSDL2*)main_viewport->PlatformUserData; bool use_opengl = (main_viewport_data->GLContext != NULL); SDL_GLContext backup_context = NULL; @@ -320,7 +320,7 @@ static void ImGui_ImplSDL2_CreateWindow(ImGuiViewport* viewport) static void ImGui_ImplSDL2_DestroyWindow(ImGuiViewport* viewport) { - if (ImGuiPlatformDataSDL2* data = (ImGuiPlatformDataSDL2*)viewport->PlatformUserData) + if (ImGuiViewportDataSDL2* data = (ImGuiViewportDataSDL2*)viewport->PlatformUserData) { if (data->GLContext) SDL_GL_DeleteContext(data->GLContext); @@ -335,7 +335,7 @@ static void ImGui_ImplSDL2_DestroyWindow(ImGuiViewport* viewport) static void ImGui_ImplSDL2_ShowWindow(ImGuiViewport* viewport) { - ImGuiPlatformDataSDL2* data = (ImGuiPlatformDataSDL2*)viewport->PlatformUserData; + ImGuiViewportDataSDL2* data = (ImGuiViewportDataSDL2*)viewport->PlatformUserData; #if defined(_WIN32) SDL_SysWMinfo info; SDL_VERSION(&info.version); @@ -368,7 +368,7 @@ static void ImGui_ImplSDL2_ShowWindow(ImGuiViewport* viewport) static ImVec2 ImGui_ImplSDL2_GetWindowPos(ImGuiViewport* viewport) { - ImGuiPlatformDataSDL2* data = (ImGuiPlatformDataSDL2*)viewport->PlatformUserData; + ImGuiViewportDataSDL2* data = (ImGuiViewportDataSDL2*)viewport->PlatformUserData; int x = 0, y = 0; SDL_GetWindowPosition(data->Window, &x, &y); return ImVec2((float)x, (float)y); @@ -376,13 +376,13 @@ static ImVec2 ImGui_ImplSDL2_GetWindowPos(ImGuiViewport* viewport) static void ImGui_ImplSDL2_SetWindowPos(ImGuiViewport* viewport, ImVec2 pos) { - ImGuiPlatformDataSDL2* data = (ImGuiPlatformDataSDL2*)viewport->PlatformUserData; + ImGuiViewportDataSDL2* data = (ImGuiViewportDataSDL2*)viewport->PlatformUserData; SDL_SetWindowPosition(data->Window, (int)pos.x, (int)pos.y); } static ImVec2 ImGui_ImplSDL2_GetWindowSize(ImGuiViewport* viewport) { - ImGuiPlatformDataSDL2* data = (ImGuiPlatformDataSDL2*)viewport->PlatformUserData; + ImGuiViewportDataSDL2* data = (ImGuiViewportDataSDL2*)viewport->PlatformUserData; int w = 0, h = 0; SDL_GetWindowSize(data->Window, &w, &h); return ImVec2((float)w, (float)h); @@ -390,26 +390,26 @@ static ImVec2 ImGui_ImplSDL2_GetWindowSize(ImGuiViewport* viewport) static void ImGui_ImplSDL2_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) { - ImGuiPlatformDataSDL2* data = (ImGuiPlatformDataSDL2*)viewport->PlatformUserData; + ImGuiViewportDataSDL2* data = (ImGuiViewportDataSDL2*)viewport->PlatformUserData; SDL_SetWindowSize(data->Window, (int)size.x, (int)size.y); } static void ImGui_ImplSDL2_SetWindowTitle(ImGuiViewport* viewport, const char* title) { - ImGuiPlatformDataSDL2* data = (ImGuiPlatformDataSDL2*)viewport->PlatformUserData; + ImGuiViewportDataSDL2* data = (ImGuiViewportDataSDL2*)viewport->PlatformUserData; SDL_SetWindowTitle(data->Window, title); } static void ImGui_ImplSDL2_RenderWindow(ImGuiViewport* viewport) { - ImGuiPlatformDataSDL2* data = (ImGuiPlatformDataSDL2*)viewport->PlatformUserData; + ImGuiViewportDataSDL2* data = (ImGuiViewportDataSDL2*)viewport->PlatformUserData; if (data->GLContext) SDL_GL_MakeCurrent(data->Window, data->GLContext); } static void ImGui_ImplSDL2_SwapBuffers(ImGuiViewport* viewport) { - ImGuiPlatformDataSDL2* data = (ImGuiPlatformDataSDL2*)viewport->PlatformUserData; + ImGuiViewportDataSDL2* data = (ImGuiViewportDataSDL2*)viewport->PlatformUserData; if (data->GLContext) { SDL_GL_MakeCurrent(data->Window, data->GLContext); @@ -423,7 +423,7 @@ static void ImGui_ImplSDL2_SwapBuffers(ImGuiViewport* viewport) #include static int ImGui_ImplSDL2_CreateVkSurface(ImGuiViewport* viewport, ImU64 vk_instance, const void* vk_allocator, ImU64* out_vk_surface) { - ImGuiPlatformDataSDL2* data = (ImGuiPlatformDataSDL2*)viewport->PlatformUserData; + ImGuiViewportDataSDL2* data = (ImGuiViewportDataSDL2*)viewport->PlatformUserData; (void)vk_allocator; SDL_bool ret = SDL_Vulkan_CreateSurface(data->Window, (VkInstance)vk_instance, (VkSurfaceKHR*)out_vk_surface); return ret ? 0 : 1; // ret ? VK_SUCCESS : VK_NOT_READY @@ -453,7 +453,7 @@ static void ImGui_ImplSDL2_InitPlatformInterface(SDL_Window* window, void* sdl_g // Register main window handle ImGuiViewport* main_viewport = ImGui::GetMainViewport(); - ImGuiPlatformDataSDL2* data = IM_NEW(ImGuiPlatformDataSDL2)(); + ImGuiViewportDataSDL2* data = IM_NEW(ImGuiViewportDataSDL2)(); data->Window = window; data->WindowID = SDL_GetWindowID(window); data->GLContext = sdl_gl_context; diff --git a/examples/imgui_impl_vulkan.cpp b/examples/imgui_impl_vulkan.cpp index 714f1dcf688a..3a9e8b8fdd49 100644 --- a/examples/imgui_impl_vulkan.cpp +++ b/examples/imgui_impl_vulkan.cpp @@ -11,6 +11,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2018-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. // 2018-03-03: Vulkan: Various refactor, created a couple of ImGui_ImplVulkanH_XXX helper that the example can use and that viewport support will use. // 2018-03-01: Vulkan: Renamed ImGui_ImplVulkan_Init_Info to ImGui_ImplVulkan_InitInfo and fields to match more closely Vulkan terminology. // 2018-02-18: Vulkan: Offset projection matrix and clipping rectangle by draw_data->DisplayPos (which will be non-zero for multi-viewport applications). @@ -1050,17 +1051,17 @@ void ImGui_ImplVulkanH_DestroyWindowData(VkInstance instance, VkDevice device, I // FIXME-PLATFORM: Vulkan support unfinished //-------------------------------------------------------------------------------------------------------- -struct ImGuiPlatformDataVulkan +struct ImGuiViewportDataVulkan { ImGui_ImplVulkan_WindowData WindowData; - ImGuiPlatformDataVulkan() { } - ~ImGuiPlatformDataVulkan() { } + ImGuiViewportDataVulkan() { } + ~ImGuiViewportDataVulkan() { } }; static void ImGui_ImplVulkan_CreateWindow(ImGuiViewport* viewport) { - ImGuiPlatformDataVulkan* data = IM_NEW(ImGuiPlatformDataVulkan)(); + ImGuiViewportDataVulkan* data = IM_NEW(ImGuiViewportDataVulkan)(); viewport->RendererUserData = data; ImGui_ImplVulkan_WindowData* wd = &data->WindowData; @@ -1095,7 +1096,7 @@ static void ImGui_ImplVulkan_CreateWindow(ImGuiViewport* viewport) static void ImGui_ImplVulkan_DestroyWindow(ImGuiViewport* viewport) { - if (ImGuiPlatformDataVulkan* data = (ImGuiPlatformDataVulkan*)viewport->RendererUserData) + if (ImGuiViewportDataVulkan* data = (ImGuiViewportDataVulkan*)viewport->RendererUserData) { ImGui_ImplVulkanH_DestroyWindowData(g_Instance, g_Device, &data->WindowData, g_Allocator); IM_DELETE(data); @@ -1105,7 +1106,7 @@ static void ImGui_ImplVulkan_DestroyWindow(ImGuiViewport* viewport) static void ImGui_ImplVulkan_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) { - ImGuiPlatformDataVulkan* data = (ImGuiPlatformDataVulkan*)viewport->RendererUserData; + ImGuiViewportDataVulkan* data = (ImGuiViewportDataVulkan*)viewport->RendererUserData; if (data == NULL) // This is NULL for the main viewport (which is left to the user/app to handle) return; data->WindowData.ClearEnable = (viewport->Flags & ImGuiViewportFlags_NoRendererClear) ? false : true; @@ -1114,7 +1115,7 @@ static void ImGui_ImplVulkan_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) static void ImGui_ImplVulkan_RenderWindow(ImGuiViewport* viewport) { - ImGuiPlatformDataVulkan* data = (ImGuiPlatformDataVulkan*)viewport->RendererUserData; + ImGuiViewportDataVulkan* data = (ImGuiViewportDataVulkan*)viewport->RendererUserData; ImGui_ImplVulkan_WindowData* wd = &data->WindowData; VkResult err; @@ -1185,7 +1186,7 @@ static void ImGui_ImplVulkan_RenderWindow(ImGuiViewport* viewport) static void ImGui_ImplVulkan_SwapBuffers(ImGuiViewport* viewport) { - ImGuiPlatformDataVulkan* data = (ImGuiPlatformDataVulkan*)viewport->RendererUserData; + ImGuiViewportDataVulkan* data = (ImGuiViewportDataVulkan*)viewport->RendererUserData; ImGui_ImplVulkan_WindowData* wd = &data->WindowData; VkResult err; diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index 744253eb0071..40075de9a853 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -351,19 +351,19 @@ float ImGui_ImplWin32_GetDpiScaleForRect(int x1, int y1, int x2, int y2) // Platform Windows // -------------------------------------------------------------------------------------------------------- -struct ImGuiPlatformDataWin32 +struct ImGuiViewportDataWin32 { HWND Hwnd; DWORD DwStyle; DWORD DwExStyle; - ImGuiPlatformDataWin32() { Hwnd = NULL; DwStyle = DwExStyle = 0; } - ~ImGuiPlatformDataWin32() { IM_ASSERT(Hwnd == NULL); } + ImGuiViewportDataWin32() { Hwnd = NULL; DwStyle = DwExStyle = 0; } + ~ImGuiViewportDataWin32() { IM_ASSERT(Hwnd == NULL); } }; static void ImGui_ImplWin32_CreateWindow(ImGuiViewport* viewport) { - ImGuiPlatformDataWin32* data = IM_NEW(ImGuiPlatformDataWin32)(); + ImGuiViewportDataWin32* data = IM_NEW(ImGuiViewportDataWin32)(); viewport->PlatformUserData = data; ImGuiIO& io = ImGui::GetIO(); @@ -393,7 +393,7 @@ static void ImGui_ImplWin32_CreateWindow(ImGuiViewport* viewport) static void ImGui_ImplWin32_DestroyWindow(ImGuiViewport* viewport) { - if (ImGuiPlatformDataWin32* data = (ImGuiPlatformDataWin32*)viewport->PlatformUserData) + if (ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData) { if (::GetCapture() == data->Hwnd) { @@ -411,7 +411,7 @@ static void ImGui_ImplWin32_DestroyWindow(ImGuiViewport* viewport) static void ImGui_ImplWin32_ShowWindow(ImGuiViewport* viewport) { - ImGuiPlatformDataWin32* data = (ImGuiPlatformDataWin32*)viewport->PlatformUserData; + ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData; IM_ASSERT(data->Hwnd != 0); if (viewport->Flags & ImGuiViewportFlags_NoFocusOnAppearing) ::ShowWindow(data->Hwnd, SW_SHOWNA); @@ -421,7 +421,7 @@ static void ImGui_ImplWin32_ShowWindow(ImGuiViewport* viewport) static ImVec2 ImGui_ImplWin32_GetWindowPos(ImGuiViewport* viewport) { - ImGuiPlatformDataWin32* data = (ImGuiPlatformDataWin32*)viewport->PlatformUserData; + ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData; IM_ASSERT(data->Hwnd != 0); POINT pos = { 0, 0 }; ::ClientToScreen(data->Hwnd, &pos); @@ -430,7 +430,7 @@ static ImVec2 ImGui_ImplWin32_GetWindowPos(ImGuiViewport* viewport) static void ImGui_ImplWin32_SetWindowPos(ImGuiViewport* viewport, ImVec2 pos) { - ImGuiPlatformDataWin32* data = (ImGuiPlatformDataWin32*)viewport->PlatformUserData; + ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData; IM_ASSERT(data->Hwnd != 0); RECT rect = { (LONG)pos.x, (LONG)pos.y, (LONG)pos.x, (LONG)pos.y }; ::AdjustWindowRectEx(&rect, data->DwStyle, FALSE, data->DwExStyle); @@ -439,7 +439,7 @@ static void ImGui_ImplWin32_SetWindowPos(ImGuiViewport* viewport, ImVec2 pos) static ImVec2 ImGui_ImplWin32_GetWindowSize(ImGuiViewport* viewport) { - ImGuiPlatformDataWin32* data = (ImGuiPlatformDataWin32*)viewport->PlatformUserData; + ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData; IM_ASSERT(data->Hwnd != 0); RECT rect; ::GetClientRect(data->Hwnd, &rect); @@ -448,7 +448,7 @@ static ImVec2 ImGui_ImplWin32_GetWindowSize(ImGuiViewport* viewport) static void ImGui_ImplWin32_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) { - ImGuiPlatformDataWin32* data = (ImGuiPlatformDataWin32*)viewport->PlatformUserData; + ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData; IM_ASSERT(data->Hwnd != 0); RECT rect = { 0, 0, (LONG)size.x, (LONG)size.y }; ::AdjustWindowRectEx(&rect, data->DwStyle, FALSE, data->DwExStyle); // Client to Screen @@ -457,14 +457,14 @@ static void ImGui_ImplWin32_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) static void ImGui_ImplWin32_SetWindowTitle(ImGuiViewport* viewport, const char* title) { - ImGuiPlatformDataWin32* data = (ImGuiPlatformDataWin32*)viewport->PlatformUserData; + ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData; IM_ASSERT(data->Hwnd != 0); ::SetWindowTextA(data->Hwnd, title); } static float ImGui_ImplWin32_GetWindowDpiScale(ImGuiViewport* viewport) { - ImGuiPlatformDataWin32* data = (ImGuiPlatformDataWin32*)viewport->PlatformUserData; + ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData; if (data && data->Hwnd) return ImGui_ImplWin32_GetDpiScaleForHwnd(data->Hwnd); @@ -534,7 +534,7 @@ static void ImGui_ImplWin32_InitPlatformInterface() // Register main window handle ImGuiViewport* main_viewport = ImGui::GetMainViewport(); - ImGuiPlatformDataWin32* data = IM_NEW(ImGuiPlatformDataWin32)(); + ImGuiViewportDataWin32* data = IM_NEW(ImGuiViewportDataWin32)(); data->Hwnd = g_hWnd; main_viewport->PlatformUserData = data; main_viewport->PlatformHandle = (void*)data->Hwnd; From 2fecd332fced95054309e289420ec5a5542340c1 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 19 Mar 2018 15:20:47 +0100 Subject: [PATCH 093/828] Viewport, Platform: Added void* to render/swap buffer functions. Comments. (#1542, #1042) --- examples/directx10_example/main.cpp | 4 ++-- examples/directx11_example/main.cpp | 4 ++-- examples/directx12_example/main.cpp | 4 ++-- examples/imgui_impl_dx10.cpp | 5 +++-- examples/imgui_impl_dx10.h | 1 + examples/imgui_impl_dx11.cpp | 5 +++-- examples/imgui_impl_dx11.h | 1 + examples/imgui_impl_dx12.cpp | 4 ++-- examples/imgui_impl_glfw.cpp | 5 +++-- examples/imgui_impl_glfw.h | 1 + examples/imgui_impl_opengl2.cpp | 3 +++ examples/imgui_impl_opengl2.h | 3 +++ examples/imgui_impl_opengl3.cpp | 6 +++++- examples/imgui_impl_opengl3.h | 4 ++++ examples/imgui_impl_sdl2.cpp | 6 +++--- examples/imgui_impl_sdl2.h | 3 +++ examples/imgui_impl_vulkan.cpp | 5 +++-- examples/imgui_impl_vulkan.h | 1 + examples/opengl3_example/main.cpp | 4 ++-- examples/sdl_opengl3_example/main.cpp | 4 ++-- examples/sdl_vulkan_example/main.cpp | 4 ++-- examples/vulkan_example/main.cpp | 4 ++-- imgui.cpp | 16 ++++++---------- imgui.h | 22 ++++++++++++---------- 24 files changed, 71 insertions(+), 48 deletions(-) diff --git a/examples/directx10_example/main.cpp b/examples/directx10_example/main.cpp index 6166a60ffb35..aea81a43a3b7 100644 --- a/examples/directx10_example/main.cpp +++ b/examples/directx10_example/main.cpp @@ -208,9 +208,9 @@ int main(int, char**) ImGui::Render(); ImGui_ImplDX10_RenderDrawData(ImGui::GetDrawData()); - // Update and Render additional Platform Windows + // Update and Render additional Platform Windows (when ImGuiConfigFlags_EnableViewports is enabled) ImGui::UpdatePlatformWindows(); - ImGui::RenderPlatformWindows(); + ImGui::RenderPlatformWindows(NULL, NULL); g_pSwapChain->Present(1, 0); // Present with vsync //g_pSwapChain->Present(0, 0); // Present without vsync diff --git a/examples/directx11_example/main.cpp b/examples/directx11_example/main.cpp index b5211009d8d8..51c5b6d4a727 100644 --- a/examples/directx11_example/main.cpp +++ b/examples/directx11_example/main.cpp @@ -226,9 +226,9 @@ int main(int, char**) ImGui::Render(); ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData()); - // Update and Render additional Platform Windows + // Update and Render additional Platform Windows (when ImGuiConfigFlags_EnableViewports is enabled) ImGui::UpdatePlatformWindows(); - ImGui::RenderPlatformWindows(); + ImGui::RenderPlatformWindows(NULL, NULL); g_pSwapChain->Present(1, 0); // Present with vsync //g_pSwapChain->Present(0, 0); // Present without vsync diff --git a/examples/directx12_example/main.cpp b/examples/directx12_example/main.cpp index f3d5d17139a4..57bd1bbccdca 100644 --- a/examples/directx12_example/main.cpp +++ b/examples/directx12_example/main.cpp @@ -401,9 +401,9 @@ int main(int, char**) g_pd3dCommandQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*)&g_pd3dCommandList); - // Update and Render additional Platform Windows + // Update and Render additional Platform Windows (when ImGuiConfigFlags_EnableViewports is enabled) ImGui::UpdatePlatformWindows(); - ImGui::RenderPlatformWindows(); + ImGui::RenderPlatformWindows(NULL, NULL); g_pSwapChain->Present(1, 0); // Present with vsync //g_pSwapChain->Present(0, 0); // Present without vsync diff --git a/examples/imgui_impl_dx10.cpp b/examples/imgui_impl_dx10.cpp index eac0ccb8c27a..1a88588727bc 100644 --- a/examples/imgui_impl_dx10.cpp +++ b/examples/imgui_impl_dx10.cpp @@ -3,6 +3,7 @@ // Implemented features: // [X] User texture binding. Use 'ID3D10ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. +// [X] Multi-viewport rendering (when ImGuiConfigFlags_EnableViewports is enabled). // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). @@ -581,7 +582,7 @@ static void ImGui_ImplDX10_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) } } -static void ImGui_ImplDX10_RenderViewport(ImGuiViewport* viewport) +static void ImGui_ImplDX10_RenderViewport(ImGuiViewport* viewport, void*) { ImGuiViewportDataDx10* data = (ImGuiViewportDataDx10*)viewport->RendererUserData; ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); @@ -591,7 +592,7 @@ static void ImGui_ImplDX10_RenderViewport(ImGuiViewport* viewport) ImGui_ImplDX10_RenderDrawData(viewport->DrawData); } -static void ImGui_ImplDX10_SwapBuffers(ImGuiViewport* viewport) +static void ImGui_ImplDX10_SwapBuffers(ImGuiViewport* viewport, void*) { ImGuiViewportDataDx10* data = (ImGuiViewportDataDx10*)viewport->RendererUserData; data->SwapChain->Present(0, 0); // Present without vsync diff --git a/examples/imgui_impl_dx10.h b/examples/imgui_impl_dx10.h index 5d972013a90e..401fa1ae6704 100644 --- a/examples/imgui_impl_dx10.h +++ b/examples/imgui_impl_dx10.h @@ -3,6 +3,7 @@ // Implemented features: // [X] User texture binding. Use 'ID3D10ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. +// [X] Multi-viewport rendering (when ImGuiConfigFlags_EnableViewports is enabled). // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). diff --git a/examples/imgui_impl_dx11.cpp b/examples/imgui_impl_dx11.cpp index 1a7ecdfdaf9b..4fdde88b8302 100644 --- a/examples/imgui_impl_dx11.cpp +++ b/examples/imgui_impl_dx11.cpp @@ -3,6 +3,7 @@ // Implemented features: // [X] User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. +// [X] Multi-viewport rendering (when ImGuiConfigFlags_EnableViewports is enabled). // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). @@ -589,7 +590,7 @@ static void ImGui_ImplDX11_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) } } -static void ImGui_ImplDX11_RenderWindow(ImGuiViewport* viewport) +static void ImGui_ImplDX11_RenderWindow(ImGuiViewport* viewport, void*) { ImGuiViewportDataDx11* data = (ImGuiViewportDataDx11*)viewport->RendererUserData; ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); @@ -599,7 +600,7 @@ static void ImGui_ImplDX11_RenderWindow(ImGuiViewport* viewport) ImGui_ImplDX11_RenderDrawData(viewport->DrawData); } -static void ImGui_ImplDX11_SwapBuffers(ImGuiViewport* viewport) +static void ImGui_ImplDX11_SwapBuffers(ImGuiViewport* viewport, void*) { ImGuiViewportDataDx11* data = (ImGuiViewportDataDx11*)viewport->RendererUserData; data->SwapChain->Present(0, 0); // Present without vsync diff --git a/examples/imgui_impl_dx11.h b/examples/imgui_impl_dx11.h index 62fb627eff97..6d75b237259f 100644 --- a/examples/imgui_impl_dx11.h +++ b/examples/imgui_impl_dx11.h @@ -3,6 +3,7 @@ // Implemented features: // [X] User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. +// [X] Multi-viewport rendering (when ImGuiConfigFlags_EnableViewports is enabled). // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). diff --git a/examples/imgui_impl_dx12.cpp b/examples/imgui_impl_dx12.cpp index 9eb5ac63f5d6..e365827ec0b7 100644 --- a/examples/imgui_impl_dx12.cpp +++ b/examples/imgui_impl_dx12.cpp @@ -726,7 +726,7 @@ static void ImGui_ImplDX12_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) */ } -static void ImGui_ImplDX12_RenderWindow(ImGuiViewport* viewport) +static void ImGui_ImplDX12_RenderWindow(ImGuiViewport* viewport, void*) { ImGuiViewportDataDx12* data = (ImGuiViewportDataDx12*)viewport->RendererUserData; IM_ASSERT(0); @@ -740,7 +740,7 @@ static void ImGui_ImplDX12_RenderWindow(ImGuiViewport* viewport) ImGui_ImplDX12_RenderDrawData(viewport->DrawData); } -static void ImGui_ImplDX12_SwapBuffers(ImGuiViewport* viewport) +static void ImGui_ImplDX12_SwapBuffers(ImGuiViewport* viewport, void*) { ImGuiViewportDataDx12* data = (ImGuiViewportDataDx12*)viewport->RendererUserData; IM_ASSERT(0); diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index d1f2e54b59ca..528dc6908052 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -4,6 +4,7 @@ // Implemented features: // [X] Gamepad navigation mapping. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. +// [X] Multi-viewport windows (when ImGuiConfigFlags_EnableViewports is enabled). // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). @@ -448,14 +449,14 @@ static void ImGui_ImplGlfw_SetWindowTitle(ImGuiViewport* viewport, const char* t glfwSetWindowTitle(data->Window, title); } -static void ImGui_ImplGlfw_RenderWindow(ImGuiViewport* viewport) +static void ImGui_ImplGlfw_RenderWindow(ImGuiViewport* viewport, void*) { ImGuiViewportDataGlfw* data = (ImGuiViewportDataGlfw*)viewport->PlatformUserData; if (g_ClientApi == GlfwClientApi_OpenGL) glfwMakeContextCurrent(data->Window); } -static void ImGui_ImplGlfw_SwapBuffers(ImGuiViewport* viewport) +static void ImGui_ImplGlfw_SwapBuffers(ImGuiViewport* viewport, void*) { ImGuiViewportDataGlfw* data = (ImGuiViewportDataGlfw*)viewport->PlatformUserData; if (g_ClientApi == GlfwClientApi_OpenGL) diff --git a/examples/imgui_impl_glfw.h b/examples/imgui_impl_glfw.h index 84ab937f8fc7..4c0cc39c66e4 100644 --- a/examples/imgui_impl_glfw.h +++ b/examples/imgui_impl_glfw.h @@ -4,6 +4,7 @@ // Implemented features: // [X] Gamepad navigation mapping. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. +// [X] Multi-viewport windows (when ImGuiConfigFlags_EnableViewports is enabled). // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). diff --git a/examples/imgui_impl_opengl2.cpp b/examples/imgui_impl_opengl2.cpp index cf763efd4c3b..752b45a2717d 100644 --- a/examples/imgui_impl_opengl2.cpp +++ b/examples/imgui_impl_opengl2.cpp @@ -1,6 +1,9 @@ // ImGui Renderer for: OpenGL2 (legacy OpenGL, fixed pipeline) // This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..) +// Implemented features: +// [X] User texture binding. Use 'GLUint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. + // **DO NOT USE THIS CODE IF YOUR CODE/ENGINE IS USING MODERN OPENGL (SHADERS, VBO, VAO, etc.)** // **Prefer using the code in imgui_impl_opengl3.cpp** // This code is mostly provided as a reference to learn how ImGui integration works, because it is shorter to read. diff --git a/examples/imgui_impl_opengl2.h b/examples/imgui_impl_opengl2.h index a80dfac4b6ab..5e9d5f22f0d1 100644 --- a/examples/imgui_impl_opengl2.h +++ b/examples/imgui_impl_opengl2.h @@ -1,6 +1,9 @@ // ImGui Renderer for: OpenGL2 (legacy OpenGL, fixed pipeline) // This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..) +// Implemented features: +// [X] User texture binding. Use 'GLUint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. + // **DO NOT USE THIS CODE IF YOUR CODE/ENGINE IS USING MODERN OPENGL (SHADERS, VBO, VAO, etc.)** // **Prefer using the code in imgui_impl_opengl3.cpp** // This code is mostly provided as a reference to learn how ImGui integration works, because it is shorter to read. diff --git a/examples/imgui_impl_opengl3.cpp b/examples/imgui_impl_opengl3.cpp index f753e2b46f51..f58d052558d9 100644 --- a/examples/imgui_impl_opengl3.cpp +++ b/examples/imgui_impl_opengl3.cpp @@ -2,6 +2,10 @@ // This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..) // (Note: We are using GL3W as a helper library to access OpenGL functions since there is no standard header to access modern OpenGL functions easily. Alternatives are GLEW, Glad, etc..) +// Implemented features: +// [X] User texture binding. Use 'GLUint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. +// [X] Multi-viewport rendering (when ImGuiConfigFlags_EnableViewports is enabled). + // CHANGELOG // (minor and older changes stripped away, please see git history for details) // 2018-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. @@ -328,7 +332,7 @@ void ImGui_ImplOpenGL3_DestroyDeviceObjects() // Platform Interface (Optional, for multi-viewport support) //-------------------------------------------------------------------------------------------------------- -static void ImGui_ImplOpenGL3_RenderWindow(ImGuiViewport* viewport) +static void ImGui_ImplOpenGL3_RenderWindow(ImGuiViewport* viewport, void*) { if (!(viewport->Flags & ImGuiViewportFlags_NoRendererClear)) { diff --git a/examples/imgui_impl_opengl3.h b/examples/imgui_impl_opengl3.h index 190148790013..5d6afde64aca 100644 --- a/examples/imgui_impl_opengl3.h +++ b/examples/imgui_impl_opengl3.h @@ -2,6 +2,10 @@ // This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..) // (Note: We are using GL3W as a helper library to access OpenGL functions since there is no standard header to access modern OpenGL functions easily. Alternatives are GLEW, Glad, etc..) +// Implemented features: +// [X] User texture binding. Use 'GLUint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. +// [X] Multi-viewport rendering (when ImGuiConfigFlags_EnableViewports is enabled). + IMGUI_API bool ImGui_ImplOpenGL3_Init(const char* glsl_version = NULL); IMGUI_API void ImGui_ImplOpenGL3_Shutdown(); IMGUI_API void ImGui_ImplOpenGL3_NewFrame(); diff --git a/examples/imgui_impl_sdl2.cpp b/examples/imgui_impl_sdl2.cpp index 17d4ec03834c..36a415f3f3ff 100644 --- a/examples/imgui_impl_sdl2.cpp +++ b/examples/imgui_impl_sdl2.cpp @@ -3,7 +3,7 @@ // (Info: SDL2 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.) // Implemented features: -// [X] User texture binding. Cast 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. +// [X] Multi-viewport windows (when ImGuiConfigFlags_EnableViewports is enabled). // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). @@ -400,14 +400,14 @@ static void ImGui_ImplSDL2_SetWindowTitle(ImGuiViewport* viewport, const char* t SDL_SetWindowTitle(data->Window, title); } -static void ImGui_ImplSDL2_RenderWindow(ImGuiViewport* viewport) +static void ImGui_ImplSDL2_RenderWindow(ImGuiViewport* viewport, void*) { ImGuiViewportDataSDL2* data = (ImGuiViewportDataSDL2*)viewport->PlatformUserData; if (data->GLContext) SDL_GL_MakeCurrent(data->Window, data->GLContext); } -static void ImGui_ImplSDL2_SwapBuffers(ImGuiViewport* viewport) +static void ImGui_ImplSDL2_SwapBuffers(ImGuiViewport* viewport, void*) { ImGuiViewportDataSDL2* data = (ImGuiViewportDataSDL2*)viewport->PlatformUserData; if (data->GLContext) diff --git a/examples/imgui_impl_sdl2.h b/examples/imgui_impl_sdl2.h index 051411d74908..171ee374e72c 100644 --- a/examples/imgui_impl_sdl2.h +++ b/examples/imgui_impl_sdl2.h @@ -2,6 +2,9 @@ // This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..) // (Info: SDL2 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.) +// Implemented features: +// [X] Multi-viewport windows (when ImGuiConfigFlags_EnableViewports is enabled). + // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). // If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp. diff --git a/examples/imgui_impl_vulkan.cpp b/examples/imgui_impl_vulkan.cpp index 3a9e8b8fdd49..284901c43911 100644 --- a/examples/imgui_impl_vulkan.cpp +++ b/examples/imgui_impl_vulkan.cpp @@ -3,6 +3,7 @@ // Missing features: // [ ] User texture binding. Changes of ImTextureID aren't supported by this binding! See https://github.com/ocornut/imgui/pull/914 +// [ ] Multi-viewport rendering (when ImGuiConfigFlags_EnableViewports is enabled). WORK-IN-PROGRESS. // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you use this binding you'll need to call 5 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXX_CreateFontsTexture(), ImGui_ImplXXXX_NewFrame(), ImGui_ImplXXXX_Render() and ImGui_ImplXXXX_Shutdown(). @@ -1113,7 +1114,7 @@ static void ImGui_ImplVulkan_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) ImGui_ImplVulkanH_CreateWindowDataSwapChainAndFramebuffer(g_PhysicalDevice, g_Device, &data->WindowData, g_Allocator, (int)size.x, (int)size.y); } -static void ImGui_ImplVulkan_RenderWindow(ImGuiViewport* viewport) +static void ImGui_ImplVulkan_RenderWindow(ImGuiViewport* viewport, void*) { ImGuiViewportDataVulkan* data = (ImGuiViewportDataVulkan*)viewport->RendererUserData; ImGui_ImplVulkan_WindowData* wd = &data->WindowData; @@ -1184,7 +1185,7 @@ static void ImGui_ImplVulkan_RenderWindow(ImGuiViewport* viewport) } } -static void ImGui_ImplVulkan_SwapBuffers(ImGuiViewport* viewport) +static void ImGui_ImplVulkan_SwapBuffers(ImGuiViewport* viewport, void*) { ImGuiViewportDataVulkan* data = (ImGuiViewportDataVulkan*)viewport->RendererUserData; ImGui_ImplVulkan_WindowData* wd = &data->WindowData; diff --git a/examples/imgui_impl_vulkan.h b/examples/imgui_impl_vulkan.h index 03d6ed3c0455..7a89d9f2ee45 100644 --- a/examples/imgui_impl_vulkan.h +++ b/examples/imgui_impl_vulkan.h @@ -3,6 +3,7 @@ // Missing features: // [ ] User texture binding. Changes of ImTextureID aren't supported by this binding! See https://github.com/ocornut/imgui/pull/914 +// [ ] Multi-viewport rendering (when ImGuiConfigFlags_EnableViewports is enabled). WORK-IN-PROGRESS. // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you use this binding you'll need to call 5 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXX_CreateFontsTexture(), ImGui_ImplXXXX_NewFrame(), ImGui_ImplXXXX_Render() and ImGui_ImplXXXX_Shutdown(). diff --git a/examples/opengl3_example/main.cpp b/examples/opengl3_example/main.cpp index b5c4f17bf834..da53fa272dbe 100644 --- a/examples/opengl3_example/main.cpp +++ b/examples/opengl3_example/main.cpp @@ -124,9 +124,9 @@ int main(int, char**) glClear(GL_COLOR_BUFFER_BIT); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); - // Update and Render additional Platform Windows + // Update and Render additional Platform Windows (when ImGuiConfigFlags_EnableViewports is enabled) ImGui::UpdatePlatformWindows(); - ImGui::RenderPlatformWindows(); + ImGui::RenderPlatformWindows(NULL, NULL); glfwMakeContextCurrent(window); glfwSwapBuffers(window); diff --git a/examples/sdl_opengl3_example/main.cpp b/examples/sdl_opengl3_example/main.cpp index 0e4c40c4ebcf..f535594441e4 100644 --- a/examples/sdl_opengl3_example/main.cpp +++ b/examples/sdl_opengl3_example/main.cpp @@ -132,9 +132,9 @@ int main(int, char**) glClear(GL_COLOR_BUFFER_BIT); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); - // Update and Render additional Platform Windows + // Update and Render additional Platform Windows (when ImGuiConfigFlags_EnableViewports is enabled) ImGui::UpdatePlatformWindows(); - ImGui::RenderPlatformWindows(); + ImGui::RenderPlatformWindows(NULL, NULL); SDL_GL_MakeCurrent(window, gl_context); SDL_GL_SwapWindow(window); diff --git a/examples/sdl_vulkan_example/main.cpp b/examples/sdl_vulkan_example/main.cpp index 4ec3083b45ed..5f92a0a21f2b 100644 --- a/examples/sdl_vulkan_example/main.cpp +++ b/examples/sdl_vulkan_example/main.cpp @@ -469,9 +469,9 @@ int main(int, char**) memcpy(&wd->ClearValue.color.float32[0], &clear_color, 4 * sizeof(float)); FrameRender(wd); - // Update and Render additional Platform Windows + // Update and Render additional Platform Windows (when ImGuiConfigFlags_EnableViewports is enabled) ImGui::UpdatePlatformWindows(); - ImGui::RenderPlatformWindows(); + ImGui::RenderPlatformWindows(NULL, NULL); FramePresent(wd); } diff --git a/examples/vulkan_example/main.cpp b/examples/vulkan_example/main.cpp index 111613f7ae3b..69e6448ecc2c 100644 --- a/examples/vulkan_example/main.cpp +++ b/examples/vulkan_example/main.cpp @@ -479,9 +479,9 @@ int main(int, char**) memcpy(&wd->ClearValue.color.float32[0], &clear_color, 4 * sizeof(float)); FrameRender(wd); - // Update and Render additional Platform Windows + // Update and Render additional Platform Windows (when ImGuiConfigFlags_EnableViewports is enabled) ImGui::UpdatePlatformWindows(); - ImGui::RenderPlatformWindows(); + ImGui::RenderPlatformWindows(NULL, NULL); FramePresent(wd); } diff --git a/imgui.cpp b/imgui.cpp index 8310917e8a18..a45381c5c31a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3559,7 +3559,7 @@ void ImGui::UpdatePlatformWindows() // This is a default/basic function for performing the rendering/swap of multiple platform windows. // Custom renderers may prefer to not call this function at all, and instead iterate the platform data and handle rendering/sync themselves. -// The Render/Swap functions stored in ImGuiPlatformInterface are merely here to allow for this helper to exist, but you can do it yourself: +// The Render/Swap functions stored in ImGuiPlatformIO are merely here to allow for this helper to exist, but you can do it yourself: // // ImGuiPlatformData* data = ImGui::GetPlatformData(); // for (int i = 1; i < data->Viewports.Size; i++) @@ -3568,7 +3568,7 @@ void ImGui::UpdatePlatformWindows() // MySwapBufferFunction(data->Viewports[i], my_args); // // Note how we intentionally skip the main viewport (index 0) which is generally rendered as part of our main application. -void ImGui::RenderPlatformWindows() +void ImGui::RenderPlatformWindows(void* platform_render_arg, void* renderer_render_arg) { if (!(ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_EnableViewports)) return; @@ -3578,18 +3578,14 @@ void ImGui::RenderPlatformWindows() for (int i = 1; i < data->Viewports.Size; i++) { ImGuiViewport* viewport = data->Viewports[i]; - if (platform_io.Platform_RenderWindow) - platform_io.Platform_RenderWindow(viewport); - if (platform_io.Renderer_RenderWindow) - platform_io.Renderer_RenderWindow(viewport); + if (platform_io.Platform_RenderWindow) platform_io.Platform_RenderWindow(viewport, platform_render_arg); + if (platform_io.Renderer_RenderWindow) platform_io.Renderer_RenderWindow(viewport, renderer_render_arg); } for (int i = 1; i < data->Viewports.Size; i++) { ImGuiViewport* viewport = data->Viewports[i]; - if (platform_io.Platform_SwapBuffers) - platform_io.Platform_SwapBuffers(viewport); - if (platform_io.Renderer_SwapBuffers) - platform_io.Renderer_SwapBuffers(viewport); + if (platform_io.Platform_SwapBuffers) platform_io.Platform_SwapBuffers(viewport, platform_render_arg); + if (platform_io.Renderer_SwapBuffers) platform_io.Renderer_SwapBuffers(viewport, renderer_render_arg); } } diff --git a/imgui.h b/imgui.h index a7415342a2c9..9df9da4942db 100644 --- a/imgui.h +++ b/imgui.h @@ -545,12 +545,12 @@ namespace ImGui IMGUI_API void SetClipboardText(const char* text); // (Optional) Platform interface for multi-viewport support - IMGUI_API ImGuiPlatformIO& GetPlatformIO(); // Platform/Renderer functions. - IMGUI_API ImGuiPlatformData* GetPlatformData(); // List of viewports. - IMGUI_API ImGuiViewport* GetMainViewport(); // GetPlatformData()->MainViewport - IMGUI_API void UpdatePlatformWindows(); // Call in main loop. Create/Destroy/Resize platform windows so there's one for each viewport - IMGUI_API void RenderPlatformWindows(); // Call in main loop. Call RenderWindow/SwapBuffers from the ImGuiPlatformIO structure. May be reimplemented by user. - IMGUI_API void DestroyPlatformWindows(); // Call in back-end shutdown. + IMGUI_API ImGuiPlatformIO& GetPlatformIO(); // Platform/Renderer function, for back-end to setup. + IMGUI_API ImGuiPlatformData* GetPlatformData(); // List of viewports. Viewport 0 is always the main viewport, followed by the secondary viewports. + IMGUI_API ImGuiViewport* GetMainViewport(); // GetPlatformData()->MainViewport + IMGUI_API void UpdatePlatformWindows(); // Call in main loop. Create/Destroy/Resize platform windows so there's one for each viewport + IMGUI_API void RenderPlatformWindows(void* platform_render_arg, void* renderer_render_arg); // Call in main loop. Call RenderWindow/SwapBuffers from the ImGuiPlatformIO structure. May be reimplemented by user. + IMGUI_API void DestroyPlatformWindows(); // (Optional) Call in back-end shutdown if you need to close Platform Windows before imgui shutdown. IMGUI_API ImGuiViewport* FindViewportByPlatformHandle(void* platform_handle); // Memory Utilities @@ -1868,6 +1868,8 @@ struct ImFont // This is designed so we can mix and match two imgui_impl_xxxx files, one for the Platform (~ Windowing), one for Renderer. // Custom engine back-ends will often provide both Platform and Renderer interfaces and thus may not need to use all functions. // Platform functions are typically called before their Renderer counterpart, apart from Destroy which are called the other way. +// RenderPlatformWindows() basically iterate secondary viewports and call Platform+Renderer's RenderWindow then Platform+Renderer's SwapBuffers, +// You may skip using RenderPlatformWindows() and call your functions yourself if you need specific behavior for your multi-window rendering. struct ImGuiPlatformIO { // Platform (e.g. Win32, GLFW, SDL2) @@ -1880,8 +1882,8 @@ struct ImGuiPlatformIO ImVec2 (*Platform_GetWindowSize)(ImGuiViewport* vp); void (*Platform_SetWindowTitle)(ImGuiViewport* vp, const char* title); void (*Platform_SetWindowAlpha)(ImGuiViewport* vp, float alpha); // (Optional) Setup window transparency - void (*Platform_RenderWindow)(ImGuiViewport* vp); // (Optional) Setup for render (platform side) - void (*Platform_SwapBuffers)(ImGuiViewport* vp); // (Optional) Call Present/SwapBuffers (platform side) + void (*Platform_RenderWindow)(ImGuiViewport* vp, void* render_arg); // (Optional) Setup for render (platform side) + void (*Platform_SwapBuffers)(ImGuiViewport* vp, void* render_arg); // (Optional) Call Present/SwapBuffers (platform side) float (*Platform_GetWindowDpiScale)(ImGuiViewport* vp); // (Optional) DPI handling: Return DPI scale for this viewport. 1.0f = 96 DPI. (FIXME-DPI) void (*Platform_OnChangedViewport)(ImGuiViewport* vp); // (Optional) DPI handling: Called during Begin() every time the viewport we are outputting into changes, so back-end has a chance to swap fonts to adjust style. int (*Platform_CreateVkSurface)(ImGuiViewport* vp, ImU64 vk_inst, const void* vk_allocators, ImU64* out_vk_surface); // (Optional) For Renderer to call into Platform code @@ -1890,8 +1892,8 @@ struct ImGuiPlatformIO void (*Renderer_CreateWindow)(ImGuiViewport* vp); // Create swap chains, frame buffers etc. void (*Renderer_DestroyWindow)(ImGuiViewport* vp); void (*Renderer_SetWindowSize)(ImGuiViewport* vp, ImVec2 size); // Resize swap chain, frame buffers etc. - void (*Renderer_RenderWindow)(ImGuiViewport* vp); // (Optional) Clear targets, Render viewport->DrawData - void (*Renderer_SwapBuffers)(ImGuiViewport* vp); // (Optional) Call Present/SwapBuffers (renderer side) + void (*Renderer_RenderWindow)(ImGuiViewport* vp, void* render_arg); // (Optional) Clear targets, Render viewport->DrawData + void (*Renderer_SwapBuffers)(ImGuiViewport* vp, void* render_arg); // (Optional) Call Present/SwapBuffers (renderer side) }; // List of viewports to render as platform window (updated by ImGui::UpdatePlatformWindows) From 9d5ec051501b96429ab75b37b8aeb0a7a9232ea7 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 19 Mar 2018 17:33:21 +0100 Subject: [PATCH 094/828] Viewport, Platform: Update the contents of GetPlatformData() in EndFrame() so back-end can access it even if UpdatePlatformWindows hasn't been called. (#1542) --- imgui.cpp | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index a45381c5c31a..df7961fd4e35 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3483,10 +3483,6 @@ void ImGui::UpdatePlatformWindows() IM_ASSERT(g.FrameCountEnded == g.FrameCount && "Forgot to call Render() or EndFrame() before UpdatePlatformWindows()?"); IM_ASSERT(g.FrameCountPlatformEnded < g.FrameCount); g.FrameCountPlatformEnded = g.FrameCount; - - g.PlatformData.MainViewport = g.Viewports[0]; - g.PlatformData.Viewports.resize(0); - g.PlatformData.Viewports.push_back(g.Viewports[0]); if (!(g.IO.ConfigFlags & ImGuiConfigFlags_EnableViewports)) return; @@ -3508,9 +3504,6 @@ void ImGui::UpdatePlatformWindows() continue; } - g.PlatformData.Viewports.push_back(viewport); - IM_ASSERT(viewport->Window != NULL); - bool is_new_window = (viewport->PlatformHandle == NULL && viewport->PlatformUserData == NULL && viewport->RendererUserData == NULL); if (is_new_window && viewport->PlatformHandle == NULL && viewport->PlatformUserData == NULL) g.PlatformIO.Platform_CreateWindow(viewport); @@ -4417,6 +4410,19 @@ void ImGui::EndFrame() } } + // Update user-side viewport list + g.PlatformData.MainViewport = g.Viewports[0]; + g.PlatformData.Viewports.resize(0); + for (int i = 0; i < g.Viewports.Size; i++) + { + ImGuiViewportP* viewport = g.Viewports[i]; + if (viewport->LastFrameActive < g.FrameCount) + continue; + if (i > 0) + IM_ASSERT(viewport->Window != NULL); + g.PlatformData.Viewports.push_back(viewport); + } + // Sort the window list so that all child windows are after their parent // We cannot do that on FocusWindow() because childs may not exist yet g.WindowsSortBuffer.resize(0); From c00523dba4f9abb5e5b6f052e29963c9cc024a09 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 19 Mar 2018 18:07:12 +0100 Subject: [PATCH 095/828] Viewport: Added extra Metrics and debug features. --- imgui.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index df7961fd4e35..0bb958eeee09 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -14054,11 +14054,16 @@ void ImGui::ShowMetricsWindow(bool* p_open) { ImGuiViewportP* viewport = g.Viewports[i]; ImGui::SetNextTreeNodeOpen(true, ImGuiCond_Once); - if (ImGui::TreeNode((void*)(intptr_t)viewport->ID, "Viewport #%d, ID: 0x%08X, DrawLists: %d, Size: (%.0f,%.0f)", i, viewport->ID, viewport->DrawDataBuilder.GetDrawListCount(), viewport->Size.x, viewport->Size.y)) + if (ImGui::TreeNode((void*)(intptr_t)viewport->ID, "Viewport #%d, ID: 0x%08X, Size: (%.0f,%.0f)", i, viewport->ID, viewport->Size.x, viewport->Size.y)) { - ImGui::BulletText("Pos: (%.0f,%.0f)", viewport->Pos.x, viewport->Pos.y); - ImGui::BulletText("PlatformPos: (%.0f,%.0f); DpiScale: %.0f%%", viewport->PlatformPos.x, viewport->PlatformPos.y, viewport->DpiScale * 100.0f); - ImGui::BulletText("Flags: 0x%04X", viewport->Flags); + ImGuiWindowFlags flags = viewport->Flags; + ImGui::BulletText("Pos: (%.0f,%.0f), PlatformPos: (%.0f,%.0f)", viewport->Pos.x, viewport->Pos.y, viewport->PlatformPos.x, viewport->PlatformPos.y); + if (i > 0) { ImGui::SameLine(); if (ImGui::SmallButton("Reset")) viewport->PlatformPos = ImVec2(0, 0); } + ImGui::BulletText("DpiScale: %.0f%%", viewport->DpiScale * 100.0f); + ImGui::BulletText("Flags: 0x%04X =%s%s%s%s", viewport->Flags, + (flags & ImGuiViewportFlags_CanHostOtherWindows) ? " CanHostOtherWindows" : "", (flags & ImGuiViewportFlags_NoDecoration) ? " NoDecoration" : "", + (flags & ImGuiViewportFlags_NoFocusOnAppearing) ? " NoFocusOnAppearing" : "", (flags & ImGuiViewportFlags_NoInputs) ? " NoInputs" : "", + (flags & ImGuiViewportFlags_NoRendererClear) ? " NoRendererClear" : ""); for (int layer_i = 0; layer_i < IM_ARRAYSIZE(viewport->DrawDataBuilder.Layers); layer_i++) for (int draw_list_i = 0; draw_list_i < viewport->DrawDataBuilder.Layers[layer_i].Size; draw_list_i++) Funcs::NodeDrawList(NULL, viewport->DrawDataBuilder.Layers[layer_i][draw_list_i], "DrawList"); From ccc9a22db359205a1558016e5bfbe06bea8b1bf2 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 19 Mar 2018 22:53:37 +0100 Subject: [PATCH 096/828] Viewport: Changelog + Internals: Renamed viewport mouse fields for consistency (going to add more). (#1542) --- CHANGELOG.txt | 31 +++++++++++++++++++++++++++++ imgui.cpp | 52 ++++++++++++++++++++++++------------------------ imgui_internal.h | 10 +++++----- 3 files changed, 62 insertions(+), 31 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 999a40666e2d..3936fa3eaa42 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -43,6 +43,37 @@ The gamepad/keyboard navigation branch (which has been in the work since July 20 Gamepad/keyboard navigation is still marked as Beta and has to be enabled explicitely. Various internal refactors have also been done, as part of the navigation work and as part of the upcoing viewport/docking work. +VIEWPORT BRANCH +(IN PROGRESS, WILL MERGE INTO THE MAIN LISTS WHEN WE MERGE THE BRANCH) + + - Viewport: Added support for multi-viewport [...] blah blah + - Viewport: Rendering: the ImDrawData structure now contains 'DisplayPos' and 'DisplaySize' fields. To support multi-viewport, you need to use those values when + creating your orthographic projection matrix. Use 'draw_data->DisplaySize' instead of 'io.DisplaySize', and 'draw_data->DisplayPos' instead of (0,0) as the upper-left point. + You also need to subtract 'draw_data->DisplayPos' from your scissor rectangles, as scissor rectangles are specified in the space of your target viewport. + - Examples: Back-ends have been refactored to separate the platform code (e.g. Win32, Glfw, SDL2) from the renderer code (e.g. DirectX11, OpenGL3, Vulkan). + before: imgui_impl_dx11.cpp --> after: imgui_impl_win32.cpp + imgui_impl_dx11.cpp + before: imgui_impl_dx12.cpp --> after: imgui_impl_win32.cpp + imgui_impl_dx12.cpp + before: imgui_impl_glfw_gl3.cpp --> after: imgui_impl_glfw.cpp + imgui_impl_opengl2.cpp + before: imgui_impl_glfw_vulkan.cpp --> after: imgui_impl_glfw.cpp + imgui_impl_vulkan.cpp + before: imgui_impl_sdl_gl3.cpp --> after: imgui_impl_sdl2.cpp + imgui_impl_opengl2.cpp + before: imgui_impl_sdl_gl3.cpp --> after: imgui_impl_sdl2.cpp + imgui_impl_opengl3.cpp + etc. + - The idea is what we can now easily combine and maintain back-ends and reduce code redundancy. Integration of imgui into a new/custom engine may also + be easier as there is less overlap between "windowing / inputs" and "rendering" code, so you may study or grab one half of the code and not the other. + - This change was motivated by the fact that adding support for multi-viewport requires more work from the platform and renderer back-ends, and the + amount of redundancy accross files was becoming too difficult to maintain. + - Some frameworks (such as the Allegro, Marmalade) handle both the "platform" and "rendering" part, and your custom engine may as well. + - Each example still has its own main.cpp which you may refer you to understand how to initialize and glue everything together. + - Examples: Win32: Added DPI-related helpers to access DPI features _without_ requiring the latest Windows SDK at compile time, and _without_ requiring Windows 10 at runtime. + - Examples: Platforms currently supporting multi-viewport: Win32, Glfw, SDL2. + - Examples: Renderers currently supporting multi-viewport: DirectX10, DirectX11, OpenGL2, OpenGL3, Vulkan (WIP). + - Examples: All imgui_impl_xxx files now have an individual Changelog at the top of the file, making it easier to follow how back-ends are evolving. + - Examples: Vulkan: Added various optional helpers in imgui_impl_vulkan.h (they are used for multi-viewport support) to make the examples main.cpp easier to read. + - Examples: Allegro: Renamed imgui_impl_a5.xxx files to imgui_impl_allegro5.xxx, ImGui_ImplA5_** symbols to ImGui_ImplAllegro5_xxx. + - Examples: Vulkan+SDL: Added a Vulkan+SDL example. (#1367) [@gmueckl] + - Metrics: Added a "Show window begin order" checkbox to visualize the order windows are submitted. + - Internal: Settings: Added ReadCloseFn handler to be able to patch/alter a loaded object after all the fields are known. + Breaking Changes: (IN PROGRESS, WILL ADD TO THIS LIST AS WE WORK ON 1.60) diff --git a/imgui.cpp b/imgui.cpp index 0bb958eeee09..bb936249acd8 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2122,7 +2122,7 @@ static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFla } // Filter by viewport - if (window->Viewport != g.MouseViewport) + if (window->Viewport != g.MousePosViewport) return false; return true; @@ -3250,21 +3250,21 @@ static void ImGui::UpdateMovingWindowDropViewport(ImGuiWindow* window) if (!(g.IO.ConfigFlags & ImGuiConfigFlags_EnableViewports)) return; - ImRect mouse_viewport_rect = g.MouseViewport->GetRect(); - ImVec2 window_pos_in_mouse_viewport = ConvertPlatformPosToViewportPos(ConvertViewportPosToPlatformPos(window->Pos, window->Viewport), g.MouseViewport); + ImRect mouse_viewport_rect = g.MousePosViewport->GetRect(); + ImVec2 window_pos_in_mouse_viewport = ConvertPlatformPosToViewportPos(ConvertViewportPosToPlatformPos(window->Pos, window->Viewport), g.MousePosViewport); ImRect window_rect_in_mouse_viewport = ImRect(window_pos_in_mouse_viewport, window_pos_in_mouse_viewport + window->Size); - if ((g.MouseViewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) && mouse_viewport_rect.Contains(window_rect_in_mouse_viewport)) + if ((g.MousePosViewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) && mouse_viewport_rect.Contains(window_rect_in_mouse_viewport)) { // Drop on an existing viewport ImGuiViewportP* old_viewport = window->Viewport; - SetWindowViewportTranslateToPreservePlatformPos(window, window->Viewport, g.MouseViewport); + SetWindowViewportTranslateToPreservePlatformPos(window, window->Viewport, g.MousePosViewport); // Our current scheme allow any window to land on a viewport, so when that viewport merges, move other windows as well // FIXME-OPT if (window->Flags & ImGuiWindowFlags_FullViewport) for (int n = 0; n < g.Windows.Size; n++) if (g.Windows[n]->Viewport == old_viewport) - SetWindowViewportTranslateToPreservePlatformPos(g.Windows[n], old_viewport, g.MouseViewport); + SetWindowViewportTranslateToPreservePlatformPos(g.Windows[n], old_viewport, g.MousePosViewport); } else { @@ -3363,8 +3363,8 @@ static void ImGui::UpdateViewports() // Destroy if (viewport == viewport_ref) viewport_ref = NULL; - if (viewport == g.MouseViewport) g.MouseViewport = NULL; - if (viewport == g.MouseLastHoveredViewport) g.MouseLastHoveredViewport = NULL; + if (viewport == g.MousePosViewport) g.MousePosViewport = NULL; + if (viewport == g.MouseHoveredPrevViewport) g.MouseHoveredPrevViewport = NULL; IM_ASSERT(viewport->RendererUserData == NULL && viewport->PlatformUserData == NULL && viewport->PlatformHandle == NULL); IM_ASSERT(g.PlatformData.Viewports.contains(viewport) == false); IM_DELETE(viewport); @@ -3419,7 +3419,7 @@ static void ImGui::UpdateViewports() if (!(g.IO.ConfigFlags & ImGuiConfigFlags_EnableViewports)) { - g.MouseViewport = g.MouseLastViewport = main_viewport; + g.MousePosViewport = g.MousePosPrevViewport = main_viewport; return; } @@ -3442,7 +3442,7 @@ static void ImGui::UpdateViewports() viewport_hovered = FindViewportHoveredFromPlatformWindowStack(mouse_platform_pos); } if (viewport_hovered != NULL) - g.MouseLastHoveredViewport = viewport_hovered; + g.MouseHoveredPrevViewport = viewport_hovered; // If reference viewport just has been deleted, use a position relative to the main viewport if (viewport_ref == NULL) @@ -3451,9 +3451,9 @@ static void ImGui::UpdateViewports() g.IO.MousePos = ConvertPlatformPosToViewportPos(mouse_platform_pos, viewport_ref); } - g.MouseLastViewport = g.MouseViewport; - g.MouseViewport = viewport_ref; - g.MouseViewport->LastFrameAsRefViewport = g.FrameCount; + g.MousePosPrevViewport = g.MousePosViewport; + g.MousePosViewport = viewport_ref; + g.MousePosViewport->LastFrameAsRefViewport = g.FrameCount; // When dragging something, always refer to the last hovered viewport (so when we are between viewport, our dragged preview will tend to show in the last viewport) const bool is_mouse_dragging_with_an_expected_destination = g.DragDropActive || (g.MovingWindow != NULL); @@ -3461,15 +3461,15 @@ static void ImGui::UpdateViewports() if (is_mouse_dragging_with_an_expected_destination || is_mouse_all_released) { if (is_mouse_dragging_with_an_expected_destination) - viewport_hovered = g.MouseLastHoveredViewport; - if (viewport_hovered != NULL && viewport_hovered != g.MouseViewport && !(viewport_hovered->Flags & ImGuiViewportFlags_NoInputs)) + viewport_hovered = g.MouseHoveredPrevViewport; + if (viewport_hovered != NULL && viewport_hovered != g.MousePosViewport && !(viewport_hovered->Flags & ImGuiViewportFlags_NoInputs)) { - g.IO.MousePos = ConvertPlatformPosToViewportPos(ConvertViewportPosToPlatformPos(g.IO.MousePos, g.MouseViewport), viewport_hovered); - g.MouseViewport = viewport_hovered; + g.IO.MousePos = ConvertPlatformPosToViewportPos(ConvertViewportPosToPlatformPos(g.IO.MousePos, g.MousePosViewport), viewport_hovered); + g.MousePosViewport = viewport_hovered; } } - IM_ASSERT(g.MouseViewport != NULL); + IM_ASSERT(g.MousePosViewport != NULL); } ImGuiPlatformData* ImGui::GetPlatformData() @@ -3698,7 +3698,7 @@ void ImGui::NewFrame() // Update mouse input state // If mouse just appeared or disappeared (usually denoted by -FLT_MAX component, but in reality we test for -256000.0f) we cancel out movement in MouseDelta - if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MousePosPrev) && g.MouseViewport == g.MouseLastViewport) + if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MousePosPrev) && g.MousePosViewport == g.MousePosPrevViewport) g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev; else g.IO.MouseDelta = ImVec2(0.0f, 0.0f); @@ -3763,7 +3763,7 @@ void ImGui::NewFrame() // - We also support the moved window toggling the NoInputs flag after moving has started in order to be able to detect windows below it, which is useful for e.g. docking mechanisms. g.HoveredWindow = (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoInputs)) ? g.MovingWindow : FindHoveredWindow(); g.HoveredRootWindow = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL; - IM_ASSERT(g.HoveredWindow == NULL || g.HoveredWindow == g.MovingWindow || g.HoveredWindow->Viewport == g.MouseViewport); + IM_ASSERT(g.HoveredWindow == NULL || g.HoveredWindow == g.MovingWindow || g.HoveredWindow->Viewport == g.MousePosViewport); ImGuiWindow* modal_window = GetFrontMostModalRootWindow(); if (modal_window != NULL) @@ -4033,7 +4033,7 @@ void ImGui::Shutdown(ImGuiContext* context) g.FontStack.clear(); g.OpenPopupStack.clear(); g.CurrentPopupStack.clear(); - g.CurrentViewport = g.MouseViewport = g.MouseLastViewport = g.MouseLastHoveredViewport = NULL; + g.CurrentViewport = g.MousePosViewport = g.MousePosPrevViewport = g.MouseHoveredPrevViewport = NULL; for (int i = 0; i < g.Viewports.Size; i++) IM_DELETE(g.Viewports[i]); g.Viewports.clear(); @@ -4476,7 +4476,7 @@ void ImGui::Render() const ImVec2 pos = g.IO.MousePos - offset; const ImTextureID tex_id = g.IO.Fonts->TexID; const float sc = g.Style.MouseCursorScale; - g.OverlayDrawList.PushClipRect(g.MouseViewport->Pos, g.MouseViewport->Pos + g.MouseViewport->Size); + g.OverlayDrawList.PushClipRect(g.MousePosViewport->Pos, g.MousePosViewport->Pos + g.MousePosViewport->Size); g.OverlayDrawList.PushTextureID(tex_id); g.OverlayDrawList.AddImage(tex_id, pos + ImVec2(1,0)*sc, pos+ImVec2(1,0)*sc + size*sc, uv[2], uv[3], IM_COL32(0,0,0,48)); // Shadow g.OverlayDrawList.AddImage(tex_id, pos + ImVec2(2,0)*sc, pos+ImVec2(2,0)*sc + size*sc, uv[2], uv[3], IM_COL32(0,0,0,48)); // Shadow @@ -4981,7 +4981,7 @@ static ImGuiWindow* FindHoveredWindow() if (window->Flags & ImGuiWindowFlags_NoInputs) continue; IM_ASSERT(window->Viewport); - if (window->Viewport != g.MouseViewport) + if (window->Viewport != g.MousePosViewport) continue; // Using the clipped AABB, a child window will typically be clipped by its parent (not always) @@ -5009,7 +5009,7 @@ bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool c const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding); if (!rect_for_touch.Contains(g.IO.MousePos)) return false; - if (!g.MouseViewport->GetRect().Overlaps(rect_clipped)) + if (!g.MousePosViewport->GetRect().Overlaps(rect_clipped)) return false; return true; } @@ -6017,7 +6017,7 @@ static void ImGui::UpdateWindowViewport(ImGuiWindow* window, bool window_pos_set if (!window_is_mouse_tooltip && !current_viewport->GetRect().Contains(window->Rect())) { // Create an undecorated, temporary OS/platform window - ImVec2 platform_pos = ConvertViewportPosToPlatformPos(g.IO.MousePos - g.ActiveIdClickOffset, g.MouseViewport); + ImVec2 platform_pos = ConvertViewportPosToPlatformPos(g.IO.MousePos - g.ActiveIdClickOffset, g.MousePosViewport); ImGuiViewportFlags viewport_flags = ImGuiViewportFlags_NoDecoration | ImGuiViewportFlags_NoFocusOnAppearing | ImGuiViewportFlags_NoInputs; ImGuiViewportP* viewport = Viewport(window, window->ID, viewport_flags, platform_pos, window->Size); window->Flags |= ImGuiWindowFlags_FullViewport; @@ -6033,7 +6033,7 @@ static void ImGui::UpdateWindowViewport(ImGuiWindow* window, bool window_pos_set // This is so we don't require of the multi-viewport windowing back-end to preserve mouse buttons after a window closure, making it easier to implement them. bool preserve_temporary_viewport = g.MovingWindow && g.MovingWindow->RootWindow == window && (window->Viewport->Flags & ImGuiWindowFlags_FullViewport); if (!preserve_temporary_viewport) - window->Viewport = g.MouseViewport; + window->Viewport = g.MousePosViewport; } } else if (g.NavWindow != NULL && g.NavWindow != window && (flags & ImGuiWindowFlags_Tooltip)) diff --git a/imgui_internal.h b/imgui_internal.h index b605c7d5a46b..762e6d79b27a 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -635,9 +635,9 @@ struct ImGuiContext ImVector Viewports; ImGuiPlatformData PlatformData; // This is essentially the public facing version of the Viewports vector (it is updated in UpdatePlatformWindows and exclude the viewports about to be destroyed) ImGuiViewportP* CurrentViewport; - ImGuiViewportP* MouseViewport; - ImGuiViewportP* MouseLastViewport; - ImGuiViewportP* MouseLastHoveredViewport; + ImGuiViewportP* MousePosViewport; + ImGuiViewportP* MousePosPrevViewport; + ImGuiViewportP* MouseHoveredPrevViewport; // Navigation data (for gamepad/keyboard) ImGuiWindow* NavWindow; // Focused window for navigation. Could be called 'FocusWindow' @@ -766,8 +766,8 @@ struct ImGuiContext NextTreeNodeOpenCond = 0; CurrentViewport = NULL; - MouseViewport = NULL; - MouseLastViewport = MouseLastHoveredViewport = NULL; + MousePosViewport = NULL; + MousePosPrevViewport = MouseHoveredPrevViewport = NULL; NavWindow = NULL; NavId = NavActivateId = NavActivateDownId = NavActivatePressedId = NavInputId = 0; From 42ad3c1dd3a8596b2cd24e73d6e84af858ea50b8 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 20 Mar 2018 22:18:18 +0100 Subject: [PATCH 097/828] Examples: OpenGL2: Added multi-viewport support in the OpenGL2 back-end. (#1542) + Metrics: Fix undisplayed flag. --- examples/imgui_impl_opengl2.cpp | 38 +++++++++++++++++++++++++++++++ examples/imgui_impl_opengl2.h | 1 + examples/opengl2_example/main.cpp | 8 +++++++ imgui.cpp | 2 +- 4 files changed, 48 insertions(+), 1 deletion(-) diff --git a/examples/imgui_impl_opengl2.cpp b/examples/imgui_impl_opengl2.cpp index 752b45a2717d..6fa19d5b3713 100644 --- a/examples/imgui_impl_opengl2.cpp +++ b/examples/imgui_impl_opengl2.cpp @@ -3,6 +3,7 @@ // Implemented features: // [X] User texture binding. Use 'GLUint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. +// [X] Multi-viewport rendering (when ImGuiConfigFlags_EnableViewports is enabled). // **DO NOT USE THIS CODE IF YOUR CODE/ENGINE IS USING MODERN OPENGL (SHADERS, VBO, VAO, etc.)** // **Prefer using the code in imgui_impl_opengl3.cpp** @@ -14,6 +15,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2018-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. // 2018-XX-XX: OpenGL: Offset projection matrix and clipping rectangle by draw_data->DisplayPos (which will be non-zero for multi-viewport applications). // 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplGlfwGL2_RenderDrawData() in the .h file so you can call it yourself. // 2017-09-01: OpenGL: Save and restore current polygon mode. @@ -39,14 +41,24 @@ // OpenGL Data static GLuint g_FontTexture = 0; +// Forward Declarations +static void ImGui_ImplOpenGL2_InitPlatformInterface(); +static void ImGui_ImplOpenGL2_ShutdownPlatformInterface(); + // Functions bool ImGui_ImplOpenGL2_Init() { + // Setup back-end capabilities flags + ImGuiIO& io = ImGui::GetIO(); + io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional) + if (io.ConfigFlags & ImGuiConfigFlags_EnableViewports) + ImGui_ImplOpenGL2_InitPlatformInterface(); return true; } void ImGui_ImplOpenGL2_Shutdown() { + ImGui_ImplOpenGL2_ShutdownPlatformInterface(); ImGui_ImplOpenGL2_DestroyDeviceObjects(); } @@ -197,3 +209,29 @@ void ImGui_ImplOpenGL2_DestroyDeviceObjects() { ImGui_ImplOpenGL2_DestroyFontsTexture(); } + +//-------------------------------------------------------------------------------------------------------- +// Platform Interface (Optional, for multi-viewport support) +//-------------------------------------------------------------------------------------------------------- + +static void ImGui_ImplOpenGL2_RenderWindow(ImGuiViewport* viewport, void*) +{ + if (!(viewport->Flags & ImGuiViewportFlags_NoRendererClear)) + { + ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); + glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w); + glClear(GL_COLOR_BUFFER_BIT); + } + ImGui_ImplOpenGL2_RenderDrawData(viewport->DrawData); +} + +static void ImGui_ImplOpenGL2_InitPlatformInterface() +{ + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.Renderer_RenderWindow = ImGui_ImplOpenGL2_RenderWindow; +} + +static void ImGui_ImplOpenGL2_ShutdownPlatformInterface() +{ + ImGui::DestroyPlatformWindows(); +} diff --git a/examples/imgui_impl_opengl2.h b/examples/imgui_impl_opengl2.h index 5e9d5f22f0d1..12e5cd2be644 100644 --- a/examples/imgui_impl_opengl2.h +++ b/examples/imgui_impl_opengl2.h @@ -3,6 +3,7 @@ // Implemented features: // [X] User texture binding. Use 'GLUint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. +// [X] Multi-viewport rendering (when ImGuiConfigFlags_EnableViewports is enabled). // **DO NOT USE THIS CODE IF YOUR CODE/ENGINE IS USING MODERN OPENGL (SHADERS, VBO, VAO, etc.)** // **Prefer using the code in imgui_impl_opengl3.cpp** diff --git a/examples/opengl2_example/main.cpp b/examples/opengl2_example/main.cpp index 70123ab9e71a..6e3bc98821f8 100644 --- a/examples/opengl2_example/main.cpp +++ b/examples/opengl2_example/main.cpp @@ -30,6 +30,8 @@ int main(int, char**) // Setup ImGui binding ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; + io.ConfigFlags |= ImGuiConfigFlags_EnableViewports; + io.ConfigFlags |= ImGuiConfigFlags_NoTaskBarForViewports; //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls ImGui_ImplGlfw_InitForOpenGL(window, true); ImGui_ImplOpenGL2_Init(); @@ -114,6 +116,12 @@ int main(int, char**) glClear(GL_COLOR_BUFFER_BIT); //glUseProgram(0); // You may want this if using this code in an OpenGL 3+ context where shaders may be bound, but prefer using the GL3+ code. ImGui_ImplOpenGL2_RenderDrawData(ImGui::GetDrawData()); + + // Update and Render additional Platform Windows (when ImGuiConfigFlags_EnableViewports is enabled) + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindows(NULL, NULL); + + glfwMakeContextCurrent(window); glfwSwapBuffers(window); } diff --git a/imgui.cpp b/imgui.cpp index 1f06179f7938..9c425a1629e5 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -14066,7 +14066,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::BulletText("Pos: (%.0f,%.0f), PlatformPos: (%.0f,%.0f)", viewport->Pos.x, viewport->Pos.y, viewport->PlatformPos.x, viewport->PlatformPos.y); if (i > 0) { ImGui::SameLine(); if (ImGui::SmallButton("Reset")) viewport->PlatformPos = ImVec2(0, 0); } ImGui::BulletText("DpiScale: %.0f%%", viewport->DpiScale * 100.0f); - ImGui::BulletText("Flags: 0x%04X =%s%s%s%s", viewport->Flags, + ImGui::BulletText("Flags: 0x%04X =%s%s%s%s%s", viewport->Flags, (flags & ImGuiViewportFlags_CanHostOtherWindows) ? " CanHostOtherWindows" : "", (flags & ImGuiViewportFlags_NoDecoration) ? " NoDecoration" : "", (flags & ImGuiViewportFlags_NoFocusOnAppearing) ? " NoFocusOnAppearing" : "", (flags & ImGuiViewportFlags_NoInputs) ? " NoInputs" : "", (flags & ImGuiViewportFlags_NoRendererClear) ? " NoRendererClear" : ""); From 200754b0137c7c9ae32e925fb44131859cc2dab7 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 20 Mar 2018 22:41:05 +0100 Subject: [PATCH 098/828] Examples: GLFW: Don't alter cursor mode if GLFW_CURSOR input mode is GLFW_CURSOR_DISABLED. (#1202) [@PhilCK] --- CHANGELOG.txt | 1 + examples/imgui_impl_glfw.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 771f697385e4..d19b6ca2cdf8 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -204,6 +204,7 @@ Other Changes: - Examples: Win32 (DirectX9,10,11,12: Support for windows using the CS_DBLCLKS class flag by handling the double-click messages (WM_LBUTTONDBLCLK etc.). (#1538, #754) [@ndandoulakis] - Examples: Win32 (DirectX9,10,11,12): Made the Win32 proc handlers not assert if there is no active context yet, to be more flexible with creation order. (#1565) - Examples: GLFW: Added support for mouse cursor shapes (the diagonal resize cursors are unfortunately not supported by GLFW at the moment. (#1495) +- Examples: GLFW: Don't attempt to change the mouse cursor input mode if it is set to GLFW_CURSOR_DISABLED by the application. (#1202) [@PhilCK] - Examples: SDL: Added support for mouse cursor shapes. (#1626) [@olls] - Examples: SDL: Using SDL_CaptureMouse() to retrieve coordinates outside of client area when dragging (SDL 2.0.4+ only, otherwise using SDL_WINDOW_INPUT_FOCUS instead of previously SDL_WINDOW_MOUSE_FOCUS). (#1559) - Examples: SDL: Enabled vsync by default so people don't come at us with demoes running at 2000 FPS burning a cpu core. diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index d172fe53bff1..8adb99173212 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -242,7 +242,7 @@ static void ImGui_ImplGlfw_UpdateMouse() } // Update OS/hardware mouse cursor if imgui isn't drawing a software cursor - if ((io.ConfigFlags & ImGuiConfigFlags_NoSetMouseCursor) == 0) + if ((io.ConfigFlags & ImGuiConfigFlags_NoSetMouseCursor) == 0 && glfwGetInputMode(g_Window, GLFW_CURSOR) != GLFW_CURSOR_DISABLED) { ImGuiMouseCursor cursor = ImGui::GetMouseCursor(); if (io.MouseDrawCursor || cursor == ImGuiMouseCursor_None) From e5ba982be007f140efe2c9319abce66ec9411f5b Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 21 Mar 2018 19:45:19 +0100 Subject: [PATCH 099/828] Nav: Fixed a crash with IMGUI_DEBUG_NAV_SCORING enabled + added info to Metrics. --- imgui.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 9c425a1629e5..46920737f829 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2338,7 +2338,9 @@ static void NavRestoreLayer(int layer) static inline void NavUpdateAnyRequestFlag() { ImGuiContext& g = *GImGui; - g.NavAnyRequest = g.NavMoveRequest || g.NavInitRequest || IMGUI_DEBUG_NAV_SCORING; + g.NavAnyRequest = g.NavMoveRequest || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL); + if (g.NavAnyRequest) + IM_ASSERT(g.NavWindow != NULL); } static bool NavMoveRequestButNoResultYet() @@ -14095,6 +14097,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::Text("HoveredId: 0x%08X/0x%08X (%.2f sec)", g.HoveredId, g.HoveredIdPreviousFrame, g.HoveredIdTimer); // Data is "in-flight" so depending on when the Metrics window is called we may see current frame information or not ImGui::Text("ActiveId: 0x%08X/0x%08X (%.2f sec), ActiveIdSource: %s", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdTimer, input_source_names[g.ActiveIdSource]); ImGui::Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL"); + ImGui::Text("MovingWindow: '%s'", g.MovingWindow ? g.MovingWindow->Name : "NULL"); ImGui::Text("NavWindow: '%s'", g.NavWindow ? g.NavWindow->Name : "NULL"); ImGui::Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer); ImGui::Text("NavInputSource: %s", input_source_names[g.NavInputSource]); From 4f112f898e1fd6ce09f75bddb8882727a8b99246 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 21 Mar 2018 22:47:34 +0100 Subject: [PATCH 100/828] Viewport: Avoid modifying MousePos in UpdateWindowViewport just for the sake of docking test, sheering MousePos during the frame is problematic + minor renaming. (#1542) --- imgui.cpp | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 46920737f829..d8c68f832691 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -738,14 +738,14 @@ static void UpdateMovingWindowDropViewport(ImGuiWindow* window); static void UpdateManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4]); static void FocusFrontMostActiveWindow(ImGuiWindow* ignore_window); -// Viewport +// Viewports const ImGuiID IMGUI_VIEWPORT_DEFAULT_ID = 0x11111111; // Using a constant instead of e.g. ImHash("ViewportDefault", 0); so it's easier to spot in the debugger. The exact value doesn't matter. static inline ImRect GetViewportRect(ImGuiWindow* window) { return window->Viewport->GetRect(); } static inline ImVec2 ConvertViewportPosToPlatformPos(const ImVec2& imgui_pos, ImGuiViewport* viewport) { return imgui_pos - viewport->Pos + viewport->PlatformPos; } static inline ImVec2 ConvertPlatformPosToViewportPos(const ImVec2& platform_pos, ImGuiViewport* viewport) { return platform_pos - viewport->PlatformPos + viewport->Pos; } static ImGuiViewportP* Viewport(ImGuiWindow* window, ImGuiID id, ImGuiViewportFlags flags, const ImVec2& platform_pos, const ImVec2& size); static void UpdateViewports(); -static void UpdateWindowViewport(ImGuiWindow* window, bool window_pos_set_by_api); +static void UpdateSelectWindowViewport(ImGuiWindow* window); static void SetCurrentViewport(ImGuiViewportP* viewport); static void SetWindowViewportTranslateToPreservePlatformPos(ImGuiWindow* window, ImGuiViewportP* old_viewport, ImGuiViewportP* new_viewport); static void ResizeViewportTranslateWindows(int viewport_idx_min, int viewport_idx_max, float pos_x_delta, int idx_delta, ImGuiViewport* viewport_to_erase); @@ -5989,11 +5989,10 @@ static void ImGui::SetWindowViewportTranslateToPreservePlatformPos(ImGuiWindow* } // FIXME-VIEWPORT: This is all super messy and ought to be clarified or rewritten. -static void ImGui::UpdateWindowViewport(ImGuiWindow* window, bool window_pos_set_by_api) +static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) { ImGuiContext& g = *GImGui; ImGuiWindowFlags flags = window->Flags; - (void)window_pos_set_by_api; // Restore main viewport if multi-viewport is not supported by the back-end ImGuiViewportP* main_viewport = g.Viewports[0]; @@ -6013,29 +6012,24 @@ static void ImGui::UpdateWindowViewport(ImGuiWindow* window, bool window_pos_set { // Code explicitly request a viewport window->Viewport = FindViewportByID(g.NextWindowData.ViewportId); - window->ViewportId = g.NextWindowData.ViewportId; // Store ID even if Viewport isn't resolved. + window->ViewportId = g.NextWindowData.ViewportId; // Store ID even if Viewport isn't resolved yet. } else if (flags & ImGuiWindowFlags_ChildWindow) { - IM_ASSERT(window->ParentWindow); window->Viewport = window->ParentWindow->Viewport; } else if (window_follow_mouse_viewport && IsMousePosValid()) { - // Calculate mouse position in OS/platform coordinates ImGuiViewportP* current_viewport = window->Viewport; if (!window_is_mouse_tooltip && !current_viewport->GetRect().Contains(window->Rect())) { - // Create an undecorated, temporary OS/platform window + // Calculate mouse position in OS/platform coordinates, create a Viewport at this position. ImVec2 platform_pos = ConvertViewportPosToPlatformPos(g.IO.MousePos - g.ActiveIdClickOffset, g.MousePosViewport); ImGuiViewportFlags viewport_flags = ImGuiViewportFlags_NoDecoration | ImGuiViewportFlags_NoFocusOnAppearing | ImGuiViewportFlags_NoInputs; ImGuiViewportP* viewport = Viewport(window, window->ID, viewport_flags, platform_pos, window->Size); window->Flags |= ImGuiWindowFlags_FullViewport; window->Viewport = viewport; created_viewport = true; - - // Preserve relative mouse position so docking title bar test stays valid mid-frame (since it isn't latched) - g.IO.MousePos = g.IO.MousePosPrev = window->Viewport->Pos + g.ActiveIdClickOffset; } else { @@ -6393,7 +6387,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // VIEWPORT // We need to do this before using any style/font sizes, as viewport with a different DPI will affect those sizes. - UpdateWindowViewport(window, window_pos_set_by_api); + UpdateSelectWindowViewport(window); SetCurrentViewport(window->Viewport); window->FontDpiScale = (g.IO.ConfigFlags & ImGuiConfigFlags_EnableDpiScaleFonts) ? window->Viewport->DpiScale : 1.0f; SetCurrentWindow(window); From ac8931b2e9a139c380d13f4a03be35baeeccbfa8 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 22 Mar 2018 22:09:42 +0100 Subject: [PATCH 101/828] Viewport: Moved OverlayDrawList to be a per-viewport instead of being global + fix overlay clipping glitch during viewport creation frame + support for software mouse cursor stradding over multiple viewport. The overlay draw list move is rather important as draw lists are not shared among viewports anymore and we can do a swap of their data without copying it. (#1542) --- imgui.cpp | 108 ++++++++++++++++++++++++++++++----------------- imgui.h | 2 +- imgui_internal.h | 12 +++--- 3 files changed, 76 insertions(+), 46 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index d8c68f832691..acc1ca4766fc 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -600,7 +600,7 @@ Q: How can I use the drawing facilities without an ImGui window? (using ImDrawList API) A: - You can create a dummy window. Call SetNextWindowBgAlpha(0.0f), call Begin() with NoTitleBar|NoResize|NoMove|NoScrollbar|NoSavedSettings|NoInputs flags. Then you can retrieve the ImDrawList* via GetWindowDrawList() and draw to it in any way you like. - - You can call ImGui::GetOverlayDrawList() and use this draw list to display contents over every other imgui windows. + - You can call ImGui::GetOverlayDrawList() and use this draw list to display contents over every other imgui windows (1 overlay per viewport). - You can create your own ImDrawList instance. You'll need to initialize them ImGui::GetDrawListSharedData(), or create your own ImDrawListSharedData. Q: I integrated Dear ImGui in my engine and the text or lines are blurry.. @@ -2245,10 +2245,11 @@ static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand) if (ImGui::IsMouseHoveringRect(cand.Min, cand.Max)) { ImFormatString(buf, IM_ARRAYSIZE(buf), "dbox (%.2f,%.2f->%.4f)\ndcen (%.2f,%.2f->%.4f)\nd (%.2f,%.2f->%.4f)\nnav %c, quadrant %c", dbx, dby, dist_box, dcx, dcy, dist_center, dax, day, dist_axial, "WENS"[g.NavMoveDir], "WENS"[quadrant]); - g.OverlayDrawList.AddRect(curr.Min, curr.Max, IM_COL32(255, 200, 0, 100)); - g.OverlayDrawList.AddRect(cand.Min, cand.Max, IM_COL32(255,255,0,200)); - g.OverlayDrawList.AddRectFilled(cand.Max-ImVec2(4,4), cand.Max+ImGui::CalcTextSize(buf)+ImVec2(4,4), IM_COL32(40,0,0,150)); - g.OverlayDrawList.AddText(g.IO.FontDefault, 13.0f, cand.Max, ~0U, buf); + ImDrawList* draw_list = ImGui::GetOverlayDrawList(window); + draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255,200,0,100)); + draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255,255,0,200)); + draw_list->AddRectFilled(cand.Max-ImVec2(4,4), cand.Max+ImGui::CalcTextSize(buf)+ImVec2(4,4), IM_COL32(40,0,0,150)); + draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Max, ~0U, buf); } else if (g.IO.KeyCtrl) // Hold to preview score in matching quadrant. Press C to rotate. { @@ -2256,8 +2257,9 @@ static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand) if (quadrant == g.NavMoveDir) { ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center); - g.OverlayDrawList.AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 200)); - g.OverlayDrawList.AddText(g.IO.FontDefault, 13.0f, cand.Min, IM_COL32(255, 255, 255, 255), buf); + ImDrawList* draw_list = ImGui::GetOverlayDrawList(window); + draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 200)); + draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Min, IM_COL32(255, 255, 255, 255), buf); } } #endif @@ -2691,9 +2693,17 @@ int ImGui::GetFrameCount() return GImGui->FrameCount; } +ImDrawList* ImGui::GetOverlayDrawList(ImGuiWindow* window) +{ + IM_ASSERT(window && window->Viewport); + return window->Viewport->OverlayDrawList; +} + ImDrawList* ImGui::GetOverlayDrawList() { - return &GImGui->OverlayDrawList; + ImGuiWindow* window = GImGui->CurrentWindow; + IM_ASSERT(window && window->Viewport); + return window->Viewport->OverlayDrawList; } ImDrawListSharedData* ImGui::GetDrawListSharedData() @@ -2946,7 +2956,7 @@ static void NavScrollToBringItemIntoView(ImGuiWindow* window, ImRect& item_rect_ { // Scroll to keep newly navigated item fully into view ImRect window_rect_rel(window->InnerRect.Min - window->Pos - ImVec2(1, 1), window->InnerRect.Max - window->Pos + ImVec2(1, 1)); - //g.OverlayDrawList.AddRect(window->Pos + window_rect_rel.Min, window->Pos + window_rect_rel.Max, IM_COL32_WHITE); // [DEBUG] + //GetOverlayDrawList(window)->AddRect(window->Pos + window_rect_rel.Min, window->Pos + window_rect_rel.Max, IM_COL32_WHITE); // [DEBUG] if (window_rect_rel.Contains(item_rect_rel)) return; @@ -3247,8 +3257,8 @@ static void ImGui::NavUpdate() //g.OverlayDrawList.AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG] g.NavScoringCount = 0; #if IMGUI_DEBUG_NAV_RECTS - if (g.NavWindow) { for (int layer = 0; layer < 2; layer++) g.OverlayDrawList.AddRect(g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Min, g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Max, IM_COL32(255,200,0,255)); } // [DEBUG] - if (g.NavWindow) { ImU32 col = (g.NavWindow->HiddenFrames <= 0) ? IM_COL32(255,0,255,255) : IM_COL32(255,0,0,255); ImVec2 p = NavCalcPreferredMousePos(); char buf[32]; ImFormatString(buf, 32, "%d", g.NavLayer); g.OverlayDrawList.AddCircleFilled(p, 3.0f, col); g.OverlayDrawList.AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); } + if (g.NavWindow) { for (int layer = 0; layer < 2; layer++) GetOverlayDrawList(g.NavWindow)->AddRect(g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Min, g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Max, IM_COL32(255,200,0,255)); } // [DEBUG] + if (g.NavWindow) { ImU32 col = (g.NavWindow->HiddenFrames <= 0) ? IM_COL32(255,0,255,255) : IM_COL32(255,0,0,255); ImVec2 p = NavCalcPreferredMousePos(); char buf[32]; ImFormatString(buf, 32, "%d", g.NavLayer); GetOverlayDrawList(g.NavWindow)->AddCircleFilled(p, 3.0f, col); GetOverlayDrawList(g.NavWindow)->AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); } #endif } @@ -3592,6 +3602,15 @@ void ImGui::RenderPlatformWindows(void* platform_render_arg, void* renderer_rend } } +static void SetupOverlayDrawList(ImDrawList* draw_list, ImGuiViewport* viewport) +{ + ImGuiContext& g = *GImGui; + draw_list->Clear(); + draw_list->PushTextureID(g.IO.Fonts->TexID); + draw_list->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size, false); + draw_list->Flags = (g.Style.AntiAliasedLines ? ImDrawListFlags_AntiAliasedLines : 0) | (g.Style.AntiAliasedFill ? ImDrawListFlags_AntiAliasedFill : 0); +} + void ImGui::NewFrame() { IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() or ImGui::SetCurrentContext()?"); @@ -3659,16 +3678,13 @@ void ImGui::NewFrame() g.DrawListSharedData.ClipRectFullscreen = ImVec4(0.0f, 0.0f, virtual_space_max.x, virtual_space_max.y); g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol; - g.OverlayDrawList.Clear(); - g.OverlayDrawList.PushTextureID(g.IO.Fonts->TexID); - g.OverlayDrawList.PushClipRectFullScreen(); - g.OverlayDrawList.Flags = (g.Style.AntiAliasedLines ? ImDrawListFlags_AntiAliasedLines : 0) | (g.Style.AntiAliasedFill ? ImDrawListFlags_AntiAliasedFill : 0); - - // Mark rendering data as invalid to prevent user who may have a handle on it to use it + // Mark rendering data as invalid to prevent user who may have a handle on it to use it. Setup Overlay draw list for the viewport. for (int n = 0; n < g.Viewports.Size; n++) { - g.Viewports[n]->DrawData = NULL; - g.Viewports[n]->DrawDataP.Clear(); + ImGuiViewportP* viewport = g.Viewports[n]; + viewport->DrawData = NULL; + viewport->DrawDataP.Clear(); + SetupOverlayDrawList(viewport->OverlayDrawList, viewport); } // Clear reference to active widget if the widget isn't alive anymore @@ -3983,7 +3999,7 @@ void ImGui::Initialize(ImGuiContext* context) g.SettingsHandlers.push_front(ini_handler); // Create default viewport - ImGuiViewportP* viewport = IM_NEW(ImGuiViewportP)(); + ImGuiViewportP* viewport = IM_NEW(ImGuiViewportP)(&g.DrawListSharedData); viewport->ID = IMGUI_VIEWPORT_DEFAULT_ID; viewport->Idx = 0; g.Viewports.push_back(viewport); @@ -4047,7 +4063,6 @@ void ImGui::Shutdown(ImGuiContext* context) for (int i = 0; i < g.Viewports.Size; i++) IM_DELETE(g.Viewports[i]); g.Viewports.clear(); - g.OverlayDrawList.ClearFreeMemory(); g.PrivateClipboard.clear(); g.InputTextState.Text.clear(); g.InputTextState.InitialText.clear(); @@ -4483,17 +4498,25 @@ void ImGui::Render() ImVec2 offset, size, uv[4]; if (g.IO.MouseDrawCursor && g.IO.Fonts->GetMouseCursorTexData(g.MouseCursor, &offset, &size, &uv[0], &uv[2])) { - const ImVec2 pos = g.IO.MousePos - offset; + // We need to account for the possibility of the mouse cursor straddling multiple viewports... + const ImVec2 main_pos = g.IO.MousePos - offset; const ImTextureID tex_id = g.IO.Fonts->TexID; const float sc = g.Style.MouseCursorScale; - g.OverlayDrawList.PushClipRect(g.MousePosViewport->Pos, g.MousePosViewport->Pos + g.MousePosViewport->Size); - g.OverlayDrawList.PushTextureID(tex_id); - g.OverlayDrawList.AddImage(tex_id, pos + ImVec2(1,0)*sc, pos+ImVec2(1,0)*sc + size*sc, uv[2], uv[3], IM_COL32(0,0,0,48)); // Shadow - g.OverlayDrawList.AddImage(tex_id, pos + ImVec2(2,0)*sc, pos+ImVec2(2,0)*sc + size*sc, uv[2], uv[3], IM_COL32(0,0,0,48)); // Shadow - g.OverlayDrawList.AddImage(tex_id, pos, pos + size*sc, uv[2], uv[3], IM_COL32(0,0,0,255)); // Black border - g.OverlayDrawList.AddImage(tex_id, pos, pos + size*sc, uv[0], uv[1], IM_COL32(255,255,255,255)); // White fill - g.OverlayDrawList.PopTextureID(); - g.OverlayDrawList.PopClipRect(); + for (int viewport_n = 0; viewport_n < g.Viewports.Size; viewport_n++) + { + ImGuiViewportP* viewport = g.Viewports[viewport_n]; + ImVec2 pos = (g.MousePosViewport == viewport) ? main_pos : ConvertPlatformPosToViewportPos(ConvertViewportPosToPlatformPos(main_pos, g.MousePosViewport), viewport); + if (viewport->GetRect().Overlaps(ImRect(pos, pos + ImVec2(2,2)*sc + size * sc))) + { + ImDrawList* draw_list = viewport->OverlayDrawList; + draw_list->PushTextureID(tex_id); + draw_list->AddImage(tex_id, pos+ImVec2(1,0)*sc, pos+ImVec2(1,0)*sc + size*sc, uv[2], uv[3], IM_COL32(0,0,0,48)); // Shadow + draw_list->AddImage(tex_id, pos+ImVec2(2,0)*sc, pos+ImVec2(2,0)*sc + size*sc, uv[2], uv[3], IM_COL32(0,0,0,48)); // Shadow + draw_list->AddImage(tex_id, pos, pos + size*sc, uv[2], uv[3], IM_COL32(0,0,0,255)); // Black border + draw_list->AddImage(tex_id, pos, pos + size*sc, uv[0], uv[1], IM_COL32(255,255,255,255)); // White fill + draw_list->PopTextureID(); + } + } } // Setup ImDrawData structures for end-user @@ -4502,7 +4525,7 @@ void ImGui::Render() { ImGuiViewportP* viewport = g.Viewports[n]; viewport->DrawDataBuilder.FlattenIntoSingleLayer(); - AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[0], &g.OverlayDrawList); + AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[0], viewport->OverlayDrawList); SetupViewportDrawData(viewport, &viewport->DrawDataBuilder.Layers[0]); g.IO.MetricsRenderVertices += viewport->DrawData->TotalVtxCount; g.IO.MetricsRenderIndices += viewport->DrawData->TotalIdxCount; @@ -4605,12 +4628,19 @@ ImGuiViewportP* ImGui::Viewport(ImGuiWindow* window, ImGuiID id, ImGuiViewportFl else { // New viewport - viewport = IM_NEW(ImGuiViewportP)(); + viewport = IM_NEW(ImGuiViewportP)(&g.DrawListSharedData); viewport->ID = id; viewport->Idx = g.Viewports.Size; viewport->Pos = ImVec2(g.Viewports.back()->GetNextX(), 0.0f); viewport->Size = size; g.Viewports.push_back(viewport); + + // We normally setup for all viewports in NewFrame() but here need to handle the mid-frame creation of a new viewport. + // 1. We need to extend the fullscreen clip rect so the OverlayDrawList clip is correct for that the first frame + // 2. Our ImDrawList system requires that there is always a command, SetupOverlayDrawList() effectively does that by setting up texture and clip rect + g.DrawListSharedData.ClipRectFullscreen.z = ImMax(g.DrawListSharedData.ClipRectFullscreen.z, viewport->Pos.x + viewport->Size.x); + g.DrawListSharedData.ClipRectFullscreen.w = ImMax(g.DrawListSharedData.ClipRectFullscreen.w, viewport->Pos.y + viewport->Size.y); + SetupOverlayDrawList(viewport->OverlayDrawList, viewport); } IM_ASSERT(viewport->Pos.y == 0.0f); @@ -6619,7 +6649,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DrawList->AddRectFilled(viewport_rect.Min, viewport_rect.Max, GetColorU32(ImGuiCol_ModalWindowDarkening, g.ModalWindowDarkeningRatio)); for (int viewport_n = 0; viewport_n < g.Viewports.Size; viewport_n++) if (g.Viewports[viewport_n] != window->Viewport) - g.OverlayDrawList.AddRectFilled(g.Viewports[viewport_n]->Pos, g.Viewports[viewport_n]->Pos + g.Viewports[viewport_n]->Size, GetColorU32(ImGuiCol_ModalWindowDarkening, g.ModalWindowDarkeningRatio)); + g.Viewports[viewport_n]->OverlayDrawList->AddRectFilled(g.Viewports[viewport_n]->Pos, g.Viewports[viewport_n]->Pos + g.Viewports[viewport_n]->Size, GetColorU32(ImGuiCol_ModalWindowDarkening, g.ModalWindowDarkeningRatio)); } // Draw navigation selection/windowing rectangle background @@ -13925,7 +13955,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) struct Funcs { - static void NodeDrawList(ImGuiWindow* window, ImDrawList* draw_list, const char* label) + static void NodeDrawList(ImGuiWindow* window, ImGuiViewportP* viewport, ImDrawList* draw_list, const char* label) { bool node_open = ImGui::TreeNode(draw_list, "%s: '%s' %d vtx, %d indices, %d cmds", label, draw_list->_OwnerName ? draw_list->_OwnerName : "", draw_list->VtxBuffer.Size, draw_list->IdxBuffer.Size, draw_list->CmdBuffer.Size); if (draw_list == ImGui::GetWindowDrawList()) @@ -13936,7 +13966,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) return; } - ImDrawList* overlay_draw_list = ImGui::GetOverlayDrawList(); // Render additional visuals into the top-most draw list + ImDrawList* overlay_draw_list = viewport->OverlayDrawList; // Render additional visuals into the top-most draw list if (window && ImGui::IsItemHovered()) overlay_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255)); if (!node_open) @@ -14008,7 +14038,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) if (!ImGui::TreeNode(window, "%s '%s', %d @ 0x%p", label, window->Name, window->Active || window->WasActive, window)) return; ImGuiWindowFlags flags = window->Flags; - NodeDrawList(window, window->DrawList, "DrawList"); + NodeDrawList(window, window->Viewport, window->DrawList, "DrawList"); ImGui::BulletText("Pos: (%.1f,%.1f), Size: (%.1f,%.1f), SizeContents (%.1f,%.1f)", window->Pos.x, window->Pos.y, window->Size.x, window->Size.y, window->SizeContents.x, window->SizeContents.y); ImGui::BulletText("Flags: 0x%08X (%s%s%s%s%s%s..)", flags, (flags & ImGuiWindowFlags_ChildWindow) ? "Child " : "", (flags & ImGuiWindowFlags_Tooltip) ? "Tooltip " : "", (flags & ImGuiWindowFlags_Popup) ? "Popup " : "", @@ -14068,7 +14098,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) (flags & ImGuiViewportFlags_NoRendererClear) ? " NoRendererClear" : ""); for (int layer_i = 0; layer_i < IM_ARRAYSIZE(viewport->DrawDataBuilder.Layers); layer_i++) for (int draw_list_i = 0; draw_list_i < viewport->DrawDataBuilder.Layers[layer_i].Size; draw_list_i++) - Funcs::NodeDrawList(NULL, viewport->DrawDataBuilder.Layers[layer_i][draw_list_i], "DrawList"); + Funcs::NodeDrawList(NULL, viewport, viewport->DrawDataBuilder.Layers[layer_i][draw_list_i], "DrawList"); ImGui::TreePop(); } } @@ -14111,8 +14141,8 @@ void ImGui::ShowMetricsWindow(bool* p_open) char buf[32]; ImFormatString(buf, IM_ARRAYSIZE(buf), "%d", window->BeginOrderWithinContext); float font_size = ImGui::GetFontSize() * 2; - g.OverlayDrawList.AddRectFilled(window->Pos, window->Pos + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255)); - g.OverlayDrawList.AddText(NULL, font_size, window->Pos, IM_COL32(255, 255, 255, 255), buf); + window->Viewport->OverlayDrawList->AddRectFilled(window->PosFloat, window->PosFloat + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255)); + window->Viewport->OverlayDrawList->AddText(NULL, font_size, window->PosFloat, IM_COL32(255, 255, 255, 255), buf); } } } diff --git a/imgui.h b/imgui.h index 79ec5531273c..2b2b22e9acfa 100644 --- a/imgui.h +++ b/imgui.h @@ -502,7 +502,7 @@ namespace ImGui IMGUI_API bool IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max); // test if rectangle (in screen space) is visible / not clipped. to perform coarse clipping on user's side. IMGUI_API float GetTime(); IMGUI_API int GetFrameCount(); - IMGUI_API ImDrawList* GetOverlayDrawList(); // this draw list will be the last rendered one, it covers all viewports. useful to quickly draw overlays shapes/text + IMGUI_API ImDrawList* GetOverlayDrawList(); // this draw list will be the last rendered. it covers the entire current viewport. useful to quickly draw overlays shapes/text IMGUI_API ImDrawListSharedData* GetDrawListSharedData(); // you may use this when creating your own ImDrawList instances IMGUI_API const char* GetStyleColorName(ImGuiCol idx); IMGUI_API void SetStateStorage(ImGuiStorage* storage); // replace current window storage with our own (if you want to manipulate it yourself, typically clear subsection of it) diff --git a/imgui_internal.h b/imgui_internal.h index 4ded494204c2..682d3e774652 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -517,11 +517,13 @@ struct ImGuiViewportP : public ImGuiViewport int LastFrameAsRefViewport; // Last frame number this viewport was io.MouseViewportRef ImGuiID LastNameHash; ImGuiWindow* Window; + ImDrawList* OverlayDrawList; // For convenience, a draw list we can render to that's always rendered last (we use it to draw software mouse cursor when io.MouseDrawCursor is set) ImDrawData DrawDataP; ImDrawDataBuilder DrawDataBuilder; ImVec2 RendererLastSize; - ImGuiViewportP() { Idx = 1; LastFrameActive = LastFrameAsRefViewport = -1; LastNameHash = 0; Window = NULL; DrawData = NULL; RendererLastSize = ImVec2(-1.0f,-1.0f); } + ImGuiViewportP(ImDrawListSharedData* draw_list_shared_data) { Idx = 1; LastFrameActive = LastFrameAsRefViewport = -1; LastNameHash = 0; Window = NULL; OverlayDrawList = IM_NEW(ImDrawList)(draw_list_shared_data); OverlayDrawList->_OwnerName = "##Overlay"; RendererLastSize = ImVec2(-1.0f,-1.0f); } + ~ImGuiViewportP() { IM_DELETE(OverlayDrawList); } ImRect GetRect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } float GetNextX() const { const float SPACING = 4.0f; return Pos.x + Size.x + SPACING; } }; @@ -634,7 +636,7 @@ struct ImGuiContext // Viewports ImVector Viewports; ImGuiPlatformData PlatformData; // This is essentially the public facing version of the Viewports vector (it is updated in UpdatePlatformWindows and exclude the viewports about to be destroyed) - ImGuiViewportP* CurrentViewport; + ImGuiViewportP* CurrentViewport; // We track changes of viewport (happening in Begin) so we can call Platform_OnChangedViewport() ImGuiViewportP* MousePosViewport; ImGuiViewportP* MousePosPrevViewport; ImGuiViewportP* MouseHoveredPrevViewport; @@ -676,7 +678,6 @@ struct ImGuiContext // Render float ModalWindowDarkeningRatio; - ImDrawList OverlayDrawList; // Optional software render of mouse cursors, if io.MouseDrawCursor is set + a few debug overlays ImGuiMouseCursor MouseCursor; // Drag and Drop @@ -731,7 +732,7 @@ struct ImGuiContext int WantTextInputNextFrame; char TempBuffer[1024*3+1]; // temporary text buffer - ImGuiContext(ImFontAtlas* shared_font_atlas) : OverlayDrawList(NULL) + ImGuiContext(ImFontAtlas* shared_font_atlas) { Initialized = false; Font = NULL; @@ -794,8 +795,6 @@ struct ImGuiContext NavMoveDir = NavMoveDirLast = ImGuiDir_None; ModalWindowDarkeningRatio = 0.0f; - OverlayDrawList._Data = &DrawListSharedData; - OverlayDrawList._OwnerName = "##Overlay"; // Give it a name for debugging MouseCursor = ImGuiMouseCursor_Arrow; DragDropActive = false; @@ -1097,6 +1096,7 @@ namespace ImGui IMGUI_API void PopItemFlag(); IMGUI_API void SetCurrentFont(ImFont* font); + IMGUI_API ImDrawList* GetOverlayDrawList(ImGuiWindow* window); IMGUI_API void OpenPopupEx(ImGuiID id); IMGUI_API void ClosePopup(ImGuiID id); From 9d8bc790258f1b31770c71e6704375a0c4d48b4d Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 5 Apr 2018 11:06:00 +0200 Subject: [PATCH 102/828] Viewport: Renamed RenderPlatformWindows() to RenderPlatformWindowsDefault(), tweaked examples and emphasis optional Viewport code path. (#1542) --- examples/directx10_example/main.cpp | 9 ++++++--- examples/directx11_example/main.cpp | 9 ++++++--- examples/directx12_example/main.cpp | 9 ++++++--- examples/opengl2_example/main.cpp | 9 ++++++--- examples/opengl3_example/main.cpp | 9 ++++++--- examples/sdl_opengl3_example/main.cpp | 9 ++++++--- examples/sdl_vulkan_example/main.cpp | 9 ++++++--- examples/vulkan_example/main.cpp | 9 ++++++--- imgui.cpp | 2 +- imgui.h | 6 +++--- 10 files changed, 52 insertions(+), 28 deletions(-) diff --git a/examples/directx10_example/main.cpp b/examples/directx10_example/main.cpp index 205b697d9f65..94864eda735a 100644 --- a/examples/directx10_example/main.cpp +++ b/examples/directx10_example/main.cpp @@ -208,9 +208,12 @@ int main(int, char**) ImGui::Render(); ImGui_ImplDX10_RenderDrawData(ImGui::GetDrawData()); - // Update and Render additional Platform Windows (when ImGuiConfigFlags_EnableViewports is enabled) - ImGui::UpdatePlatformWindows(); - ImGui::RenderPlatformWindows(NULL, NULL); + // Update and Render additional Platform Windows + if (io.ConfigFlags & ImGuiConfigFlags_EnableViewports) + { + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); + } g_pSwapChain->Present(1, 0); // Present with vsync //g_pSwapChain->Present(0, 0); // Present without vsync diff --git a/examples/directx11_example/main.cpp b/examples/directx11_example/main.cpp index 759fbe7a1509..8f7df9d8c12d 100644 --- a/examples/directx11_example/main.cpp +++ b/examples/directx11_example/main.cpp @@ -226,9 +226,12 @@ int main(int, char**) ImGui::Render(); ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData()); - // Update and Render additional Platform Windows (when ImGuiConfigFlags_EnableViewports is enabled) - ImGui::UpdatePlatformWindows(); - ImGui::RenderPlatformWindows(NULL, NULL); + // Update and Render additional Platform Windows + if (io.ConfigFlags & ImGuiConfigFlags_EnableViewports) + { + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); + } g_pSwapChain->Present(1, 0); // Present with vsync //g_pSwapChain->Present(0, 0); // Present without vsync diff --git a/examples/directx12_example/main.cpp b/examples/directx12_example/main.cpp index 57bd1bbccdca..2597e5f31d44 100644 --- a/examples/directx12_example/main.cpp +++ b/examples/directx12_example/main.cpp @@ -401,9 +401,12 @@ int main(int, char**) g_pd3dCommandQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*)&g_pd3dCommandList); - // Update and Render additional Platform Windows (when ImGuiConfigFlags_EnableViewports is enabled) - ImGui::UpdatePlatformWindows(); - ImGui::RenderPlatformWindows(NULL, NULL); + // Update and Render additional Platform Windows + if (io.ConfigFlags & ImGuiConfigFlags_EnableViewports) + { + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); + } g_pSwapChain->Present(1, 0); // Present with vsync //g_pSwapChain->Present(0, 0); // Present without vsync diff --git a/examples/opengl2_example/main.cpp b/examples/opengl2_example/main.cpp index 6e3bc98821f8..d22d593f5d2e 100644 --- a/examples/opengl2_example/main.cpp +++ b/examples/opengl2_example/main.cpp @@ -117,9 +117,12 @@ int main(int, char**) //glUseProgram(0); // You may want this if using this code in an OpenGL 3+ context where shaders may be bound, but prefer using the GL3+ code. ImGui_ImplOpenGL2_RenderDrawData(ImGui::GetDrawData()); - // Update and Render additional Platform Windows (when ImGuiConfigFlags_EnableViewports is enabled) - ImGui::UpdatePlatformWindows(); - ImGui::RenderPlatformWindows(NULL, NULL); + // Update and Render additional Platform Windows + if (io.ConfigFlags & ImGuiConfigFlags_EnableViewports) + { + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); + } glfwMakeContextCurrent(window); glfwSwapBuffers(window); diff --git a/examples/opengl3_example/main.cpp b/examples/opengl3_example/main.cpp index 961513890a5a..527a4f1447c7 100644 --- a/examples/opengl3_example/main.cpp +++ b/examples/opengl3_example/main.cpp @@ -124,9 +124,12 @@ int main(int, char**) glClear(GL_COLOR_BUFFER_BIT); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); - // Update and Render additional Platform Windows (when ImGuiConfigFlags_EnableViewports is enabled) - ImGui::UpdatePlatformWindows(); - ImGui::RenderPlatformWindows(NULL, NULL); + // Update and Render additional Platform Windows + if (io.ConfigFlags & ImGuiConfigFlags_EnableViewports) + { + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); + } glfwMakeContextCurrent(window); glfwSwapBuffers(window); diff --git a/examples/sdl_opengl3_example/main.cpp b/examples/sdl_opengl3_example/main.cpp index c87816304fbd..366bc222297e 100644 --- a/examples/sdl_opengl3_example/main.cpp +++ b/examples/sdl_opengl3_example/main.cpp @@ -132,9 +132,12 @@ int main(int, char**) glClear(GL_COLOR_BUFFER_BIT); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); - // Update and Render additional Platform Windows (when ImGuiConfigFlags_EnableViewports is enabled) - ImGui::UpdatePlatformWindows(); - ImGui::RenderPlatformWindows(NULL, NULL); + // Update and Render additional Platform Windows + if (io.ConfigFlags & ImGuiConfigFlags_EnableViewports) + { + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); + } SDL_GL_MakeCurrent(window, gl_context); SDL_GL_SwapWindow(window); diff --git a/examples/sdl_vulkan_example/main.cpp b/examples/sdl_vulkan_example/main.cpp index dcad5343e077..53de0fbb8e17 100644 --- a/examples/sdl_vulkan_example/main.cpp +++ b/examples/sdl_vulkan_example/main.cpp @@ -469,9 +469,12 @@ int main(int, char**) memcpy(&wd->ClearValue.color.float32[0], &clear_color, 4 * sizeof(float)); FrameRender(wd); - // Update and Render additional Platform Windows (when ImGuiConfigFlags_EnableViewports is enabled) - ImGui::UpdatePlatformWindows(); - ImGui::RenderPlatformWindows(NULL, NULL); + // Update and Render additional Platform Windows + if (io.ConfigFlags & ImGuiConfigFlags_EnableViewports) + { + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); + } FramePresent(wd); } diff --git a/examples/vulkan_example/main.cpp b/examples/vulkan_example/main.cpp index 57697135574b..6400a2ab07f7 100644 --- a/examples/vulkan_example/main.cpp +++ b/examples/vulkan_example/main.cpp @@ -479,9 +479,12 @@ int main(int, char**) memcpy(&wd->ClearValue.color.float32[0], &clear_color, 4 * sizeof(float)); FrameRender(wd); - // Update and Render additional Platform Windows (when ImGuiConfigFlags_EnableViewports is enabled) - ImGui::UpdatePlatformWindows(); - ImGui::RenderPlatformWindows(NULL, NULL); + // Update and Render additional Platform Windows + if (io.ConfigFlags & ImGuiConfigFlags_EnableViewports) + { + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); + } FramePresent(wd); } diff --git a/imgui.cpp b/imgui.cpp index 024066fc5211..9459a39b7412 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3602,7 +3602,7 @@ void ImGui::UpdatePlatformWindows() // MySwapBufferFunction(data->Viewports[i], my_args); // // Note how we intentionally skip the main viewport (index 0) which is generally rendered as part of our main application. -void ImGui::RenderPlatformWindows(void* platform_render_arg, void* renderer_render_arg) +void ImGui::RenderPlatformWindowsDefault(void* platform_render_arg, void* renderer_render_arg) { if (!(ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_EnableViewports)) return; diff --git a/imgui.h b/imgui.h index eb324f55fd47..beae3f39b282 100644 --- a/imgui.h +++ b/imgui.h @@ -551,7 +551,7 @@ namespace ImGui IMGUI_API ImGuiPlatformData* GetPlatformData(); // List of viewports. Viewport 0 is always the main viewport, followed by the secondary viewports. IMGUI_API ImGuiViewport* GetMainViewport(); // GetPlatformData()->MainViewport IMGUI_API void UpdatePlatformWindows(); // Call in main loop. Create/Destroy/Resize platform windows so there's one for each viewport - IMGUI_API void RenderPlatformWindows(void* platform_render_arg, void* renderer_render_arg); // Call in main loop. Call RenderWindow/SwapBuffers from the ImGuiPlatformIO structure. May be reimplemented by user. + IMGUI_API void RenderPlatformWindowsDefault(void* platform_render_arg = NULL, void* renderer_render_arg = NULL); // Call in main loop. Call RenderWindow/SwapBuffers from the ImGuiPlatformIO structure. May be reimplemented by user. IMGUI_API void DestroyPlatformWindows(); // (Optional) Call in back-end shutdown if you need to close Platform Windows before imgui shutdown. IMGUI_API ImGuiViewport* FindViewportByPlatformHandle(void* platform_handle); @@ -1878,8 +1878,8 @@ struct ImFont // This is designed so we can mix and match two imgui_impl_xxxx files, one for the Platform (~ Windowing), one for Renderer. // Custom engine back-ends will often provide both Platform and Renderer interfaces and thus may not need to use all functions. // Platform functions are typically called before their Renderer counterpart, apart from Destroy which are called the other way. -// RenderPlatformWindows() basically iterate secondary viewports and call Platform+Renderer's RenderWindow then Platform+Renderer's SwapBuffers, -// You may skip using RenderPlatformWindows() and call your functions yourself if you need specific behavior for your multi-window rendering. +// RenderPlatformWindowsDefault() basically iterate secondary viewports and call Platform+Renderer's RenderWindow then Platform+Renderer's SwapBuffers, +// You may skip using RenderPlatformWindowsDefault() and call your draw/swap functions yourself if you need specific behavior for your multi-window rendering. struct ImGuiPlatformIO { // Platform (e.g. Win32, GLFW, SDL2) From c23b5463c70217ce943e158088065a1c0ce69913 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 5 Apr 2018 11:40:33 +0200 Subject: [PATCH 103/828] Viewport: Moving a window accross viewports tries to preserve the dragging pivot (that is assuming that the window will be evenly scaled by DPI, aka Style is scaled). (#1542) --- imgui.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 9459a39b7412..0f38c9f008e2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -13982,11 +13982,17 @@ static void ScaleWindow(ImGuiWindow* window, float scale) void ImGui::ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale) { ImGuiContext& g = *GImGui; + + if (g.MovingWindow != NULL) + g.ActiveIdClickOffset = ImFloor(g.ActiveIdClickOffset * scale); + /* if (g.IO.MousePosViewport == viewport->ID) { - //g.IO.MousePos = g.IO.MousePosPrev = ImFloor((g.IO.MousePos - viewport->Pos) * scale) + viewport->Pos; - //g.IO.MouseDelta = ImVec2(0,0); + g.IO.MousePos = g.IO.MousePosPrev = ImFloor((g.IO.MousePos - viewport->Pos) * scale) + viewport->Pos; + g.IO.MouseDelta = ImVec2(0,0); } + */ + if (viewport->Window) { ScaleWindow(viewport->Window, scale); From 84c6ea0ceebabe2c5eb0f11967e7e5ab683df6e1 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 5 Apr 2018 12:00:07 +0200 Subject: [PATCH 104/828] Viewport: Comments about honoring ImGuiViewportFlags_NoInputs and MouseHoveredViewport. (#1542) --- examples/imgui_impl_glfw.cpp | 4 ++++ examples/imgui_impl_win32.cpp | 5 ++++- imgui.h | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index 8adb99173212..11025fe6a08a 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -387,6 +387,10 @@ static LRESULT CALLBACK WndProcNoInputs(HWND hWnd, UINT msg, WPARAM wParam, LPAR { if (msg == WM_NCHITTEST) { + // Let mouse pass-through the window. This will allow the back-end to set io.MouseHoveredViewport properly (which is OPTIONAL). + // The ImGuiViewportFlags_NoInputs flag is set while dragging a viewport, as want to detect the window behind the one we are dragging. + // If you cannot easily access those viewport flags from your windowing/event code: you may manually synchronize its state e.g. in + // your main loop after calling UpdatePlatformWindows(). Iterate all viewports/platform windows and pass the flag to your windowing system. ImGuiViewport* viewport = (ImGuiViewport*)::GetPropA(hWnd, "IMGUI_VIEWPORT"); if (viewport->Flags & ImGuiViewportFlags_NoInputs) return HTTRANSPARENT; diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index 6131749eef74..e34583b18d58 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -503,7 +503,10 @@ static LRESULT CALLBACK ImGui_ImplWin32_WndProcHandler_PlatformWindow(HWND hWnd, viewport->PlatformRequestResize = true; break; case WM_NCHITTEST: - // Let mouse pass-through the window, this is used while e.g. dragging a window, we creates a temporary overlay but want the cursor to aim behind our overlay. + // Let mouse pass-through the window. This will allow the back-end to set io.MouseHoveredViewport properly (which is OPTIONAL). + // The ImGuiViewportFlags_NoInputs flag is set while dragging a viewport, as want to detect the window behind the one we are dragging. + // If you cannot easily access those viewport flags from your windowing/event code: you may manually synchronize its state e.g. in + // your main loop after calling UpdatePlatformWindows(). Iterate all viewports/platform windows and pass the flag to your windowing system. if (viewport->Flags & ImGuiViewportFlags_NoInputs) return HTTRANSPARENT; break; diff --git a/imgui.h b/imgui.h index beae3f39b282..cf6410a33cbf 100644 --- a/imgui.h +++ b/imgui.h @@ -1079,7 +1079,7 @@ struct ImGuiIO float MouseWheel; // Mouse wheel Vertical: 1 unit scrolls about 5 lines text. float MouseWheelH; // Mouse wheel Horizontal. Most users don't have a mouse with an horizontal wheel, may not be filled by all back-ends. ImGuiID MousePosViewport; // (Optional) When using multiple viewports: viewport from which io.MousePos is based from (when dragging this is generally the captured/focused viewport, even though we can drag outside of it and then it's not hovered anymore). (0 == default viewport) - ImGuiID MouseHoveredViewport; // (Optional) When using multiple viewports: viewport the OS mouse cursor is hovering. (0 == default viewport) + ImGuiID MouseHoveredViewport; // (Optional) When using multiple viewports: viewport the OS mouse cursor is hovering. (0 == default viewport) _IGNORING_ viewports with the ImGuiBackendFlags_HasMouseHoveredViewport flag, and _REGARDLESS_ of whether another viewport is focused. Set io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport if you can provide this information. bool MouseDrawCursor; // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). bool KeyCtrl; // Keyboard modifier pressed: Control bool KeyShift; // Keyboard modifier pressed: Shift From 72899318e6cca13b88b88bcfa8ef78c4ac9a8354 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 5 Apr 2018 17:06:23 +0200 Subject: [PATCH 105/828] Viewport, Platform, Examples: Added support for transparent window via PlatformIO Platform_SetWindowAlpha (#1542) + fixes for GLFW 3.3 --- examples/imgui_impl_glfw.cpp | 16 ++++++++++++++-- examples/imgui_impl_win32.cpp | 19 +++++++++++++++++++ imgui.cpp | 5 +++++ imgui_internal.h | 4 +++- 4 files changed, 41 insertions(+), 3 deletions(-) diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index 11025fe6a08a..6d65eef6f3a8 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -39,6 +39,7 @@ #else #define GLFW_HAS_GLFW_HOVERED 0 #endif +#define GLFW_HAS_WINDOW_ALPHA (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ #define GLFW_HAS_VULKAN (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ // Data @@ -235,8 +236,8 @@ static void ImGui_ImplGlfw_UpdateMouse() } #if GLFW_HAS_GLFW_HOVERED - io.ConfigFlags |= ImGuiConfigFlags_PlatformHasMouseHoveredViewport; - if (glfwGetWindowAttrib(data->Window, GLFW_HOVERED) && !(viewport->Flags & ImGuiViewportFlags_NoInputs)) + io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; + if (glfwGetWindowAttrib(window, GLFW_HOVERED) && !(viewport->Flags & ImGuiViewportFlags_NoInputs)) io.MouseHoveredViewport = viewport->ID; #endif } @@ -469,6 +470,14 @@ static void ImGui_ImplGlfw_SetWindowTitle(ImGuiViewport* viewport, const char* t glfwSetWindowTitle(data->Window, title); } +#if GLFW_HAS_WINDOW_ALPHA +static void ImGui_ImplGlfw_SetWindowAlpha(ImGuiViewport* viewport, float alpha) +{ + ImGuiViewportDataGlfw* data = (ImGuiViewportDataGlfw*)viewport->PlatformUserData; + glfwSetWindowOpacity(data->Window, alpha); +} +#endif + static void ImGui_ImplGlfw_RenderWindow(ImGuiViewport* viewport, void*) { ImGuiViewportDataGlfw* data = (ImGuiViewportDataGlfw*)viewport->PlatformUserData; @@ -522,6 +531,9 @@ static void ImGui_ImplGlfw_InitPlatformInterface() platform_io.Platform_SetWindowTitle = ImGui_ImplGlfw_SetWindowTitle; platform_io.Platform_RenderWindow = ImGui_ImplGlfw_RenderWindow; platform_io.Platform_SwapBuffers = ImGui_ImplGlfw_SwapBuffers; +#if GLFW_HAS_WINDOW_ALPHA + platform_io.Platform_SetWindowAlpha = ImGui_ImplGlfw_SetWindowAlpha; +#endif #if GLFW_HAS_VULKAN platform_io.Platform_CreateVkSurface = ImGui_ImplGlfw_CreateVkSurface; #endif diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index e34583b18d58..01493b9d0fcd 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -472,6 +472,24 @@ static void ImGui_ImplWin32_SetWindowTitle(ImGuiViewport* viewport, const char* ::SetWindowTextA(data->Hwnd, title); } +static void ImGui_ImplWin32_SetWindowAlpha(ImGuiViewport* viewport, float alpha) +{ + ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData; + IM_ASSERT(data->Hwnd != 0); + IM_ASSERT(alpha >= 0.0f && alpha <= 1.0f); + if (alpha < 1.0f) + { + DWORD style = ::GetWindowLongW(data->Hwnd, GWL_EXSTYLE) | WS_EX_LAYERED; + ::SetWindowLongW(data->Hwnd, GWL_EXSTYLE, style); + ::SetLayeredWindowAttributes(data->Hwnd, 0, (BYTE)(255 * alpha), LWA_ALPHA); + } + else + { + DWORD style = ::GetWindowLongW(data->Hwnd, GWL_EXSTYLE) & ~WS_EX_LAYERED; + ::SetWindowLongW(data->Hwnd, GWL_EXSTYLE, style); + } +} + static float ImGui_ImplWin32_GetWindowDpiScale(ImGuiViewport* viewport) { ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData; @@ -543,6 +561,7 @@ static void ImGui_ImplWin32_InitPlatformInterface() platform_io.Platform_SetWindowSize = ImGui_ImplWin32_SetWindowSize; platform_io.Platform_GetWindowSize = ImGui_ImplWin32_GetWindowSize; platform_io.Platform_SetWindowTitle = ImGui_ImplWin32_SetWindowTitle; + platform_io.Platform_SetWindowAlpha = ImGui_ImplWin32_SetWindowAlpha; platform_io.Platform_GetWindowDpiScale = ImGui_ImplWin32_GetWindowDpiScale; // Register main window handle diff --git a/imgui.cpp b/imgui.cpp index 0f38c9f008e2..26df72cecf33 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3578,6 +3578,11 @@ void ImGui::UpdatePlatformWindows() ImGui::MemFree(title_displayed); } + // Update alpha + if (viewport->LastAlpha != viewport->Alpha && g.PlatformIO.Platform_SetWindowAlpha) + g.PlatformIO.Platform_SetWindowAlpha(viewport, viewport->Alpha); + viewport->LastAlpha = viewport->Alpha; + // Show window. On startup ensure platform window don't get focus. if (is_new_window) { diff --git a/imgui_internal.h b/imgui_internal.h index 62aa8337b3aa..06b6bbd40156 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -517,13 +517,15 @@ struct ImGuiViewportP : public ImGuiViewport int LastFrameActive; // Last frame number this viewport was activated by a window int LastFrameAsRefViewport; // Last frame number this viewport was io.MouseViewportRef ImGuiID LastNameHash; + float Alpha; // Window opacity (when dragging dockable windows/viewports we make them transparent) + float LastAlpha; ImGuiWindow* Window; ImDrawList* OverlayDrawList; // For convenience, a draw list we can render to that's always rendered last (we use it to draw software mouse cursor when io.MouseDrawCursor is set) ImDrawData DrawDataP; ImDrawDataBuilder DrawDataBuilder; ImVec2 RendererLastSize; - ImGuiViewportP(ImDrawListSharedData* draw_list_shared_data) { Idx = 1; LastFrameActive = LastFrameAsRefViewport = -1; LastNameHash = 0; Window = NULL; OverlayDrawList = IM_NEW(ImDrawList)(draw_list_shared_data); OverlayDrawList->_OwnerName = "##Overlay"; RendererLastSize = ImVec2(-1.0f,-1.0f); } + ImGuiViewportP(ImDrawListSharedData* draw_list_shared_data) { Idx = 1; LastFrameActive = LastFrameAsRefViewport = -1; LastNameHash = 0; Alpha = LastAlpha = 1.0f; Window = NULL; OverlayDrawList = IM_NEW(ImDrawList)(draw_list_shared_data); OverlayDrawList->_OwnerName = "##Overlay"; RendererLastSize = ImVec2(-1.0f,-1.0f); } ~ImGuiViewportP() { IM_DELETE(OverlayDrawList); } ImRect GetRect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } float GetNextX() const { const float SPACING = 4.0f; return Pos.x + Size.x + SPACING; } From d4dd44851115be69a6b402dc78495c017a2ca7fb Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 9 Apr 2018 22:01:24 +0200 Subject: [PATCH 106/828] Examples, Platform, Viewport: Fixed inconsistent window ownership issues. Added comments. Made Win32/SDL back-ends track ownership. --- examples/imgui_impl_dx10.cpp | 1 + examples/imgui_impl_dx11.cpp | 1 + examples/imgui_impl_dx12.cpp | 1 + examples/imgui_impl_glfw.cpp | 13 ++++++++----- examples/imgui_impl_sdl2.cpp | 16 +++++++++------- examples/imgui_impl_vulkan.cpp | 1 + examples/imgui_impl_win32.cpp | 19 +++++++++++-------- imgui.cpp | 12 ++++++++---- imgui.h | 8 ++++---- 9 files changed, 44 insertions(+), 28 deletions(-) diff --git a/examples/imgui_impl_dx10.cpp b/examples/imgui_impl_dx10.cpp index ad19ecf48d11..de0ed14f9529 100644 --- a/examples/imgui_impl_dx10.cpp +++ b/examples/imgui_impl_dx10.cpp @@ -554,6 +554,7 @@ static void ImGui_ImplDX10_CreateWindow(ImGuiViewport* viewport) static void ImGui_ImplDX10_DestroyWindow(ImGuiViewport* viewport) { + // The main viewport (owned by the application) will always have RendererUserData == NULL here since we didn't create the data for it. if (ImGuiViewportDataDx10* data = (ImGuiViewportDataDx10*)viewport->RendererUserData) { if (data->SwapChain) diff --git a/examples/imgui_impl_dx11.cpp b/examples/imgui_impl_dx11.cpp index 1463fc316c21..1ce2d7fb416b 100644 --- a/examples/imgui_impl_dx11.cpp +++ b/examples/imgui_impl_dx11.cpp @@ -561,6 +561,7 @@ static void ImGui_ImplDX11_CreateWindow(ImGuiViewport* viewport) static void ImGui_ImplDX11_DestroyWindow(ImGuiViewport* viewport) { + // The main viewport (owned by the application) will always have RendererUserData == NULL since we didn't create the data for it. if (ImGuiViewportDataDx11* data = (ImGuiViewportDataDx11*)viewport->RendererUserData) { if (data->SwapChain) diff --git a/examples/imgui_impl_dx12.cpp b/examples/imgui_impl_dx12.cpp index 206a24c430d3..00dfb5fa2214 100644 --- a/examples/imgui_impl_dx12.cpp +++ b/examples/imgui_impl_dx12.cpp @@ -690,6 +690,7 @@ static void ImGui_ImplDX12_CreateWindow(ImGuiViewport* viewport) static void ImGui_ImplDX12_DestroyWindow(ImGuiViewport* viewport) { + // The main viewport (owned by the application) will always have RendererUserData == NULL since we didn't create the data for it. if (ImGuiViewportDataDx12* data = (ImGuiViewportDataDx12*)viewport->RendererUserData) { IM_ASSERT(0); diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index 8b1bb5acdad4..f7e16a473357 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -371,12 +371,14 @@ static void ImGui_ImplGlfw_DestroyWindow(ImGuiViewport* viewport) { if (ImGuiViewportDataGlfw* data = (ImGuiViewportDataGlfw*)viewport->PlatformUserData) { + if (data->WindowOwned) + { #if GLFW_HAS_GLFW_HOVERED - HWND hwnd = glfwGetWin32Window(data->Window); - ::RemovePropA(hwnd, "IMGUI_VIEWPORT"); - #endif - if (data->Window && data->WindowOwned) + HWND hwnd = glfwGetWin32Window(data->Window); + ::RemovePropA(hwnd, "IMGUI_VIEWPORT"); +#endif glfwDestroyWindow(data->Window); + } data->Window = NULL; IM_DELETE(data); } @@ -539,12 +541,13 @@ static void ImGui_ImplGlfw_InitPlatformInterface() platform_io.Platform_CreateVkSurface = ImGui_ImplGlfw_CreateVkSurface; #endif - // Register main window handle + // Register main window handle (which is owned by the main application, not by us) ImGuiViewport* main_viewport = ImGui::GetMainViewport(); ImGuiViewportDataGlfw* data = IM_NEW(ImGuiViewportDataGlfw)(); data->Window = g_Window; data->WindowOwned = false; main_viewport->PlatformUserData = data; + main_viewport->PlatformHandle = (void*)g_Window; } static void ImGui_ImplGlfw_ShutdownPlatformInterface() diff --git a/examples/imgui_impl_sdl2.cpp b/examples/imgui_impl_sdl2.cpp index 8b0f9b7804ff..e720ca9d96c7 100644 --- a/examples/imgui_impl_sdl2.cpp +++ b/examples/imgui_impl_sdl2.cpp @@ -285,9 +285,10 @@ struct ImGuiViewportDataSDL2 { SDL_Window* Window; Uint32 WindowID; + bool WindowOwned; SDL_GLContext GLContext; - ImGuiViewportDataSDL2() { Window = NULL; WindowID = 0; GLContext = NULL; } + ImGuiViewportDataSDL2() { Window = NULL; WindowID = 0; WindowOwned = false; GLContext = NULL; } ~ImGuiViewportDataSDL2() { IM_ASSERT(Window == NULL && GLContext == NULL); } }; @@ -316,8 +317,8 @@ static void ImGui_ImplSDL2_CreateWindow(ImGuiViewport* viewport) sdl_flags |= SDL_WINDOW_HIDDEN; sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? SDL_WINDOW_BORDERLESS : 0; sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? 0 : SDL_WINDOW_RESIZABLE; - data->Window = SDL_CreateWindow("No Title Yet", - (int)viewport->PlatformPos.x, (int)viewport->PlatformPos.y, (int)viewport->Size.x, (int)viewport->Size.y, sdl_flags); + data->Window = SDL_CreateWindow("No Title Yet", (int)viewport->PlatformPos.x, (int)viewport->PlatformPos.y, (int)viewport->Size.x, (int)viewport->Size.y, sdl_flags); + data->WindowOwned = true; if (use_opengl) data->GLContext = SDL_GL_CreateContext(data->Window); if (use_opengl && backup_context) @@ -329,11 +330,11 @@ static void ImGui_ImplSDL2_DestroyWindow(ImGuiViewport* viewport) { if (ImGuiViewportDataSDL2* data = (ImGuiViewportDataSDL2*)viewport->PlatformUserData) { - if (data->GLContext) + if (data->GLContext && data->WindowOwned) SDL_GL_DeleteContext(data->GLContext); - data->GLContext = NULL; - if (data->Window) + if (data->Window && data->WindowOwned) SDL_DestroyWindow(data->Window); + data->GLContext = NULL; data->Window = NULL; IM_DELETE(data); } @@ -455,11 +456,12 @@ static void ImGui_ImplSDL2_InitPlatformInterface(SDL_Window* window, void* sdl_g platform_io.Platform_CreateVkSurface = ImGui_ImplSDL2_CreateVkSurface; #endif - // Register main window handle + // Register main window handle (which is owned by the main application, not by us) ImGuiViewport* main_viewport = ImGui::GetMainViewport(); ImGuiViewportDataSDL2* data = IM_NEW(ImGuiViewportDataSDL2)(); data->Window = window; data->WindowID = SDL_GetWindowID(window); + data->WindowOwned = false; data->GLContext = sdl_gl_context; main_viewport->PlatformUserData = data; main_viewport->PlatformHandle = data->Window; diff --git a/examples/imgui_impl_vulkan.cpp b/examples/imgui_impl_vulkan.cpp index 3d2192c300e1..0dec6e89df28 100644 --- a/examples/imgui_impl_vulkan.cpp +++ b/examples/imgui_impl_vulkan.cpp @@ -1108,6 +1108,7 @@ static void ImGui_ImplVulkan_CreateWindow(ImGuiViewport* viewport) static void ImGui_ImplVulkan_DestroyWindow(ImGuiViewport* viewport) { + // The main viewport (owned by the application) will always have RendererUserData == NULL since we didn't create the data for it. if (ImGuiViewportDataVulkan* data = (ImGuiViewportDataVulkan*)viewport->RendererUserData) { ImGui_ImplVulkanH_DestroyWindowData(g_Instance, g_Device, &data->WindowData, g_Allocator); diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index 47a6592f6c7a..411e5295083d 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -364,10 +364,11 @@ float ImGui_ImplWin32_GetDpiScaleForRect(int x1, int y1, int x2, int y2) struct ImGuiViewportDataWin32 { HWND Hwnd; + bool HwndOwned; DWORD DwStyle; DWORD DwExStyle; - ImGuiViewportDataWin32() { Hwnd = NULL; DwStyle = DwExStyle = 0; } + ImGuiViewportDataWin32() { Hwnd = NULL; HwndOwned = false; DwStyle = DwExStyle = 0; } ~ImGuiViewportDataWin32() { IM_ASSERT(Hwnd == NULL); } }; @@ -393,10 +394,11 @@ static void ImGui_ImplWin32_CreateWindow(ImGuiViewport* viewport) // Create window RECT rect = { (LONG)viewport->PlatformPos.x, (LONG)viewport->PlatformPos.y, (LONG)(viewport->PlatformPos.x + viewport->Size.x), (LONG)(viewport->PlatformPos.y + viewport->Size.y) }; ::AdjustWindowRectEx(&rect, data->DwStyle, FALSE, data->DwExStyle); - data->Hwnd = ::CreateWindowExA( - data->DwExStyle, "ImGui Platform", "No Title Yet", data->DwStyle, // Style, class name, window name - rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, // Window area - g_hWnd, NULL, ::GetModuleHandle(NULL), NULL); // Parent window, Menu, Instance, Param + data->Hwnd = ::CreateWindowEx( + data->DwExStyle, _T("ImGui Platform"), _T("No Title Yet"), data->DwStyle, // Style, class name, window name + rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, // Window area + g_hWnd, NULL, ::GetModuleHandle(NULL), NULL); // Parent window, Menu, Instance, Param + data->HwndOwned = true; viewport->PlatformRequestResize = false; viewport->PlatformHandle = data->Hwnd; } @@ -411,7 +413,7 @@ static void ImGui_ImplWin32_DestroyWindow(ImGuiViewport* viewport) ::ReleaseCapture(); ::SetCapture(g_hWnd); } - if (data->Hwnd) + if (data->Hwnd && data->HwndOwned) ::DestroyWindow(data->Hwnd); data->Hwnd = NULL; IM_DELETE(data); @@ -564,12 +566,13 @@ static void ImGui_ImplWin32_InitPlatformInterface() platform_io.Platform_SetWindowAlpha = ImGui_ImplWin32_SetWindowAlpha; platform_io.Platform_GetWindowDpiScale = ImGui_ImplWin32_GetWindowDpiScale; - // Register main window handle + // Register main window handle (which is owned by the main application, not by us) ImGuiViewport* main_viewport = ImGui::GetMainViewport(); ImGuiViewportDataWin32* data = IM_NEW(ImGuiViewportDataWin32)(); data->Hwnd = g_hWnd; + data->HwndOwned = false; main_viewport->PlatformUserData = data; - main_viewport->PlatformHandle = (void*)data->Hwnd; + main_viewport->PlatformHandle = (void*)g_hWnd; } static void ImGui_ImplWin32_ShutdownPlatformInterface() diff --git a/imgui.cpp b/imgui.cpp index dd4ade472a88..abf5f2a3e2c8 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -762,7 +762,7 @@ static void UpdateManualResize(ImGuiWindow* window, const ImVec2& si static void FocusFrontMostActiveWindow(ImGuiWindow* ignore_window); // Viewports -const ImGuiID IMGUI_VIEWPORT_DEFAULT_ID = 0x11111111; // Using a constant instead of e.g. ImHash("ViewportDefault", 0); so it's easier to spot in the debugger. The exact value doesn't matter. +const ImGuiID IMGUI_VIEWPORT_DEFAULT_ID = 0x11111111; // Using an arbitrary constant instead of e.g. ImHash("ViewportDefault", 0); so it's easier to spot in the debugger. The exact value doesn't matter. static inline ImRect GetViewportRect(ImGuiWindow* window) { return window->Viewport->GetRect(); } static inline ImVec2 ConvertViewportPosToPlatformPos(const ImVec2& imgui_pos, ImGuiViewport* viewport) { return imgui_pos - viewport->Pos + viewport->PlatformPos; } static inline ImVec2 ConvertPlatformPosToViewportPos(const ImVec2& platform_pos, ImGuiViewport* viewport) { return platform_pos - viewport->PlatformPos + viewport->Pos; } @@ -3530,7 +3530,8 @@ void ImGui::UpdatePlatformWindows() if (!(g.IO.ConfigFlags & ImGuiConfigFlags_EnableViewports)) return; - // Create/resize/destroy platform windows and update the list that the user can process + // Create/resize/destroy platform windows to match each active viewport. Update the user-facing list. + // Skip the main viewport (index 0), which is always fully handled by the application! for (int i = 1; i < g.Viewports.Size; i++) { ImGuiViewportP* viewport = g.Viewports[i]; @@ -3609,12 +3610,12 @@ void ImGui::UpdatePlatformWindows() // for (int i = 1; i < data->Viewports.Size; i++) // MySwapBufferFunction(data->Viewports[i], my_args); // -// Note how we intentionally skip the main viewport (index 0) which is generally rendered as part of our main application. void ImGui::RenderPlatformWindowsDefault(void* platform_render_arg, void* renderer_render_arg) { if (!(ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_EnableViewports)) return; + // Skip the main viewport (index 0), which is always fully handled by the application! ImGuiPlatformData* data = ImGui::GetPlatformData(); ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); for (int i = 1; i < data->Viewports.Size; i++) @@ -3779,7 +3780,7 @@ void ImGui::NewFrame() IM_ASSERT(g.FrameCount == 0 || g.FrameCountPlatformEnded == g.FrameCount && "Forgot to call UpdatePlatformWindows() at the end of the previous frame?"); IM_ASSERT(g.PlatformIO.Platform_CreateWindow != NULL && "Platform init didn't install handlers?"); IM_ASSERT(g.PlatformIO.Platform_DestroyWindow != NULL && "Platform init didn't install handlers?"); - IM_ASSERT(g.Viewports[0]->PlatformUserData != NULL && "Platform init didn't setup main viewport."); + IM_ASSERT((g.Viewports[0]->PlatformUserData != NULL || g.Viewports[0]->PlatformHandle != NULL) && "Platform init didn't setup main viewport."); #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS IM_ASSERT(g.IO.RenderDrawListsFn == NULL); // Call ImGui::Render() then pass ImGui::GetDrawData() yourself to your render function! #endif @@ -4059,6 +4060,9 @@ void ImGui::Initialize(ImGuiContext* context) void ImGui::DestroyPlatformWindows() { + // We call the destroy window on the main viewport (index 0) to give a chance to the back-end to clear any data it may hold on it. + // It is expected that the back-end stored a flag to remember that it doesn't own the window created for the main viewport, + // and won't destroy the underlying platform/renderer data. ImGuiContext& g = *GImGui; if (g.PlatformIO.Renderer_DestroyWindow) for (int i = 0; i < g.Viewports.Size; i++) diff --git a/imgui.h b/imgui.h index e23621eb85c7..81d5232572b0 100644 --- a/imgui.h +++ b/imgui.h @@ -549,10 +549,10 @@ namespace ImGui // (Optional) Platform interface for multi-viewport support IMGUI_API ImGuiPlatformIO& GetPlatformIO(); // Platform/Renderer function, for back-end to setup. IMGUI_API ImGuiPlatformData* GetPlatformData(); // List of viewports. Viewport 0 is always the main viewport, followed by the secondary viewports. - IMGUI_API ImGuiViewport* GetMainViewport(); // GetPlatformData()->MainViewport - IMGUI_API void UpdatePlatformWindows(); // Call in main loop. Create/Destroy/Resize platform windows so there's one for each viewport - IMGUI_API void RenderPlatformWindowsDefault(void* platform_render_arg = NULL, void* renderer_render_arg = NULL); // Call in main loop. Call RenderWindow/SwapBuffers from the ImGuiPlatformIO structure. May be reimplemented by user. - IMGUI_API void DestroyPlatformWindows(); // (Optional) Call in back-end shutdown if you need to close Platform Windows before imgui shutdown. + IMGUI_API ImGuiViewport* GetMainViewport(); // == GetPlatformData()->MainViewport == GetPlatformData()->Viewports[0] + IMGUI_API void UpdatePlatformWindows(); // Call in main loop. Will call CreateWindow/ResizeWindow/etc. platform functions for each secondary viewport, and DestroyWindow for each inactive viewport. + IMGUI_API void RenderPlatformWindowsDefault(void* platform_arg = NULL, void* renderer_arg = NULL); // Call in main loop. Will call RenderWindow/SwapBuffers platform functions for each secondary viewport. May be reimplemented by user for custom rendering needs. + IMGUI_API void DestroyPlatformWindows(); // (Optional) Call DestroyWindow platform functions for all viewports. Call from back-end Shutdown() if you need to close platform windows before imgui shutdown. Otherwise will be called by DestroyContext(). IMGUI_API ImGuiViewport* FindViewportByPlatformHandle(void* platform_handle); // Memory Utilities From 74a11e2087cdbce5af6fd84c7e840713c2d1d7df Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 9 Apr 2018 22:15:46 +0200 Subject: [PATCH 107/828] Viewport: Merge fix conflicting flags. --- imgui.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/imgui.h b/imgui.h index 81d5232572b0..ef3abe8b58cf 100644 --- a/imgui.h +++ b/imgui.h @@ -798,14 +798,14 @@ enum ImGuiConfigFlags_ ImGuiConfigFlags_NoMouseCursorChange = 1 << 5, // Instruct back-end to not alter mouse cursor shape and visibility. // [BETA] Viewports - ImGuiConfigFlags_EnableViewports = 1 << 5, // Viewport enable flags (require both ImGuiConfigFlags_PlatformHasViewports + ImGuiConfigFlags_RendererHasViewports set by the respective back-ends) - ImGuiConfigFlags_EnableDpiScaleViewports= 1 << 6, - ImGuiConfigFlags_EnableDpiScaleFonts = 1 << 7, - ImGuiConfigFlags_NoTaskBarForViewports = 1 << 8, + ImGuiConfigFlags_EnableViewports = 1 << 10, // Viewport enable flags (require both ImGuiConfigFlags_PlatformHasViewports + ImGuiConfigFlags_RendererHasViewports set by the respective back-ends) + ImGuiConfigFlags_EnableDpiScaleViewports= 1 << 11, + ImGuiConfigFlags_EnableDpiScaleFonts = 1 << 12, + ImGuiConfigFlags_NoTaskBarForViewports = 1 << 13, // User storage (to allow your back-end/engine to communicate to code that may be shared between multiple projects. Those flags are not used by core ImGui) - ImGuiConfigFlags_IsSRGB = 1 << 20, // Application is SRGB-aware. - ImGuiConfigFlags_IsTouchScreen = 1 << 21 // Application is using a touch screen instead of a mouse. + ImGuiConfigFlags_IsSRGB = 1 << 20, // Application is SRGB-aware. + ImGuiConfigFlags_IsTouchScreen = 1 << 21 // Application is using a touch screen instead of a mouse. }; // Back-end capabilities flags stored in io.BackendFlags. Set by imgui_impl_xxx or custom back-end. From 0eaddb4dcd1b428d2f27e885bf674c9585863b62 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 10 Apr 2018 17:05:20 +0200 Subject: [PATCH 108/828] Viewport: shuffled some code in the UpdateSelectWindowViewport() function. --- imgui.cpp | 63 +++++++++++++++++++++++++------------------------------ 1 file changed, 28 insertions(+), 35 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index abf5f2a3e2c8..3cfb34250a70 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3312,7 +3312,7 @@ static void ImGui::NewFrameUpdateMovingWindowDropViewport(ImGuiWindow* window) } else { - // Create new viewport + // Create/persist new viewport ImVec2 platform_pos = ConvertViewportPosToPlatformPos(window->Pos, window->Viewport); ImGuiViewportP* viewport = Viewport(window, window->ID, 0, platform_pos, window->Size); SetWindowViewportTranslateToPreservePlatformPos(window, window->Viewport, viewport); @@ -6089,10 +6089,32 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) } const bool window_is_mouse_tooltip = (flags & ImGuiWindowFlags_Tooltip) && g.NavDisableHighlight && !g.NavDisableMouseHover; - const bool window_follow_mouse_viewport = (window_is_mouse_tooltip || (g.MovingWindow && g.MovingWindow->RootWindow == window)); + const bool window_follow_mouse_viewport = window_is_mouse_tooltip || (g.MovingWindow && g.MovingWindow->RootWindow == window); bool created_viewport = false; + // Appearing popups reset their viewport so they can inherit again + const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFrames > 0); + if ((flags & ImGuiWindowFlags_Popup) && window_just_appearing_after_hidden_for_resize) + window->Viewport = NULL; + + // By default inherit from parent window + if (window->Viewport == NULL && window->ParentWindow) + window->Viewport = window->ParentWindow->Viewport; + + // Attempt to restore saved viewport id (= window that hasn't been activated yet), try to restore the viewport based on saved 'window->ViewportPlatformPos' from .ini file + if (window->Viewport == NULL && window->ViewportId != 0) + { + window->Viewport = FindViewportByID(window->ViewportId); + if (window->Viewport == NULL && window->ViewportPlatformPos.x != FLT_MAX && window->ViewportPlatformPos.y != FLT_MAX) + { + ImGuiViewportP* viewport = Viewport(window, window->ID, ImGuiViewportFlags_NoDecoration, window->ViewportPlatformPos, window->Size); + window->Flags |= ImGuiWindowFlags_FullViewport; + window->Viewport = viewport; + created_viewport = true; + } + } + if (g.NextWindowData.ViewportCond) { // Code explicitly request a viewport @@ -6106,7 +6128,7 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) else if (window_follow_mouse_viewport && IsMousePosValid()) { ImGuiViewportP* current_viewport = window->Viewport; - if (!window_is_mouse_tooltip && !current_viewport->GetRect().Contains(window->Rect())) + if (!window_is_mouse_tooltip && (current_viewport == NULL || !current_viewport->GetRect().Contains(window->Rect()))) { // Calculate mouse position in OS/platform coordinates, create a Viewport at this position. ImVec2 platform_pos = ConvertViewportPosToPlatformPos(g.IO.MousePos - g.ActiveIdClickOffset, g.MousePosViewport); @@ -6120,8 +6142,8 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) { // When dragging a window back into another, only change viewport on mouse release (in UpdateMovingWindow()). // This is so we don't require of the multi-viewport windowing back-end to preserve mouse buttons after a window closure, making it easier to implement them. - bool preserve_temporary_viewport = g.MovingWindow && g.MovingWindow->RootWindow == window && (window->Viewport->Flags & ImGuiWindowFlags_FullViewport); - if (!preserve_temporary_viewport) + bool preserve_viewport = g.MovingWindow && g.MovingWindow->RootWindow == window && (window->Viewport->Flags & ImGuiWindowFlags_FullViewport); + if (!preserve_viewport) window->Viewport = g.MousePosViewport; } } @@ -6130,35 +6152,6 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) window->Viewport = g.NavWindow->Viewport; } - // Appearing popups reset their viewport so they can inherit again - const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFrames == 1); - if ((flags & ImGuiWindowFlags_Popup) && window_just_appearing_after_hidden_for_resize) - window->Viewport = NULL; - - // By default inherit from parent window - if (window->Viewport == NULL && window->ParentWindow) - window->Viewport = window->ParentWindow->Viewport; - - // Restore a viewport id (= window that hasn't been activated yet), try to restore the viewport based on saved 'window->ViewportPlatformPos' - if (window->Viewport == NULL && window->ViewportId != 0) - { - window->Viewport = FindViewportByID(window->ViewportId); - if (window->Viewport == NULL) - { - if (window->ViewportPlatformPos.x != FLT_MAX && window->ViewportPlatformPos.y != FLT_MAX) - { - ImGuiViewportP* viewport = Viewport(window, window->ID, ImGuiViewportFlags_NoDecoration, window->ViewportPlatformPos, window->Size); - window->Flags |= ImGuiWindowFlags_FullViewport; - window->Viewport = viewport; - created_viewport = true; - } - else - { - window->ViewportId = 0; - } - } - } - // Fallback to default viewport if (window->Viewport == NULL) window->Viewport = main_viewport; @@ -14151,7 +14144,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::BulletText("NavRectRel[0]: (%.1f,%.1f)(%.1f,%.1f)", window->NavRectRel[0].Min.x, window->NavRectRel[0].Min.y, window->NavRectRel[0].Max.x, window->NavRectRel[0].Max.y); else ImGui::BulletText("NavRectRel[0]: "); - ImGui::BulletText("Viewport: %d, ViewportId: 0x%08X", window->Viewport ? window->Viewport->Idx : -1, window->ViewportId); + ImGui::BulletText("Viewport: %d, ViewportId: 0x%08X, ViewportPlatformPos: (%.1f,%.1f)", window->Viewport ? window->Viewport->Idx : -1, window->ViewportId, window->ViewportPlatformPos.x, window->ViewportPlatformPos.y); if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow"); if (window->DC.ChildWindows.Size > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows"); if (window->ColumnsStorage.Size > 0 && ImGui::TreeNode("Columns", "Columns sets (%d)", window->ColumnsStorage.Size)) From 0d5042f0f3ebaf90b0bc01a644f31223f64c61ed Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 10 Apr 2018 19:15:44 +0200 Subject: [PATCH 109/828] Viewport: Popups, Tooltips can individually request no task bar icons to the platform layer. (#1542) --- examples/directx10_example/main.cpp | 2 +- examples/directx11_example/main.cpp | 2 +- examples/imgui_impl_glfw.cpp | 3 +-- examples/imgui_impl_sdl2.cpp | 3 +-- examples/imgui_impl_win32.cpp | 7 +++---- examples/opengl2_example/main.cpp | 2 +- examples/opengl3_example/main.cpp | 2 +- examples/sdl_opengl3_example/main.cpp | 2 +- examples/sdl_vulkan_example/main.cpp | 2 +- examples/vulkan_example/main.cpp | 2 +- imgui.cpp | 7 +++++++ imgui.h | 11 ++++++----- 12 files changed, 25 insertions(+), 20 deletions(-) diff --git a/examples/directx10_example/main.cpp b/examples/directx10_example/main.cpp index 90e87120eae6..9a5160553a78 100644 --- a/examples/directx10_example/main.cpp +++ b/examples/directx10_example/main.cpp @@ -118,7 +118,7 @@ int main(int, char**) ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_EnableViewports; - io.ConfigFlags |= ImGuiConfigFlags_NoTaskBarForViewports; + io.ConfigFlags |= ImGuiConfigFlags_NoTaskBarIconsForViewports; //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls ImGui_ImplWin32_Init(hwnd); diff --git a/examples/directx11_example/main.cpp b/examples/directx11_example/main.cpp index 77cc1640adf0..54985b891da9 100644 --- a/examples/directx11_example/main.cpp +++ b/examples/directx11_example/main.cpp @@ -136,7 +136,7 @@ int main(int, char**) io.ConfigFlags |= ImGuiConfigFlags_EnableViewports; io.ConfigFlags |= ImGuiConfigFlags_EnableDpiScaleFonts; io.ConfigFlags |= ImGuiConfigFlags_EnableDpiScaleViewports; - io.ConfigFlags |= ImGuiConfigFlags_NoTaskBarForViewports; + io.ConfigFlags |= ImGuiConfigFlags_NoTaskBarIconsForViewports; //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls ImGui_ImplWin32_Init(hwnd); diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index f7e16a473357..951be7f97712 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -410,8 +410,7 @@ static void ImGui_ImplGlfw_ShowWindow(ImGuiViewport* viewport) #if defined(_WIN32) // GLFW hack: Hide icon from task bar HWND hwnd = glfwGetWin32Window(data->Window); - ImGuiIO& io = ImGui::GetIO(); - if (io.ConfigFlags & ImGuiConfigFlags_NoTaskBarForViewports) + if (viewport->Flags & ImGuiViewportFlags_NoTaskBarIcon) { LONG ex_style = ::GetWindowLong(hwnd, GWL_EXSTYLE); ex_style &= ~WS_EX_APPWINDOW; diff --git a/examples/imgui_impl_sdl2.cpp b/examples/imgui_impl_sdl2.cpp index e720ca9d96c7..35443fbfd4bd 100644 --- a/examples/imgui_impl_sdl2.cpp +++ b/examples/imgui_impl_sdl2.cpp @@ -353,8 +353,7 @@ static void ImGui_ImplSDL2_ShowWindow(ImGuiViewport* viewport) // SDL hack: Hide icon from task bar // Note: SDL 2.0.6+ has a SDL_WINDOW_SKIP_TASKBAR flag which is supported under Windows but the way it create the window breaks our seamless transition. - ImGuiIO& io = ImGui::GetIO(); - if (io.ConfigFlags & ImGuiConfigFlags_NoTaskBarForViewports) + if (viewport->Flags & ImGuiViewportFlags_NoTaskBarIcon) { LONG ex_style = ::GetWindowLong(hwnd, GWL_EXSTYLE); ex_style &= ~WS_EX_APPWINDOW; diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index 411e5295083d..75ccbb77055e 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -377,18 +377,17 @@ static void ImGui_ImplWin32_CreateWindow(ImGuiViewport* viewport) ImGuiViewportDataWin32* data = IM_NEW(ImGuiViewportDataWin32)(); viewport->PlatformUserData = data; - ImGuiIO& io = ImGui::GetIO(); bool no_decoration = (viewport->Flags & ImGuiViewportFlags_NoDecoration) != 0; - bool no_task_bar = (io.ConfigFlags & ImGuiConfigFlags_NoTaskBarForViewports) != 0; + bool no_task_bar_icon = (viewport->Flags & ImGuiViewportFlags_NoTaskBarIcon) != 0; if (no_decoration) { data->DwStyle = WS_POPUP; - data->DwExStyle = no_task_bar ? WS_EX_TOOLWINDOW : WS_EX_APPWINDOW; + data->DwExStyle = no_task_bar_icon ? WS_EX_TOOLWINDOW : WS_EX_APPWINDOW; } else { data->DwStyle = WS_OVERLAPPEDWINDOW; - data->DwExStyle = no_task_bar ? WS_EX_TOOLWINDOW : WS_EX_APPWINDOW; + data->DwExStyle = no_task_bar_icon ? WS_EX_TOOLWINDOW : WS_EX_APPWINDOW; } // Create window diff --git a/examples/opengl2_example/main.cpp b/examples/opengl2_example/main.cpp index ac4db3a5cb78..991a4d40229a 100644 --- a/examples/opengl2_example/main.cpp +++ b/examples/opengl2_example/main.cpp @@ -31,7 +31,7 @@ int main(int, char**) ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_EnableViewports; - io.ConfigFlags |= ImGuiConfigFlags_NoTaskBarForViewports; + io.ConfigFlags |= ImGuiConfigFlags_NoTaskBarIconsForViewports; //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls ImGui_ImplGlfw_InitForOpenGL(window, true); ImGui_ImplOpenGL2_Init(); diff --git a/examples/opengl3_example/main.cpp b/examples/opengl3_example/main.cpp index b9170c9e0cff..485bca128f22 100644 --- a/examples/opengl3_example/main.cpp +++ b/examples/opengl3_example/main.cpp @@ -36,7 +36,7 @@ int main(int, char**) ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_EnableViewports; - io.ConfigFlags |= ImGuiConfigFlags_NoTaskBarForViewports; + io.ConfigFlags |= ImGuiConfigFlags_NoTaskBarIconsForViewports; //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls diff --git a/examples/sdl_opengl3_example/main.cpp b/examples/sdl_opengl3_example/main.cpp index 366bc222297e..37081b664699 100644 --- a/examples/sdl_opengl3_example/main.cpp +++ b/examples/sdl_opengl3_example/main.cpp @@ -38,7 +38,7 @@ int main(int, char**) ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_EnableViewports; - io.ConfigFlags |= ImGuiConfigFlags_NoTaskBarForViewports; + io.ConfigFlags |= ImGuiConfigFlags_NoTaskBarIconsForViewports; //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls ImGui_ImplSDL2_Init(window, gl_context); diff --git a/examples/sdl_vulkan_example/main.cpp b/examples/sdl_vulkan_example/main.cpp index 53de0fbb8e17..3c128d470cfb 100644 --- a/examples/sdl_vulkan_example/main.cpp +++ b/examples/sdl_vulkan_example/main.cpp @@ -335,7 +335,7 @@ int main(int, char**) ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_EnableViewports; - io.ConfigFlags |= ImGuiConfigFlags_NoTaskBarForViewports; + io.ConfigFlags |= ImGuiConfigFlags_NoTaskBarIconsForViewports; //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls // Setup SDL binding diff --git a/examples/vulkan_example/main.cpp b/examples/vulkan_example/main.cpp index 4ff79e6d4f77..bc1eb5d70971 100644 --- a/examples/vulkan_example/main.cpp +++ b/examples/vulkan_example/main.cpp @@ -347,7 +347,7 @@ int main(int, char**) ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_EnableViewports; - io.ConfigFlags |= ImGuiConfigFlags_NoTaskBarForViewports; + io.ConfigFlags |= ImGuiConfigFlags_NoTaskBarIconsForViewports; //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls // Setup GLFW binding diff --git a/imgui.cpp b/imgui.cpp index 3cfb34250a70..d39976b568f2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3549,6 +3549,13 @@ void ImGui::UpdatePlatformWindows() continue; } + // Update ImGuiViewportFlags_NoTaskBarIcon flag + if (viewport->Window != NULL) + { + bool no_task_bar_icon = (g.IO.ConfigFlags & ImGuiConfigFlags_NoTaskBarIconsForViewports) != 0 || (viewport->Window->Flags & (ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) != 0; + viewport->Flags = no_task_bar_icon ? (viewport->Flags | ImGuiViewportFlags_NoTaskBarIcon) : (viewport->Flags & ~ImGuiViewportFlags_NoTaskBarIcon); + } + bool is_new_window = (viewport->PlatformHandle == NULL && viewport->PlatformUserData == NULL && viewport->RendererUserData == NULL); if (is_new_window && viewport->PlatformHandle == NULL && viewport->PlatformUserData == NULL) g.PlatformIO.Platform_CreateWindow(viewport); diff --git a/imgui.h b/imgui.h index ef3abe8b58cf..bf010d7c5f9d 100644 --- a/imgui.h +++ b/imgui.h @@ -798,10 +798,10 @@ enum ImGuiConfigFlags_ ImGuiConfigFlags_NoMouseCursorChange = 1 << 5, // Instruct back-end to not alter mouse cursor shape and visibility. // [BETA] Viewports - ImGuiConfigFlags_EnableViewports = 1 << 10, // Viewport enable flags (require both ImGuiConfigFlags_PlatformHasViewports + ImGuiConfigFlags_RendererHasViewports set by the respective back-ends) - ImGuiConfigFlags_EnableDpiScaleViewports= 1 << 11, - ImGuiConfigFlags_EnableDpiScaleFonts = 1 << 12, - ImGuiConfigFlags_NoTaskBarForViewports = 1 << 13, + ImGuiConfigFlags_EnableViewports = 1 << 10, // Viewport enable flags (require both ImGuiConfigFlags_PlatformHasViewports + ImGuiConfigFlags_RendererHasViewports set by the respective back-ends) + ImGuiConfigFlags_EnableDpiScaleViewports = 1 << 11, + ImGuiConfigFlags_EnableDpiScaleFonts = 1 << 12, + ImGuiConfigFlags_NoTaskBarIconsForViewports = 1 << 13, // User storage (to allow your back-end/engine to communicate to code that may be shared between multiple projects. Those flags are not used by core ImGui) ImGuiConfigFlags_IsSRGB = 1 << 20, // Application is SRGB-aware. @@ -1924,7 +1924,8 @@ enum ImGuiViewportFlags_ ImGuiViewportFlags_NoDecoration = 1 << 0, // Platform Window: Disable platform title bar, borders, etc. ImGuiViewportFlags_NoFocusOnAppearing = 1 << 1, // Platform Window: Don't take focus when created. ImGuiViewportFlags_NoInputs = 1 << 2, // Platform Window: Make mouse pass through so we can drag this window while peaking behind it. - ImGuiViewportFlags_NoRendererClear = 1 << 3 // Platform Window: Renderer doesn't need to clear the framebuffer ahead. + ImGuiViewportFlags_NoTaskBarIcon = 1 << 3, // Platform Window: Disable platform task bar icon (for popups, menus, or all windows if ImGuiConfigFlags_NoTaskBarIconsForViewports if set) + ImGuiViewportFlags_NoRendererClear = 1 << 4 // Platform Window: Renderer doesn't need to clear the framebuffer ahead. }; // The viewports created and managed by imgui. The role of the platform back-end is to create the platform/OS windows corresponding to each viewport. From 7ddc22b326dfd1c5605879fb13254e97d31614bf Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 10 Apr 2018 19:21:52 +0200 Subject: [PATCH 110/828] Viewports, DPI: Renamed config flags. (#1542) --- examples/directx10_example/main.cpp | 6 +++--- examples/directx11_example/main.cpp | 12 ++++++------ examples/directx12_example/main.cpp | 4 ++-- examples/imgui_impl_dx10.cpp | 4 ++-- examples/imgui_impl_dx10.h | 2 +- examples/imgui_impl_dx11.cpp | 4 ++-- examples/imgui_impl_dx11.h | 2 +- examples/imgui_impl_dx12.cpp | 2 +- examples/imgui_impl_glfw.cpp | 4 ++-- examples/imgui_impl_glfw.h | 2 +- examples/imgui_impl_opengl2.cpp | 4 ++-- examples/imgui_impl_opengl2.h | 2 +- examples/imgui_impl_opengl3.cpp | 4 ++-- examples/imgui_impl_opengl3.h | 2 +- examples/imgui_impl_sdl2.cpp | 4 ++-- examples/imgui_impl_sdl2.h | 2 +- examples/imgui_impl_vulkan.cpp | 6 +++--- examples/imgui_impl_vulkan.h | 2 +- examples/imgui_impl_win32.cpp | 2 +- examples/opengl2_example/main.cpp | 6 +++--- examples/opengl3_example/main.cpp | 6 +++--- examples/sdl_opengl3_example/main.cpp | 6 +++--- examples/sdl_vulkan_example/main.cpp | 6 +++--- examples/vulkan_example/main.cpp | 6 +++--- imgui.cpp | 22 +++++++++++----------- imgui.h | 14 +++++++------- 26 files changed, 68 insertions(+), 68 deletions(-) diff --git a/examples/directx10_example/main.cpp b/examples/directx10_example/main.cpp index 9a5160553a78..ea9093fa2a4e 100644 --- a/examples/directx10_example/main.cpp +++ b/examples/directx10_example/main.cpp @@ -117,8 +117,8 @@ int main(int, char**) // Setup ImGui binding ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - io.ConfigFlags |= ImGuiConfigFlags_EnableViewports; - io.ConfigFlags |= ImGuiConfigFlags_NoTaskBarIconsForViewports; + io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; + io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoTaskBarIcons; //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls ImGui_ImplWin32_Init(hwnd); @@ -209,7 +209,7 @@ int main(int, char**) ImGui_ImplDX10_RenderDrawData(ImGui::GetDrawData()); // Update and Render additional Platform Windows - if (io.ConfigFlags & ImGuiConfigFlags_EnableViewports) + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { ImGui::UpdatePlatformWindows(); ImGui::RenderPlatformWindowsDefault(); diff --git a/examples/directx11_example/main.cpp b/examples/directx11_example/main.cpp index 54985b891da9..a887ea758235 100644 --- a/examples/directx11_example/main.cpp +++ b/examples/directx11_example/main.cpp @@ -97,7 +97,7 @@ LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) PostQuitMessage(0); return 0; case WM_DPICHANGED: - if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_EnableDpiScaleViewports) + if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_DpiEnableScaleViewports) { //const int dpi = HIWORD(wParam); //printf("WM_DPICHANGED to %d (%.0f%%)\n", dpi, (float)dpi / 96.0f * 100.0f); @@ -133,10 +133,10 @@ int main(int, char**) // Setup ImGui binding ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - io.ConfigFlags |= ImGuiConfigFlags_EnableViewports; - io.ConfigFlags |= ImGuiConfigFlags_EnableDpiScaleFonts; - io.ConfigFlags |= ImGuiConfigFlags_EnableDpiScaleViewports; - io.ConfigFlags |= ImGuiConfigFlags_NoTaskBarIconsForViewports; + io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; + io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoTaskBarIcons; + io.ConfigFlags |= ImGuiConfigFlags_DpiEnableScaleFonts; + io.ConfigFlags |= ImGuiConfigFlags_DpiEnableScaleViewports; //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls ImGui_ImplWin32_Init(hwnd); @@ -227,7 +227,7 @@ int main(int, char**) ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData()); // Update and Render additional Platform Windows - if (io.ConfigFlags & ImGuiConfigFlags_EnableViewports) + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { ImGui::UpdatePlatformWindows(); ImGui::RenderPlatformWindowsDefault(); diff --git a/examples/directx12_example/main.cpp b/examples/directx12_example/main.cpp index 01ba3d9746a3..f35a0cca206a 100644 --- a/examples/directx12_example/main.cpp +++ b/examples/directx12_example/main.cpp @@ -289,7 +289,7 @@ int main(int, char**) // Setup ImGui binding ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - io.ConfigFlags |= ImGuiConfigFlags_EnableViewports; + io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls ImGui_ImplWin32_Init(hwnd); @@ -402,7 +402,7 @@ int main(int, char**) g_pd3dCommandQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*)&g_pd3dCommandList); // Update and Render additional Platform Windows - if (io.ConfigFlags & ImGuiConfigFlags_EnableViewports) + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { ImGui::UpdatePlatformWindows(); ImGui::RenderPlatformWindowsDefault(); diff --git a/examples/imgui_impl_dx10.cpp b/examples/imgui_impl_dx10.cpp index de0ed14f9529..4769840390af 100644 --- a/examples/imgui_impl_dx10.cpp +++ b/examples/imgui_impl_dx10.cpp @@ -3,7 +3,7 @@ // Implemented features: // [X] User texture binding. Use 'ID3D10ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. -// [X] Multi-viewport rendering (when ImGuiConfigFlags_EnableViewports is enabled). +// [X] Multi-viewport rendering (when ImGuiConfigFlags_ViewportsEnable is enabled). // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). @@ -484,7 +484,7 @@ bool ImGui_ImplDX10_Init(ID3D10Device* device) // Setup back-end capabilities flags ImGuiIO& io = ImGui::GetIO(); io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional) - if (io.ConfigFlags & ImGuiConfigFlags_EnableViewports) + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) ImGui_ImplDX10_InitPlatformInterface(); return true; } diff --git a/examples/imgui_impl_dx10.h b/examples/imgui_impl_dx10.h index 401fa1ae6704..3deacad5ad7e 100644 --- a/examples/imgui_impl_dx10.h +++ b/examples/imgui_impl_dx10.h @@ -3,7 +3,7 @@ // Implemented features: // [X] User texture binding. Use 'ID3D10ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. -// [X] Multi-viewport rendering (when ImGuiConfigFlags_EnableViewports is enabled). +// [X] Multi-viewport rendering (when ImGuiConfigFlags_ViewportsEnable is enabled). // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). diff --git a/examples/imgui_impl_dx11.cpp b/examples/imgui_impl_dx11.cpp index 1ce2d7fb416b..f8971bc668f1 100644 --- a/examples/imgui_impl_dx11.cpp +++ b/examples/imgui_impl_dx11.cpp @@ -3,7 +3,7 @@ // Implemented features: // [X] User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. -// [X] Multi-viewport rendering (when ImGuiConfigFlags_EnableViewports is enabled). +// [X] Multi-viewport rendering (when ImGuiConfigFlags_ViewportsEnable is enabled). // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). @@ -491,7 +491,7 @@ bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_co // Setup back-end capabilities flags ImGuiIO& io = ImGui::GetIO(); io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional) - if (io.ConfigFlags & ImGuiConfigFlags_EnableViewports) + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) ImGui_ImplDX11_InitPlatformInterface(); return true; } diff --git a/examples/imgui_impl_dx11.h b/examples/imgui_impl_dx11.h index 6d75b237259f..7444570e31cb 100644 --- a/examples/imgui_impl_dx11.h +++ b/examples/imgui_impl_dx11.h @@ -3,7 +3,7 @@ // Implemented features: // [X] User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. -// [X] Multi-viewport rendering (when ImGuiConfigFlags_EnableViewports is enabled). +// [X] Multi-viewport rendering (when ImGuiConfigFlags_ViewportsEnable is enabled). // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). diff --git a/examples/imgui_impl_dx12.cpp b/examples/imgui_impl_dx12.cpp index 00dfb5fa2214..1aa0845b29bd 100644 --- a/examples/imgui_impl_dx12.cpp +++ b/examples/imgui_impl_dx12.cpp @@ -608,7 +608,7 @@ bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FO // FIXME-VIEWPORT: Actually unfinshed.. ImGuiIO& io = ImGui::GetIO(); io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional) - if (io.ConfigFlags & ImGuiConfigFlags_EnableViewports) + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) ImGui_ImplDX12_InitPlatformInterface(); return true; diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index 951be7f97712..0241542bfaf9 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -4,7 +4,7 @@ // Implemented features: // [X] Gamepad navigation mapping. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. -// [X] Multi-viewport windows (when ImGuiConfigFlags_EnableViewports is enabled). +// [X] Multi-viewport windows (when ImGuiConfigFlags_ViewportsEnable is enabled). // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). @@ -170,7 +170,7 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw // Our mouse update function expect PlatformHandle to be filled for the main viewport ImGuiViewport* main_viewport = ImGui::GetMainViewport(); main_viewport->PlatformHandle = (void*)g_Window; - if (io.ConfigFlags & ImGuiConfigFlags_EnableViewports) + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) ImGui_ImplGlfw_InitPlatformInterface(); g_ClientApi = client_api; diff --git a/examples/imgui_impl_glfw.h b/examples/imgui_impl_glfw.h index 4c0cc39c66e4..4375fa60a0cf 100644 --- a/examples/imgui_impl_glfw.h +++ b/examples/imgui_impl_glfw.h @@ -4,7 +4,7 @@ // Implemented features: // [X] Gamepad navigation mapping. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. -// [X] Multi-viewport windows (when ImGuiConfigFlags_EnableViewports is enabled). +// [X] Multi-viewport windows (when ImGuiConfigFlags_ViewportsEnable is enabled). // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). diff --git a/examples/imgui_impl_opengl2.cpp b/examples/imgui_impl_opengl2.cpp index 6fa19d5b3713..f4d848a8abbd 100644 --- a/examples/imgui_impl_opengl2.cpp +++ b/examples/imgui_impl_opengl2.cpp @@ -3,7 +3,7 @@ // Implemented features: // [X] User texture binding. Use 'GLUint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. -// [X] Multi-viewport rendering (when ImGuiConfigFlags_EnableViewports is enabled). +// [X] Multi-viewport rendering (when ImGuiConfigFlags_ViewportsEnable is enabled). // **DO NOT USE THIS CODE IF YOUR CODE/ENGINE IS USING MODERN OPENGL (SHADERS, VBO, VAO, etc.)** // **Prefer using the code in imgui_impl_opengl3.cpp** @@ -51,7 +51,7 @@ bool ImGui_ImplOpenGL2_Init() // Setup back-end capabilities flags ImGuiIO& io = ImGui::GetIO(); io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional) - if (io.ConfigFlags & ImGuiConfigFlags_EnableViewports) + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) ImGui_ImplOpenGL2_InitPlatformInterface(); return true; } diff --git a/examples/imgui_impl_opengl2.h b/examples/imgui_impl_opengl2.h index 12e5cd2be644..cae02b8d918b 100644 --- a/examples/imgui_impl_opengl2.h +++ b/examples/imgui_impl_opengl2.h @@ -3,7 +3,7 @@ // Implemented features: // [X] User texture binding. Use 'GLUint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. -// [X] Multi-viewport rendering (when ImGuiConfigFlags_EnableViewports is enabled). +// [X] Multi-viewport rendering (when ImGuiConfigFlags_ViewportsEnable is enabled). // **DO NOT USE THIS CODE IF YOUR CODE/ENGINE IS USING MODERN OPENGL (SHADERS, VBO, VAO, etc.)** // **Prefer using the code in imgui_impl_opengl3.cpp** diff --git a/examples/imgui_impl_opengl3.cpp b/examples/imgui_impl_opengl3.cpp index b6410900c938..99012c83e58a 100644 --- a/examples/imgui_impl_opengl3.cpp +++ b/examples/imgui_impl_opengl3.cpp @@ -4,7 +4,7 @@ // Implemented features: // [X] User texture binding. Use 'GLUint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. -// [X] Multi-viewport rendering (when ImGuiConfigFlags_EnableViewports is enabled). +// [X] Multi-viewport rendering (when ImGuiConfigFlags_ViewportsEnable is enabled). // CHANGELOG // (minor and older changes stripped away, please see git history for details) @@ -53,7 +53,7 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version) // Setup back-end capabilities flags ImGuiIO& io = ImGui::GetIO(); io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional) - if (io.ConfigFlags & ImGuiConfigFlags_EnableViewports) + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) ImGui_ImplOpenGL3_InitPlatformInterface(); return true; } diff --git a/examples/imgui_impl_opengl3.h b/examples/imgui_impl_opengl3.h index 5d6afde64aca..c90b62bab9f4 100644 --- a/examples/imgui_impl_opengl3.h +++ b/examples/imgui_impl_opengl3.h @@ -4,7 +4,7 @@ // Implemented features: // [X] User texture binding. Use 'GLUint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. -// [X] Multi-viewport rendering (when ImGuiConfigFlags_EnableViewports is enabled). +// [X] Multi-viewport rendering (when ImGuiConfigFlags_ViewportsEnable is enabled). IMGUI_API bool ImGui_ImplOpenGL3_Init(const char* glsl_version = NULL); IMGUI_API void ImGui_ImplOpenGL3_Shutdown(); diff --git a/examples/imgui_impl_sdl2.cpp b/examples/imgui_impl_sdl2.cpp index 35443fbfd4bd..c1e0c8eaaa95 100644 --- a/examples/imgui_impl_sdl2.cpp +++ b/examples/imgui_impl_sdl2.cpp @@ -3,7 +3,7 @@ // (Info: SDL2 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.) // Implemented features: -// [X] Multi-viewport windows (when ImGuiConfigFlags_EnableViewports is enabled). +// [X] Multi-viewport windows (when ImGuiConfigFlags_ViewportsEnable is enabled). // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). @@ -178,7 +178,7 @@ bool ImGui_ImplSDL2_Init(SDL_Window* window, void* sdl_gl_context) // We need SDL_CaptureMouse(), SDL_GetGlobalMouseState() from SDL 2.0.4+ to support multiple viewports. // We left the call to ImGui_ImplSDL2_InitPlatformInterface() outside of #ifdef to avoid unused-function warnings. - if ((io.ConfigFlags & ImGuiConfigFlags_EnableViewports) && (io.BackendFlags & ImGuiBackendFlags_PlatformHasViewports)) + if ((io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) && (io.BackendFlags & ImGuiBackendFlags_PlatformHasViewports)) ImGui_ImplSDL2_InitPlatformInterface(window, sdl_gl_context); return true; diff --git a/examples/imgui_impl_sdl2.h b/examples/imgui_impl_sdl2.h index 171ee374e72c..eb217a9c2e08 100644 --- a/examples/imgui_impl_sdl2.h +++ b/examples/imgui_impl_sdl2.h @@ -3,7 +3,7 @@ // (Info: SDL2 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.) // Implemented features: -// [X] Multi-viewport windows (when ImGuiConfigFlags_EnableViewports is enabled). +// [X] Multi-viewport windows (when ImGuiConfigFlags_ViewportsEnable is enabled). // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). diff --git a/examples/imgui_impl_vulkan.cpp b/examples/imgui_impl_vulkan.cpp index 0dec6e89df28..4824b0b1cb23 100644 --- a/examples/imgui_impl_vulkan.cpp +++ b/examples/imgui_impl_vulkan.cpp @@ -3,7 +3,7 @@ // Missing features: // [ ] User texture binding. Changes of ImTextureID aren't supported by this binding! See https://github.com/ocornut/imgui/pull/914 -// [ ] Multi-viewport rendering (when ImGuiConfigFlags_EnableViewports is enabled). WORK-IN-PROGRESS. +// [ ] Multi-viewport rendering (when ImGuiConfigFlags_ViewportsEnable is enabled). WORK-IN-PROGRESS. // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you use this binding you'll need to call 5 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXX_CreateFontsTexture(), ImGui_ImplXXXX_NewFrame(), ImGui_ImplXXXX_Render() and ImGui_ImplXXXX_Shutdown(). @@ -718,7 +718,7 @@ bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info, VkRenderPass rend // Setup back-end capabilities flags ImGuiIO& io = ImGui::GetIO(); io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional) - if (io.ConfigFlags & ImGuiConfigFlags_EnableViewports) + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) ImGui_ImplVulkan_InitPlatformInterface(); return true; @@ -1221,7 +1221,7 @@ static void ImGui_ImplVulkan_SwapBuffers(ImGuiViewport* viewport, void*) void ImGui_ImplVulkan_InitPlatformInterface() { ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); - if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_EnableViewports) + if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) IM_ASSERT(platform_io.Platform_CreateVkSurface != NULL && "Platform needs to setup the CreateVkSurface handler."); platform_io.Renderer_CreateWindow = ImGui_ImplVulkan_CreateWindow; platform_io.Renderer_DestroyWindow = ImGui_ImplVulkan_DestroyWindow; diff --git a/examples/imgui_impl_vulkan.h b/examples/imgui_impl_vulkan.h index 7a89d9f2ee45..b6df028265c1 100644 --- a/examples/imgui_impl_vulkan.h +++ b/examples/imgui_impl_vulkan.h @@ -3,7 +3,7 @@ // Missing features: // [ ] User texture binding. Changes of ImTextureID aren't supported by this binding! See https://github.com/ocornut/imgui/pull/914 -// [ ] Multi-viewport rendering (when ImGuiConfigFlags_EnableViewports is enabled). WORK-IN-PROGRESS. +// [ ] Multi-viewport rendering (when ImGuiConfigFlags_ViewportsEnable is enabled). WORK-IN-PROGRESS. // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you use this binding you'll need to call 5 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXX_CreateFontsTexture(), ImGui_ImplXXXX_NewFrame(), ImGui_ImplXXXX_Render() and ImGui_ImplXXXX_Shutdown(). diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index 75ccbb77055e..a6b6a1bfbcc9 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -50,7 +50,7 @@ bool ImGui_ImplWin32_Init(void* hwnd) g_hWnd = (HWND)hwnd; ImGuiViewport* main_viewport = ImGui::GetMainViewport(); main_viewport->PlatformHandle = (void*)g_hWnd; - if (io.ConfigFlags & ImGuiConfigFlags_EnableViewports) + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) ImGui_ImplWin32_InitPlatformInterface(); // Keyboard mapping. ImGui will use those indices to peek into the io.KeyDown[] array that we will update during the application lifetime. diff --git a/examples/opengl2_example/main.cpp b/examples/opengl2_example/main.cpp index 991a4d40229a..2c67b0b85876 100644 --- a/examples/opengl2_example/main.cpp +++ b/examples/opengl2_example/main.cpp @@ -30,8 +30,8 @@ int main(int, char**) // Setup ImGui binding ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - io.ConfigFlags |= ImGuiConfigFlags_EnableViewports; - io.ConfigFlags |= ImGuiConfigFlags_NoTaskBarIconsForViewports; + io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; + io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoTaskBarIcons; //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls ImGui_ImplGlfw_InitForOpenGL(window, true); ImGui_ImplOpenGL2_Init(); @@ -118,7 +118,7 @@ int main(int, char**) ImGui_ImplOpenGL2_RenderDrawData(ImGui::GetDrawData()); // Update and Render additional Platform Windows - if (io.ConfigFlags & ImGuiConfigFlags_EnableViewports) + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { ImGui::UpdatePlatformWindows(); ImGui::RenderPlatformWindowsDefault(); diff --git a/examples/opengl3_example/main.cpp b/examples/opengl3_example/main.cpp index 485bca128f22..6ae71d4f87ab 100644 --- a/examples/opengl3_example/main.cpp +++ b/examples/opengl3_example/main.cpp @@ -35,8 +35,8 @@ int main(int, char**) // Setup ImGui binding ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - io.ConfigFlags |= ImGuiConfigFlags_EnableViewports; - io.ConfigFlags |= ImGuiConfigFlags_NoTaskBarIconsForViewports; + io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; + io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoTaskBarIcons; //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls @@ -125,7 +125,7 @@ int main(int, char**) ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); // Update and Render additional Platform Windows - if (io.ConfigFlags & ImGuiConfigFlags_EnableViewports) + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { ImGui::UpdatePlatformWindows(); ImGui::RenderPlatformWindowsDefault(); diff --git a/examples/sdl_opengl3_example/main.cpp b/examples/sdl_opengl3_example/main.cpp index 37081b664699..88557fe916d3 100644 --- a/examples/sdl_opengl3_example/main.cpp +++ b/examples/sdl_opengl3_example/main.cpp @@ -37,8 +37,8 @@ int main(int, char**) // Setup ImGui binding ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - io.ConfigFlags |= ImGuiConfigFlags_EnableViewports; - io.ConfigFlags |= ImGuiConfigFlags_NoTaskBarIconsForViewports; + io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; + io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoTaskBarIcons; //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls ImGui_ImplSDL2_Init(window, gl_context); @@ -133,7 +133,7 @@ int main(int, char**) ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); // Update and Render additional Platform Windows - if (io.ConfigFlags & ImGuiConfigFlags_EnableViewports) + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { ImGui::UpdatePlatformWindows(); ImGui::RenderPlatformWindowsDefault(); diff --git a/examples/sdl_vulkan_example/main.cpp b/examples/sdl_vulkan_example/main.cpp index 3c128d470cfb..f22c1612c034 100644 --- a/examples/sdl_vulkan_example/main.cpp +++ b/examples/sdl_vulkan_example/main.cpp @@ -334,8 +334,8 @@ int main(int, char**) // Setup ImGui binding ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - io.ConfigFlags |= ImGuiConfigFlags_EnableViewports; - io.ConfigFlags |= ImGuiConfigFlags_NoTaskBarIconsForViewports; + io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; + io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoTaskBarIcons; //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls // Setup SDL binding @@ -470,7 +470,7 @@ int main(int, char**) FrameRender(wd); // Update and Render additional Platform Windows - if (io.ConfigFlags & ImGuiConfigFlags_EnableViewports) + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { ImGui::UpdatePlatformWindows(); ImGui::RenderPlatformWindowsDefault(); diff --git a/examples/vulkan_example/main.cpp b/examples/vulkan_example/main.cpp index bc1eb5d70971..07efda978b1a 100644 --- a/examples/vulkan_example/main.cpp +++ b/examples/vulkan_example/main.cpp @@ -346,8 +346,8 @@ int main(int, char**) // Setup ImGui binding ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - io.ConfigFlags |= ImGuiConfigFlags_EnableViewports; - io.ConfigFlags |= ImGuiConfigFlags_NoTaskBarIconsForViewports; + io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; + io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoTaskBarIcons; //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls // Setup GLFW binding @@ -480,7 +480,7 @@ int main(int, char**) FrameRender(wd); // Update and Render additional Platform Windows - if (io.ConfigFlags & ImGuiConfigFlags_EnableViewports) + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { ImGui::UpdatePlatformWindows(); ImGui::RenderPlatformWindowsDefault(); diff --git a/imgui.cpp b/imgui.cpp index d39976b568f2..73f5b0035294 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3291,7 +3291,7 @@ static void ImGui::NewFrameUpdateMovingWindowDropViewport(ImGuiWindow* window) // On release we either drop window over an existing viewport or create a new one // (We convert position from one viewport space to another, which is unnecessary at the moment but allows us to have viewport overlapping in term of imgui position) ImGuiContext& g = *GImGui; - if (!(g.IO.ConfigFlags & ImGuiConfigFlags_EnableViewports)) + if (!(g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) return; ImRect mouse_viewport_rect = g.MousePosViewport->GetRect(); @@ -3441,7 +3441,7 @@ static void ImGui::UpdateViewports() if (viewport->DpiScale != 0.0f && new_dpi_scale != viewport->DpiScale) { float scale_factor = new_dpi_scale / viewport->DpiScale; - if (g.IO.ConfigFlags & ImGuiConfigFlags_EnableDpiScaleViewports) + if (g.IO.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleViewports) ScaleWindowsInViewport(viewport, scale_factor); //if (viewport == GetMainViewport()) // g.PlatformInterface.SetWindowSize(viewport, viewport->Size * scale_factor); @@ -3457,11 +3457,11 @@ static void ImGui::UpdateViewports() ImGuiViewportP* main_viewport = g.Viewports[0]; IM_ASSERT(main_viewport->ID == IMGUI_VIEWPORT_DEFAULT_ID); ImVec2 main_viewport_platform_pos = ImVec2(0.0f, 0.0f); - if ((g.IO.ConfigFlags & ImGuiConfigFlags_EnableViewports)) + if ((g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) main_viewport_platform_pos = g.PlatformIO.Platform_GetWindowPos(main_viewport); Viewport(NULL, IMGUI_VIEWPORT_DEFAULT_ID, ImGuiViewportFlags_CanHostOtherWindows, main_viewport_platform_pos, g.IO.DisplaySize); - if (!(g.IO.ConfigFlags & ImGuiConfigFlags_EnableViewports)) + if (!(g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) { g.MousePosViewport = g.MousePosPrevViewport = main_viewport; return; @@ -3527,7 +3527,7 @@ void ImGui::UpdatePlatformWindows() IM_ASSERT(g.FrameCountEnded == g.FrameCount && "Forgot to call Render() or EndFrame() before UpdatePlatformWindows()?"); IM_ASSERT(g.FrameCountPlatformEnded < g.FrameCount); g.FrameCountPlatformEnded = g.FrameCount; - if (!(g.IO.ConfigFlags & ImGuiConfigFlags_EnableViewports)) + if (!(g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) return; // Create/resize/destroy platform windows to match each active viewport. Update the user-facing list. @@ -3552,7 +3552,7 @@ void ImGui::UpdatePlatformWindows() // Update ImGuiViewportFlags_NoTaskBarIcon flag if (viewport->Window != NULL) { - bool no_task_bar_icon = (g.IO.ConfigFlags & ImGuiConfigFlags_NoTaskBarIconsForViewports) != 0 || (viewport->Window->Flags & (ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) != 0; + bool no_task_bar_icon = (g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsNoTaskBarIcons) != 0 || (viewport->Window->Flags & (ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) != 0; viewport->Flags = no_task_bar_icon ? (viewport->Flags | ImGuiViewportFlags_NoTaskBarIcon) : (viewport->Flags & ~ImGuiViewportFlags_NoTaskBarIcon); } @@ -3619,7 +3619,7 @@ void ImGui::UpdatePlatformWindows() // void ImGui::RenderPlatformWindowsDefault(void* platform_render_arg, void* renderer_render_arg) { - if (!(ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_EnableViewports)) + if (!(ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) return; // Skip the main viewport (index 0), which is always fully handled by the application! @@ -3780,7 +3780,7 @@ void ImGui::NewFrame() IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation."); // Perform simple checks for multi-viewport and platform windows support - if (g.IO.ConfigFlags & ImGuiConfigFlags_EnableViewports) + if (g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { if ((g.IO.BackendFlags & ImGuiBackendFlags_PlatformHasViewports) && (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasViewports)) { @@ -3795,7 +3795,7 @@ void ImGui::NewFrame() else { // Disable feature, our back-ends do not support it - g.IO.ConfigFlags &= ~ImGuiConfigFlags_EnableViewports; + g.IO.ConfigFlags &= ~ImGuiConfigFlags_ViewportsEnable; } } @@ -6088,7 +6088,7 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) // Restore main viewport if multi-viewport is not supported by the back-end ImGuiViewportP* main_viewport = g.Viewports[0]; - if (!(g.IO.ConfigFlags & ImGuiConfigFlags_EnableViewports)) + if (!(g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) { window->Viewport = main_viewport; window->ViewportId = main_viewport->ID; @@ -6475,7 +6475,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) UpdateSelectWindowViewport(window); SetCurrentViewport(window->Viewport); - window->FontDpiScale = (g.IO.ConfigFlags & ImGuiConfigFlags_EnableDpiScaleFonts) ? window->Viewport->DpiScale : 1.0f; + window->FontDpiScale = (g.IO.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleFonts) ? window->Viewport->DpiScale : 1.0f; SetCurrentWindow(window); flags = window->Flags; diff --git a/imgui.h b/imgui.h index bf010d7c5f9d..437f6d8dac9b 100644 --- a/imgui.h +++ b/imgui.h @@ -798,10 +798,10 @@ enum ImGuiConfigFlags_ ImGuiConfigFlags_NoMouseCursorChange = 1 << 5, // Instruct back-end to not alter mouse cursor shape and visibility. // [BETA] Viewports - ImGuiConfigFlags_EnableViewports = 1 << 10, // Viewport enable flags (require both ImGuiConfigFlags_PlatformHasViewports + ImGuiConfigFlags_RendererHasViewports set by the respective back-ends) - ImGuiConfigFlags_EnableDpiScaleViewports = 1 << 11, - ImGuiConfigFlags_EnableDpiScaleFonts = 1 << 12, - ImGuiConfigFlags_NoTaskBarIconsForViewports = 1 << 13, + ImGuiConfigFlags_ViewportsEnable = 1 << 10, // Viewport enable flags (require both ImGuiConfigFlags_PlatformHasViewports + ImGuiConfigFlags_RendererHasViewports set by the respective back-ends) + ImGuiConfigFlags_ViewportsNoTaskBarIcons = 1 << 11, // Disable task bars icons for all secondary viewports (will set ImGuiViewportFlags_NoTaskBarIcon on them) + ImGuiConfigFlags_DpiEnableScaleViewports = 1 << 12, + ImGuiConfigFlags_DpiEnableScaleFonts = 1 << 13, // User storage (to allow your back-end/engine to communicate to code that may be shared between multiple projects. Those flags are not used by core ImGui) ImGuiConfigFlags_IsSRGB = 1 << 20, // Application is SRGB-aware. @@ -1875,8 +1875,8 @@ struct ImFont // - if you are new to dear imgui and trying to integrate it into your engine, you should probably ignore this for now. //----------------------------------------------------------------------------- -// (Optional) Setup required only if (io.ConfigFlags & ImGuiConfigFlags_EnableViewports) is enabled -// This is designed so we can mix and match two imgui_impl_xxxx files, one for the Platform (~ Windowing), one for Renderer. +// (Optional) Setup required only if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) is enabled +// This is designed so we can mix and match two imgui_impl_xxxx files, one for the Platform (~window handling), one for Renderer. // Custom engine back-ends will often provide both Platform and Renderer interfaces and thus may not need to use all functions. // Platform functions are typically called before their Renderer counterpart, apart from Destroy which are called the other way. // RenderPlatformWindowsDefault() basically iterate secondary viewports and call Platform+Renderer's RenderWindow then Platform+Renderer's SwapBuffers, @@ -1924,7 +1924,7 @@ enum ImGuiViewportFlags_ ImGuiViewportFlags_NoDecoration = 1 << 0, // Platform Window: Disable platform title bar, borders, etc. ImGuiViewportFlags_NoFocusOnAppearing = 1 << 1, // Platform Window: Don't take focus when created. ImGuiViewportFlags_NoInputs = 1 << 2, // Platform Window: Make mouse pass through so we can drag this window while peaking behind it. - ImGuiViewportFlags_NoTaskBarIcon = 1 << 3, // Platform Window: Disable platform task bar icon (for popups, menus, or all windows if ImGuiConfigFlags_NoTaskBarIconsForViewports if set) + ImGuiViewportFlags_NoTaskBarIcon = 1 << 3, // Platform Window: Disable platform task bar icon (for popups, menus, or all windows if ImGuiConfigFlags_ViewportsNoTaskBarIcons if set) ImGuiViewportFlags_NoRendererClear = 1 << 4 // Platform Window: Renderer doesn't need to clear the framebuffer ahead. }; From 32ee0a3947fe35e06eba64e213830b03e6f53520 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 10 Apr 2018 22:32:08 +0200 Subject: [PATCH 111/828] Viewport, Platform: Added a way to register monitor bounds to imgui so they can be used to clamp individual-viewport tooltips/popups so they don't straddle monitors. (#1542) --- examples/imgui_impl_glfw.cpp | 19 +++++++++++++++++++ examples/imgui_impl_sdl2.cpp | 17 +++++++++++++++++ examples/imgui_impl_win32.cpp | 18 ++++++++++++++++++ imgui.cpp | 10 ++++++++++ imgui.h | 21 +++++++++++++++------ 5 files changed, 79 insertions(+), 6 deletions(-) diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index 0241542bfaf9..4750341fa8f3 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -519,6 +519,23 @@ static int ImGui_ImplGlfw_CreateVkSurface(ImGuiViewport* viewport, ImU64 vk_inst } #endif // GLFW_HAS_VULKAN +// FIXME-PLATFORM: Update when changed (using glfwSetMonitorCallback?) +static void ImGui_ImplGlfw_UpdateMonitors() +{ + ImGuiPlatformData* platform_data = ImGui::GetPlatformData(); + int monitors_count = 0; + GLFWmonitor** glfw_monitors = glfwGetMonitors(&monitors_count); + platform_data->Monitors.resize(monitors_count); + for (int n = 0; n < monitors_count; n++) + { + int x, y; + glfwGetMonitorPos(glfw_monitors[n], &x, &y); + const GLFWvidmode* vid_mode = glfwGetVideoMode(glfw_monitors[n]); + platform_data->Monitors[n].Pos = ImVec2((float)x, (float)y); + platform_data->Monitors[n].Size = ImVec2((float)vid_mode->width, (float)vid_mode->height); + } +} + static void ImGui_ImplGlfw_InitPlatformInterface() { // Register platform interface (will be coupled with a renderer interface) @@ -540,6 +557,8 @@ static void ImGui_ImplGlfw_InitPlatformInterface() platform_io.Platform_CreateVkSurface = ImGui_ImplGlfw_CreateVkSurface; #endif + ImGui_ImplGlfw_UpdateMonitors(); + // Register main window handle (which is owned by the main application, not by us) ImGuiViewport* main_viewport = ImGui::GetMainViewport(); ImGuiViewportDataGlfw* data = IM_NEW(ImGuiViewportDataGlfw)(); diff --git a/examples/imgui_impl_sdl2.cpp b/examples/imgui_impl_sdl2.cpp index c1e0c8eaaa95..ae49a39a8d24 100644 --- a/examples/imgui_impl_sdl2.cpp +++ b/examples/imgui_impl_sdl2.cpp @@ -437,6 +437,21 @@ static int ImGui_ImplSDL2_CreateVkSurface(ImGuiViewport* viewport, ImU64 vk_inst } #endif // SDL_HAS_VULKAN +// FIXME-PLATFORM: Update when changed? +static void ImGui_ImplSDL2_UpdateMonitors() +{ + ImGuiPlatformData* platform_data = ImGui::GetPlatformData(); + int display_count = SDL_GetNumVideoDisplays(); + platform_data->Monitors.resize(display_count); + for (int n = 0; n < display_count; n++) + { + SDL_Rect r; + SDL_GetDisplayBounds(n, &r); + platform_data->Monitors[n].Pos = ImVec2((float)r.x, (float)r.y); + platform_data->Monitors[n].Size = ImVec2((float)r.w, (float)r.h); + } +} + static void ImGui_ImplSDL2_InitPlatformInterface(SDL_Window* window, void* sdl_gl_context) { // Register platform interface (will be coupled with a renderer interface) @@ -455,6 +470,8 @@ static void ImGui_ImplSDL2_InitPlatformInterface(SDL_Window* window, void* sdl_g platform_io.Platform_CreateVkSurface = ImGui_ImplSDL2_CreateVkSurface; #endif + ImGui_ImplSDL2_UpdateMonitors(); + // Register main window handle (which is owned by the main application, not by us) ImGuiViewport* main_viewport = ImGui::GetMainViewport(); ImGuiViewportDataSDL2* data = IM_NEW(ImGuiViewportDataSDL2)(); diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index a6b6a1bfbcc9..6be55ced607e 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -535,6 +535,22 @@ static LRESULT CALLBACK ImGui_ImplWin32_WndProcHandler_PlatformWindow(HWND hWnd, return DefWindowProc(hWnd, msg, wParam, lParam); } +static BOOL CALLBACK ImGui_ImplWin32_UpdateMonitors_EnumFunc(HMONITOR, HDC, LPRECT rect, LPARAM) +{ + ImGuiPlatformMonitor imgui_monitor; + imgui_monitor.Pos = ImVec2((float)rect->left, (float)rect->top); + imgui_monitor.Size = ImVec2((float)(rect->right - rect->left), (float)(rect->bottom - rect->top)); + ImGui::GetPlatformData()->Monitors.push_back(imgui_monitor); + return TRUE; +} + +// FIXME-PLATFORM: Update list when changed (WM_DISPLAYCHANGE?) +static void ImGui_ImplWin32_UpdateMonitors() +{ + ImGui::GetPlatformData()->Monitors.resize(0); + ::EnumDisplayMonitors(NULL, NULL, ImGui_ImplWin32_UpdateMonitors_EnumFunc, NULL); +} + static void ImGui_ImplWin32_InitPlatformInterface() { WNDCLASSEX wcex; @@ -552,6 +568,8 @@ static void ImGui_ImplWin32_InitPlatformInterface() wcex.hIconSm = NULL; ::RegisterClassEx(&wcex); + ImGui_ImplWin32_UpdateMonitors(); + // Register platform interface (will be coupled with a renderer interface) ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); platform_io.Platform_CreateWindow = ImGui_ImplWin32_CreateWindow; diff --git a/imgui.cpp b/imgui.cpp index 73f5b0035294..99167347d7e1 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -14182,6 +14182,16 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::Indent(ImGui::GetTreeNodeToLabelSpacing()); ImGui::ShowViewportThumbnails(); ImGui::Unindent(ImGui::GetTreeNodeToLabelSpacing()); + if (g.PlatformData.Monitors.Size > 0 && ImGui::TreeNode("Monitors", "Monitors (%d)", g.PlatformData.Monitors.Size)) + { + ImGui::TextWrapped("(When viewports are enabled, imgui optionally uses monitor data to position popup/tooltips so they don't straddle monitors.)"); + for (int i = 0; i < g.PlatformData.Monitors.Size; i++) + { + const ImGuiPlatformMonitor& mon = g.PlatformData.Monitors[i]; + ImGui::BulletText("Monitor #%d: Min (%.0f,%.0f) Max (%.0f,%.0f) Size (%.0f,%.0f)", i, mon.Pos.x, mon.Pos.y, mon.Pos.x + mon.Size.x, mon.Pos.y + mon.Size.y, mon.Size.x, mon.Size.y); + } + ImGui::TreePop(); + } for (int i = 0; i < g.Viewports.Size; i++) { ImGuiViewportP* viewport = g.Viewports[i]; diff --git a/imgui.h b/imgui.h index 437f6d8dac9b..ce13fcbbb906 100644 --- a/imgui.h +++ b/imgui.h @@ -74,7 +74,7 @@ struct ImGuiListClipper; // Helper to manually clip large list of ite struct ImGuiPayload; // User data payload for drag and drop operations struct ImGuiViewport; // Viewport (generally ~1 per window to output to at the OS level. Need per-platform support to use multiple viewports) struct ImGuiPlatformIO; // Multi-viewport support: interface for Platform/Renderer back-ends -struct ImGuiPlatformData; // Multi-viewport support: list of viewports to render +struct ImGuiPlatformData; // Multi-viewport support: list of viewports to render + list of monitors provided by back-end. struct ImGuiContext; // ImGui context (opaque) #ifndef ImTextureID @@ -1907,13 +1907,22 @@ struct ImGuiPlatformIO void (*Renderer_SwapBuffers)(ImGuiViewport* vp, void* render_arg); // (Optional) Call Present/SwapBuffers (renderer side) }; -// List of viewports to render as platform window (updated by ImGui::UpdatePlatformWindows) +struct ImGuiPlatformMonitor +{ + ImVec2 Pos; + ImVec2 Size; +}; + +// List of viewports to render as platform window, updated by ImGui::UpdatePlatformWindows() +// FIXME: Merge into ImGuiPlatformIO struct ImGuiPlatformData { - // Viewports[0] is guaranteed to be _always_ the same as MainViewport. Following it are the secondary viewports. - // The main viewport is included in the list because it is more convenient for looping code. - ImGuiViewport* MainViewport; - ImVector Viewports; + // Viewports (written by: imgui, used by: app/back-end to turn into displayable platform windows) + ImGuiViewport* MainViewport; // Guaranteed to be == Viewports[0] + ImVector Viewports; // Main viewports, followed by all secondary viewports. + + // Monitors (written by: app/back-end, used by: imgui to clamp popups/tooltips within same monitor and not have them straddle monitors) + ImVector Monitors; ImGuiPlatformData() { MainViewport = NULL; } }; From 83bd3595a42821ca6b0048d181a2b21f00c53d1a Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 10 Apr 2018 22:58:39 +0200 Subject: [PATCH 112/828] Viewport: Merged ImGuiPlatformData into ImGuiPlatformIO. Comments. (#1542) --- examples/imgui_impl_glfw.cpp | 14 ++++---- examples/imgui_impl_sdl2.cpp | 8 ++--- examples/imgui_impl_win32.cpp | 6 ++-- imgui.cpp | 40 ++++++++++------------- imgui.h | 61 ++++++++++++++++++----------------- imgui_internal.h | 2 -- 6 files changed, 63 insertions(+), 68 deletions(-) diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index 4750341fa8f3..4d54f5019695 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -220,10 +220,10 @@ static void ImGui_ImplGlfw_UpdateMouse() g_MouseJustPressed[i] = false; } - ImGuiPlatformData* platform_data = ImGui::GetPlatformData(); - for (int n = 0; n < platform_data->Viewports.Size; n++) + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + for (int n = 0; n < platform_io.Viewports.Size; n++) { - ImGuiViewport* viewport = platform_data->Viewports[n]; + ImGuiViewport* viewport = platform_io.Viewports[n]; GLFWwindow* window = (GLFWwindow*)viewport->PlatformHandle; IM_ASSERT(window != NULL); if (glfwGetWindowAttrib(window, GLFW_FOCUSED)) @@ -522,17 +522,17 @@ static int ImGui_ImplGlfw_CreateVkSurface(ImGuiViewport* viewport, ImU64 vk_inst // FIXME-PLATFORM: Update when changed (using glfwSetMonitorCallback?) static void ImGui_ImplGlfw_UpdateMonitors() { - ImGuiPlatformData* platform_data = ImGui::GetPlatformData(); + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); int monitors_count = 0; GLFWmonitor** glfw_monitors = glfwGetMonitors(&monitors_count); - platform_data->Monitors.resize(monitors_count); + platform_io.Monitors.resize(monitors_count); for (int n = 0; n < monitors_count; n++) { int x, y; glfwGetMonitorPos(glfw_monitors[n], &x, &y); const GLFWvidmode* vid_mode = glfwGetVideoMode(glfw_monitors[n]); - platform_data->Monitors[n].Pos = ImVec2((float)x, (float)y); - platform_data->Monitors[n].Size = ImVec2((float)vid_mode->width, (float)vid_mode->height); + platform_io.Monitors[n].Pos = ImVec2((float)x, (float)y); + platform_io.Monitors[n].Size = ImVec2((float)vid_mode->width, (float)vid_mode->height); } } diff --git a/examples/imgui_impl_sdl2.cpp b/examples/imgui_impl_sdl2.cpp index ae49a39a8d24..9bea456b7d76 100644 --- a/examples/imgui_impl_sdl2.cpp +++ b/examples/imgui_impl_sdl2.cpp @@ -440,15 +440,15 @@ static int ImGui_ImplSDL2_CreateVkSurface(ImGuiViewport* viewport, ImU64 vk_inst // FIXME-PLATFORM: Update when changed? static void ImGui_ImplSDL2_UpdateMonitors() { - ImGuiPlatformData* platform_data = ImGui::GetPlatformData(); + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); int display_count = SDL_GetNumVideoDisplays(); - platform_data->Monitors.resize(display_count); + platform_io.Monitors.resize(display_count); for (int n = 0; n < display_count; n++) { SDL_Rect r; SDL_GetDisplayBounds(n, &r); - platform_data->Monitors[n].Pos = ImVec2((float)r.x, (float)r.y); - platform_data->Monitors[n].Size = ImVec2((float)r.w, (float)r.h); + platform_io.Monitors[n].Pos = ImVec2((float)r.x, (float)r.y); + platform_io.Monitors[n].Size = ImVec2((float)r.w, (float)r.h); } } diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index 6be55ced607e..bf440bd558b9 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -408,7 +408,7 @@ static void ImGui_ImplWin32_DestroyWindow(ImGuiViewport* viewport) { if (::GetCapture() == data->Hwnd) { - // Transfer capture so if we started dragging from a window that later disappears, we'll still release the MOUSEUP event. + // Transfer capture so if we started dragging from a window that later disappears, we'll still receive the MOUSEUP event. ::ReleaseCapture(); ::SetCapture(g_hWnd); } @@ -540,14 +540,14 @@ static BOOL CALLBACK ImGui_ImplWin32_UpdateMonitors_EnumFunc(HMONITOR, HDC, LPRE ImGuiPlatformMonitor imgui_monitor; imgui_monitor.Pos = ImVec2((float)rect->left, (float)rect->top); imgui_monitor.Size = ImVec2((float)(rect->right - rect->left), (float)(rect->bottom - rect->top)); - ImGui::GetPlatformData()->Monitors.push_back(imgui_monitor); + ImGui::GetPlatformIO().Monitors.push_back(imgui_monitor); return TRUE; } // FIXME-PLATFORM: Update list when changed (WM_DISPLAYCHANGE?) static void ImGui_ImplWin32_UpdateMonitors() { - ImGui::GetPlatformData()->Monitors.resize(0); + ImGui::GetPlatformIO().Monitors.resize(0); ::EnumDisplayMonitors(NULL, NULL, ImGui_ImplWin32_UpdateMonitors_EnumFunc, NULL); } diff --git a/imgui.cpp b/imgui.cpp index 99167347d7e1..8d276c9b1888 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3386,7 +3386,7 @@ static ImGuiViewportP* FindViewportHoveredFromPlatformWindowStack(const ImVec2 m static void ImGui::UpdateViewports() { ImGuiContext& g = *GImGui; - IM_ASSERT(g.PlatformData.Viewports.Size <= g.Viewports.Size); + IM_ASSERT(g.PlatformIO.Viewports.Size <= g.Viewports.Size); // Mouse handling: latch the expected mouse OS position (if any) before processing viewport erasure ImGuiViewportP* viewport_ref = g.IO.MousePosViewport ? FindViewportByID(g.IO.MousePosViewport) : g.Viewports[0]; @@ -3410,7 +3410,7 @@ static void ImGui::UpdateViewports() if (viewport == g.MousePosViewport) g.MousePosViewport = NULL; if (viewport == g.MouseHoveredPrevViewport) g.MouseHoveredPrevViewport = NULL; IM_ASSERT(viewport->RendererUserData == NULL && viewport->PlatformUserData == NULL && viewport->PlatformHandle == NULL); - IM_ASSERT(g.PlatformData.Viewports.contains(viewport) == false); + IM_ASSERT(g.PlatformIO.Viewports.contains(viewport) == false); IM_DELETE(viewport); n--; continue; @@ -3516,11 +3516,6 @@ static void ImGui::UpdateViewports() IM_ASSERT(g.MousePosViewport != NULL); } -ImGuiPlatformData* ImGui::GetPlatformData() -{ - return &GImGui->PlatformData; -} - void ImGui::UpdatePlatformWindows() { ImGuiContext& g = *GImGui; @@ -3611,11 +3606,11 @@ void ImGui::UpdatePlatformWindows() // Custom renderers may prefer to not call this function at all, and instead iterate the platform data and handle rendering/sync themselves. // The Render/Swap functions stored in ImGuiPlatformIO are merely here to allow for this helper to exist, but you can do it yourself: // -// ImGuiPlatformData* data = ImGui::GetPlatformData(); -// for (int i = 1; i < data->Viewports.Size; i++) -// MyRenderFunction(data->Viewports[i], my_args); -// for (int i = 1; i < data->Viewports.Size; i++) -// MySwapBufferFunction(data->Viewports[i], my_args); +// ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); +// for (int i = 1; i < platform_io.Viewports.Size; i++) +// MyRenderFunction(platform_io.Viewports[i], my_args); +// for (int i = 1; i < platform_io.Viewports.Size; i++) +// MySwapBufferFunction(platform_io.Viewports[i], my_args); // void ImGui::RenderPlatformWindowsDefault(void* platform_render_arg, void* renderer_render_arg) { @@ -3623,17 +3618,16 @@ void ImGui::RenderPlatformWindowsDefault(void* platform_render_arg, void* render return; // Skip the main viewport (index 0), which is always fully handled by the application! - ImGuiPlatformData* data = ImGui::GetPlatformData(); ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); - for (int i = 1; i < data->Viewports.Size; i++) + for (int i = 1; i < platform_io.Viewports.Size; i++) { - ImGuiViewport* viewport = data->Viewports[i]; + ImGuiViewport* viewport = platform_io.Viewports[i]; if (platform_io.Platform_RenderWindow) platform_io.Platform_RenderWindow(viewport, platform_render_arg); if (platform_io.Renderer_RenderWindow) platform_io.Renderer_RenderWindow(viewport, renderer_render_arg); } - for (int i = 1; i < data->Viewports.Size; i++) + for (int i = 1; i < platform_io.Viewports.Size; i++) { - ImGuiViewport* viewport = data->Viewports[i]; + ImGuiViewport* viewport = platform_io.Viewports[i]; if (platform_io.Platform_SwapBuffers) platform_io.Platform_SwapBuffers(viewport, platform_render_arg); if (platform_io.Renderer_SwapBuffers) platform_io.Renderer_SwapBuffers(viewport, renderer_render_arg); } @@ -4497,8 +4491,8 @@ void ImGui::EndFrame() } // Update user-side viewport list - g.PlatformData.MainViewport = g.Viewports[0]; - g.PlatformData.Viewports.resize(0); + g.PlatformIO.MainViewport = g.Viewports[0]; + g.PlatformIO.Viewports.resize(0); for (int i = 0; i < g.Viewports.Size; i++) { ImGuiViewportP* viewport = g.Viewports[i]; @@ -4506,7 +4500,7 @@ void ImGui::EndFrame() continue; if (i > 0) IM_ASSERT(viewport->Window != NULL); - g.PlatformData.Viewports.push_back(viewport); + g.PlatformIO.Viewports.push_back(viewport); } // Sort the window list so that all child windows are after their parent @@ -14182,12 +14176,12 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::Indent(ImGui::GetTreeNodeToLabelSpacing()); ImGui::ShowViewportThumbnails(); ImGui::Unindent(ImGui::GetTreeNodeToLabelSpacing()); - if (g.PlatformData.Monitors.Size > 0 && ImGui::TreeNode("Monitors", "Monitors (%d)", g.PlatformData.Monitors.Size)) + if (g.PlatformIO.Monitors.Size > 0 && ImGui::TreeNode("Monitors", "Monitors (%d)", g.PlatformIO.Monitors.Size)) { ImGui::TextWrapped("(When viewports are enabled, imgui optionally uses monitor data to position popup/tooltips so they don't straddle monitors.)"); - for (int i = 0; i < g.PlatformData.Monitors.Size; i++) + for (int i = 0; i < g.PlatformIO.Monitors.Size; i++) { - const ImGuiPlatformMonitor& mon = g.PlatformData.Monitors[i]; + const ImGuiPlatformMonitor& mon = g.PlatformIO.Monitors[i]; ImGui::BulletText("Monitor #%d: Min (%.0f,%.0f) Max (%.0f,%.0f) Size (%.0f,%.0f)", i, mon.Pos.x, mon.Pos.y, mon.Pos.x + mon.Size.x, mon.Pos.y + mon.Size.y, mon.Size.x, mon.Size.y); } ImGui::TreePop(); diff --git a/imgui.h b/imgui.h index ce13fcbbb906..79c9a0ca56de 100644 --- a/imgui.h +++ b/imgui.h @@ -73,8 +73,7 @@ struct ImGuiSizeCallbackData; // Structure used to constraint window size struct ImGuiListClipper; // Helper to manually clip large list of items struct ImGuiPayload; // User data payload for drag and drop operations struct ImGuiViewport; // Viewport (generally ~1 per window to output to at the OS level. Need per-platform support to use multiple viewports) -struct ImGuiPlatformIO; // Multi-viewport support: interface for Platform/Renderer back-ends -struct ImGuiPlatformData; // Multi-viewport support: list of viewports to render + list of monitors provided by back-end. +struct ImGuiPlatformIO; // Multi-viewport support: interface for Platform/Renderer back-ends + viewports to render struct ImGuiContext; // ImGui context (opaque) #ifndef ImTextureID @@ -546,14 +545,13 @@ namespace ImGui IMGUI_API const char* GetClipboardText(); IMGUI_API void SetClipboardText(const char* text); - // (Optional) Platform interface for multi-viewport support - IMGUI_API ImGuiPlatformIO& GetPlatformIO(); // Platform/Renderer function, for back-end to setup. - IMGUI_API ImGuiPlatformData* GetPlatformData(); // List of viewports. Viewport 0 is always the main viewport, followed by the secondary viewports. - IMGUI_API ImGuiViewport* GetMainViewport(); // == GetPlatformData()->MainViewport == GetPlatformData()->Viewports[0] - IMGUI_API void UpdatePlatformWindows(); // Call in main loop. Will call CreateWindow/ResizeWindow/etc. platform functions for each secondary viewport, and DestroyWindow for each inactive viewport. - IMGUI_API void RenderPlatformWindowsDefault(void* platform_arg = NULL, void* renderer_arg = NULL); // Call in main loop. Will call RenderWindow/SwapBuffers platform functions for each secondary viewport. May be reimplemented by user for custom rendering needs. - IMGUI_API void DestroyPlatformWindows(); // (Optional) Call DestroyWindow platform functions for all viewports. Call from back-end Shutdown() if you need to close platform windows before imgui shutdown. Otherwise will be called by DestroyContext(). - IMGUI_API ImGuiViewport* FindViewportByPlatformHandle(void* platform_handle); + // (Optional) Platform/OS interface for multi-viewport support + IMGUI_API ImGuiPlatformIO& GetPlatformIO(); // platform/renderer functions, for back-end to setup + viewports list. + IMGUI_API ImGuiViewport* GetMainViewport(); // shortcut to == GetPlatformIO().MainViewport == GetPlatformIO().Viewports[0] + IMGUI_API void UpdatePlatformWindows(); // call in main loop. will call CreateWindow/ResizeWindow/etc. platform functions for each secondary viewport, and DestroyWindow for each inactive viewport. + IMGUI_API void RenderPlatformWindowsDefault(void* platform_arg = NULL, void* renderer_arg = NULL); // call in main loop. will call RenderWindow/SwapBuffers platform functions for each secondary viewport. may be reimplemented by user for custom rendering needs. + IMGUI_API void DestroyPlatformWindows(); // call DestroyWindow platform functions for all viewports. call from back-end Shutdown() if you need to close platform windows before imgui shutdown. otherwise will be called by DestroyContext(). + IMGUI_API ImGuiViewport* FindViewportByPlatformHandle(void* platform_handle); // this is a helper for back-ends. the type platform_handle is decided by the back-end (e.g. HWND, MyWindow*, GLFWwindow* etc.) // Memory Utilities // All those functions are not reliant on the current context. @@ -1875,6 +1873,14 @@ struct ImFont // - if you are new to dear imgui and trying to integrate it into your engine, you should probably ignore this for now. //----------------------------------------------------------------------------- +// (Optional) Represent the bounds of each connected monitor/display +// Dear ImGui only uses this to clamp the position of popups and tooltips so they don't straddle multiple monitors +struct ImGuiPlatformMonitor +{ + ImVec2 Pos; + ImVec2 Size; +}; + // (Optional) Setup required only if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) is enabled // This is designed so we can mix and match two imgui_impl_xxxx files, one for the Platform (~window handling), one for Renderer. // Custom engine back-ends will often provide both Platform and Renderer interfaces and thus may not need to use all functions. @@ -1883,10 +1889,14 @@ struct ImFont // You may skip using RenderPlatformWindowsDefault() and call your draw/swap functions yourself if you need specific behavior for your multi-window rendering. struct ImGuiPlatformIO { - // Platform (e.g. Win32, GLFW, SDL2) + //------------------------------------------------------------------ + // Input - Back-end interface/functions + Monitor List + //------------------------------------------------------------------ + + // Platform functions (e.g. Win32, GLFW, SDL2) void (*Platform_CreateWindow)(ImGuiViewport* vp); // Create a new platform window for the given viewport void (*Platform_DestroyWindow)(ImGuiViewport* vp); - void (*Platform_ShowWindow)(ImGuiViewport* vp); // Newly created windows are initially hidden so we have a chance to call SetWindowPos/Size/Title on them. + void (*Platform_ShowWindow)(ImGuiViewport* vp); // Newly created windows are initially hidden so SetWindowPos/Size/Title can be called on them first void (*Platform_SetWindowPos)(ImGuiViewport* vp, ImVec2 pos); ImVec2 (*Platform_GetWindowPos)(ImGuiViewport* vp); void (*Platform_SetWindowSize)(ImGuiViewport* vp, ImVec2 size); @@ -1899,32 +1909,25 @@ struct ImGuiPlatformIO void (*Platform_OnChangedViewport)(ImGuiViewport* vp); // (Optional) DPI handling: Called during Begin() every time the viewport we are outputting into changes, so back-end has a chance to swap fonts to adjust style. int (*Platform_CreateVkSurface)(ImGuiViewport* vp, ImU64 vk_inst, const void* vk_allocators, ImU64* out_vk_surface); // (Optional) For Renderer to call into Platform code - // Renderer (e.g. DirectX, OpenGL3, Vulkan) + // Renderer functions (e.g. DirectX, OpenGL3, Vulkan) void (*Renderer_CreateWindow)(ImGuiViewport* vp); // Create swap chains, frame buffers etc. void (*Renderer_DestroyWindow)(ImGuiViewport* vp); void (*Renderer_SetWindowSize)(ImGuiViewport* vp, ImVec2 size); // Resize swap chain, frame buffers etc. void (*Renderer_RenderWindow)(ImGuiViewport* vp, void* render_arg); // (Optional) Clear targets, Render viewport->DrawData void (*Renderer_SwapBuffers)(ImGuiViewport* vp, void* render_arg); // (Optional) Call Present/SwapBuffers (renderer side) -}; -struct ImGuiPlatformMonitor -{ - ImVec2 Pos; - ImVec2 Size; -}; + // List of monitors (updated by: app/back-end, used by: imgui to clamp popups/tooltips within same monitor and not have them straddle monitors) + ImVector Monitors; -// List of viewports to render as platform window, updated by ImGui::UpdatePlatformWindows() -// FIXME: Merge into ImGuiPlatformIO -struct ImGuiPlatformData -{ - // Viewports (written by: imgui, used by: app/back-end to turn into displayable platform windows) - ImGuiViewport* MainViewport; // Guaranteed to be == Viewports[0] - ImVector Viewports; // Main viewports, followed by all secondary viewports. + //------------------------------------------------------------------ + // Output - List of viewports to render into platform windows + //------------------------------------------------------------------ - // Monitors (written by: app/back-end, used by: imgui to clamp popups/tooltips within same monitor and not have them straddle monitors) - ImVector Monitors; + // List of viewports (updated by ImGui::UpdatePlatformWindows() along when calling the Platform/Renderer functions) + ImGuiViewport* MainViewport; // Guaranteed to be == Viewports[0] + ImVector Viewports; // Main viewports, followed by all secondary viewports. - ImGuiPlatformData() { MainViewport = NULL; } + ImGuiPlatformIO() { memset(this, 0, sizeof(*this)); } // Zero clear }; // Flags stored in ImGuiViewport::Flags, giving indications to the platform back-ends diff --git a/imgui_internal.h b/imgui_internal.h index ad9ee7fb211c..a80cece8ba75 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -638,7 +638,6 @@ struct ImGuiContext // Viewports ImVector Viewports; - ImGuiPlatformData PlatformData; // This is essentially the public facing version of the Viewports vector (it is updated in UpdatePlatformWindows and exclude the viewports about to be destroyed) ImGuiViewportP* CurrentViewport; // We track changes of viewport (happening in Begin) so we can call Platform_OnChangedViewport() ImGuiViewportP* MousePosViewport; ImGuiViewportP* MousePosPrevViewport; @@ -742,7 +741,6 @@ struct ImGuiContext FontSize = FontBaseSize = 0.0f; FontAtlasOwnedByContext = shared_font_atlas ? false : true; IO.Fonts = shared_font_atlas ? shared_font_atlas : IM_NEW(ImFontAtlas)(); - memset(&PlatformIO, 0, sizeof(PlatformIO)); Time = 0.0f; FrameCount = 0; From 8be6f40ae14b63f09fc18c0601fe6b1652388d97 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 11 Apr 2018 13:04:29 +0200 Subject: [PATCH 113/828] Viewport: per-viewport overlay draw list created on demand. With this pattern it'll be easier to consider adding more (e.g. background draw list). (#545) --- imgui.cpp | 65 ++++++++++++++++++++++++++---------------------- imgui_internal.h | 8 +++--- 2 files changed, 40 insertions(+), 33 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 8d276c9b1888..b822779ac40c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2717,17 +2717,32 @@ int ImGui::GetFrameCount() return GImGui->FrameCount; } -ImDrawList* ImGui::GetOverlayDrawList(ImGuiWindow* window) +ImDrawList* ImGui::GetOverlayDrawList(ImGuiViewportP* viewport) { - IM_ASSERT(window && window->Viewport); - return window->Viewport->OverlayDrawList; + // Create the draw list on demand, because it is not frequently used for all viewports + ImGuiContext& g = *GImGui; + if (viewport->OverlayDrawList == NULL) + { + viewport->OverlayDrawList = IM_NEW(ImDrawList)(&g.DrawListSharedData); + viewport->OverlayDrawList->_OwnerName = "##Overlay"; + } + + // Our ImDrawList system requires that there is always a command + if (viewport->LastFrameOverlayDrawList != g.FrameCount) + { + viewport->OverlayDrawList->Clear(); + viewport->OverlayDrawList->PushTextureID(g.IO.Fonts->TexID); + viewport->OverlayDrawList->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size, false); + viewport->OverlayDrawList->Flags = (g.Style.AntiAliasedLines ? ImDrawListFlags_AntiAliasedLines : 0) | (g.Style.AntiAliasedFill ? ImDrawListFlags_AntiAliasedFill : 0); + viewport->LastFrameOverlayDrawList = g.FrameCount; + } + return viewport->OverlayDrawList; } ImDrawList* ImGui::GetOverlayDrawList() { ImGuiWindow* window = GImGui->CurrentWindow; - IM_ASSERT(window && window->Viewport); - return window->Viewport->OverlayDrawList; + return GetOverlayDrawList(window->Viewport); } ImDrawListSharedData* ImGui::GetDrawListSharedData() @@ -3633,15 +3648,6 @@ void ImGui::RenderPlatformWindowsDefault(void* platform_render_arg, void* render } } -static void SetupOverlayDrawList(ImDrawList* draw_list, ImGuiViewport* viewport) -{ - ImGuiContext& g = *GImGui; - draw_list->Clear(); - draw_list->PushTextureID(g.IO.Fonts->TexID); - draw_list->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size, false); - draw_list->Flags = (g.Style.AntiAliasedLines ? ImDrawListFlags_AntiAliasedLines : 0) | (g.Style.AntiAliasedFill ? ImDrawListFlags_AntiAliasedFill : 0); -} - static void NewFrameUpdateMouseInputs() { ImGuiContext& g = *GImGui; @@ -3832,7 +3838,6 @@ void ImGui::NewFrame() ImGuiViewportP* viewport = g.Viewports[n]; viewport->DrawData = NULL; viewport->DrawDataP.Clear(); - SetupOverlayDrawList(viewport->OverlayDrawList, viewport); } // Clear reference to active widget if the widget isn't alive anymore @@ -4051,7 +4056,7 @@ void ImGui::Initialize(ImGuiContext* context) g.SettingsHandlers.push_front(ini_handler); // Create default viewport - ImGuiViewportP* viewport = IM_NEW(ImGuiViewportP)(&g.DrawListSharedData); + ImGuiViewportP* viewport = IM_NEW(ImGuiViewportP)(); viewport->ID = IMGUI_VIEWPORT_DEFAULT_ID; viewport->Idx = 0; g.Viewports.push_back(viewport); @@ -4563,7 +4568,7 @@ void ImGui::Render() ImVec2 pos = (g.MousePosViewport == viewport) ? main_pos : ConvertPlatformPosToViewportPos(ConvertViewportPosToPlatformPos(main_pos, g.MousePosViewport), viewport); if (viewport->GetRect().Overlaps(ImRect(pos, pos + ImVec2(2,2)*sc + size * sc))) { - ImDrawList* draw_list = viewport->OverlayDrawList; + ImDrawList* draw_list = GetOverlayDrawList(viewport); draw_list->PushTextureID(tex_id); draw_list->AddImage(tex_id, pos+ImVec2(1,0)*sc, pos+ImVec2(1,0)*sc + size*sc, uv[2], uv[3], IM_COL32(0,0,0,48)); // Shadow draw_list->AddImage(tex_id, pos+ImVec2(2,0)*sc, pos+ImVec2(2,0)*sc + size*sc, uv[2], uv[3], IM_COL32(0,0,0,48)); // Shadow @@ -4580,7 +4585,8 @@ void ImGui::Render() { ImGuiViewportP* viewport = g.Viewports[n]; viewport->DrawDataBuilder.FlattenIntoSingleLayer(); - AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[0], viewport->OverlayDrawList); + if (viewport->OverlayDrawList != NULL) + AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[0], GetOverlayDrawList(viewport)); SetupViewportDrawData(viewport, &viewport->DrawDataBuilder.Layers[0]); g.IO.MetricsRenderVertices += viewport->DrawData->TotalVtxCount; g.IO.MetricsRenderIndices += viewport->DrawData->TotalIdxCount; @@ -4683,7 +4689,7 @@ ImGuiViewportP* ImGui::Viewport(ImGuiWindow* window, ImGuiID id, ImGuiViewportFl else { // New viewport - viewport = IM_NEW(ImGuiViewportP)(&g.DrawListSharedData); + viewport = IM_NEW(ImGuiViewportP)(); viewport->ID = id; viewport->Idx = g.Viewports.Size; viewport->Pos = ImVec2(g.Viewports.back()->GetNextX(), 0.0f); @@ -4691,11 +4697,9 @@ ImGuiViewportP* ImGui::Viewport(ImGuiWindow* window, ImGuiID id, ImGuiViewportFl g.Viewports.push_back(viewport); // We normally setup for all viewports in NewFrame() but here need to handle the mid-frame creation of a new viewport. - // 1. We need to extend the fullscreen clip rect so the OverlayDrawList clip is correct for that the first frame - // 2. Our ImDrawList system requires that there is always a command, SetupOverlayDrawList() effectively does that by setting up texture and clip rect + // We need to extend the fullscreen clip rect so the OverlayDrawList clip is correct for that the first frame g.DrawListSharedData.ClipRectFullscreen.z = ImMax(g.DrawListSharedData.ClipRectFullscreen.z, viewport->Pos.x + viewport->Size.x); g.DrawListSharedData.ClipRectFullscreen.w = ImMax(g.DrawListSharedData.ClipRectFullscreen.w, viewport->Pos.y + viewport->Size.y); - SetupOverlayDrawList(viewport->OverlayDrawList, viewport); } IM_ASSERT(viewport->Pos.y == 0.0f); @@ -6699,12 +6703,12 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Draw modal window background (darkens what is behind them, all viewports) if ((flags & ImGuiWindowFlags_Modal) != 0 && window == GetFrontMostModalRootWindow() && window->HiddenFrames <= 0) - { - window->DrawList->AddRectFilled(viewport_rect.Min, viewport_rect.Max, GetColorU32(ImGuiCol_ModalWindowDarkening, g.ModalWindowDarkeningRatio)); for (int viewport_n = 0; viewport_n < g.Viewports.Size; viewport_n++) - if (g.Viewports[viewport_n] != window->Viewport) - g.Viewports[viewport_n]->OverlayDrawList->AddRectFilled(g.Viewports[viewport_n]->Pos, g.Viewports[viewport_n]->Pos + g.Viewports[viewport_n]->Size, GetColorU32(ImGuiCol_ModalWindowDarkening, g.ModalWindowDarkeningRatio)); - } + { + ImGuiViewportP* viewport = g.Viewports[viewport_n]; + ImDrawList* draw_list = (viewport == window->Viewport) ? window->DrawList : GetOverlayDrawList(viewport); + draw_list->AddRectFilled(viewport->Pos, viewport->Pos + viewport->Size, GetColorU32(ImGuiCol_ModalWindowDarkening, g.ModalWindowDarkeningRatio)); + } // Draw navigation selection/windowing rectangle background if (g.NavWindowingTarget == window) @@ -14060,7 +14064,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) return; } - ImDrawList* overlay_draw_list = viewport->OverlayDrawList; // Render additional visuals into the top-most draw list + ImDrawList* overlay_draw_list = GetOverlayDrawList(viewport); // Render additional visuals into the top-most draw list if (window && ImGui::IsItemHovered()) overlay_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255)); if (!node_open) @@ -14245,8 +14249,9 @@ void ImGui::ShowMetricsWindow(bool* p_open) char buf[32]; ImFormatString(buf, IM_ARRAYSIZE(buf), "%d", window->BeginOrderWithinContext); float font_size = ImGui::GetFontSize() * 2; - window->Viewport->OverlayDrawList->AddRectFilled(window->PosFloat, window->PosFloat + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255)); - window->Viewport->OverlayDrawList->AddText(NULL, font_size, window->PosFloat, IM_COL32(255, 255, 255, 255), buf); + ImDrawList* overlay_draw_list = GetOverlayDrawList(window->Viewport); + overlay_draw_list->AddRectFilled(window->PosFloat, window->PosFloat + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255)); + overlay_draw_list->AddText(NULL, font_size, window->PosFloat, IM_COL32(255, 255, 255, 255), buf); } } } diff --git a/imgui_internal.h b/imgui_internal.h index a80cece8ba75..3baf91c4cced 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -516,6 +516,7 @@ struct ImGuiViewportP : public ImGuiViewport int Idx; int LastFrameActive; // Last frame number this viewport was activated by a window int LastFrameAsRefViewport; // Last frame number this viewport was io.MouseViewportRef + int LastFrameOverlayDrawList; ImGuiID LastNameHash; float Alpha; // Window opacity (when dragging dockable windows/viewports we make them transparent) float LastAlpha; @@ -525,8 +526,8 @@ struct ImGuiViewportP : public ImGuiViewport ImDrawDataBuilder DrawDataBuilder; ImVec2 RendererLastSize; - ImGuiViewportP(ImDrawListSharedData* draw_list_shared_data) { Idx = 1; LastFrameActive = LastFrameAsRefViewport = -1; LastNameHash = 0; Alpha = LastAlpha = 1.0f; Window = NULL; OverlayDrawList = IM_NEW(ImDrawList)(draw_list_shared_data); OverlayDrawList->_OwnerName = "##Overlay"; RendererLastSize = ImVec2(-1.0f,-1.0f); } - ~ImGuiViewportP() { IM_DELETE(OverlayDrawList); } + ImGuiViewportP() { Idx = 1; LastFrameActive = LastFrameAsRefViewport = LastFrameOverlayDrawList = -1; LastNameHash = 0; Alpha = LastAlpha = 1.0f; Window = NULL; OverlayDrawList = NULL; RendererLastSize = ImVec2(-1.0f,-1.0f); } + ~ImGuiViewportP() { if (OverlayDrawList) IM_DELETE(OverlayDrawList); } ImRect GetRect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } float GetNextX() const { const float SPACING = 4.0f; return Pos.x + Size.x + SPACING; } }; @@ -1099,7 +1100,8 @@ namespace ImGui IMGUI_API void PopItemFlag(); IMGUI_API void SetCurrentFont(ImFont* font); - IMGUI_API ImDrawList* GetOverlayDrawList(ImGuiWindow* window); + IMGUI_API ImDrawList* GetOverlayDrawList(ImGuiViewportP* viewport); + inline ImDrawList* GetOverlayDrawList(ImGuiWindow* window) { return GetOverlayDrawList(window->Viewport); } IMGUI_API void OpenPopupEx(ImGuiID id); IMGUI_API void ClosePopup(ImGuiID id); From 285269ef555be388252c8b180326c6233a579317 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 11 Apr 2018 21:27:19 +0200 Subject: [PATCH 114/828] Viewport, Platform: Added ImGuiViewportFlags_TopMost that will be used by tooltip window (without it, clicking and holding the window emitting the tooltip would move the tooltip to the back) + GLFW backend fixes for non-win32 platforms. (#1542) --- examples/imgui_impl_glfw.cpp | 9 +++++++-- examples/imgui_impl_sdl2.cpp | 4 ++++ examples/imgui_impl_win32.cpp | 2 ++ imgui.cpp | 24 +++++++++++++++--------- imgui.h | 3 ++- 5 files changed, 30 insertions(+), 12 deletions(-) diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index 4d54f5019695..416e0f99bb38 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -41,8 +41,10 @@ #define GLFW_HAS_GLFW_HOVERED 0 #endif #define GLFW_HAS_WINDOW_ALPHA (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ +#define GLFW_HAS_WINDOW_TOPMOST (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ #define GLFW_HAS_VULKAN (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ + // Data enum GlfwClientApi { @@ -357,6 +359,9 @@ static void ImGui_ImplGlfw_CreateWindow(ImGuiViewport* viewport) glfwWindowHint(GLFW_VISIBLE, false); glfwWindowHint(GLFW_FOCUSED, false); glfwWindowHint(GLFW_DECORATED, (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? false : true); +#if GLFW_HAS_WINDOW_TOPMOST + glfwWindowHint(GLFW_FLOATING, (viewport->Flags & imGuiViewportFlags_TopMost) ? true : false); +#endif GLFWwindow* share_window = (g_ClientApi == GlfwClientApi_OpenGL) ? g_Window : NULL; data->Window = glfwCreateWindow((int)viewport->Size.x, (int)viewport->Size.y, "No Title Yet", NULL, share_window); data->WindowOwned = true; @@ -373,7 +378,7 @@ static void ImGui_ImplGlfw_DestroyWindow(ImGuiViewport* viewport) { if (data->WindowOwned) { -#if GLFW_HAS_GLFW_HOVERED +#if GLFW_HAS_GLFW_HOVERED && defined(_WIN32) HWND hwnd = glfwGetWin32Window(data->Window); ::RemovePropA(hwnd, "IMGUI_VIEWPORT"); #endif @@ -419,7 +424,7 @@ static void ImGui_ImplGlfw_ShowWindow(ImGuiViewport* viewport) } // GLFW hack: install hook for WM_NCHITTEST message handler -#if GLFW_HAS_GLFW_HOVERED +#if GLFW_HAS_GLFW_HOVERED && defined(_WIN32) ::SetPropA(hwnd, "IMGUI_VIEWPORT", viewport); if (g_GlfwWndProc == NULL) g_GlfwWndProc = (WNDPROC)::GetWindowLongPtr(hwnd, GWLP_WNDPROC); diff --git a/examples/imgui_impl_sdl2.cpp b/examples/imgui_impl_sdl2.cpp index 9bea456b7d76..e01bd79dc189 100644 --- a/examples/imgui_impl_sdl2.cpp +++ b/examples/imgui_impl_sdl2.cpp @@ -34,6 +34,7 @@ #include #define SDL_HAS_CAPTURE_MOUSE SDL_VERSION_ATLEAST(2,0,4) #define SDL_HAS_WINDOW_OPACITY SDL_VERSION_ATLEAST(2,0,5) +#define SDL_HAS_ALWAYS_ON_TOP SDL_VERSION_ATLEAST(2,0,5) #define SDL_HAS_VULKAN SDL_VERSION_ATLEAST(2,0,6) #if !SDL_HAS_VULKAN static const Uint32 SDL_WINDOW_VULKAN = 0x10000000; @@ -317,6 +318,9 @@ static void ImGui_ImplSDL2_CreateWindow(ImGuiViewport* viewport) sdl_flags |= SDL_WINDOW_HIDDEN; sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? SDL_WINDOW_BORDERLESS : 0; sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? 0 : SDL_WINDOW_RESIZABLE; +#if SDL_HAS_ALWAYS_ON_TOP + sdl_flags |= (viewport->Flags & imGuiViewportFlags_TopMost) ? SDL_WINDOW_ALWAYS_ON_TOP : 0; +#endif data->Window = SDL_CreateWindow("No Title Yet", (int)viewport->PlatformPos.x, (int)viewport->PlatformPos.y, (int)viewport->Size.x, (int)viewport->Size.y, sdl_flags); data->WindowOwned = true; if (use_opengl) diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index bf440bd558b9..610a2e23eb06 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -389,6 +389,8 @@ static void ImGui_ImplWin32_CreateWindow(ImGuiViewport* viewport) data->DwStyle = WS_OVERLAPPEDWINDOW; data->DwExStyle = no_task_bar_icon ? WS_EX_TOOLWINDOW : WS_EX_APPWINDOW; } + if (viewport->Flags & imGuiViewportFlags_TopMost) + data->DwExStyle |= WS_EX_TOPMOST; // Create window RECT rect = { (LONG)viewport->PlatformPos.x, (LONG)viewport->PlatformPos.y, (LONG)(viewport->PlatformPos.x + viewport->Size.x), (LONG)(viewport->PlatformPos.y + viewport->Size.y) }; diff --git a/imgui.cpp b/imgui.cpp index 7ffffe7eacff..1ecc8dfb8933 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -434,7 +434,7 @@ perfectly fine, as the bool toggle fairly rarely. If you have on a touch device, you might find use for an early call to NewFrameUpdateHoveredWindowAndCaptureFlags(). Note: Text input widget releases focus on "Return KeyDown", so the subsequent "Return KeyUp" event that your application receive will typically have 'io.WantCaptureKeyboard=false'. Depending on your application logic it may or not be inconvenient. You might want to track which key-downs - were targetted for Dear ImGui, e.g. with an array of bool, and filter out the corresponding key-ups.) + were targeted for Dear ImGui, e.g. with an array of bool, and filter out the corresponding key-ups.) Q: How can I display an image? What is ImTextureID, how does it works? A: ImTextureID is a void* used to pass renderer-agnostic texture references around until it hits your render function. @@ -766,7 +766,7 @@ const ImGuiID IMGUI_VIEWPORT_DEFAULT_ID = 0x11111111; // Using an arbi static inline ImRect GetViewportRect(ImGuiWindow* window) { return window->Viewport->GetRect(); } static inline ImVec2 ConvertViewportPosToPlatformPos(const ImVec2& imgui_pos, ImGuiViewport* viewport) { return imgui_pos - viewport->Pos + viewport->PlatformPos; } static inline ImVec2 ConvertPlatformPosToViewportPos(const ImVec2& platform_pos, ImGuiViewport* viewport) { return platform_pos - viewport->PlatformPos + viewport->Pos; } -static ImGuiViewportP* Viewport(ImGuiWindow* window, ImGuiID id, ImGuiViewportFlags flags, const ImVec2& platform_pos, const ImVec2& size); +static ImGuiViewportP* AddViewport(ImGuiWindow* window, ImGuiID id, ImGuiViewportFlags flags, const ImVec2& platform_pos, const ImVec2& size); static void UpdateViewports(); static void UpdateSelectWindowViewport(ImGuiWindow* window); static void SetCurrentViewport(ImGuiViewportP* viewport); @@ -3330,7 +3330,7 @@ static void ImGui::NewFrameUpdateMovingWindowDropViewport(ImGuiWindow* window) { // Create/persist new viewport ImVec2 platform_pos = ConvertViewportPosToPlatformPos(window->Pos, window->Viewport); - ImGuiViewportP* viewport = Viewport(window, window->ID, 0, platform_pos, window->Size); + ImGuiViewportP* viewport = AddViewport(window, window->ID, 0, platform_pos, window->Size); SetWindowViewportTranslateToPreservePlatformPos(window, window->Viewport, viewport); } } @@ -3475,7 +3475,7 @@ static void ImGui::UpdateViewports() ImVec2 main_viewport_platform_pos = ImVec2(0.0f, 0.0f); if ((g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) main_viewport_platform_pos = g.PlatformIO.Platform_GetWindowPos(main_viewport); - Viewport(NULL, IMGUI_VIEWPORT_DEFAULT_ID, ImGuiViewportFlags_CanHostOtherWindows, main_viewport_platform_pos, g.IO.DisplaySize); + AddViewport(NULL, IMGUI_VIEWPORT_DEFAULT_ID, ImGuiViewportFlags_CanHostOtherWindows, main_viewport_platform_pos, g.IO.DisplaySize); if (!(g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) { @@ -3560,10 +3560,16 @@ void ImGui::UpdatePlatformWindows() continue; } - // Update ImGuiViewportFlags_NoTaskBarIcon flag + // New windows that appears directly in a new viewport won't always have a size on their frame + if (viewport->Size.x <= 0 || viewport->Size.y <= 0) + continue; + + // Update viewport flags if (viewport->Window != NULL) { + bool topmost = (viewport->Window->Flags & ImGuiWindowFlags_Tooltip) != 0; bool no_task_bar_icon = (g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsNoTaskBarIcons) != 0 || (viewport->Window->Flags & (ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) != 0; + viewport->Flags = topmost ? (viewport->Flags | imGuiViewportFlags_TopMost) : (viewport->Flags & ~imGuiViewportFlags_TopMost); viewport->Flags = no_task_bar_icon ? (viewport->Flags | ImGuiViewportFlags_NoTaskBarIcon) : (viewport->Flags & ~ImGuiViewportFlags_NoTaskBarIcon); } @@ -4506,7 +4512,7 @@ void ImGui::EndFrame() for (int i = 0; i < g.Viewports.Size; i++) { ImGuiViewportP* viewport = g.Viewports[i]; - if (viewport->LastFrameActive < g.FrameCount) + if (viewport->LastFrameActive < g.FrameCount || viewport->Size.x <= 0.0f || viewport->Size.y <= 0.0f) continue; if (i > 0) IM_ASSERT(viewport->Window != NULL); @@ -4679,7 +4685,7 @@ void ImGui::SetCurrentViewport(ImGuiViewportP* viewport) g.PlatformIO.Platform_OnChangedViewport(g.CurrentViewport); } -ImGuiViewportP* ImGui::Viewport(ImGuiWindow* window, ImGuiID id, ImGuiViewportFlags flags, const ImVec2& platform_pos, const ImVec2& size) +ImGuiViewportP* ImGui::AddViewport(ImGuiWindow* window, ImGuiID id, ImGuiViewportFlags flags, const ImVec2& platform_pos, const ImVec2& size) { ImGuiContext& g = *GImGui; IM_ASSERT(id != 0); @@ -6164,7 +6170,7 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) window->Viewport = FindViewportByID(window->ViewportId); if (window->Viewport == NULL && window->ViewportPlatformPos.x != FLT_MAX && window->ViewportPlatformPos.y != FLT_MAX) { - ImGuiViewportP* viewport = Viewport(window, window->ID, ImGuiViewportFlags_NoDecoration, window->ViewportPlatformPos, window->Size); + ImGuiViewportP* viewport = AddViewport(window, window->ID, ImGuiViewportFlags_NoDecoration, window->ViewportPlatformPos, window->Size); window->Flags |= ImGuiWindowFlags_FullViewport; window->Viewport = viewport; created_viewport = true; @@ -6189,7 +6195,7 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) // Calculate mouse position in OS/platform coordinates, create a Viewport at this position. ImVec2 platform_pos = ConvertViewportPosToPlatformPos(g.IO.MousePos - g.ActiveIdClickOffset, g.MousePosViewport); ImGuiViewportFlags viewport_flags = ImGuiViewportFlags_NoDecoration | ImGuiViewportFlags_NoFocusOnAppearing | ImGuiViewportFlags_NoInputs; - ImGuiViewportP* viewport = Viewport(window, window->ID, viewport_flags, platform_pos, window->Size); + ImGuiViewportP* viewport = AddViewport(window, window->ID, viewport_flags, platform_pos, window->Size); window->Flags |= ImGuiWindowFlags_FullViewport; window->Viewport = viewport; created_viewport = true; diff --git a/imgui.h b/imgui.h index 90892e6e11f5..6b4781d0ac59 100644 --- a/imgui.h +++ b/imgui.h @@ -1937,7 +1937,8 @@ enum ImGuiViewportFlags_ ImGuiViewportFlags_NoFocusOnAppearing = 1 << 1, // Platform Window: Don't take focus when created. ImGuiViewportFlags_NoInputs = 1 << 2, // Platform Window: Make mouse pass through so we can drag this window while peaking behind it. ImGuiViewportFlags_NoTaskBarIcon = 1 << 3, // Platform Window: Disable platform task bar icon (for popups, menus, or all windows if ImGuiConfigFlags_ViewportsNoTaskBarIcons if set) - ImGuiViewportFlags_NoRendererClear = 1 << 4 // Platform Window: Renderer doesn't need to clear the framebuffer ahead. + ImGuiViewportFlags_NoRendererClear = 1 << 4, // Platform Window: Renderer doesn't need to clear the framebuffer ahead. + imGuiViewportFlags_TopMost = 1 << 5 // Platform Window: Display on top (for tooltips only) }; // The viewports created and managed by imgui. The role of the platform back-end is to create the platform/OS windows corresponding to each viewport. From 0b7f3edc2607d8c9912a4f3527eaa071e1e71648 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 12 Apr 2018 16:27:38 +0200 Subject: [PATCH 115/828] Viewport: Debug/metrics tweaks + renaming comments --- imgui.cpp | 67 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 1ecc8dfb8933..36644a4377a5 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -771,7 +771,7 @@ static void UpdateViewports(); static void UpdateSelectWindowViewport(ImGuiWindow* window); static void SetCurrentViewport(ImGuiViewportP* viewport); static void SetWindowViewportTranslateToPreservePlatformPos(ImGuiWindow* window, ImGuiViewportP* old_viewport, ImGuiViewportP* new_viewport); -static void ResizeViewportTranslateWindows(int viewport_idx_min, int viewport_idx_max, float pos_x_delta, int idx_delta, ImGuiViewport* viewport_to_erase); +static void TranslateOrEraseViewports(int viewport_idx_min, int viewport_idx_max, float delta_x, int delta_idx, ImGuiViewport* viewport_to_erase); } //----------------------------------------------------------------------------- @@ -3418,7 +3418,7 @@ static void ImGui::UpdateViewports() if (n > 0 && viewport->LastFrameActive < g.FrameCount - 2) { // Translate windows like if we were resizing the viewport to be zero-width - ResizeViewportTranslateWindows(n + 1, g.Viewports.Size, viewport->Pos.x - viewport->GetNextX(), -1, viewport); + TranslateOrEraseViewports(n + 1, g.Viewports.Size, viewport->Pos.x - viewport->GetNextX(), -1, viewport); g.Viewports.erase(g.Viewports.Data + n); // Destroy @@ -3437,7 +3437,7 @@ static void ImGui::UpdateViewports() { float dx = viewport->GetNextX() - g.Viewports[viewport->Idx + 1]->Pos.x; if (dx != 0.0f) - ResizeViewportTranslateWindows(viewport->Idx + 1, g.Viewports.Size, dx, 0, NULL); + TranslateOrEraseViewports(viewport->Idx + 1, g.Viewports.Size, dx, 0, NULL); } // Apply Position and Size (from Platform Window to ImGui) if requested @@ -4648,26 +4648,27 @@ static void TranslateWindowX(ImGuiWindow* window, float dx) window->DC.LastItemDisplayRect.Translate(dx, 0.0f); } -static void ImGui::ResizeViewportTranslateWindows(int viewport_idx_min, int viewport_idx_max, float pos_x_delta, int idx_delta, ImGuiViewport* viewport_to_erase) +static void ImGui::TranslateOrEraseViewports(int viewport_idx_min, int viewport_idx_max, float delta_x, int delta_idx, ImGuiViewport* viewport_to_erase) { ImGuiContext& g = *GImGui; - IM_ASSERT(pos_x_delta != 0.0f || idx_delta != 0); + IM_ASSERT(delta_x != 0.0f || delta_idx != 0); IM_ASSERT(g.CurrentViewport == NULL); // We only resize at the beginning of the frame for (int n = 0; n < g.Windows.Size; n++) { ImGuiWindow* window = g.Windows[n]; if (window->Viewport == viewport_to_erase) - window->Viewport = NULL; // Set to NULL so window->ViewportId becomes the master data + window->Viewport = NULL; // Set to NULL, window->ViewportId becomes the master data if (window->Viewport == NULL) continue; if (window->Viewport->Idx < viewport_idx_min || window->Viewport->Idx > viewport_idx_max) continue; - TranslateWindowX(window, pos_x_delta); + TranslateWindowX(window, delta_x); } for (int n = viewport_idx_min; n < viewport_idx_max; n++) { - g.Viewports[n]->Pos.x += pos_x_delta; - g.Viewports[n]->Idx += idx_delta; + ImGuiViewportP* viewport = g.Viewports[n]; + viewport->Pos.x += delta_x; + viewport->Idx += delta_idx; } } @@ -4720,6 +4721,7 @@ ImGuiViewportP* ImGui::AddViewport(ImGuiWindow* window, ImGuiID id, ImGuiViewpor viewport->LastFrameActive = g.FrameCount; // Request an initial DpiScale before the OS platform window creation + // This is so we can select an appropriate font size on the first frame of our window lifetime if (g.PlatformIO.Platform_GetWindowDpiScale) viewport->DpiScale = g.PlatformIO.Platform_GetWindowDpiScale(viewport); return viewport; @@ -6664,17 +6666,15 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->PosFloat = FindBestWindowPosForPopup(window); // Clamp position so window stays visible within its viewport + // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing. ImRect viewport_rect(GetViewportRect(window)); if (!window_pos_set_by_api && !(flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_FullViewport)) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) - { - // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing. if (viewport_rect.GetWidth() > 0 && viewport_rect.GetHeight() > 0.0f) { ImVec2 padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding); window->PosFloat = ImMax(window->PosFloat + window->Size, viewport_rect.Min + padding) - window->Size; window->PosFloat = ImMin(window->PosFloat, viewport_rect.Max - padding); } - } window->Pos = ImFloor(window->PosFloat); // Default item width. Make it proportional to window size if window manually resizes @@ -6974,6 +6974,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) } else { + // Append SetCurrentViewport(window->Viewport); SetCurrentWindow(window); } @@ -13981,6 +13982,7 @@ static void RenderViewportThumbnail(ImDrawList* draw_list, const ImRect& bb, con ImRect viewport_r(viewport_pos, viewport_pos + viewport_size); ImVec2 scale = bb.GetSize() / viewport_size; + window->DrawList->AddRect(bb.Min, bb.Max, ImGui::GetColorU32(ImGuiCol_Border)); for (int i = 0; i != g.Windows.Size; i++) { ImGuiWindow* thumb_window = g.Windows[i]; @@ -14055,7 +14057,6 @@ void ImGui::ShowViewportThumbnails() if (n > 0) ImGui::SameLine(); ImRect bb(p + (viewport->Pos) * SCALE, p + (viewport->Pos + viewport->Size) * SCALE); - window->DrawList->AddRect(bb.Min, bb.Max, ImGui::GetColorU32(ImGuiCol_Border)); RenderViewportThumbnail(window->DrawList, bb, viewport->Pos, viewport->Size); char buf[64]; ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f", viewport->Pos.x); @@ -14197,6 +14198,26 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::BulletText("Storage: %d bytes", window->StateStorage.Data.Size * (int)sizeof(ImGuiStorage::Pair)); ImGui::TreePop(); } + + static void NodeViewport(ImGuiViewportP* viewport) + { + ImGui::SetNextTreeNodeOpen(true, ImGuiCond_Once); + if (ImGui::TreeNode((void*)(intptr_t)viewport->ID, "Viewport #%d, ID: 0x%08X, Window: \"%s\"", viewport->Idx, viewport->ID, viewport->Window ? viewport->Window->Name : "N/A")) + { + ImGuiWindowFlags flags = viewport->Flags; + ImGui::BulletText("Pos: (%.0f,%.0f), PlatformPos: (%.0f,%.0f)", viewport->Pos.x, viewport->Pos.y, viewport->PlatformPos.x, viewport->PlatformPos.y); + if (viewport->Idx > 0) { ImGui::SameLine(); if (ImGui::SmallButton("Reset")) viewport->PlatformPos = ImVec2(0, 0); } + ImGui::BulletText("Size: (%0.f,%.0f), DpiScale: %.0f%%", viewport->Size.x, viewport->Size.y, viewport->DpiScale * 100.0f); + ImGui::BulletText("Flags: 0x%04X =%s%s%s%s%s", viewport->Flags, + (flags & ImGuiViewportFlags_CanHostOtherWindows) ? " CanHostOtherWindows" : "", (flags & ImGuiViewportFlags_NoDecoration) ? " NoDecoration" : "", + (flags & ImGuiViewportFlags_NoFocusOnAppearing) ? " NoFocusOnAppearing" : "", (flags & ImGuiViewportFlags_NoInputs) ? " NoInputs" : "", + (flags & ImGuiViewportFlags_NoRendererClear) ? " NoRendererClear" : ""); + for (int layer_i = 0; layer_i < IM_ARRAYSIZE(viewport->DrawDataBuilder.Layers); layer_i++) + for (int draw_list_i = 0; draw_list_i < viewport->DrawDataBuilder.Layers[layer_i].Size; draw_list_i++) + Funcs::NodeDrawList(NULL, viewport, viewport->DrawDataBuilder.Layers[layer_i][draw_list_i], "DrawList"); + ImGui::TreePop(); + } + } }; // Access private state, we are going to display the draw lists from last frame @@ -14218,25 +14239,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::TreePop(); } for (int i = 0; i < g.Viewports.Size; i++) - { - ImGuiViewportP* viewport = g.Viewports[i]; - ImGui::SetNextTreeNodeOpen(true, ImGuiCond_Once); - if (ImGui::TreeNode((void*)(intptr_t)viewport->ID, "Viewport #%d, ID: 0x%08X, Size: (%.0f,%.0f)", i, viewport->ID, viewport->Size.x, viewport->Size.y)) - { - ImGuiWindowFlags flags = viewport->Flags; - ImGui::BulletText("Pos: (%.0f,%.0f), PlatformPos: (%.0f,%.0f)", viewport->Pos.x, viewport->Pos.y, viewport->PlatformPos.x, viewport->PlatformPos.y); - if (i > 0) { ImGui::SameLine(); if (ImGui::SmallButton("Reset")) viewport->PlatformPos = ImVec2(0, 0); } - ImGui::BulletText("DpiScale: %.0f%%", viewport->DpiScale * 100.0f); - ImGui::BulletText("Flags: 0x%04X =%s%s%s%s%s", viewport->Flags, - (flags & ImGuiViewportFlags_CanHostOtherWindows) ? " CanHostOtherWindows" : "", (flags & ImGuiViewportFlags_NoDecoration) ? " NoDecoration" : "", - (flags & ImGuiViewportFlags_NoFocusOnAppearing) ? " NoFocusOnAppearing" : "", (flags & ImGuiViewportFlags_NoInputs) ? " NoInputs" : "", - (flags & ImGuiViewportFlags_NoRendererClear) ? " NoRendererClear" : ""); - for (int layer_i = 0; layer_i < IM_ARRAYSIZE(viewport->DrawDataBuilder.Layers); layer_i++) - for (int draw_list_i = 0; draw_list_i < viewport->DrawDataBuilder.Layers[layer_i].Size; draw_list_i++) - Funcs::NodeDrawList(NULL, viewport, viewport->DrawDataBuilder.Layers[layer_i][draw_list_i], "DrawList"); - ImGui::TreePop(); - } - } + Funcs::NodeViewport(g.Viewports[i]); ImGui::TreePop(); } if (ImGui::TreeNode("Popups", "Open Popups Stack (%d)", g.OpenPopupStack.Size)) From 38e357ef109a27599dd09f2b6324731209ba1f64 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 12 Apr 2018 19:56:14 +0200 Subject: [PATCH 116/828] Viewport: Virtual mouse position are patched immediately when viewports are moved in the virtual space, avoiding interaction glitchs on a resizing frame. (#1542) --- imgui.cpp | 16 +++++++++++++++- imgui_demo.cpp | 1 + imgui_internal.h | 2 ++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 36644a4377a5..cbe288761e9b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3424,6 +3424,7 @@ static void ImGui::UpdateViewports() // Destroy if (viewport == viewport_ref) viewport_ref = NULL; if (viewport == g.MousePosViewport) g.MousePosViewport = NULL; + if (viewport == g.MousePosPrevViewport) g.MousePosPrevViewport = NULL; if (viewport == g.MouseHoveredPrevViewport) g.MouseHoveredPrevViewport = NULL; IM_ASSERT(viewport->RendererUserData == NULL && viewport->PlatformUserData == NULL && viewport->PlatformHandle == NULL); IM_ASSERT(g.PlatformIO.Viewports.contains(viewport) == false); @@ -3690,6 +3691,7 @@ static void NewFrameUpdateMouseInputs() g.IO.MouseClickedPos[i] = g.IO.MousePos; g.IO.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f); g.IO.MouseDragMaxDistanceSqr[i] = 0.0f; + g.MouseClickedPosViewportId[i] = g.MousePosViewport->ID; } else if (g.IO.MouseDown[i]) { @@ -4664,11 +4666,22 @@ static void ImGui::TranslateOrEraseViewports(int viewport_idx_min, int viewport_ continue; TranslateWindowX(window, delta_x); } + for (int n = viewport_idx_min; n < viewport_idx_max; n++) { ImGuiViewportP* viewport = g.Viewports[n]; viewport->Pos.x += delta_x; viewport->Idx += delta_idx; + + // Patch mouse positions immediately so mouse delta will not appears to jump around + ImGuiID viewport_id = viewport->ID; + if (viewport_id == g.IO.MousePosViewport) // We are early in NewFrame and g.MousePosViewport hasn't been set, patch the source. + g.IO.MousePos.x += delta_x; + if (viewport == g.MousePosPrevViewport) + g.IO.MousePosPrev.x += delta_x; + for (int mouse_n = 0; mouse_n < IM_ARRAYSIZE(g.MouseClickedPosViewportId); mouse_n++) + if (g.MouseClickedPosViewportId[mouse_n] == viewport_id) + g.IO.MouseClickedPos[mouse_n].x += delta_x; } } @@ -5277,7 +5290,8 @@ ImVec2 ImGui::GetMouseDragDelta(int button, float lock_threshold) lock_threshold = g.IO.MouseDragThreshold; if (g.IO.MouseDown[button]) if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold) - return g.IO.MousePos - g.IO.MouseClickedPos[button]; // Assume we can only get active with left-mouse button (at the moment). + if (g.MousePosViewport->ID == g.MouseClickedPosViewportId[button]) + return g.IO.MousePos - g.IO.MouseClickedPos[button]; // Assume we can only get active with left-mouse button (at the moment). return ImVec2(0.0f, 0.0f); } diff --git a/imgui_demo.cpp b/imgui_demo.cpp index ca2be4cd7e9b..3111599e7333 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1865,6 +1865,7 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::Text("Mouse pos: (%g, %g)", io.MousePos.x, io.MousePos.y); else ImGui::Text("Mouse pos: "); + ImGui::Text("Mouse delta: (%g, %g)", io.MouseDelta.x, io.MouseDelta.y); ImGui::Text("Mouse down:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (io.MouseDownDuration[i] >= 0.0f) { ImGui::SameLine(); ImGui::Text("b%d (%.02f secs)", i, io.MouseDownDuration[i]); } ImGui::Text("Mouse clicked:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseClicked(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } ImGui::Text("Mouse dbl-clicked:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseDoubleClicked(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } diff --git a/imgui_internal.h b/imgui_internal.h index f5e81b3431d4..a210592d8204 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -643,6 +643,7 @@ struct ImGuiContext ImGuiViewportP* MousePosViewport; ImGuiViewportP* MousePosPrevViewport; ImGuiViewportP* MouseHoveredPrevViewport; + ImGuiID MouseClickedPosViewportId[5]; // For rarely used fields we only compare to, store viewport ID only so we don't have to clean dangling pointers // Navigation data (for gamepad/keyboard) ImGuiWindow* NavWindow; // Focused window for navigation. Could be called 'FocusWindow' @@ -771,6 +772,7 @@ struct ImGuiContext CurrentViewport = NULL; MousePosViewport = NULL; MousePosPrevViewport = MouseHoveredPrevViewport = NULL; + memset(MouseClickedPosViewportId, 0, sizeof(MouseClickedPosViewportId)); NavWindow = NULL; NavId = NavActivateId = NavActivateDownId = NavActivatePressedId = NavInputId = 0; From 22d6f001103e826a0efbbc5cc28c171a289c1b83 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 13 Apr 2018 00:01:55 +0200 Subject: [PATCH 117/828] Viewport: Fixed DPI changing viewport from interfering with moving another window (disabling code) + metrics crash fix on closed viewport window + Windows 10 call to SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2) for DPI, with no noticeable improvements. (#1542, #1676) --- examples/imgui_impl_win32.cpp | 23 ++++++++++++++++++----- imgui.cpp | 15 +++++++++------ 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index 610a2e23eb06..efe5b461aa8c 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -286,9 +286,7 @@ IMGUI_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARAM wPa // So we dynamically select and load those functions to avoid dependencies. This is the scheme successfully // used by GLFW (from which we borrowed some of the code here) and other applications aiming to be portable. //--------------------------------------------------------------------------------------------------------- -// FIXME-DPI: For now we just call SetProcessDpiAwareness(PROCESS_PER_MONITOR_AWARE) without requiring SDK 8.1 or 10. -// We may allow/aim calling the most-recent-available version, e.g. Windows 10 Creators Update has SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); -// At this point ImGui_ImplWin32_EnableDpiAwareness() is just a helper called by main.cpp, we don't call it ourselves. +// At this point ImGui_ImplWin32_EnableDpiAwareness() is just a helper called by main.cpp, we don't call it automatically. //--------------------------------------------------------------------------------------------------------- static BOOL IsWindowsVersionOrGreater(WORD major, WORD minor, WORD sp) @@ -307,11 +305,26 @@ static BOOL IsWindowsVersionOrGreater(WORD major, WORD minor, WORD sp) typedef enum { PROCESS_DPI_UNAWARE = 0, PROCESS_SYSTEM_DPI_AWARE = 1, PROCESS_PER_MONITOR_DPI_AWARE = 2 } PROCESS_DPI_AWARENESS; typedef enum { MDT_EFFECTIVE_DPI = 0, MDT_ANGULAR_DPI = 1, MDT_RAW_DPI = 2, MDT_DEFAULT = MDT_EFFECTIVE_DPI } MONITOR_DPI_TYPE; #endif -typedef HRESULT(WINAPI * PFN_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS); // Shcore.lib+dll, Windows 8.1 -typedef HRESULT(WINAPI * PFN_GetDpiForMonitor)(HMONITOR, MONITOR_DPI_TYPE, UINT*, UINT*); // Shcore.lib+dll, Windows 8.1 +#ifndef _DPI_AWARENESS_CONTEXTS_ +DECLARE_HANDLE(DPI_AWARENESS_CONTEXT); +#define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE (DPI_AWARENESS_CONTEXT)-3 +#define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 (DPI_AWARENESS_CONTEXT)-4 +#endif +typedef HRESULT(WINAPI * PFN_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS); // Shcore.lib+dll, Windows 8.1 +typedef HRESULT(WINAPI * PFN_GetDpiForMonitor)(HMONITOR, MONITOR_DPI_TYPE, UINT*, UINT*); // Shcore.lib+dll, Windows 8.1 +typedef DPI_AWARENESS_CONTEXT(WINAPI * PFN_SetThreadDpiAwarenessContext)(DPI_AWARENESS_CONTEXT); // User32.lib+dll, Windows 10 v1607 (Creators Update) void ImGui_ImplWin32_EnableDpiAwareness() { + // if (IsWindows10OrGreater()) // FIXME-DPI: This needs a manifest to succeed. Instead we try to grab the function pointer. + { + static HINSTANCE user32_dll = ::LoadLibraryA("user32.dll"); // Reference counted per-process + if (PFN_SetThreadDpiAwarenessContext SetThreadDpiAwarenessContextFn = (PFN_SetThreadDpiAwarenessContext)::GetProcAddress(user32_dll, "SetThreadDpiAwarenessContext")) + { + SetThreadDpiAwarenessContextFn(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); + return; + } + } if (IsWindows8Point1OrGreater()) { static HINSTANCE shcore_dll = ::LoadLibraryA("shcore.dll"); // Reference counted per-process diff --git a/imgui.cpp b/imgui.cpp index cbe288761e9b..aded38a81d24 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -14035,8 +14035,11 @@ void ImGui::ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale) { ImGuiContext& g = *GImGui; - if (g.MovingWindow != NULL) - g.ActiveIdClickOffset = ImFloor(g.ActiveIdClickOffset * scale); + // FIXME-DPI: This is meant to have the window rescale around the mouse. It currently creates feedback loop when a window is straddling a DPI transition border. + // NB: since our sizes do not perfectly linearly scale, deferring the ClickOffset scale until we know the actual window scale ratio may get us slightly more precise mouse positioning. + //if (g.MovingWindow != NULL && g.MovingWindow->Viewport == viewport) + // g.ActiveIdClickOffset = ImFloor(g.ActiveIdClickOffset * scale); + /* if (g.IO.MousePosViewport == viewport->ID) { @@ -14106,8 +14109,8 @@ void ImGui::ShowMetricsWindow(bool* p_open) return; } - ImDrawList* overlay_draw_list = GetOverlayDrawList(viewport); // Render additional visuals into the top-most draw list - if (window && ImGui::IsItemHovered()) + ImDrawList* overlay_draw_list = viewport ? GetOverlayDrawList(viewport) : NULL; // Render additional visuals into the top-most draw list + if (window && overlay_draw_list && ImGui::IsItemHovered()) overlay_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255)); if (!node_open) return; @@ -14124,7 +14127,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) } ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL; bool pcmd_node_open = ImGui::TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "Draw %4d %s vtx, tex 0x%p, clip_rect (%4.0f,%4.0f)-(%4.0f,%4.0f)", pcmd->ElemCount, draw_list->IdxBuffer.Size > 0 ? "indexed" : "non-indexed", pcmd->TextureId, pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w); - if (show_clip_rects && ImGui::IsItemHovered()) + if (show_clip_rects && overlay_draw_list && ImGui::IsItemHovered()) { ImRect clip_rect = pcmd->ClipRect; ImRect vtxs_rect; @@ -14151,7 +14154,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) buf_p += ImFormatString(buf_p, (int)(buf_end - buf_p), "%s %04d: pos (%8.2f,%8.2f), uv (%.6f,%.6f), col %08X\n", (n == 0) ? "vtx" : " ", vtx_i, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col); } ImGui::Selectable(buf, false); - if (ImGui::IsItemHovered()) + if (overlay_draw_list && ImGui::IsItemHovered()) { ImDrawListFlags backup_flags = overlay_draw_list->Flags; overlay_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines at is more readable for very large and thin triangles. From f270d6c52cdd4ce2dac1c6a52b49966a93f1b6a0 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 13 Apr 2018 16:21:34 +0200 Subject: [PATCH 118/828] Viewport: Fix to allow changing/animated window name to be reflected in the OS e.g. task bar, so named documents can appear properly. (#1542) --- imgui.cpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index aded38a81d24..e32fb60af7ab 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3596,15 +3596,15 @@ void ImGui::UpdatePlatformWindows() // Update title bar const char* title_begin = viewport->Window->Name; - const char* title_end = ImGui::FindRenderedTextEnd(title_begin); + char* title_end = (char*)ImGui::FindRenderedTextEnd(title_begin); const ImGuiID title_hash = ImHash(title_begin, (int)(title_end - title_begin)); if (viewport->LastNameHash != title_hash) { viewport->LastNameHash = title_hash; - char* title_displayed = ImStrdup(viewport->Window->Name); - title_displayed[title_end - title_begin] = 0; - g.PlatformIO.Platform_SetWindowTitle(viewport, title_displayed); - ImGui::MemFree(title_displayed); + char title_end_backup_c = *title_end; + *title_end = 0; // Cut existing buffer short instead of doing an alloc/free + g.PlatformIO.Platform_SetWindowTitle(viewport, title_begin); + *title_end = title_end_backup_c; } // Update alpha @@ -6422,6 +6422,13 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window = CreateNewWindow(name, size_on_first_use, flags); } + // Update name when it changes (which can only happen with the "###" operator), but only if it is meant to be displayed to the end user, else there is no point. + if (!window_just_created && window->Viewport && window->Viewport->Window == window && strcmp(name, window->Name) != 0) + { + IM_DELETE(window->Name); + window->Name = ImStrdup(name); + } + // Automatically disable manual moving/resizing when NoInputs is set if (flags & ImGuiWindowFlags_NoInputs) flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize; From 950539b7688e8f7776f7c087d59792765b760c84 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 13 Apr 2018 18:07:23 +0200 Subject: [PATCH 119/828] Viewport, Platform, DPI: Back-end store per-monitor DPI information in ImGuiPlatformMonitor. (#1542, #1676) --- examples/imgui_impl_glfw.cpp | 22 ++++++++++++---------- examples/imgui_impl_sdl2.cpp | 17 ++++++++++++----- examples/imgui_impl_win32.cpp | 3 ++- imgui.cpp | 2 +- imgui.h | 2 ++ 5 files changed, 29 insertions(+), 17 deletions(-) diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index 416e0f99bb38..cd628b716cb0 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -35,15 +35,11 @@ #define GLFW_EXPOSE_NATIVE_WIN32 #include // for glfwGetWin32Window #endif -#ifdef GLFW_HOVERED -#define GLFW_HAS_GLFW_HOVERED 1 -#else -#define GLFW_HAS_GLFW_HOVERED 0 -#endif -#define GLFW_HAS_WINDOW_ALPHA (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ -#define GLFW_HAS_WINDOW_TOPMOST (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ -#define GLFW_HAS_VULKAN (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ - +#define GLFW_HAS_WINDOW_TOPMOST (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ GLFW_FLOATING +#define GLFW_HAS_WINDOW_HOVERED (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ GLFW_HOVERED +#define GLFW_HAS_WINDOW_ALPHA (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ glfwSetWindowOpacity +#define GLFW_HAS_PER_MONITOR_DPI (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ glfwGetMonitorContentScale +#define GLFW_HAS_VULKAN (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ glfwCreateWindowSurface // Data enum GlfwClientApi @@ -530,7 +526,7 @@ static void ImGui_ImplGlfw_UpdateMonitors() ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); int monitors_count = 0; GLFWmonitor** glfw_monitors = glfwGetMonitors(&monitors_count); - platform_io.Monitors.resize(monitors_count); + platform_io.Monitors.resize(monitors_count, ImGuiPlatformMonitor()); for (int n = 0; n < monitors_count; n++) { int x, y; @@ -538,6 +534,12 @@ static void ImGui_ImplGlfw_UpdateMonitors() const GLFWvidmode* vid_mode = glfwGetVideoMode(glfw_monitors[n]); platform_io.Monitors[n].Pos = ImVec2((float)x, (float)y); platform_io.Monitors[n].Size = ImVec2((float)vid_mode->width, (float)vid_mode->height); +#if GLFW_HAS_PER_MONITOR_DPI + // Warning: the validity of monitor DPI information on Windows depends on the application DPI awareness settings, which generally needs to be set in the manifest or at runtime. + float x_scale, y_scale; + glfwGetMonitorContentScale(glfw_monitors[n], &x_scale, &y_scale); + platform_io.Monitors[n].DpiScale = x_scale; +#endif } } diff --git a/examples/imgui_impl_sdl2.cpp b/examples/imgui_impl_sdl2.cpp index e01bd79dc189..09391004963c 100644 --- a/examples/imgui_impl_sdl2.cpp +++ b/examples/imgui_impl_sdl2.cpp @@ -32,10 +32,11 @@ // SDL #include #include -#define SDL_HAS_CAPTURE_MOUSE SDL_VERSION_ATLEAST(2,0,4) -#define SDL_HAS_WINDOW_OPACITY SDL_VERSION_ATLEAST(2,0,5) -#define SDL_HAS_ALWAYS_ON_TOP SDL_VERSION_ATLEAST(2,0,5) -#define SDL_HAS_VULKAN SDL_VERSION_ATLEAST(2,0,6) +#define SDL_HAS_CAPTURE_MOUSE SDL_VERSION_ATLEAST(2,0,4) +#define SDL_HAS_WINDOW_OPACITY SDL_VERSION_ATLEAST(2,0,5) +#define SDL_HAS_ALWAYS_ON_TOP SDL_VERSION_ATLEAST(2,0,5) +#define SDL_HAS_PER_MONITOR_DPI SDL_VERSION_ATLEAST(2,0,4) +#define SDL_HAS_VULKAN SDL_VERSION_ATLEAST(2,0,6) #if !SDL_HAS_VULKAN static const Uint32 SDL_WINDOW_VULKAN = 0x10000000; #endif @@ -446,13 +447,19 @@ static void ImGui_ImplSDL2_UpdateMonitors() { ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); int display_count = SDL_GetNumVideoDisplays(); - platform_io.Monitors.resize(display_count); + platform_io.Monitors.resize(display_count, ImGuiPlatformMonitor()); for (int n = 0; n < display_count; n++) { + // Warning: the validity of monitor DPI information on Windows depends on the application DPI awareness settings, which generally needs to be set in the manifest or at runtime. SDL_Rect r; SDL_GetDisplayBounds(n, &r); platform_io.Monitors[n].Pos = ImVec2((float)r.x, (float)r.y); platform_io.Monitors[n].Size = ImVec2((float)r.w, (float)r.h); +#if SDL_HAS_PER_MONITOR_DPI + float dpi = 0.0f; + SDL_GetDisplayDPI(n, &dpi, NULL, NULL); + platform_io.Monitors[n].DpiScale = dpi / 96.0f; +#endif } } diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index efe5b461aa8c..1849a2d63f57 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -550,11 +550,12 @@ static LRESULT CALLBACK ImGui_ImplWin32_WndProcHandler_PlatformWindow(HWND hWnd, return DefWindowProc(hWnd, msg, wParam, lParam); } -static BOOL CALLBACK ImGui_ImplWin32_UpdateMonitors_EnumFunc(HMONITOR, HDC, LPRECT rect, LPARAM) +static BOOL CALLBACK ImGui_ImplWin32_UpdateMonitors_EnumFunc(HMONITOR monitor, HDC, LPRECT rect, LPARAM) { ImGuiPlatformMonitor imgui_monitor; imgui_monitor.Pos = ImVec2((float)rect->left, (float)rect->top); imgui_monitor.Size = ImVec2((float)(rect->right - rect->left), (float)(rect->bottom - rect->top)); + imgui_monitor.DpiScale = ImGui_ImplWin32_GetDpiScaleForMonitor(monitor); ImGui::GetPlatformIO().Monitors.push_back(imgui_monitor); return TRUE; } diff --git a/imgui.cpp b/imgui.cpp index e32fb60af7ab..789c5f4f3685 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -14258,7 +14258,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) for (int i = 0; i < g.PlatformIO.Monitors.Size; i++) { const ImGuiPlatformMonitor& mon = g.PlatformIO.Monitors[i]; - ImGui::BulletText("Monitor #%d: Min (%.0f,%.0f) Max (%.0f,%.0f) Size (%.0f,%.0f)", i, mon.Pos.x, mon.Pos.y, mon.Pos.x + mon.Size.x, mon.Pos.y + mon.Size.y, mon.Size.x, mon.Size.y); + ImGui::BulletText("Monitor #%d: DPI %.0f%%, Min (%.0f,%.0f), Max (%.0f,%.0f), Size (%.0f,%.0f)", i, mon.DpiScale * 100.0f, mon.Pos.x, mon.Pos.y, mon.Pos.x + mon.Size.x, mon.Pos.y + mon.Size.y, mon.Size.x, mon.Size.y); } ImGui::TreePop(); } diff --git a/imgui.h b/imgui.h index 6b4781d0ac59..2b4d88716250 100644 --- a/imgui.h +++ b/imgui.h @@ -1879,6 +1879,8 @@ struct ImGuiPlatformMonitor { ImVec2 Pos; ImVec2 Size; + float DpiScale; + ImGuiPlatformMonitor() { Pos = ImVec2(0,0); Size = ImVec2(0,0); DpiScale = 1.0f; } }; // (Optional) Setup required only if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) is enabled From 2ae19801a920b70b2923be88dbf2bca11f4a1c5f Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 13 Apr 2018 20:28:08 +0200 Subject: [PATCH 120/828] Viewport: Remove old code that never worked (testing wrong flag type, always false) + shuffled a few things in Begin() to clarify dependencies. Fixed viewport change on Begin/Begin/End/End patterns. (#1542) --- imgui.cpp | 43 +++++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 789c5f4f3685..e3321ef8f8b3 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5915,7 +5915,7 @@ static ImVec2 FindBestWindowPosForPopup(ImGuiWindow* window) ImRect r_screen = FindScreenRectForWindow(window); if (window->Flags & ImGuiWindowFlags_ChildMenu) { - // Child menus typically request _any_ position within the parent menu item, and then our FindBestPopupWindowPos() function will move the new menu outside the parent bounds. + // Child menus typically request _any_ position within the parent menu item, and then our FindBestWindowPosForPopup() function will move the new menu outside the parent bounds. // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu. IM_ASSERT(g.CurrentWindow == window); ImGuiWindow* parent_menu = g.CurrentWindowStack[g.CurrentWindowStack.Size - 2]; @@ -6205,6 +6205,8 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) } else if (window_follow_mouse_viewport && IsMousePosValid()) { + // 2018-04-13: the if() below tends to succeed but for a misleading reason: when moving a window and hovering another, UpdateMovingWindow would + // already have displaced the window outside of its viewport boundaries. While this is currently working it is very smelly. ImGuiViewportP* current_viewport = window->Viewport; if (!window_is_mouse_tooltip && (current_viewport == NULL || !current_viewport->GetRect().Contains(window->Rect()))) { @@ -6218,11 +6220,7 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) } else { - // When dragging a window back into another, only change viewport on mouse release (in UpdateMovingWindow()). - // This is so we don't require of the multi-viewport windowing back-end to preserve mouse buttons after a window closure, making it easier to implement them. - bool preserve_viewport = g.MovingWindow && g.MovingWindow->RootWindow == window && (window->Viewport->Flags & ImGuiWindowFlags_FullViewport); - if (!preserve_viewport) - window->Viewport = g.MousePosViewport; + window->Viewport = g.MousePosViewport; } } else if (g.NavWindow != NULL && g.NavWindow != window && (flags & ImGuiWindowFlags_Tooltip)) @@ -6557,12 +6555,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) SetCurrentWindow(window); flags = window->Flags; - if (p_open != NULL && window->Viewport->PlatformRequestClose && window->Viewport != GetMainViewport()) - { - window->Viewport->PlatformRequestClose = false; - *p_open = false; - } - // Lock window rounding, border size and padding for the frame (so that altering them doesn't cause inconsistencies) window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding; if (window->Flags & ImGuiWindowFlags_FullViewport) @@ -6590,14 +6582,18 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) } window->CollapseToggleWanted = false; - // SIZE + // UPDATE CONTENTS SIZE, REAPPEARING SIZE AND HIDDEN STATUS // Update contents size from last frame for auto-fitting (unless explicitly specified) window->SizeContents = CalcSizeContents(window); - - // Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows) if (window->HiddenFrames > 0) window->HiddenFrames--; + + // Hide new windows for one frame until they calculate their size + if (window_just_created && (!window_size_x_set_by_api || !window_size_y_set_by_api)) + window->HiddenFrames = 1; + + // Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows) if (window_just_activated_by_user && (flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0) { window->HiddenFrames = 1; @@ -6611,9 +6607,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) } } - // Hide new windows for one frame until they calculate their size - if (window_just_created && (!window_size_x_set_by_api || !window_size_y_set_by_api)) - window->HiddenFrames = 1; + // SIZE // Calculate auto-fit size, handle automatic resize const ImVec2 size_auto_fit = CalcSizeAutoFit(window, window->SizeContents); @@ -6912,6 +6906,13 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) NavInitWindow(window, false); } + // Close from platform window + if (p_open != NULL && window->Viewport->PlatformRequestClose && window->Viewport != GetMainViewport()) + { + window->Viewport->PlatformRequestClose = false; + *p_open = false; + } + // Title bar if (!(flags & ImGuiWindowFlags_NoTitleBar)) { @@ -7067,6 +7068,8 @@ void ImGui::End() g.CurrentPopupStack.pop_back(); CheckStacksSize(window, false); SetCurrentWindow(g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back()); + if (g.CurrentWindow) + SetCurrentViewport(g.CurrentWindow->Viewport); } // Vertical scrollbar @@ -12049,13 +12052,13 @@ bool ImGui::BeginMenu(const char* label, bool enabled) if (menuset_is_open) g.NavWindow = window; // Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent) - // The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu (using FindBestPopupWindowPos). + // The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu (using FindBestWindowPosForPopup). ImVec2 popup_pos, pos = window->DC.CursorPos; if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) { // Menu inside an horizontal menu bar // Selectable extend their highlight by half ItemSpacing in each direction. - // For ChildMenu, the popup position will be overwritten by the call to FindBestPopupWindowPos() in Begin() + // For ChildMenu, the popup position will be overwritten by the call to FindBestWindowPosForPopup() in Begin() popup_pos = ImVec2(pos.x - window->WindowPadding.x, pos.y - style.FramePadding.y + window->MenuBarHeight()); window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * 0.5f); PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing * 2.0f); From 0d16492eb787db77e9609a549af7c1cc1ea4d9d4 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 16 Apr 2018 10:47:33 +0200 Subject: [PATCH 121/828] Viewport: Moved ImGuiWindowFlags_FullViewport to an internal ViewportOwned flag. Renamed AddViewport to AddUpdateViewport. (#Viewport: Shuffled code in Begin to reset size of reappearing popups prior to selecting viewport, to avoid mistakenly using an old size. Moved code out of UpdateSelectWindowViewport() to the section of Begin where positions is being locked down, to avoid code in-between duplicating this. (#1542)) --- imgui.cpp | 57 +++++++++++++++++++++++++----------------------- imgui.h | 3 +-- imgui_internal.h | 3 ++- 3 files changed, 33 insertions(+), 30 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index e3321ef8f8b3..8cea73d5ebc2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -766,7 +766,7 @@ const ImGuiID IMGUI_VIEWPORT_DEFAULT_ID = 0x11111111; // Using an arbi static inline ImRect GetViewportRect(ImGuiWindow* window) { return window->Viewport->GetRect(); } static inline ImVec2 ConvertViewportPosToPlatformPos(const ImVec2& imgui_pos, ImGuiViewport* viewport) { return imgui_pos - viewport->Pos + viewport->PlatformPos; } static inline ImVec2 ConvertPlatformPosToViewportPos(const ImVec2& platform_pos, ImGuiViewport* viewport) { return platform_pos - viewport->PlatformPos + viewport->Pos; } -static ImGuiViewportP* AddViewport(ImGuiWindow* window, ImGuiID id, ImGuiViewportFlags flags, const ImVec2& platform_pos, const ImVec2& size); +static ImGuiViewportP* AddUpdateViewport(ImGuiWindow* window, ImGuiID id, ImGuiViewportFlags flags, const ImVec2& platform_pos, const ImVec2& size); static void UpdateViewports(); static void UpdateSelectWindowViewport(ImGuiWindow* window); static void SetCurrentViewport(ImGuiViewportP* viewport); @@ -1948,8 +1948,9 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) Scroll = ImVec2(0.0f, 0.0f); ScrollTarget = ImVec2(FLT_MAX, FLT_MAX); ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f); - ScrollbarX = ScrollbarY = false; ScrollbarSizes = ImVec2(0.0f, 0.0f); + ScrollbarX = ScrollbarY = false; + ViewportOwned = false; Active = WasActive = false; WriteAccessed = false; Collapsed = false; @@ -3313,24 +3314,25 @@ static void ImGui::NewFrameUpdateMovingWindowDropViewport(ImGuiWindow* window) ImRect mouse_viewport_rect = g.MousePosViewport->GetRect(); ImVec2 window_pos_in_mouse_viewport = ConvertPlatformPosToViewportPos(ConvertViewportPosToPlatformPos(window->Pos, window->Viewport), g.MousePosViewport); ImRect window_rect_in_mouse_viewport = ImRect(window_pos_in_mouse_viewport, window_pos_in_mouse_viewport + window->Size); - if ((g.MousePosViewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) && mouse_viewport_rect.Contains(window_rect_in_mouse_viewport)) + if ((g.MousePosViewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) && mouse_viewport_rect.Contains(window_rect_in_mouse_viewport)) { // Drop on an existing viewport ImGuiViewportP* old_viewport = window->Viewport; SetWindowViewportTranslateToPreservePlatformPos(window, window->Viewport, g.MousePosViewport); - // Our current scheme allow any window to land on a viewport, so when that viewport merges, move other windows as well - // FIXME-OPT - if (window->Flags & ImGuiWindowFlags_FullViewport) + // Move child/hosted windows as well (FIXME-OPT) + if (window->ViewportOwned) for (int n = 0; n < g.Windows.Size; n++) if (g.Windows[n]->Viewport == old_viewport) SetWindowViewportTranslateToPreservePlatformPos(g.Windows[n], old_viewport, g.MousePosViewport); + window->ViewportOwned = false; } else { // Create/persist new viewport ImVec2 platform_pos = ConvertViewportPosToPlatformPos(window->Pos, window->Viewport); - ImGuiViewportP* viewport = AddViewport(window, window->ID, 0, platform_pos, window->Size); + ImGuiViewportP* viewport = AddUpdateViewport(window, window->ID, 0, platform_pos, window->Size); + IM_ASSERT(viewport == window->Viewport); SetWindowViewportTranslateToPreservePlatformPos(window, window->Viewport, viewport); } } @@ -3476,7 +3478,7 @@ static void ImGui::UpdateViewports() ImVec2 main_viewport_platform_pos = ImVec2(0.0f, 0.0f); if ((g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) main_viewport_platform_pos = g.PlatformIO.Platform_GetWindowPos(main_viewport); - AddViewport(NULL, IMGUI_VIEWPORT_DEFAULT_ID, ImGuiViewportFlags_CanHostOtherWindows, main_viewport_platform_pos, g.IO.DisplaySize); + AddUpdateViewport(NULL, IMGUI_VIEWPORT_DEFAULT_ID, ImGuiViewportFlags_CanHostOtherWindows, main_viewport_platform_pos, g.IO.DisplaySize); if (!(g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) { @@ -4699,7 +4701,7 @@ void ImGui::SetCurrentViewport(ImGuiViewportP* viewport) g.PlatformIO.Platform_OnChangedViewport(g.CurrentViewport); } -ImGuiViewportP* ImGui::AddViewport(ImGuiWindow* window, ImGuiID id, ImGuiViewportFlags flags, const ImVec2& platform_pos, const ImVec2& size) +ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, ImGuiViewportFlags flags, const ImVec2& platform_pos, const ImVec2& size) { ImGuiContext& g = *GImGui; IM_ASSERT(id != 0); @@ -4733,6 +4735,9 @@ ImGuiViewportP* ImGui::AddViewport(ImGuiWindow* window, ImGuiID id, ImGuiViewpor viewport->PlatformPos = platform_pos; viewport->LastFrameActive = g.FrameCount; + if (window != NULL) + window->ViewportOwned = true; + // Request an initial DpiScale before the OS platform window creation // This is so we can select an appropriate font size on the first frame of our window lifetime if (g.PlatformIO.Platform_GetWindowDpiScale) @@ -6070,7 +6075,7 @@ static ImVec2 CalcSizeAutoFit(ImGuiWindow* window, const ImVec2& size_contents) else { // When the window cannot fit all contents (either because of constraints, either because screen is too small): we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than ViewportSize-WindowPadding. - ImVec2 viewport_size = (window->Flags & ImGuiWindowFlags_FullViewport) ? ImVec2(FLT_MAX, FLT_MAX) : window->Viewport->Size; + ImVec2 viewport_size = window->ViewportOwned ? ImVec2(FLT_MAX, FLT_MAX) : window->Viewport->Size; size_auto_fit = ImClamp(size_contents, style.WindowMinSize, ImMax(style.WindowMinSize, viewport_size - g.Style.DisplaySafeAreaPadding)); ImVec2 size_auto_fit_after_constraint = CalcSizeAfterConstraint(window, size_auto_fit); if (size_auto_fit_after_constraint.x < size_contents.x && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar)) @@ -6156,6 +6161,7 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) { ImGuiContext& g = *GImGui; ImGuiWindowFlags flags = window->Flags; + window->ViewportOwned = false; // Restore main viewport if multi-viewport is not supported by the back-end ImGuiViewportP* main_viewport = g.Viewports[0]; @@ -6186,8 +6192,7 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) window->Viewport = FindViewportByID(window->ViewportId); if (window->Viewport == NULL && window->ViewportPlatformPos.x != FLT_MAX && window->ViewportPlatformPos.y != FLT_MAX) { - ImGuiViewportP* viewport = AddViewport(window, window->ID, ImGuiViewportFlags_NoDecoration, window->ViewportPlatformPos, window->Size); - window->Flags |= ImGuiWindowFlags_FullViewport; + ImGuiViewportP* viewport = AddUpdateViewport(window, window->ID, ImGuiViewportFlags_NoDecoration, window->ViewportPlatformPos, window->Size); window->Viewport = viewport; created_viewport = true; } @@ -6213,8 +6218,7 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) // Calculate mouse position in OS/platform coordinates, create a Viewport at this position. ImVec2 platform_pos = ConvertViewportPosToPlatformPos(g.IO.MousePos - g.ActiveIdClickOffset, g.MousePosViewport); ImGuiViewportFlags viewport_flags = ImGuiViewportFlags_NoDecoration | ImGuiViewportFlags_NoFocusOnAppearing | ImGuiViewportFlags_NoInputs; - ImGuiViewportP* viewport = AddViewport(window, window->ID, viewport_flags, platform_pos, window->Size); - window->Flags |= ImGuiWindowFlags_FullViewport; + ImGuiViewportP* viewport = AddUpdateViewport(window, window->ID, viewport_flags, platform_pos, window->Size); window->Viewport = viewport; created_viewport = true; } @@ -6239,14 +6243,14 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) if (!window->Viewport->PlatformRequestResize) window->Viewport->Size = window->Size; window->Viewport->PlatformPos = ConvertViewportPosToPlatformPos(window->Pos, window->Viewport); - window->Flags |= ImGuiWindowFlags_FullViewport; + window->ViewportOwned = true; } // If the OS window has a title bar, hide our imgui title bar - if ((window->Flags & ImGuiWindowFlags_FullViewport) && !(window->Viewport->Flags & ImGuiViewportFlags_NoDecoration)) + if (window->ViewportOwned && !(window->Viewport->Flags & ImGuiViewportFlags_NoDecoration)) window->Flags |= ImGuiWindowFlags_NoTitleBar; - if (window->Flags & ImGuiWindowFlags_FullViewport) + if (window->ViewportOwned) { // We currently have window fully covering a viewport and we disable WindowBg alpha, so clearing is not necessary window->Viewport->Flags |= ImGuiViewportFlags_NoRendererClear; @@ -6386,7 +6390,7 @@ static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au } if (pos_target.x != FLT_MAX) { - if (window->Flags & ImGuiWindowFlags_FullViewport) + if (window->ViewportOwned) window->Viewport->PlatformPos = ConvertViewportPosToPlatformPos(ImFloor(pos_target), window->Viewport); else window->Pos = window->PosFloat = ImFloor(pos_target); @@ -6557,7 +6561,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Lock window rounding, border size and padding for the frame (so that altering them doesn't cause inconsistencies) window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding; - if (window->Flags & ImGuiWindowFlags_FullViewport) + if (window->ViewportOwned) window->WindowRounding = 0.0f; window->WindowBorderSize = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildBorderSize : ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize; window->WindowPadding = style.WindowPadding; @@ -6683,7 +6687,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Clamp position so window stays visible within its viewport // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing. ImRect viewport_rect(GetViewportRect(window)); - if (!window_pos_set_by_api && !(flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_FullViewport)) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) + if (!window_pos_set_by_api && !(flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) && !window->ViewportOwned && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) if (viewport_rect.GetWidth() > 0 && viewport_rect.GetHeight() > 0.0f) { ImVec2 padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding); @@ -6724,12 +6728,11 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // When a window is marked as owning its viewport, we immediately update the viewport after a resize window->ViewportPlatformPos = window->Viewport->PlatformPos; - if (flags & ImGuiWindowFlags_FullViewport) - if (window->Size.x != window->Viewport->Size.x || window->Size.y != window->Viewport->Size.y) - { - window->Viewport->Size = window->SizeFull; - viewport_rect = GetViewportRect(window); - } + if (window->ViewportOwned && (window->Size.x != window->Viewport->Size.x || window->Size.y != window->Viewport->Size.y)) + { + window->Viewport->Size = window->SizeFull; + viewport_rect = GetViewportRect(window); + } // DRAWING @@ -6780,7 +6783,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) ImU32 bg_col = GetColorU32(GetWindowBgColorIdxFromFlags(flags)); if (g.NextWindowData.BgAlphaCond != 0) bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(g.NextWindowData.BgAlphaVal) << IM_COL32_A_SHIFT); - if (window->Flags & ImGuiWindowFlags_FullViewport) + if (window->ViewportOwned) bg_col = (bg_col | IM_COL32_A_MASK); window->DrawList->AddRectFilled(window->Pos+ImVec2(0,window->TitleBarHeight()), window->Pos+window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Bot); diff --git a/imgui.h b/imgui.h index 2b4d88716250..9b9a3d98d31a 100644 --- a/imgui.h +++ b/imgui.h @@ -593,8 +593,7 @@ enum ImGuiWindowFlags_ ImGuiWindowFlags_Tooltip = 1 << 25, // Don't use! For internal use by BeginTooltip() ImGuiWindowFlags_Popup = 1 << 26, // Don't use! For internal use by BeginPopup() ImGuiWindowFlags_Modal = 1 << 27, // Don't use! For internal use by BeginPopupModal() - ImGuiWindowFlags_ChildMenu = 1 << 28, // Don't use! For internal use by BeginMenu() - ImGuiWindowFlags_FullViewport = 1 << 29 // Don't use! For internal use by Begin() and viewports. + ImGuiWindowFlags_ChildMenu = 1 << 28 // Don't use! For internal use by BeginMenu() }; // Flags for ImGui::InputText() diff --git a/imgui_internal.h b/imgui_internal.h index a210592d8204..e7141765182a 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -955,8 +955,9 @@ struct IMGUI_API ImGuiWindow ImVec2 Scroll; ImVec2 ScrollTarget; // target scroll position. stored as cursor position with scrolling canceled out, so the highest point is always 0.0f. (FLT_MAX for no change) ImVec2 ScrollTargetCenterRatio; // 0.0f = scroll so that target position is at top, 0.5f = scroll so that target position is centered - bool ScrollbarX, ScrollbarY; ImVec2 ScrollbarSizes; + bool ScrollbarX, ScrollbarY; + bool ViewportOwned; bool Active; // Set to true on Begin(), unless Collapsed bool WasActive; bool WriteAccessed; // Set to true when any widget access the current window From 2bd0ee27f0b771e8de3952e1506b941e43dd5ba4 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 16 Apr 2018 11:21:14 +0200 Subject: [PATCH 122/828] Viewport: Shuffled code in Begin to reset size of reappearing popups prior to selecting viewport, to avoid mistakenly using an old size. Moved code out of UpdateSelectWindowViewport() to the section of Begin where positions is being locked down, to avoid code in-between duplicating this. (#1542) --- imgui.cpp | 77 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 39 insertions(+), 38 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 8cea73d5ebc2..1673e865229e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6250,17 +6250,6 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) if (window->ViewportOwned && !(window->Viewport->Flags & ImGuiViewportFlags_NoDecoration)) window->Flags |= ImGuiWindowFlags_NoTitleBar; - if (window->ViewportOwned) - { - // We currently have window fully covering a viewport and we disable WindowBg alpha, so clearing is not necessary - window->Viewport->Flags |= ImGuiViewportFlags_NoRendererClear; - - // Position - SetWindowPos(window, window->Viewport->Pos, ImGuiCond_Always); - if (window->Viewport->PlatformRequestResize) - SetWindowSize(window, window->Viewport->Size, ImGuiCond_Always); - } - window->ViewportId = window->Viewport->ID; } @@ -6550,8 +6539,34 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->LastFrameActive = current_frame; window->IDStack.resize(1); - // VIEWPORT - // We need to do this before using any style/font sizes, as viewport with a different DPI will affect those sizes. + // UPDATE CONTENTS SIZE, UPDATE HIDDEN STATUS + + // Update contents size from last frame for auto-fitting (or use explicit size) + window->SizeContents = CalcSizeContents(window); + if (window->HiddenFrames > 0) + window->HiddenFrames--; + + // Hide new windows for one frame until they calculate their size + if (window_just_created && (!window_size_x_set_by_api || !window_size_y_set_by_api)) + window->HiddenFrames = 1; + + // Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows) + // We reset Size/SizeContents for reappearing popups/tooltips early in this function, so further code won't be tempted to use the old size. + if (window_just_activated_by_user && (flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0) + { + window->HiddenFrames = 1; + if (flags & ImGuiWindowFlags_AlwaysAutoResize) + { + if (!window_size_x_set_by_api) + window->Size.x = window->SizeFull.x = 0.f; + if (!window_size_y_set_by_api) + window->Size.y = window->SizeFull.y = 0.f; + window->SizeContents = ImVec2(0.f, 0.f); + } + } + + // SELECT VIEWPORT + // We need to do this before using any style/font sizes, as viewport with a different DPI may affect font sizes. UpdateSelectWindowViewport(window); SetCurrentViewport(window->Viewport); @@ -6586,31 +6601,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) } window->CollapseToggleWanted = false; - // UPDATE CONTENTS SIZE, REAPPEARING SIZE AND HIDDEN STATUS - - // Update contents size from last frame for auto-fitting (unless explicitly specified) - window->SizeContents = CalcSizeContents(window); - if (window->HiddenFrames > 0) - window->HiddenFrames--; - - // Hide new windows for one frame until they calculate their size - if (window_just_created && (!window_size_x_set_by_api || !window_size_y_set_by_api)) - window->HiddenFrames = 1; - - // Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows) - if (window_just_activated_by_user && (flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0) - { - window->HiddenFrames = 1; - if (flags & ImGuiWindowFlags_AlwaysAutoResize) - { - if (!window_size_x_set_by_api) - window->Size.x = window->SizeFull.x = 0.f; - if (!window_size_y_set_by_api) - window->Size.y = window->SizeFull.y = 0.f; - window->SizeContents = ImVec2(0.f, 0.f); - } - } - // SIZE // Calculate auto-fit size, handle automatic resize @@ -6694,6 +6684,17 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->PosFloat = ImMax(window->PosFloat + window->Size, viewport_rect.Min + padding) - window->Size; window->PosFloat = ImMin(window->PosFloat, viewport_rect.Max - padding); } + + // Position window to fit within viewport + // We can also tell the back-end that clearing the platform window won't be necessary, as our window is filling the viewport and we have disabled BgAlpha + if (window->ViewportOwned) + { + window->Viewport->Flags |= ImGuiViewportFlags_NoRendererClear; + window->PosFloat = window->Viewport->Pos; + if (window->Viewport->PlatformRequestResize) + window->Size = window->SizeFull = window->Viewport->Size; + } + window->Pos = ImFloor(window->PosFloat); // Default item width. Make it proportional to window size if window manually resizes From 4649bf042e4894882dc5acba42a131ad134e849c Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 16 Apr 2018 17:08:23 +0200 Subject: [PATCH 123/828] Viewport: Render: Fix draw list build code to allow child windows to be in a different viewports (which will happen with e.g. extruding menus). (#1542) --- imgui.cpp | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 1673e865229e..807aadbf06dd 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -727,10 +727,6 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWind static void CheckStacksSize(ImGuiWindow* window, bool write); static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window); -static void AddDrawListToDrawData(ImVector* out_list, ImDrawList* draw_list); -static void AddWindowToDrawData(ImVector* out_list, ImGuiWindow* window); -static void AddWindowToSortedBuffer(ImVector* out_sorted_windows, ImGuiWindow* window); - static ImGuiWindowSettings* AddWindowSettings(const char* name); static void LoadIniSettingsFromDisk(const char* ini_filename); @@ -4369,25 +4365,24 @@ static void AddDrawListToDrawData(ImVector* out_list, ImDrawList* d out_list->push_back(draw_list); } -static void AddWindowToDrawData(ImVector* out_list, ImGuiWindow* window) +static void AddWindowToDrawData(ImGuiWindow* window, int layer) { - AddDrawListToDrawData(out_list, window->DrawList); + AddDrawListToDrawData(&window->Viewport->DrawDataBuilder.Layers[layer], window->DrawList); for (int i = 0; i < window->DC.ChildWindows.Size; i++) { ImGuiWindow* child = window->DC.ChildWindows[i]; - if (child->Active && child->HiddenFrames == 0) // clipped children may have been marked not active - AddWindowToDrawData(out_list, child); + if (child->Active && child->HiddenFrames == 0) // Clipped children may have been marked not active + AddWindowToDrawData(child, layer); } } -static void AddWindowToDrawDataSelectLayer(ImGuiWindow* window) +// Layer is locked for the root window, however child windows may use a different viewport (e.g. extruding menu) +static void AddRootWindowToDrawData(ImGuiWindow* window) { ImGuiContext& g = *GImGui; g.IO.MetricsActiveWindows++; - if (window->Flags & ImGuiWindowFlags_Tooltip) - AddWindowToDrawData(&window->Viewport->DrawDataBuilder.Layers[1], window); - else - AddWindowToDrawData(&window->Viewport->DrawDataBuilder.Layers[0], window); + int layer = (window->Flags & ImGuiWindowFlags_Tooltip) ? 1 : 0; + AddWindowToDrawData(window, layer); } void ImDrawDataBuilder::FlattenIntoSingleLayer() @@ -4564,10 +4559,10 @@ void ImGui::Render() { ImGuiWindow* window = g.Windows[n]; if (window->Active && window->HiddenFrames == 0 && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != window_to_render_front_most) - AddWindowToDrawDataSelectLayer(window); + AddRootWindowToDrawData(window); } if (window_to_render_front_most && window_to_render_front_most->Active && window_to_render_front_most->HiddenFrames == 0) // NavWindowingTarget is always temporarily displayed as the front-most window - AddWindowToDrawDataSelectLayer(window_to_render_front_most); + AddRootWindowToDrawData(window_to_render_front_most); // Draw software mouse cursor if requested ImVec2 offset, size, uv[4]; @@ -12160,6 +12155,7 @@ bool ImGui::BeginMenu(const char* label, bool enabled) if (menu_is_open) { + // Sub-menus are ChildWindow so that mouse can be hovering across them (otherwise top-most popup menu would steal focus and not allow hovering on parent menu) SetNextWindowPos(popup_pos, ImGuiCond_Always); ImGuiWindowFlags flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings | ((window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) ? ImGuiWindowFlags_ChildMenu|ImGuiWindowFlags_ChildWindow : ImGuiWindowFlags_ChildMenu); menu_is_open = BeginPopupEx(id, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display) From 50e453a3e2529b73702574459acb72c8bdcceb48 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 16 Apr 2018 17:14:10 +0200 Subject: [PATCH 124/828] Viewport: Comments, tweaks, renaming. Removed unnecessary stuff. Fixed zealous Clang warning. (#1542) --- imgui.cpp | 110 ++++++++++++++++++++++++----------------------- imgui_internal.h | 19 ++++---- 2 files changed, 66 insertions(+), 63 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 807aadbf06dd..3455a15a6edc 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -759,7 +759,6 @@ static void FocusFrontMostActiveWindow(ImGuiWindow* ignore_window); // Viewports const ImGuiID IMGUI_VIEWPORT_DEFAULT_ID = 0x11111111; // Using an arbitrary constant instead of e.g. ImHash("ViewportDefault", 0); so it's easier to spot in the debugger. The exact value doesn't matter. -static inline ImRect GetViewportRect(ImGuiWindow* window) { return window->Viewport->GetRect(); } static inline ImVec2 ConvertViewportPosToPlatformPos(const ImVec2& imgui_pos, ImGuiViewport* viewport) { return imgui_pos - viewport->Pos + viewport->PlatformPos; } static inline ImVec2 ConvertPlatformPosToViewportPos(const ImVec2& platform_pos, ImGuiViewport* viewport) { return platform_pos - viewport->PlatformPos + viewport->Pos; } static ImGuiViewportP* AddUpdateViewport(ImGuiWindow* window, ImGuiID id, ImGuiViewportFlags flags, const ImVec2& platform_pos, const ImVec2& size); @@ -2147,7 +2146,7 @@ static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFla } // Filter by viewport - if (window->Viewport != g.MousePosViewport) + if (window->Viewport != g.MouseRefViewport) return false; return true; @@ -2775,12 +2774,11 @@ void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit) static ImVec2 NavCalcPreferredMousePos() { ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.NavWindow; - if (!window) + if (!g.NavWindow) return g.IO.MousePos; - const ImRect& rect_rel = window->NavRectRel[g.NavLayer]; + const ImRect& rect_rel = g.NavWindow->NavRectRel[g.NavLayer]; ImVec2 pos = g.NavWindow->Pos + ImVec2(rect_rel.Min.x + ImMin(g.Style.FramePadding.x*4, rect_rel.GetWidth()), rect_rel.Max.y - ImMin(g.Style.FramePadding.y, rect_rel.GetHeight())); - ImRect visible_rect = ImGui::GetViewportRect(window); + ImRect visible_rect = g.NavWindow->Viewport->GetRect(); return ImFloor(ImClamp(pos, visible_rect.Min, visible_rect.Max)); // ImFloor() is important because non-integer mouse position application in back-end might be lossy and result in undesirable non-zero delta. } @@ -3307,20 +3305,20 @@ static void ImGui::NewFrameUpdateMovingWindowDropViewport(ImGuiWindow* window) if (!(g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) return; - ImRect mouse_viewport_rect = g.MousePosViewport->GetRect(); - ImVec2 window_pos_in_mouse_viewport = ConvertPlatformPosToViewportPos(ConvertViewportPosToPlatformPos(window->Pos, window->Viewport), g.MousePosViewport); + ImRect mouse_viewport_rect = g.MouseRefViewport->GetRect(); + ImVec2 window_pos_in_mouse_viewport = ConvertPlatformPosToViewportPos(ConvertViewportPosToPlatformPos(window->Pos, window->Viewport), g.MouseRefViewport); ImRect window_rect_in_mouse_viewport = ImRect(window_pos_in_mouse_viewport, window_pos_in_mouse_viewport + window->Size); - if ((g.MousePosViewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) && mouse_viewport_rect.Contains(window_rect_in_mouse_viewport)) + if ((g.MouseRefViewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) && mouse_viewport_rect.Contains(window_rect_in_mouse_viewport)) { // Drop on an existing viewport ImGuiViewportP* old_viewport = window->Viewport; - SetWindowViewportTranslateToPreservePlatformPos(window, window->Viewport, g.MousePosViewport); + SetWindowViewportTranslateToPreservePlatformPos(window, window->Viewport, g.MouseRefViewport); // Move child/hosted windows as well (FIXME-OPT) if (window->ViewportOwned) for (int n = 0; n < g.Windows.Size; n++) if (g.Windows[n]->Viewport == old_viewport) - SetWindowViewportTranslateToPreservePlatformPos(g.Windows[n], old_viewport, g.MousePosViewport); + SetWindowViewportTranslateToPreservePlatformPos(g.Windows[n], old_viewport, g.MouseRefViewport); window->ViewportOwned = false; } else @@ -3421,9 +3419,9 @@ static void ImGui::UpdateViewports() // Destroy if (viewport == viewport_ref) viewport_ref = NULL; - if (viewport == g.MousePosViewport) g.MousePosViewport = NULL; - if (viewport == g.MousePosPrevViewport) g.MousePosPrevViewport = NULL; - if (viewport == g.MouseHoveredPrevViewport) g.MouseHoveredPrevViewport = NULL; + if (viewport == g.MouseRefViewport) g.MouseRefViewport = NULL; + if (viewport == g.MouseRefPrevViewport) g.MouseRefPrevViewport = NULL; + if (viewport == g.MouseHoveredLastViewport) g.MouseHoveredLastViewport = NULL; IM_ASSERT(viewport->RendererUserData == NULL && viewport->PlatformUserData == NULL && viewport->PlatformHandle == NULL); IM_ASSERT(g.PlatformIO.Viewports.contains(viewport) == false); IM_DELETE(viewport); @@ -3478,7 +3476,7 @@ static void ImGui::UpdateViewports() if (!(g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) { - g.MousePosViewport = g.MousePosPrevViewport = main_viewport; + g.MouseRefViewport = g.MouseRefPrevViewport = main_viewport; return; } @@ -3496,12 +3494,13 @@ static void ImGui::UpdateViewports() } else { - // If the back-end doesn't set MouseLastHoveredViewport or doesn't honor ImGuiViewportFlags_NoInputs, we do a search ourselves. - // This search won't take account of the possibility that non-imgui windows may be in-between our dragged window and our target window. + // If the back-end doesn't know how to honor ImGuiViewportFlags_NoInputs, we do a search ourselves. Note that this search: + // A) won't take account of the possibility that non-imgui windows may be in-between our dragged window and our target window. + // B) uses LastFrameAsRefViewport as a flawed replacement for the last time a window was focused (we could/should fix that by introducing Focus functions in PlatformIO) viewport_hovered = FindViewportHoveredFromPlatformWindowStack(mouse_platform_pos); } if (viewport_hovered != NULL) - g.MouseHoveredPrevViewport = viewport_hovered; + g.MouseHoveredLastViewport = viewport_hovered; // If reference viewport just has been deleted, use a position relative to the main viewport if (viewport_ref == NULL) @@ -3510,9 +3509,9 @@ static void ImGui::UpdateViewports() g.IO.MousePos = ConvertPlatformPosToViewportPos(mouse_platform_pos, viewport_ref); } - g.MousePosPrevViewport = g.MousePosViewport; - g.MousePosViewport = viewport_ref; - g.MousePosViewport->LastFrameAsRefViewport = g.FrameCount; + g.MouseRefPrevViewport = g.MouseRefViewport; + g.MouseRefViewport = viewport_ref; + g.MouseRefViewport->LastFrameAsRefViewport = g.FrameCount; // When dragging something, always refer to the last hovered viewport (so when we are between viewport, our dragged preview will tend to show in the last viewport) const bool is_mouse_dragging_with_an_expected_destination = g.DragDropActive || (g.MovingWindow != NULL); @@ -3520,15 +3519,15 @@ static void ImGui::UpdateViewports() if (is_mouse_dragging_with_an_expected_destination || is_mouse_all_released) { if (is_mouse_dragging_with_an_expected_destination) - viewport_hovered = g.MouseHoveredPrevViewport; - if (viewport_hovered != NULL && viewport_hovered != g.MousePosViewport && !(viewport_hovered->Flags & ImGuiViewportFlags_NoInputs)) + viewport_hovered = g.MouseHoveredLastViewport; + if (viewport_hovered != NULL && viewport_hovered != g.MouseRefViewport && !(viewport_hovered->Flags & ImGuiViewportFlags_NoInputs)) { - g.IO.MousePos = ConvertPlatformPosToViewportPos(ConvertViewportPosToPlatformPos(g.IO.MousePos, g.MousePosViewport), viewport_hovered); - g.MousePosViewport = viewport_hovered; + g.IO.MousePos = ConvertPlatformPosToViewportPos(ConvertViewportPosToPlatformPos(g.IO.MousePos, g.MouseRefViewport), viewport_hovered); + g.MouseRefViewport = viewport_hovered; } } - IM_ASSERT(g.MousePosViewport != NULL); + IM_ASSERT(g.MouseRefViewport != NULL); } void ImGui::UpdatePlatformWindows() @@ -3594,7 +3593,7 @@ void ImGui::UpdatePlatformWindows() // Update title bar const char* title_begin = viewport->Window->Name; - char* title_end = (char*)ImGui::FindRenderedTextEnd(title_begin); + char* title_end = (char*)(intptr_t)ImGui::FindRenderedTextEnd(title_begin); const ImGuiID title_hash = ImHash(title_begin, (int)(title_end - title_begin)); if (viewport->LastNameHash != title_hash) { @@ -3659,7 +3658,7 @@ static void NewFrameUpdateMouseInputs() ImGuiContext& g = *GImGui; // If mouse just appeared or disappeared (usually denoted by -FLT_MAX component, but in reality we test for -256000.0f) we cancel out movement in MouseDelta - if (ImGui::IsMousePosValid(&g.IO.MousePos) && ImGui::IsMousePosValid(&g.IO.MousePosPrev) && g.MousePosViewport == g.MousePosPrevViewport) + if (ImGui::IsMousePosValid(&g.IO.MousePos) && ImGui::IsMousePosValid(&g.IO.MousePosPrev) && g.MouseRefViewport == g.MouseRefPrevViewport) g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev; else g.IO.MouseDelta = ImVec2(0.0f, 0.0f); @@ -3689,7 +3688,7 @@ static void NewFrameUpdateMouseInputs() g.IO.MouseClickedPos[i] = g.IO.MousePos; g.IO.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f); g.IO.MouseDragMaxDistanceSqr[i] = 0.0f; - g.MouseClickedPosViewportId[i] = g.MousePosViewport->ID; + g.MouseClickedPosViewportId[i] = g.MouseRefViewport->ID; } else if (g.IO.MouseDown[i]) { @@ -3714,7 +3713,7 @@ void ImGui::NewFrameUpdateHoveredWindowAndCaptureFlags() // - We also support the moved window toggling the NoInputs flag after moving has started in order to be able to detect windows below it, which is useful for e.g. docking mechanisms. g.HoveredWindow = (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoInputs)) ? g.MovingWindow : FindHoveredWindow(); g.HoveredRootWindow = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL; - IM_ASSERT(g.HoveredWindow == NULL || g.HoveredWindow == g.MovingWindow || g.HoveredWindow->Viewport == g.MousePosViewport); + IM_ASSERT(g.HoveredWindow == NULL || g.HoveredWindow == g.MovingWindow || g.HoveredWindow->Viewport == g.MouseRefViewport); // Modal windows prevents cursor from hovering behind them. ImGuiWindow* modal_window = GetFrontMostPopupModal(); @@ -4127,7 +4126,7 @@ void ImGui::Shutdown(ImGuiContext* context) g.FontStack.clear(); g.OpenPopupStack.clear(); g.CurrentPopupStack.clear(); - g.CurrentViewport = g.MousePosViewport = g.MousePosPrevViewport = g.MouseHoveredPrevViewport = NULL; + g.CurrentViewport = g.MouseRefViewport = g.MouseRefPrevViewport = g.MouseHoveredLastViewport = NULL; for (int i = 0; i < g.Viewports.Size; i++) IM_DELETE(g.Viewports[i]); g.Viewports.clear(); @@ -4575,7 +4574,7 @@ void ImGui::Render() for (int viewport_n = 0; viewport_n < g.Viewports.Size; viewport_n++) { ImGuiViewportP* viewport = g.Viewports[viewport_n]; - ImVec2 pos = (g.MousePosViewport == viewport) ? main_pos : ConvertPlatformPosToViewportPos(ConvertViewportPosToPlatformPos(main_pos, g.MousePosViewport), viewport); + ImVec2 pos = (g.MouseRefViewport == viewport) ? main_pos : ConvertPlatformPosToViewportPos(ConvertViewportPosToPlatformPos(main_pos, g.MouseRefViewport), viewport); if (viewport->GetRect().Overlaps(ImRect(pos, pos + ImVec2(2,2)*sc + size * sc))) { ImDrawList* draw_list = GetOverlayDrawList(viewport); @@ -4674,7 +4673,7 @@ static void ImGui::TranslateOrEraseViewports(int viewport_idx_min, int viewport_ ImGuiID viewport_id = viewport->ID; if (viewport_id == g.IO.MousePosViewport) // We are early in NewFrame and g.MousePosViewport hasn't been set, patch the source. g.IO.MousePos.x += delta_x; - if (viewport == g.MousePosPrevViewport) + if (viewport == g.MouseRefPrevViewport) g.IO.MousePosPrev.x += delta_x; for (int mouse_n = 0; mouse_n < IM_ARRAYSIZE(g.MouseClickedPosViewportId); mouse_n++) if (g.MouseClickedPosViewportId[mouse_n] == viewport_id) @@ -5106,7 +5105,7 @@ static ImGuiWindow* FindHoveredWindow() if (window->Flags & ImGuiWindowFlags_NoInputs) continue; IM_ASSERT(window->Viewport); - if (window->Viewport != g.MousePosViewport) + if (window->Viewport != g.MouseRefViewport) continue; // Using the clipped AABB, a child window will typically be clipped by its parent (not always) @@ -5134,7 +5133,7 @@ bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool c const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding); if (!rect_for_touch.Contains(g.IO.MousePos)) return false; - if (!g.MousePosViewport->GetRect().Overlaps(rect_clipped)) + if (!g.MouseRefViewport->GetRect().Overlaps(rect_clipped)) return false; return true; } @@ -5290,7 +5289,7 @@ ImVec2 ImGui::GetMouseDragDelta(int button, float lock_threshold) lock_threshold = g.IO.MouseDragThreshold; if (g.IO.MouseDown[button]) if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold) - if (g.MousePosViewport->ID == g.MouseClickedPosViewportId[button]) + if (g.MouseRefViewport->ID == g.MouseClickedPosViewportId[button]) return g.IO.MousePos - g.IO.MouseClickedPos[button]; // Assume we can only get active with left-mouse button (at the moment). return ImVec2(0.0f, 0.0f); } @@ -5623,7 +5622,7 @@ bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags fla // Center modal windows by default // FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window. if (g.NextWindowData.PosCond == 0) - SetNextWindowPos(GetViewportRect(window).GetCenter(), ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); + SetNextWindowPos(window->Viewport->GetCenter(), ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); bool is_open = Begin(name, p_open, flags | ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings); if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display) @@ -5848,7 +5847,7 @@ enum ImGuiPopupPositionPolicy static ImRect FindScreenRectForWindow(ImGuiWindow* window) { ImVec2 padding = GImGui->Style.DisplaySafeAreaPadding; - ImRect r_screen(ImGui::GetViewportRect(window)); + ImRect r_screen(window->Viewport->GetRect()); r_screen.Expand(ImVec2((window->Size.x - r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (window->Size.y - r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f)); return r_screen; } @@ -5927,11 +5926,13 @@ static ImVec2 FindBestWindowPosForPopup(ImGuiWindow* window) r_avoid = ImRect(parent_menu->Pos.x + horizontal_overlap, -FLT_MAX, parent_menu->Pos.x + parent_menu->Size.x - horizontal_overlap - parent_menu->ScrollbarSizes.x, FLT_MAX); return FindBestWindowPosForPopupEx(window->PosFloat, window->Size, &window->AutoPosLastDirection, r_screen, r_avoid); } + if (window->Flags & ImGuiWindowFlags_Popup) { ImRect r_avoid(window->PosFloat.x - 1, window->PosFloat.y - 1, window->PosFloat.x + 1, window->PosFloat.y + 1); return FindBestWindowPosForPopupEx(window->PosFloat, window->Size, &window->AutoPosLastDirection, r_screen, r_avoid); } + if (window->Flags & ImGuiWindowFlags_Tooltip) { // Position tooltip (always follows mouse) @@ -6060,25 +6061,25 @@ static ImVec2 CalcSizeAutoFit(ImGuiWindow* window, const ImVec2& size_contents) { ImGuiContext& g = *GImGui; ImGuiStyle& style = g.Style; - ImGuiWindowFlags flags = window->Flags; - ImVec2 size_auto_fit; - if ((flags & ImGuiWindowFlags_Tooltip) != 0) + if (window->Flags & ImGuiWindowFlags_Tooltip) { - // Tooltip always resize. We keep the spacing symmetric on both axises for aesthetic purpose. - size_auto_fit = size_contents; + // Tooltip always resize + return size_contents; } else { - // When the window cannot fit all contents (either because of constraints, either because screen is too small): we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than ViewportSize-WindowPadding. - ImVec2 viewport_size = window->ViewportOwned ? ImVec2(FLT_MAX, FLT_MAX) : window->Viewport->Size; - size_auto_fit = ImClamp(size_contents, style.WindowMinSize, ImMax(style.WindowMinSize, viewport_size - g.Style.DisplaySafeAreaPadding)); + ImVec2 avail_size = window->ViewportOwned ? ImVec2(FLT_MAX, FLT_MAX) : window->Viewport->Size; + ImVec2 size_auto_fit = ImClamp(size_contents, style.WindowMinSize, ImMax(style.WindowMinSize, avail_size - g.Style.DisplaySafeAreaPadding)); + + // When the window cannot fit all contents (either because of constraints, either because screen is too small), + // we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than ViewportSize-WindowPadding. ImVec2 size_auto_fit_after_constraint = CalcSizeAfterConstraint(window, size_auto_fit); - if (size_auto_fit_after_constraint.x < size_contents.x && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar)) + if (size_auto_fit_after_constraint.x < size_contents.x && !(window->Flags & ImGuiWindowFlags_NoScrollbar) && (window->Flags & ImGuiWindowFlags_HorizontalScrollbar)) size_auto_fit.y += style.ScrollbarSize; - if (size_auto_fit_after_constraint.y < size_contents.y && !(flags & ImGuiWindowFlags_NoScrollbar)) + if (size_auto_fit_after_constraint.y < size_contents.y && !(window->Flags & ImGuiWindowFlags_NoScrollbar)) size_auto_fit.x += style.ScrollbarSize; + return size_auto_fit; } - return size_auto_fit; } static float GetScrollMaxX(ImGuiWindow* window) @@ -6201,6 +6202,7 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) } else if (flags & ImGuiWindowFlags_ChildWindow) { + // Inherit viewport from parent window window->Viewport = window->ParentWindow->Viewport; } else if (window_follow_mouse_viewport && IsMousePosValid()) @@ -6211,7 +6213,7 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) if (!window_is_mouse_tooltip && (current_viewport == NULL || !current_viewport->GetRect().Contains(window->Rect()))) { // Calculate mouse position in OS/platform coordinates, create a Viewport at this position. - ImVec2 platform_pos = ConvertViewportPosToPlatformPos(g.IO.MousePos - g.ActiveIdClickOffset, g.MousePosViewport); + ImVec2 platform_pos = ConvertViewportPosToPlatformPos(g.IO.MousePos - g.ActiveIdClickOffset, g.MouseRefViewport); ImGuiViewportFlags viewport_flags = ImGuiViewportFlags_NoDecoration | ImGuiViewportFlags_NoFocusOnAppearing | ImGuiViewportFlags_NoInputs; ImGuiViewportP* viewport = AddUpdateViewport(window, window->ID, viewport_flags, platform_pos, window->Size); window->Viewport = viewport; @@ -6219,7 +6221,7 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) } else { - window->Viewport = g.MousePosViewport; + window->Viewport = g.MouseRefViewport; } } else if (g.NavWindow != NULL && g.NavWindow != window && (flags & ImGuiWindowFlags_Tooltip)) @@ -6231,7 +6233,7 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) if (window->Viewport == NULL) window->Viewport = main_viewport; - // When we own the viewport update its size + // When we own the viewport update its size/position if (window == window->Viewport->Window && !created_viewport) { window->Viewport->Flags |= ImGuiViewportFlags_NoDecoration; @@ -6671,7 +6673,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Clamp position so window stays visible within its viewport // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing. - ImRect viewport_rect(GetViewportRect(window)); + ImRect viewport_rect = window->Viewport->GetRect(); if (!window_pos_set_by_api && !(flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) && !window->ViewportOwned && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) if (viewport_rect.GetWidth() > 0 && viewport_rect.GetHeight() > 0.0f) { @@ -6727,7 +6729,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if (window->ViewportOwned && (window->Size.x != window->Viewport->Size.x || window->Size.y != window->Viewport->Size.y)) { window->Viewport->Size = window->SizeFull; - viewport_rect = GetViewportRect(window); + viewport_rect = window->Viewport->GetRect(); } // DRAWING diff --git a/imgui_internal.h b/imgui_internal.h index e7141765182a..278648d84cbd 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -529,6 +529,7 @@ struct ImGuiViewportP : public ImGuiViewport ImGuiViewportP() { Idx = 1; LastFrameActive = LastFrameAsRefViewport = LastFrameOverlayDrawList = -1; LastNameHash = 0; Alpha = LastAlpha = 1.0f; Window = NULL; OverlayDrawList = NULL; RendererLastSize = ImVec2(-1.0f,-1.0f); } ~ImGuiViewportP() { if (OverlayDrawList) IM_DELETE(OverlayDrawList); } ImRect GetRect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } + ImVec2 GetCenter() const{ return ImVec2(Pos.x + Size.x * 0.5f, Pos.y + Size.y * 0.5f); } float GetNextX() const { const float SPACING = 4.0f; return Pos.x + Size.x + SPACING; } }; @@ -638,11 +639,11 @@ struct ImGuiContext ImGuiCond NextTreeNodeOpenCond; // Viewports - ImVector Viewports; + ImVector Viewports; // Active viewports (always 1+, and generally 1 unless multi-viewports are enabled). Each viewports hold their copy of ImDrawData. ImGuiViewportP* CurrentViewport; // We track changes of viewport (happening in Begin) so we can call Platform_OnChangedViewport() - ImGuiViewportP* MousePosViewport; - ImGuiViewportP* MousePosPrevViewport; - ImGuiViewportP* MouseHoveredPrevViewport; + ImGuiViewportP* MouseRefViewport; + ImGuiViewportP* MouseRefPrevViewport; + ImGuiViewportP* MouseHoveredLastViewport; // Last viewport that was hovered by mouse (even if we are not hovering any viewport any more) ImGuiID MouseClickedPosViewportId[5]; // For rarely used fields we only compare to, store viewport ID only so we don't have to clean dangling pointers // Navigation data (for gamepad/keyboard) @@ -728,13 +729,13 @@ struct ImGuiContext int LogAutoExpandMaxDepth; // Misc - float FramerateSecPerFrame[120]; // calculate estimate of framerate for user + float FramerateSecPerFrame[120]; // Calculate estimate of framerate for user over the last 2 seconds. int FramerateSecPerFrameIdx; float FramerateSecPerFrameAccum; - int WantCaptureMouseNextFrame; // explicit capture via CaptureInputs() sets those flags + int WantCaptureMouseNextFrame; // Explicit capture via CaptureKeyboardFromApp()/CaptureMouseFromApp() sets those flags int WantCaptureKeyboardNextFrame; int WantTextInputNextFrame; - char TempBuffer[1024*3+1]; // temporary text buffer + char TempBuffer[1024*3+1]; // Temporary text buffer ImGuiContext(ImFontAtlas* shared_font_atlas) { @@ -770,8 +771,8 @@ struct ImGuiContext NextTreeNodeOpenCond = 0; CurrentViewport = NULL; - MousePosViewport = NULL; - MousePosPrevViewport = MouseHoveredPrevViewport = NULL; + MouseRefViewport = NULL; + MouseRefPrevViewport = MouseHoveredLastViewport = NULL; memset(MouseClickedPosViewportId, 0, sizeof(MouseClickedPosViewportId)); NavWindow = NULL; From 6ac50634e3b60b9378e1cedd0c0c55b9927d39a1 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 17 Apr 2018 18:23:54 +0200 Subject: [PATCH 125/828] Viewport: Tweaked selection of g.MouseRefViewport, in particular aimed at fixing browsing child-menus while holding mouse buttons with menus in multiple viewports. (#1542) --- imgui.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 3455a15a6edc..e2727f58c60f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3513,12 +3513,13 @@ static void ImGui::UpdateViewports() g.MouseRefViewport = viewport_ref; g.MouseRefViewport->LastFrameAsRefViewport = g.FrameCount; - // When dragging something, always refer to the last hovered viewport (so when we are between viewport, our dragged preview will tend to show in the last viewport) + // When dragging something, always refer to the last hovered viewport. + // (So when we are between viewports, our dragged preview will tend to show in the last viewport even if we don't have tooltips in viewports) + // Also consider the case of holding on a menu item to browse child menus: even thought a mouse button is held, there's no active id because menu items only react on mouse release. const bool is_mouse_dragging_with_an_expected_destination = g.DragDropActive || (g.MovingWindow != NULL); - const bool is_mouse_all_released = !ImGui::IsAnyMouseDown(); - if (is_mouse_dragging_with_an_expected_destination || is_mouse_all_released) + if (is_mouse_dragging_with_an_expected_destination || g.ActiveId == 0 || !ImGui::IsAnyMouseDown()) { - if (is_mouse_dragging_with_an_expected_destination) + if (is_mouse_dragging_with_an_expected_destination && viewport_hovered == NULL) viewport_hovered = g.MouseHoveredLastViewport; if (viewport_hovered != NULL && viewport_hovered != g.MouseRefViewport && !(viewport_hovered->Flags & ImGuiViewportFlags_NoInputs)) { From 25f25d546d360a7653772fc712ee20c19f8048d5 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 17 Apr 2018 22:09:01 +0200 Subject: [PATCH 126/828] Viewport: Fixed manual mouse resize to handle mouse viewport change, which will allow resizing a window while it changes viewport. Releasing manual resize may merge a window. Added ConvertViewportPosToViewportPos() helper. Renamed NewFrameUpdateMovingWindowDropViewport() to UpdateTryMergeWindowIntoHostViewport() and removed the now-unnecessary half of the function. + renaming (#1542) --- imgui.cpp | 78 ++++++++++++++++++++++++++---------------------- imgui_internal.h | 1 + 2 files changed, 43 insertions(+), 36 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index e2727f58c60f..846ae9d7a9ca 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -753,7 +753,6 @@ static void NavUpdateWindowing(); static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id); static void NewFrameUpdateMovingWindow(); -static void NewFrameUpdateMovingWindowDropViewport(ImGuiWindow* window); static void UpdateManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4]); static void FocusFrontMostActiveWindow(ImGuiWindow* ignore_window); @@ -761,9 +760,11 @@ static void FocusFrontMostActiveWindow(ImGuiWindow* ignore_window); const ImGuiID IMGUI_VIEWPORT_DEFAULT_ID = 0x11111111; // Using an arbitrary constant instead of e.g. ImHash("ViewportDefault", 0); so it's easier to spot in the debugger. The exact value doesn't matter. static inline ImVec2 ConvertViewportPosToPlatformPos(const ImVec2& imgui_pos, ImGuiViewport* viewport) { return imgui_pos - viewport->Pos + viewport->PlatformPos; } static inline ImVec2 ConvertPlatformPosToViewportPos(const ImVec2& platform_pos, ImGuiViewport* viewport) { return platform_pos - viewport->PlatformPos + viewport->Pos; } +static inline ImVec2 ConvertViewportPosToViewportPos(const ImVec2& imgui_pos, ImGuiViewport* viewport_src, ImGuiViewport* viewport_dst) { if (viewport_src == viewport_dst) return imgui_pos; return (imgui_pos - viewport_src->Pos + viewport_src->PlatformPos - viewport_dst->PlatformPos + viewport_dst->Pos); } static ImGuiViewportP* AddUpdateViewport(ImGuiWindow* window, ImGuiID id, ImGuiViewportFlags flags, const ImVec2& platform_pos, const ImVec2& size); static void UpdateViewports(); static void UpdateSelectWindowViewport(ImGuiWindow* window); +static void UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* host_viewport); static void SetCurrentViewport(ImGuiViewportP* viewport); static void SetWindowViewportTranslateToPreservePlatformPos(ImGuiWindow* window, ImGuiViewportP* old_viewport, ImGuiViewportP* new_viewport); static void TranslateOrEraseViewports(int viewport_idx_min, int viewport_idx_max, float delta_x, int delta_idx, ImGuiViewport* viewport_to_erase); @@ -1946,6 +1947,7 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) ScrollbarSizes = ImVec2(0.0f, 0.0f); ScrollbarX = ScrollbarY = false; ViewportOwned = false; + ViewportTryMerge = false; Active = WasActive = false; WriteAccessed = false; Collapsed = false; @@ -3297,37 +3299,31 @@ static void ImGui::NavUpdate() #endif } -static void ImGui::NewFrameUpdateMovingWindowDropViewport(ImGuiWindow* window) +static void ImGui::UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* host_viewport) { - // On release we either drop window over an existing viewport or create a new one - // (We convert position from one viewport space to another, which is unnecessary at the moment but allows us to have viewport overlapping in term of imgui position) ImGuiContext& g = *GImGui; if (!(g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) return; + if (!(host_viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) || window->Viewport == host_viewport) + return; - ImRect mouse_viewport_rect = g.MouseRefViewport->GetRect(); - ImVec2 window_pos_in_mouse_viewport = ConvertPlatformPosToViewportPos(ConvertViewportPosToPlatformPos(window->Pos, window->Viewport), g.MouseRefViewport); - ImRect window_rect_in_mouse_viewport = ImRect(window_pos_in_mouse_viewport, window_pos_in_mouse_viewport + window->Size); - if ((g.MouseRefViewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) && mouse_viewport_rect.Contains(window_rect_in_mouse_viewport)) - { - // Drop on an existing viewport - ImGuiViewportP* old_viewport = window->Viewport; - SetWindowViewportTranslateToPreservePlatformPos(window, window->Viewport, g.MouseRefViewport); + ImRect host_viewport_rect = host_viewport->GetRect(); + ImVec2 window_pos_in_host_viewport = ConvertViewportPosToViewportPos(window->Pos, window->Viewport, host_viewport); + ImRect window_rect_in_host_viewport = ImRect(window_pos_in_host_viewport, window_pos_in_host_viewport + window->Size); + if (!host_viewport_rect.Contains(window_rect_in_host_viewport)) + return; - // Move child/hosted windows as well (FIXME-OPT) - if (window->ViewportOwned) - for (int n = 0; n < g.Windows.Size; n++) - if (g.Windows[n]->Viewport == old_viewport) - SetWindowViewportTranslateToPreservePlatformPos(g.Windows[n], old_viewport, g.MouseRefViewport); - window->ViewportOwned = false; - } - else + // Move to the existing viewport + ImGuiViewportP* old_viewport = window->Viewport; + SetWindowViewportTranslateToPreservePlatformPos(window, window->Viewport, host_viewport); + + // Move child/hosted windows as well (FIXME-OPT) + if (window->ViewportOwned) { - // Create/persist new viewport - ImVec2 platform_pos = ConvertViewportPosToPlatformPos(window->Pos, window->Viewport); - ImGuiViewportP* viewport = AddUpdateViewport(window, window->ID, 0, platform_pos, window->Size); - IM_ASSERT(viewport == window->Viewport); - SetWindowViewportTranslateToPreservePlatformPos(window, window->Viewport, viewport); + for (int n = 0; n < g.Windows.Size; n++) + if (g.Windows[n]->Viewport == old_viewport) + SetWindowViewportTranslateToPreservePlatformPos(g.Windows[n], old_viewport, host_viewport); + window->ViewportOwned = false; } } @@ -3353,7 +3349,7 @@ static void ImGui::NewFrameUpdateMovingWindow() } else { - NewFrameUpdateMovingWindowDropViewport(moving_window); + UpdateTryMergeWindowIntoHostViewport(moving_window, g.MouseRefViewport); // Clear the NoInput flag set by the Viewport system moving_window->Viewport->Flags &= ~ImGuiViewportFlags_NoInputs; @@ -3523,7 +3519,7 @@ static void ImGui::UpdateViewports() viewport_hovered = g.MouseHoveredLastViewport; if (viewport_hovered != NULL && viewport_hovered != g.MouseRefViewport && !(viewport_hovered->Flags & ImGuiViewportFlags_NoInputs)) { - g.IO.MousePos = ConvertPlatformPosToViewportPos(ConvertViewportPosToPlatformPos(g.IO.MousePos, g.MouseRefViewport), viewport_hovered); + g.IO.MousePos = ConvertViewportPosToViewportPos(g.IO.MousePos, g.MouseRefViewport, viewport_hovered); g.MouseRefViewport = viewport_hovered; } } @@ -4575,7 +4571,7 @@ void ImGui::Render() for (int viewport_n = 0; viewport_n < g.Viewports.Size; viewport_n++) { ImGuiViewportP* viewport = g.Viewports[viewport_n]; - ImVec2 pos = (g.MouseRefViewport == viewport) ? main_pos : ConvertPlatformPosToViewportPos(ConvertViewportPosToPlatformPos(main_pos, g.MouseRefViewport), viewport); + ImVec2 pos = (g.MouseRefViewport == viewport) ? main_pos : ConvertViewportPosToViewportPos(main_pos, g.MouseRefViewport, viewport); if (viewport->GetRect().Overlaps(ImRect(pos, pos + ImVec2(2,2)*sc + size * sc))) { ImDrawList* draw_list = GetOverlayDrawList(viewport); @@ -5918,13 +5914,13 @@ static ImVec2 FindBestWindowPosForPopup(ImGuiWindow* window) // Child menus typically request _any_ position within the parent menu item, and then our FindBestWindowPosForPopup() function will move the new menu outside the parent bounds. // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu. IM_ASSERT(g.CurrentWindow == window); - ImGuiWindow* parent_menu = g.CurrentWindowStack[g.CurrentWindowStack.Size - 2]; + ImGuiWindow* parent_window = g.CurrentWindowStack[g.CurrentWindowStack.Size - 2]; float horizontal_overlap = g.Style.ItemSpacing.x; // We want some overlap to convey the relative depth of each menu (currently the amount of overlap is hard-coded to style.ItemSpacing.x). ImRect r_avoid; - if (parent_menu->DC.MenuBarAppending) - r_avoid = ImRect(-FLT_MAX, parent_menu->Pos.y + parent_menu->TitleBarHeight(), FLT_MAX, parent_menu->Pos.y + parent_menu->TitleBarHeight() + parent_menu->MenuBarHeight()); + if (parent_window->DC.MenuBarAppending) + r_avoid = ImRect(-FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight(), FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight() + parent_window->MenuBarHeight()); else - r_avoid = ImRect(parent_menu->Pos.x + horizontal_overlap, -FLT_MAX, parent_menu->Pos.x + parent_menu->Size.x - horizontal_overlap - parent_menu->ScrollbarSizes.x, FLT_MAX); + r_avoid = ImRect(parent_window->Pos.x + horizontal_overlap, -FLT_MAX, parent_window->Pos.x + parent_window->Size.x - horizontal_overlap - parent_window->ScrollbarSizes.x, FLT_MAX); return FindBestWindowPosForPopupEx(window->PosFloat, window->Size, &window->AutoPosLastDirection, r_screen, r_avoid); } @@ -6138,7 +6134,7 @@ static void ImGui::SetWindowViewportTranslateToPreservePlatformPos(ImGuiWindow* { if (prev_viewport == curr_viewport) return; - ImVec2 new_pos = ConvertPlatformPosToViewportPos(ConvertViewportPosToPlatformPos(window->PosFloat, prev_viewport), curr_viewport); + ImVec2 new_pos = ConvertViewportPosToViewportPos(window->PosFloat, prev_viewport, curr_viewport); if ((window->FlagsPreviousFrame ^ window->Flags) & ImGuiWindowFlags_NoTitleBar) { // As a convenience, automatically adjust for client rect difference for the common use case of toggling the imgui title-bar when we move our tools to a separate OS window @@ -6158,7 +6154,6 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) { ImGuiContext& g = *GImGui; ImGuiWindowFlags flags = window->Flags; - window->ViewportOwned = false; // Restore main viewport if multi-viewport is not supported by the back-end ImGuiViewportP* main_viewport = g.Viewports[0]; @@ -6166,9 +6161,17 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) { window->Viewport = main_viewport; window->ViewportId = main_viewport->ID; + window->ViewportOwned = false; return; } + if (window->ViewportOwned && window->ViewportTryMerge && g.ActiveId == 0) + { + UpdateTryMergeWindowIntoHostViewport(window, g.Viewports[0]); + window->ViewportTryMerge = false; + } + window->ViewportOwned = false; + const bool window_is_mouse_tooltip = (flags & ImGuiWindowFlags_Tooltip) && g.NavDisableHighlight && !g.NavDisableMouseHover; const bool window_follow_mouse_viewport = window_is_mouse_tooltip || (g.MovingWindow && g.MovingWindow->RootWindow == window); @@ -6318,7 +6321,7 @@ static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au { // Resize from any of the four corners // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position - ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + resize_rect.GetSize() * grip.CornerPos; // Corner of the window corresponding to our corner grip + ImVec2 corner_target = ConvertViewportPosToViewportPos(g.IO.MousePos, g.MouseRefViewport, window->Viewport) - g.ActiveIdClickOffset + resize_rect.GetSize() * grip.CornerPos; // Corner of the window corresponding to our corner grip CalcResizePosSizeFromAnyCorner(window, corner_target, grip.CornerPos, &pos_target, &size_target); } if (resize_grip_n == 0 || held || hovered) @@ -6370,9 +6373,11 @@ static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au } // Apply back modified position/size to window - if (size_target.x != FLT_MAX) + if (size_target.x != FLT_MAX && (size_target.x != window->SizeFull.x || size_target.y != window->SizeFull.y)) { window->SizeFull = size_target; + if (window->ViewportOwned) + window->ViewportTryMerge = true; MarkIniSettingsDirty(window); } if (pos_target.x != FLT_MAX) @@ -14297,6 +14302,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::Text("NavActivateId: 0x%08X, NavInputId: 0x%08X", g.NavActivateId, g.NavInputId); ImGui::Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover); ImGui::Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize); + ImGui::Text("MousePosViewport: 0x%08X, Hovered: 0x%08X -> Ref 0x%08X", g.IO.MousePosViewport, g.IO.MouseHoveredViewport, g.MouseRefViewport->ID); ImGui::TreePop(); } if (show_window_begin_order) diff --git a/imgui_internal.h b/imgui_internal.h index 278648d84cbd..19e9247511e2 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -959,6 +959,7 @@ struct IMGUI_API ImGuiWindow ImVec2 ScrollbarSizes; bool ScrollbarX, ScrollbarY; bool ViewportOwned; + bool ViewportTryMerge; bool Active; // Set to true on Begin(), unless Collapsed bool WasActive; bool WriteAccessed; // Set to true when any widget access the current window From 637d9c42bf55bab751940bb5eba4516c3ca11140 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 19 Apr 2018 13:12:02 +0200 Subject: [PATCH 127/828] Viewport: WIP for Tooltips, Popups, Menus to create their own viewport. Resizing a window allows it to leave the main viewport. (#1542) --- examples/directx11_example/main.cpp | 6 +- imgui.cpp | 206 +++++++++++++++++++--------- imgui_internal.h | 4 +- 3 files changed, 147 insertions(+), 69 deletions(-) diff --git a/examples/directx11_example/main.cpp b/examples/directx11_example/main.cpp index a887ea758235..308fb12b7f56 100644 --- a/examples/directx11_example/main.cpp +++ b/examples/directx11_example/main.cpp @@ -134,10 +134,10 @@ int main(int, char**) ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; - io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoTaskBarIcons; + //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoTaskBarIcons; io.ConfigFlags |= ImGuiConfigFlags_DpiEnableScaleFonts; io.ConfigFlags |= ImGuiConfigFlags_DpiEnableScaleViewports; - //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls ImGui_ImplWin32_Init(hwnd); ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext); @@ -145,6 +145,8 @@ int main(int, char**) // Setup style ImGui::StyleColorsDark(); //ImGui::StyleColorsClassic(); + ImGuiStyle& style = ImGui::GetStyle(); + style.WindowRounding = 0.0f; // Load Fonts // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. diff --git a/imgui.cpp b/imgui.cpp index cc1d0420f26e..d3e0fe5afc0d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1932,6 +1932,7 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) Flags = FlagsPreviousFrame = 0; Viewport = NULL; ViewportId = 0; + ViewportPlatformAllowMonitorExtend = -1; ViewportPlatformPos = ImVec2(FLT_MAX, FLT_MAX); PosFloat = Pos = ImVec2(0.0f, 0.0f); Size = SizeFull = ImVec2(0.0f, 0.0f); @@ -1947,7 +1948,7 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) ScrollbarSizes = ImVec2(0.0f, 0.0f); ScrollbarX = ScrollbarY = false; ViewportOwned = false; - ViewportTryMerge = false; + ViewportTryMerge = ViewportTrySplit = false; Active = WasActive = false; WriteAccessed = false; Collapsed = false; @@ -2773,14 +2774,20 @@ void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit) } } -static ImVec2 NavCalcPreferredMousePos() +static ImVec2 NavCalcPreferredMousePos(ImGuiViewportP** out_viewport) { ImGuiContext& g = *GImGui; - if (!g.NavWindow) - return g.IO.MousePos; + if (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow) + { + if (out_viewport) *out_viewport = g.MouseRefViewport; + return ImFloor(g.IO.MousePos); + } + + // When navigation is active and mouse is disabled, decide on an arbitrary position around the bottom left of the currently navigated item const ImRect& rect_rel = g.NavWindow->NavRectRel[g.NavLayer]; ImVec2 pos = g.NavWindow->Pos + ImVec2(rect_rel.Min.x + ImMin(g.Style.FramePadding.x*4, rect_rel.GetWidth()), rect_rel.Max.y - ImMin(g.Style.FramePadding.y, rect_rel.GetHeight())); ImRect visible_rect = g.NavWindow->Viewport->GetRect(); + if (out_viewport) *out_viewport = g.NavWindow->Viewport; return ImFloor(ImClamp(pos, visible_rect.Min, visible_rect.Max)); // ImFloor() is important because non-integer mouse position application in back-end might be lossy and result in undesirable non-zero delta. } @@ -3109,10 +3116,11 @@ static void ImGui::NavUpdate() // Apply application mouse position movement, after we had a chance to process move request result. if (g.NavMousePosDirty && g.NavIdIsAlive) { - // Set mouse position given our knowledge of the nav widget position from last frame + // Set mouse position given our knowledge of the navigated item position from last frame if ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (g.IO.BackendFlags & ImGuiBackendFlags_HasSetMousePos)) { - g.IO.MousePos = g.IO.MousePosPrev = NavCalcPreferredMousePos(); + IM_ASSERT(!g.NavDisableHighlight && g.NavDisableMouseHover); + g.IO.MousePos = g.IO.MousePosPrev = NavCalcPreferredMousePos(NULL); g.IO.WantSetMousePos = true; } g.NavMousePosDirty = false; @@ -3295,7 +3303,7 @@ static void ImGui::NavUpdate() g.NavScoringCount = 0; #if IMGUI_DEBUG_NAV_RECTS if (g.NavWindow) { for (int layer = 0; layer < 2; layer++) GetOverlayDrawList(g.NavWindow)->AddRect(g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Min, g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Max, IM_COL32(255,200,0,255)); } // [DEBUG] - if (g.NavWindow) { ImU32 col = (g.NavWindow->HiddenFrames == 0) ? IM_COL32(255,0,255,255) : IM_COL32(255,0,0,255); ImVec2 p = NavCalcPreferredMousePos(); char buf[32]; ImFormatString(buf, 32, "%d", g.NavLayer); GetOverlayDrawList(g.NavWindow)->AddCircleFilled(p, 3.0f, col); GetOverlayDrawList(g.NavWindow)->AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); } + if (g.NavWindow) { ImU32 col = (g.NavWindow->HiddenFrames == 0) ? IM_COL32(255,0,255,255) : IM_COL32(255,0,0,255); ImVec2 p = NavCalcPreferredMousePos(NULL); char buf[32]; ImFormatString(buf, 32, "%d", g.NavLayer); GetOverlayDrawList(g.NavWindow)->AddCircleFilled(p, 3.0f, col); GetOverlayDrawList(g.NavWindow)->AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); } #endif } @@ -5446,7 +5454,7 @@ void ImGui::OpenPopupEx(ImGuiID id) popup_ref.OpenFrameCount = g.FrameCount; popup_ref.OpenParentId = parent_window->IDStack.back(); popup_ref.OpenMousePos = g.IO.MousePos; - popup_ref.OpenPopupPos = (!g.NavDisableHighlight && g.NavDisableMouseHover) ? NavCalcPreferredMousePos() : g.IO.MousePos; + popup_ref.OpenPopupPos = NavCalcPreferredMousePos(NULL); //printf("[%05d] OpenPopupEx(0x%08X)\n", g.FrameCount, id); if (g.OpenPopupStack.Size < current_stack_size + 1) @@ -5834,16 +5842,11 @@ enum ImGuiPopupPositionPolicy ImGuiPopupPositionPolicy_ComboBox }; -static ImRect FindScreenRectForWindow(ImGuiWindow* window) -{ - ImVec2 padding = GImGui->Style.DisplaySafeAreaPadding; - ImRect r_screen(window->Viewport->GetRect()); - r_screen.Expand(ImVec2((r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f)); - return r_screen; -} - // r_avoid = the rectangle to avoid (e.g. for tooltip it is a rectangle around the mouse cursor which we want to avoid. for popups it's a small point around the cursor.) // r_outer = the visible area rectangle, minus safe area padding. If our popup size won't fit because of safe area padding we ignore it. +// (r_outer is usually equivalent to the viewport rectangle minus padding, but when multi-viewports are enabled and monitor +// information are available, it may represent the entire platform monitor from the frame of reference of the current viewport. +// this allows us to have tooltips/popups displayed out of the parent viewport.) static ImVec2 FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy = ImGuiPopupPositionPolicy_Default) { ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size); @@ -5897,49 +5900,68 @@ static ImVec2 FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& s return pos; } -static ImVec2 FindBestWindowPosForPopup(ImGuiWindow* window) +static ImRect FindAllowedExtentRectForWindow(ImGuiWindow* window) { ImGuiContext& g = *GImGui; + ImRect r_screen; + if (window->ViewportPlatformAllowMonitorExtend != -1) + { + // Extent with be in the frame of reference of the given viewport (so Min is likely to be negative here) + const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[window->ViewportPlatformAllowMonitorExtend]; + r_screen.Min = ImGui::ConvertPlatformPosToViewportPos(monitor.Pos, window->Viewport); + r_screen.Max = ImGui::ConvertPlatformPosToViewportPos(monitor.Pos + monitor.Size, window->Viewport); + } + else + { + r_screen.Min = window->Viewport->Pos; + r_screen.Max = window->Viewport->Pos + window->Viewport->Size; + } + ImVec2 padding = g.Style.DisplaySafeAreaPadding; + r_screen.Expand(ImVec2((r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f)); + return r_screen; +} - ImRect r_screen = FindScreenRectForWindow(window); +static ImVec2 FindBestWindowPosForPopup(ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; if (window->Flags & ImGuiWindowFlags_ChildMenu) { // Child menus typically request _any_ position within the parent menu item, and then our FindBestWindowPosForPopup() function will move the new menu outside the parent bounds. // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu. - IM_ASSERT(g.CurrentWindow == window); - ImGuiWindow* parent_window = g.CurrentWindowStack[g.CurrentWindowStack.Size - 2]; + ImGuiWindow* parent_window = window->ParentWindow; float horizontal_overlap = g.Style.ItemSpacing.x; // We want some overlap to convey the relative depth of each menu (currently the amount of overlap is hard-coded to style.ItemSpacing.x). + ImRect r_outer = FindAllowedExtentRectForWindow(window); ImRect r_avoid; if (parent_window->DC.MenuBarAppending) r_avoid = ImRect(-FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight(), FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight() + parent_window->MenuBarHeight()); else r_avoid = ImRect(parent_window->Pos.x + horizontal_overlap, -FLT_MAX, parent_window->Pos.x + parent_window->Size.x - horizontal_overlap - parent_window->ScrollbarSizes.x, FLT_MAX); - return FindBestWindowPosForPopupEx(window->PosFloat, window->Size, &window->AutoPosLastDirection, r_screen, r_avoid); + return FindBestWindowPosForPopupEx(window->PosFloat, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid); } - if (window->Flags & ImGuiWindowFlags_Popup) { - ImRect r_avoid(window->PosFloat.x - 1, window->PosFloat.y - 1, window->PosFloat.x + 1, window->PosFloat.y + 1); - return FindBestWindowPosForPopupEx(window->PosFloat, window->Size, &window->AutoPosLastDirection, r_screen, r_avoid); + ImRect r_outer = FindAllowedExtentRectForWindow(window); + ImRect r_avoid = ImRect(window->PosFloat.x - 1, window->PosFloat.y - 1, window->PosFloat.x + 1, window->PosFloat.y + 1); + return FindBestWindowPosForPopupEx(window->PosFloat, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid); } - if (window->Flags & ImGuiWindowFlags_Tooltip) { // Position tooltip (always follows mouse) float sc = g.Style.MouseCursorScale; - ImVec2 ref_pos = (!g.NavDisableHighlight && g.NavDisableMouseHover) ? NavCalcPreferredMousePos() : g.IO.MousePos; + ImVec2 ref_pos = NavCalcPreferredMousePos(NULL); + ImRect r_outer = FindAllowedExtentRectForWindow(window); ImRect r_avoid; if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos)) r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8); else r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24 * sc, ref_pos.y + 24 * sc); // FIXME: Hard-coded based on mouse cursor shape expectation. Exact dimension not very important. - ImVec2 pos = FindBestWindowPosForPopupEx(ref_pos, window->Size, &window->AutoPosLastDirection, r_screen, r_avoid); + ImVec2 pos = FindBestWindowPosForPopupEx(ref_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid); if (window->AutoPosLastDirection == ImGuiDir_None) pos = ref_pos + ImVec2(2, 2); // If there's not enough room, for tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible. return pos; } IM_ASSERT(0); - return window->Pos; + return window->PosFloat; } static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled) @@ -6058,7 +6080,11 @@ static ImVec2 CalcSizeAutoFit(ImGuiWindow* window, const ImVec2& size_contents) } else { - ImVec2 avail_size = window->ViewportOwned ? ImVec2(FLT_MAX, FLT_MAX) : window->Viewport->Size; + // Maximum window size is determined by the viewport size or monitor size + const int monitor_idx = window->ViewportPlatformAllowMonitorExtend; + ImVec2 avail_size = window->Viewport->Size; + if (window->ViewportOwned) + avail_size = (monitor_idx >= 0 && monitor_idx < g.PlatformIO.Monitors.Size) ? g.PlatformIO.Monitors[monitor_idx].Size : ImVec2(FLT_MAX, FLT_MAX); ImVec2 size_auto_fit = ImClamp(size_contents, style.WindowMinSize, ImMax(style.WindowMinSize, avail_size - g.Style.DisplaySafeAreaPadding * 2.0f)); // When the window cannot fit all contents (either because of constraints, either because screen is too small), @@ -6142,11 +6168,25 @@ static void ImGui::SetWindowViewportTranslateToPreservePlatformPos(ImGuiWindow* window->ViewportId = curr_viewport->ID; } +static int FindPlatformMonitorForPlatformPos(ImVec2 platform_pos) +{ + ImGuiContext& g = *GImGui; + for (int monitor_n = 0; monitor_n < g.PlatformIO.Monitors.Size; monitor_n++) + { + const ImGuiPlatformMonitor* monitor = &g.PlatformIO.Monitors[monitor_n]; + ImRect monitor_rect(monitor->Pos, monitor->Pos + monitor->Size); + if (monitor_rect.Contains(platform_pos)) + return monitor_n; + } + return -1; +} + // FIXME-VIEWPORT: This is all super messy and ought to be clarified or rewritten. static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) { ImGuiContext& g = *GImGui; ImGuiWindowFlags flags = window->Flags; + window->ViewportPlatformAllowMonitorExtend = -1; // Restore main viewport if multi-viewport is not supported by the back-end ImGuiViewportP* main_viewport = g.Viewports[0]; @@ -6158,6 +6198,7 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) return; } + // Merge into host viewports (after moving, resizing) if (window->ViewportOwned && window->ViewportTryMerge && g.ActiveId == 0) { UpdateTryMergeWindowIntoHostViewport(window, g.Viewports[0]); @@ -6165,15 +6206,12 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) } window->ViewportOwned = false; - const bool window_is_mouse_tooltip = (flags & ImGuiWindowFlags_Tooltip) && g.NavDisableHighlight && !g.NavDisableMouseHover; - const bool window_follow_mouse_viewport = window_is_mouse_tooltip || (g.MovingWindow && g.MovingWindow->RootWindow == window); - - bool created_viewport = false; - // Appearing popups reset their viewport so they can inherit again - const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFrames > 0); - if ((flags & ImGuiWindowFlags_Popup) && window_just_appearing_after_hidden_for_resize) + if ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && window->Appearing) + { window->Viewport = NULL; + window->ViewportId = 0; + } // By default inherit from parent window if (window->Viewport == NULL && window->ParentWindow) @@ -6184,11 +6222,7 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) { window->Viewport = FindViewportByID(window->ViewportId); if (window->Viewport == NULL && window->ViewportPlatformPos.x != FLT_MAX && window->ViewportPlatformPos.y != FLT_MAX) - { - ImGuiViewportP* viewport = AddUpdateViewport(window, window->ID, ImGuiViewportFlags_NoDecoration, window->ViewportPlatformPos, window->Size); - window->Viewport = viewport; - created_viewport = true; - } + window->Viewport = AddUpdateViewport(window, window->ID, ImGuiViewportFlags_NoDecoration, window->ViewportPlatformPos, window->Size); } if (g.NextWindowData.ViewportCond) @@ -6197,41 +6231,52 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) window->Viewport = FindViewportByID(g.NextWindowData.ViewportId); window->ViewportId = g.NextWindowData.ViewportId; // Store ID even if Viewport isn't resolved yet. } - else if (flags & ImGuiWindowFlags_ChildWindow) + else if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_ChildMenu)) { // Inherit viewport from parent window window->Viewport = window->ParentWindow->Viewport; } - else if (window_follow_mouse_viewport && IsMousePosValid()) + else if (flags & ImGuiWindowFlags_Tooltip) + { + window->Viewport = g.MouseRefViewport; + } + else if (g.MovingWindow && g.MovingWindow->RootWindow == window && IsMousePosValid()) { // 2018-04-13: the if() below tends to succeed but for a misleading reason: when moving a window and hovering another, UpdateMovingWindow would // already have displaced the window outside of its viewport boundaries. While this is currently working it is very smelly. - ImGuiViewportP* current_viewport = window->Viewport; - if (!window_is_mouse_tooltip && (current_viewport == NULL || !current_viewport->GetRect().Contains(window->Rect()))) + if (window->Viewport == NULL || !window->Viewport->GetRect().Contains(window->Rect())) { // Calculate mouse position in OS/platform coordinates, create a Viewport at this position. ImVec2 platform_pos = ConvertViewportPosToPlatformPos(g.IO.MousePos - g.ActiveIdClickOffset, g.MouseRefViewport); ImGuiViewportFlags viewport_flags = ImGuiViewportFlags_NoDecoration | ImGuiViewportFlags_NoFocusOnAppearing | ImGuiViewportFlags_NoInputs; - ImGuiViewportP* viewport = AddUpdateViewport(window, window->ID, viewport_flags, platform_pos, window->Size); - window->Viewport = viewport; - created_viewport = true; + window->Viewport = AddUpdateViewport(window, window->ID, viewport_flags, platform_pos, window->Size); } else { window->Viewport = g.MouseRefViewport; } } - else if (g.NavWindow != NULL && g.NavWindow != window && (flags & ImGuiWindowFlags_Tooltip)) + + // Mark window as allowed to protrude outside of its viewport and into the current monitor + bool allow_protrude_on_whole_monitor = false; + allow_protrude_on_whole_monitor |= (flags & ImGuiWindowFlags_Tooltip) != 0; + allow_protrude_on_whole_monitor |= (flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) != 0; + if (allow_protrude_on_whole_monitor) { - window->Viewport = g.NavWindow->Viewport; + ImGuiViewportP* ref_viewport; + ImVec2 ref_pos = NavCalcPreferredMousePos(&ref_viewport); + window->ViewportPlatformAllowMonitorExtend = FindPlatformMonitorForPlatformPos(ConvertViewportPosToPlatformPos(ref_pos, ref_viewport)); } + if (window->ViewportTrySplit && window->ViewportPlatformAllowMonitorExtend == -1) + window->ViewportPlatformAllowMonitorExtend = FindPlatformMonitorForPlatformPos(window->Viewport->PlatformPos); + window->ViewportTrySplit = false; // Fallback to default viewport if (window->Viewport == NULL) window->Viewport = main_viewport; // When we own the viewport update its size/position - if (window == window->Viewport->Window && !created_viewport) + if (window == window->Viewport->Window && window->Viewport->LastFrameActive != g.FrameCount) { window->Viewport->Flags |= ImGuiViewportFlags_NoDecoration; if (!window->Viewport->PlatformRequestResize) @@ -6371,6 +6416,8 @@ static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au window->SizeFull = size_target; if (window->ViewportOwned) window->ViewportTryMerge = true; + else + window->ViewportTrySplit = true; MarkIniSettingsDirty(window); } if (pos_target.x != FLT_MAX) @@ -6570,10 +6617,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) SetCurrentWindow(window); flags = window->Flags; - // Lock window rounding, border size and padding for the frame (so that altering them doesn't cause inconsistencies) - window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding; - if (window->ViewportOwned) - window->WindowRounding = 0.0f; + // Lock border size and padding for the frame (so that altering them doesn't cause inconsistencies) window->WindowBorderSize = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildBorderSize : ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize; window->WindowPadding = style.WindowPadding; if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_Popup)) && window->WindowBorderSize == 0.0f) @@ -6672,16 +6716,24 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip) window->PosFloat = FindBestWindowPosForPopup(window); - // Clamp position so window stays visible within its viewport - // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing. - ImRect viewport_rect = window->Viewport->GetRect(); - if (!window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow) && !window->ViewportOwned && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) - if (viewport_rect.GetWidth() > 0 && viewport_rect.GetHeight() > 0.0f) + if (window->ViewportPlatformAllowMonitorExtend != -1 && !window->ViewportOwned) + { + if (!window->Viewport->GetRect().Contains(ImRect(window->PosFloat, window->PosFloat + window->Size))) { - ImVec2 padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding); - window->PosFloat = ImMax(window->PosFloat + window->Size, viewport_rect.Min + padding) - window->Size; - window->PosFloat = ImMin(window->PosFloat, viewport_rect.Max - padding); + // Late create viewport, based on the assumption that with our calculations, the DPI will be known ahead (same as the DPI of the selection done in UpdateSelectWindowViewport) + //ImGuiViewport* old_viewport = window->Viewport; + ImGuiViewportFlags viewport_flags = ImGuiViewportFlags_NoDecoration | ImGuiViewportFlags_NoFocusOnAppearing | ((window->Flags & ImGuiWindowFlags_NoInputs) ? ImGuiViewportFlags_NoInputs : 0); + ImVec2 platform_pos = ConvertViewportPosToPlatformPos(window->PosFloat, window->Viewport); + window->Viewport = AddUpdateViewport(window, window->ID, viewport_flags, platform_pos, window->Size); + window->ViewportOwned = true; + + // FIXME-DPI + //IM_ASSERT(old_viewport->DpiScale == window->Viewport->DpiScale); // FIXME-DPI: Something went wrong + SetCurrentViewport(window->Viewport); + window->FontDpiScale = (g.IO.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleFonts) ? window->Viewport->DpiScale : 1.0f; + SetCurrentWindow(window); } + } // Position window to fit within viewport // We can also tell the back-end that clearing the platform window won't be necessary, as our window is filling the viewport and we have disabled BgAlpha @@ -6693,8 +6745,24 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->Size = window->SizeFull = window->Viewport->Size; } + // Clamp position so window stays visible within its viewport + // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing. + ImRect viewport_rect = window->Viewport->GetRect(); + if (!window->ViewportOwned && !window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) + if (viewport_rect.GetWidth() > 0 && viewport_rect.GetHeight() > 0.0f) + { + ImVec2 padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding); + window->PosFloat = ImMax(window->PosFloat + window->Size, viewport_rect.Min + padding) - window->Size; + window->PosFloat = ImMin(window->PosFloat, viewport_rect.Max - padding); + } + window->Pos = ImFloor(window->PosFloat); + // Lock window rounding for the frame (so that altering them doesn't cause inconsistencies) + window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding; + if (window->ViewportOwned) + window->WindowRounding = 0.0f; + // Prepare for focus requests window->FocusIdxAllRequestCurrent = (window->FocusIdxAllRequestNext == INT_MAX || window->FocusIdxAllCounter == -1) ? INT_MAX : (window->FocusIdxAllRequestNext + (window->FocusIdxAllCounter+1)) % (window->FocusIdxAllCounter+1); window->FocusIdxTabRequestCurrent = (window->FocusIdxTabRequestNext == INT_MAX || window->FocusIdxTabCounter == -1) ? INT_MAX : (window->FocusIdxTabRequestNext + (window->FocusIdxTabCounter+1)) % (window->FocusIdxTabCounter+1); @@ -6721,7 +6789,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // When a window is marked as owning its viewport, we immediately update the viewport after a resize window->ViewportPlatformPos = window->Viewport->PlatformPos; - if (window->ViewportOwned && (window->Size.x != window->Viewport->Size.x || window->Size.y != window->Viewport->Size.y)) + if (window->ViewportOwned && !window->Viewport->PlatformRequestResize && (window->SizeFull.x != window->Viewport->Size.x || window->SizeFull.y != window->Viewport->Size.y)) { window->Viewport->Size = window->SizeFull; viewport_rect = window->Viewport->GetRect(); @@ -6783,7 +6851,10 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if (g.NextWindowData.BgAlphaCond != 0) bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(g.NextWindowData.BgAlphaVal) << IM_COL32_A_SHIFT); if (window->ViewportOwned) + { + //window->Viewport->Alpha = ((bg_col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT) / 255.0f; bg_col = (bg_col | IM_COL32_A_MASK); + } window->DrawList->AddRectFilled(window->Pos+ImVec2(0,window->TitleBarHeight()), window->Pos+window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Bot); // Title bar @@ -11569,7 +11640,7 @@ bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboF ImVec2 size_expected = CalcSizeAfterConstraint(popup_window, CalcSizeAutoFit(popup_window, size_contents)); if (flags & ImGuiComboFlags_PopupAlignLeft) popup_window->AutoPosLastDirection = ImGuiDir_Left; - ImRect r_outer = FindScreenRectForWindow(popup_window); + ImRect r_outer = FindAllowedExtentRectForWindow(popup_window); ImVec2 pos = FindBestWindowPosForPopupEx(frame_bb.GetBL(), size_expected, &popup_window->AutoPosLastDirection, r_outer, frame_bb, ImGuiPopupPositionPolicy_ComboBox); SetNextWindowPos(pos); } @@ -12162,7 +12233,8 @@ bool ImGui::BeginMenu(const char* label, bool enabled) if (menu_is_open) { // Sub-menus are ChildWindow so that mouse can be hovering across them (otherwise top-most popup menu would steal focus and not allow hovering on parent menu) - SetNextWindowPos(popup_pos, ImGuiCond_Always); + //SetNextWindowPos(popup_pos, ImGuiCond_Always); + SetNextWindowPos(popup_pos, ImGuiCond_Appearing); // FIXME-VIEWPORT: This needs to work with ImGuiCond_Always ImGuiWindowFlags flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings | ((window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) ? ImGuiWindowFlags_ChildMenu|ImGuiWindowFlags_ChildWindow : ImGuiWindowFlags_ChildMenu); menu_is_open = BeginPopupEx(id, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display) } @@ -14300,6 +14372,8 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::Text("NavActivateId: 0x%08X, NavInputId: 0x%08X", g.NavActivateId, g.NavInputId); ImGui::Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover); ImGui::Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize); + ImVec2 mouse_platform_pos = ConvertViewportPosToPlatformPos(g.IO.MousePos, g.MouseRefViewport); + ImGui::Text("MousePlatformPos: (%.1f,%.1f)", mouse_platform_pos.x, mouse_platform_pos.y); ImGui::Text("MousePosViewport: 0x%08X, Hovered: 0x%08X -> Ref 0x%08X", g.IO.MousePosViewport, g.IO.MouseHoveredViewport, g.MouseRefViewport->ID); ImGui::TreePop(); } diff --git a/imgui_internal.h b/imgui_internal.h index f697b71a6082..edef57bbc582 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -941,6 +941,7 @@ struct IMGUI_API ImGuiWindow ImGuiWindowFlags Flags, FlagsPreviousFrame; // See enum ImGuiWindowFlags_ ImGuiViewportP* Viewport; // Always set in Begin(), only inactive windows may have a NULL value here ImGuiID ViewportId; // Inactive windows preserve their last viewport id (since the viewport may disappear with the window inactivity) + int ViewportPlatformAllowMonitorExtend; // Reset to -1 every frame (index is guaranteed to be valid between NewFrame..EndFrame), only used in the Appearing frame of a tooltip/popup to enforce clamping to a given monitor ImVec2 ViewportPlatformPos; ImVec2 PosFloat; ImVec2 Pos; // Position rounded-up to nearest pixel @@ -961,7 +962,8 @@ struct IMGUI_API ImGuiWindow ImVec2 ScrollbarSizes; bool ScrollbarX, ScrollbarY; bool ViewportOwned; - bool ViewportTryMerge; + bool ViewportTryMerge; // Request attempt to merge into a host viewport and destroy our owned viewport + bool ViewportTrySplit; // Request attempt to split out of a host viewport and create our owned viewport bool Active; // Set to true on Begin(), unless Collapsed bool WasActive; bool WriteAccessed; // Set to true when any widget access the current window From 17a7f352b519a9fd850d29dcef92d4bd64ff34c1 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 19 Apr 2018 17:23:43 +0200 Subject: [PATCH 128/828] Viewporrt. Examples: DirectX10,11: Make the platform SetWindowSize handler not crash on failure to resize, which could happen (rarely) on invalid data or bug in the code. --- examples/imgui_impl_dx10.cpp | 6 ++++++ examples/imgui_impl_dx11.cpp | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/examples/imgui_impl_dx10.cpp b/examples/imgui_impl_dx10.cpp index 4769840390af..9d59724610ae 100644 --- a/examples/imgui_impl_dx10.cpp +++ b/examples/imgui_impl_dx10.cpp @@ -23,6 +23,7 @@ #include "imgui_impl_dx10.h" // DirectX +#include #include #include #include @@ -581,6 +582,11 @@ static void ImGui_ImplDX10_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) ID3D10Texture2D* pBackBuffer = NULL; data->SwapChain->ResizeBuffers(0, (UINT)size.x, (UINT)size.y, DXGI_FORMAT_UNKNOWN, 0); data->SwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer)); + if (pBackBuffer == NULL) + { + fprintf(stderr, "ImGui_ImplDX10_SetWindowSize() can't created buffers.\n"); + return; + } g_pd3dDevice->CreateRenderTargetView(pBackBuffer, NULL, &data->RTView); pBackBuffer->Release(); } diff --git a/examples/imgui_impl_dx11.cpp b/examples/imgui_impl_dx11.cpp index f8971bc668f1..9f5b3824996c 100644 --- a/examples/imgui_impl_dx11.cpp +++ b/examples/imgui_impl_dx11.cpp @@ -22,6 +22,7 @@ #include "imgui_impl_dx11.h" // DirectX +#include #include #include @@ -588,6 +589,11 @@ static void ImGui_ImplDX11_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) ID3D11Texture2D* pBackBuffer = NULL; data->SwapChain->ResizeBuffers(0, (UINT)size.x, (UINT)size.y, DXGI_FORMAT_UNKNOWN, 0); data->SwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer)); + if (pBackBuffer == NULL) + { + fprintf(stderr, "ImGui_ImplDX11_SetWindowSize() can't created buffers.\n"); + return; + } g_pd3dDevice->CreateRenderTargetView(pBackBuffer, NULL, &data->RTView); pBackBuffer->Release(); } From 456bbffcc4d42349693d88f76ba447ac44273dc1 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 20 Apr 2018 21:29:16 +0200 Subject: [PATCH 129/828] Viewport: Switched to using unified platform-absolute mouse coordinates, which simplify lots of problems and simplify/reduce tricky conversions, makes ImVec2 less ambiguous. Fixed various viewport/windowing/popups/synchronization bugs. Settings on host-viewport are stored as relative (made settings decently compatible between viewport enable/disabled settings). Merged ImGuiViewport::Pos and ::PlatformPos. Tweaked thumbnails. Better, smaller code. (#1542) --- examples/imgui_impl_sdl2.cpp | 2 +- examples/imgui_impl_win32.cpp | 6 +- imgui.cpp | 445 ++++++++++++++-------------------- imgui.h | 9 +- imgui_demo.cpp | 6 +- imgui_internal.h | 14 +- 6 files changed, 196 insertions(+), 286 deletions(-) diff --git a/examples/imgui_impl_sdl2.cpp b/examples/imgui_impl_sdl2.cpp index a4c9f0421c09..b7d801a8617b 100644 --- a/examples/imgui_impl_sdl2.cpp +++ b/examples/imgui_impl_sdl2.cpp @@ -322,7 +322,7 @@ static void ImGui_ImplSDL2_CreateWindow(ImGuiViewport* viewport) #if SDL_HAS_ALWAYS_ON_TOP sdl_flags |= (viewport->Flags & imGuiViewportFlags_TopMost) ? SDL_WINDOW_ALWAYS_ON_TOP : 0; #endif - data->Window = SDL_CreateWindow("No Title Yet", (int)viewport->PlatformPos.x, (int)viewport->PlatformPos.y, (int)viewport->Size.x, (int)viewport->Size.y, sdl_flags); + data->Window = SDL_CreateWindow("No Title Yet", (int)viewport->Pos.x, (int)viewport->Pos.y, (int)viewport->Size.x, (int)viewport->Size.y, sdl_flags); data->WindowOwned = true; if (use_opengl) data->GLContext = SDL_GL_CreateContext(data->Window); diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index 82c8724f7cd8..47560a4e4768 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -406,7 +406,7 @@ static void ImGui_ImplWin32_CreateWindow(ImGuiViewport* viewport) data->DwExStyle |= WS_EX_TOPMOST; // Create window - RECT rect = { (LONG)viewport->PlatformPos.x, (LONG)viewport->PlatformPos.y, (LONG)(viewport->PlatformPos.x + viewport->Size.x), (LONG)(viewport->PlatformPos.y + viewport->Size.y) }; + RECT rect = { (LONG)viewport->Pos.x, (LONG)viewport->Pos.y, (LONG)(viewport->Pos.x + viewport->Size.x), (LONG)(viewport->Pos.y + viewport->Size.y) }; ::AdjustWindowRectEx(&rect, data->DwStyle, FALSE, data->DwExStyle); data->Hwnd = ::CreateWindowEx( data->DwExStyle, _T("ImGui Platform"), _T("No Title Yet"), data->DwStyle, // Style, class name, window name @@ -514,8 +514,8 @@ static float ImGui_ImplWin32_GetWindowDpiScale(ImGuiViewport* viewport) // The first frame a viewport is created we don't have a window yet return ImGui_ImplWin32_GetDpiScaleForRect( - (int)(viewport->PlatformPos.x), (int)(viewport->PlatformPos.y), - (int)(viewport->PlatformPos.x + viewport->Size.x), (int)(viewport->PlatformPos.y + viewport->Size.y)); + (int)(viewport->Pos.x), (int)(viewport->Pos.y), + (int)(viewport->Pos.x + viewport->Size.x), (int)(viewport->Pos.y + viewport->Size.y)); } static LRESULT CALLBACK ImGui_ImplWin32_WndProcHandler_PlatformWindow(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) diff --git a/imgui.cpp b/imgui.cpp index d3e0fe5afc0d..59fdd2b0a942 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -758,16 +758,11 @@ static void FocusFrontMostActiveWindow(ImGuiWindow* ignore_window); // Viewports const ImGuiID IMGUI_VIEWPORT_DEFAULT_ID = 0x11111111; // Using an arbitrary constant instead of e.g. ImHash("ViewportDefault", 0); so it's easier to spot in the debugger. The exact value doesn't matter. -static inline ImVec2 ConvertViewportPosToPlatformPos(const ImVec2& imgui_pos, ImGuiViewport* viewport) { return imgui_pos - viewport->Pos + viewport->PlatformPos; } -static inline ImVec2 ConvertPlatformPosToViewportPos(const ImVec2& platform_pos, ImGuiViewport* viewport) { return platform_pos - viewport->PlatformPos + viewport->Pos; } -static inline ImVec2 ConvertViewportPosToViewportPos(const ImVec2& imgui_pos, ImGuiViewport* viewport_src, ImGuiViewport* viewport_dst) { if (viewport_src == viewport_dst) return imgui_pos; return (imgui_pos - viewport_src->Pos + viewport_src->PlatformPos - viewport_dst->PlatformPos + viewport_dst->Pos); } static ImGuiViewportP* AddUpdateViewport(ImGuiWindow* window, ImGuiID id, ImGuiViewportFlags flags, const ImVec2& platform_pos, const ImVec2& size); static void UpdateViewports(); static void UpdateSelectWindowViewport(ImGuiWindow* window); static void UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* host_viewport); static void SetCurrentViewport(ImGuiViewportP* viewport); -static void SetWindowViewportTranslateToPreservePlatformPos(ImGuiWindow* window, ImGuiViewportP* old_viewport, ImGuiViewportP* new_viewport); -static void TranslateOrEraseViewports(int viewport_idx_min, int viewport_idx_max, float delta_x, int delta_idx, ImGuiViewport* viewport_to_erase); } //----------------------------------------------------------------------------- @@ -1932,8 +1927,8 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) Flags = FlagsPreviousFrame = 0; Viewport = NULL; ViewportId = 0; - ViewportPlatformAllowMonitorExtend = -1; - ViewportPlatformPos = ImVec2(FLT_MAX, FLT_MAX); + ViewportAllowPlatformMonitorExtend = -1; + ViewportPos = ImVec2(FLT_MAX, FLT_MAX); PosFloat = Pos = ImVec2(0.0f, 0.0f); Size = SizeFull = ImVec2(0.0f, 0.0f); SizeContents = SizeContentsExplicit = ImVec2(0.0f, 0.0f); @@ -2774,20 +2769,16 @@ void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit) } } -static ImVec2 NavCalcPreferredMousePos(ImGuiViewportP** out_viewport) +static ImVec2 NavCalcPreferredRefPos() { ImGuiContext& g = *GImGui; if (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow) - { - if (out_viewport) *out_viewport = g.MouseRefViewport; return ImFloor(g.IO.MousePos); - } // When navigation is active and mouse is disabled, decide on an arbitrary position around the bottom left of the currently navigated item const ImRect& rect_rel = g.NavWindow->NavRectRel[g.NavLayer]; ImVec2 pos = g.NavWindow->Pos + ImVec2(rect_rel.Min.x + ImMin(g.Style.FramePadding.x*4, rect_rel.GetWidth()), rect_rel.Max.y - ImMin(g.Style.FramePadding.y, rect_rel.GetHeight())); ImRect visible_rect = g.NavWindow->Viewport->GetRect(); - if (out_viewport) *out_viewport = g.NavWindow->Viewport; return ImFloor(ImClamp(pos, visible_rect.Min, visible_rect.Max)); // ImFloor() is important because non-integer mouse position application in back-end might be lossy and result in undesirable non-zero delta. } @@ -3120,7 +3111,7 @@ static void ImGui::NavUpdate() if ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (g.IO.BackendFlags & ImGuiBackendFlags_HasSetMousePos)) { IM_ASSERT(!g.NavDisableHighlight && g.NavDisableMouseHover); - g.IO.MousePos = g.IO.MousePosPrev = NavCalcPreferredMousePos(NULL); + g.IO.MousePos = g.IO.MousePosPrev = NavCalcPreferredRefPos(); g.IO.WantSetMousePos = true; } g.NavMousePosDirty = false; @@ -3303,36 +3294,34 @@ static void ImGui::NavUpdate() g.NavScoringCount = 0; #if IMGUI_DEBUG_NAV_RECTS if (g.NavWindow) { for (int layer = 0; layer < 2; layer++) GetOverlayDrawList(g.NavWindow)->AddRect(g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Min, g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Max, IM_COL32(255,200,0,255)); } // [DEBUG] - if (g.NavWindow) { ImU32 col = (g.NavWindow->HiddenFrames == 0) ? IM_COL32(255,0,255,255) : IM_COL32(255,0,0,255); ImVec2 p = NavCalcPreferredMousePos(NULL); char buf[32]; ImFormatString(buf, 32, "%d", g.NavLayer); GetOverlayDrawList(g.NavWindow)->AddCircleFilled(p, 3.0f, col); GetOverlayDrawList(g.NavWindow)->AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); } + if (g.NavWindow) { ImU32 col = (g.NavWindow->HiddenFrames == 0) ? IM_COL32(255,0,255,255) : IM_COL32(255,0,0,255); ImVec2 p = NavCalcPreferredRefPos(NULL); char buf[32]; ImFormatString(buf, 32, "%d", g.NavLayer); GetOverlayDrawList(g.NavWindow)->AddCircleFilled(p, 3.0f, col); GetOverlayDrawList(g.NavWindow)->AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); } #endif } -static void ImGui::UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* host_viewport) +static void SetWindowViewport(ImGuiWindow* window, ImGuiViewportP* viewport) +{ + window->Viewport = viewport; + window->ViewportId = viewport->ID; + window->ViewportOwned = (viewport->Window == window); +} + +static void ImGui::UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* viewport) { ImGuiContext& g = *GImGui; if (!(g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) return; - if (!(host_viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) || window->Viewport == host_viewport) + if (!(viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) || window->Viewport == viewport) return; - - ImRect host_viewport_rect = host_viewport->GetRect(); - ImVec2 window_pos_in_host_viewport = ConvertViewportPosToViewportPos(window->Pos, window->Viewport, host_viewport); - ImRect window_rect_in_host_viewport = ImRect(window_pos_in_host_viewport, window_pos_in_host_viewport + window->Size); - if (!host_viewport_rect.Contains(window_rect_in_host_viewport)) + if (!viewport->GetRect().Contains(window->Rect())) return; - // Move to the existing viewport + // Move to the existing viewport, Move child/hosted windows as well (FIXME-OPT: iterate child) ImGuiViewportP* old_viewport = window->Viewport; - SetWindowViewportTranslateToPreservePlatformPos(window, window->Viewport, host_viewport); - - // Move child/hosted windows as well (FIXME-OPT) if (window->ViewportOwned) - { for (int n = 0; n < g.Windows.Size; n++) if (g.Windows[n]->Viewport == old_viewport) - SetWindowViewportTranslateToPreservePlatformPos(g.Windows[n], old_viewport, host_viewport); - window->ViewportOwned = false; - } + SetWindowViewport(g.Windows[n], viewport); + SetWindowViewport(window, viewport); } static void ImGui::NewFrameUpdateMovingWindow() @@ -3384,6 +3373,7 @@ static void ImGui::NewFrameUpdateMovingWindow() // If the back-end doesn't set MouseLastHoveredViewport or doesn't honor ImGuiViewportFlags_NoInputs, we do a search ourselves. // This search won't take account of the possibility that non-imgui windows may be in-between our dragged window and our target window. +// FIXME-VIEWPORT: Need a proper notion of focus. At least use the equivalent of LastFrameAsRefViewport on a per-window basis. static ImGuiViewportP* FindViewportHoveredFromPlatformWindowStack(const ImVec2 mouse_platform_pos) { ImGuiContext& g = *GImGui; @@ -3391,40 +3381,62 @@ static ImGuiViewportP* FindViewportHoveredFromPlatformWindowStack(const ImVec2 m for (int n = 0; n < g.Viewports.Size; n++) { ImGuiViewportP* viewport = g.Viewports[n]; - ImRect platform_rect = ImRect(viewport->PlatformPos, viewport->PlatformPos + viewport->Size); - if (!(viewport->Flags & ImGuiViewportFlags_NoInputs) && platform_rect.Contains(mouse_platform_pos)) + if (!(viewport->Flags & ImGuiViewportFlags_NoInputs) && viewport->GetRect().Contains(mouse_platform_pos)) if (best_candidate == NULL || best_candidate->LastFrameAsRefViewport < viewport->LastFrameAsRefViewport) best_candidate = viewport; } return best_candidate; } +static void TranslateWindow(ImGuiWindow* window, const ImVec2& delta) +{ + window->Pos += delta; + window->PosFloat += delta; + window->ClipRect.Translate(delta); + window->WindowRectClipped.Translate(delta); + window->InnerRect.Translate(delta); + window->DC.CursorPos += delta; + window->DC.CursorStartPos += delta; + window->DC.CursorMaxPos += delta; + window->DC.LastItemRect.Translate(delta); + window->DC.LastItemDisplayRect.Translate(delta); +} + static void ImGui::UpdateViewports() { ImGuiContext& g = *GImGui; IM_ASSERT(g.PlatformIO.Viewports.Size <= g.Viewports.Size); - // Mouse handling: latch the expected mouse OS position (if any) before processing viewport erasure - ImGuiViewportP* viewport_ref = g.IO.MousePosViewport ? FindViewportByID(g.IO.MousePosViewport) : g.Viewports[0]; - const ImVec2 mouse_platform_pos = ConvertViewportPosToPlatformPos(g.IO.MousePos, viewport_ref); + // Update mouse reference viewport + g.MouseRefPrevViewport = g.MouseRefViewport; + g.MouseRefViewport = g.IO.MousePosViewport ? FindViewportByID(g.IO.MousePosViewport) : g.Viewports[0]; + + // Update main viewport with current size (and OS window position, if known) + ImGuiViewportP* main_viewport = g.Viewports[0]; + IM_ASSERT(main_viewport->ID == IMGUI_VIEWPORT_DEFAULT_ID); + ImVec2 main_viewport_platform_pos = ImVec2(0.0f, 0.0f); + if ((g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) + main_viewport_platform_pos = g.PlatformIO.Platform_GetWindowPos(main_viewport); + AddUpdateViewport(NULL, IMGUI_VIEWPORT_DEFAULT_ID, ImGuiViewportFlags_CanHostOtherWindows, main_viewport_platform_pos, g.IO.DisplaySize); g.CurrentViewport = NULL; for (int n = 0; n < g.Viewports.Size; n++) { // Erase unused viewports ImGuiViewportP* viewport = g.Viewports[n]; - IM_ASSERT(viewport->Idx == n); + viewport->Idx = n; if (n > 0 && viewport->LastFrameActive < g.FrameCount - 2) { - // Translate windows like if we were resizing the viewport to be zero-width - TranslateOrEraseViewports(n + 1, g.Viewports.Size, viewport->Pos.x - viewport->GetNextX(), -1, viewport); + // Clear references to this viewport in windows (window->ViewportId becomes the master data) + for (int window_n = 0; window_n < g.Windows.Size; window_n++) + if (g.Windows[window_n]->Viewport == viewport) + g.Windows[window_n]->Viewport = NULL; g.Viewports.erase(g.Viewports.Data + n); // Destroy - if (viewport == viewport_ref) viewport_ref = NULL; - if (viewport == g.MouseRefViewport) g.MouseRefViewport = NULL; - if (viewport == g.MouseRefPrevViewport) g.MouseRefPrevViewport = NULL; + if (viewport == g.MouseRefViewport) g.MouseRefViewport = main_viewport; + if (viewport == g.MouseRefPrevViewport) g.MouseRefPrevViewport = main_viewport; if (viewport == g.MouseHoveredLastViewport) g.MouseHoveredLastViewport = NULL; IM_ASSERT(viewport->RendererUserData == NULL && viewport->PlatformUserData == NULL && viewport->PlatformHandle == NULL); IM_ASSERT(g.PlatformIO.Viewports.contains(viewport) == false); @@ -3433,22 +3445,21 @@ static void ImGui::UpdateViewports() continue; } - // Translate resized viewports - if (n + 1 < g.Viewports.Size) - { - float dx = viewport->GetNextX() - g.Viewports[viewport->Idx + 1]->Pos.x; - if (dx != 0.0f) - TranslateOrEraseViewports(viewport->Idx + 1, g.Viewports.Size, dx, 0, NULL); - } - // Apply Position and Size (from Platform Window to ImGui) if requested // We do it here early in the frame instead of UpdatePlatformWindows() to allow the platform back-end to set PlatformRequestResize early // (e.g. in their own message handler before NewFrame) and not have a frame of lag. if (viewport->PlatformRequestMove) - viewport->PlatformPos = g.PlatformIO.Platform_GetWindowPos(viewport); + viewport->Pos = g.PlatformIO.Platform_GetWindowPos(viewport); if (viewport->PlatformRequestResize) viewport->Size = g.PlatformIO.Platform_GetWindowSize(viewport); + // Translate resized viewports + ImVec2 delta = viewport->Pos - viewport->LastPos; + if ((viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) && (delta.x != 0.0f || delta.y != 0.0f)) + for (int window_n = 0; window_n < g.Windows.Size; window_n++) + if (g.Windows[window_n]->Viewport == viewport) + TranslateWindow(g.Windows[window_n], delta); + // Update DPI Scale float new_dpi_scale; if (g.PlatformIO.Platform_GetWindowDpiScale) @@ -3470,19 +3481,13 @@ static void ImGui::UpdateViewports() viewport->DpiScale = new_dpi_scale; } - // Update main viewport with current size (and OS window position, if known) - ImGuiViewportP* main_viewport = g.Viewports[0]; - IM_ASSERT(main_viewport->ID == IMGUI_VIEWPORT_DEFAULT_ID); - ImVec2 main_viewport_platform_pos = ImVec2(0.0f, 0.0f); - if ((g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) - main_viewport_platform_pos = g.PlatformIO.Platform_GetWindowPos(main_viewport); - AddUpdateViewport(NULL, IMGUI_VIEWPORT_DEFAULT_ID, ImGuiViewportFlags_CanHostOtherWindows, main_viewport_platform_pos, g.IO.DisplaySize); - if (!(g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) { g.MouseRefViewport = g.MouseRefPrevViewport = main_viewport; + g.MouseRefViewport->LastFrameAsRefViewport = g.FrameCount; return; } + g.MouseRefViewport->LastFrameAsRefViewport = g.FrameCount; // Mouse handling: decide on the actual mouse viewport for this frame between the active/focused viewport and the hovered viewport. ImGuiViewportP* viewport_hovered = NULL; @@ -3493,7 +3498,7 @@ static void ImGui::UpdateViewports() { // Back-end failed at honoring its contract IM_ASSERT(0); - viewport_hovered = FindViewportHoveredFromPlatformWindowStack(mouse_platform_pos); + viewport_hovered = FindViewportHoveredFromPlatformWindowStack(g.IO.MousePos); } } else @@ -3501,22 +3506,11 @@ static void ImGui::UpdateViewports() // If the back-end doesn't know how to honor ImGuiViewportFlags_NoInputs, we do a search ourselves. Note that this search: // A) won't take account of the possibility that non-imgui windows may be in-between our dragged window and our target window. // B) uses LastFrameAsRefViewport as a flawed replacement for the last time a window was focused (we could/should fix that by introducing Focus functions in PlatformIO) - viewport_hovered = FindViewportHoveredFromPlatformWindowStack(mouse_platform_pos); + viewport_hovered = FindViewportHoveredFromPlatformWindowStack(g.IO.MousePos); } if (viewport_hovered != NULL) g.MouseHoveredLastViewport = viewport_hovered; - // If reference viewport just has been deleted, use a position relative to the main viewport - if (viewport_ref == NULL) - { - viewport_ref = main_viewport; - g.IO.MousePos = ConvertPlatformPosToViewportPos(mouse_platform_pos, viewport_ref); - } - - g.MouseRefPrevViewport = g.MouseRefViewport; - g.MouseRefViewport = viewport_ref; - g.MouseRefViewport->LastFrameAsRefViewport = g.FrameCount; - // When dragging something, always refer to the last hovered viewport. // (So when we are between viewports, our dragged preview will tend to show in the last viewport even if we don't have tooltips in viewports) // Also consider the case of holding on a menu item to browse child menus: even thought a mouse button is held, there's no active id because menu items only react on mouse release. @@ -3526,10 +3520,7 @@ static void ImGui::UpdateViewports() if (is_mouse_dragging_with_an_expected_destination && viewport_hovered == NULL) viewport_hovered = g.MouseHoveredLastViewport; if (viewport_hovered != NULL && viewport_hovered != g.MouseRefViewport && !(viewport_hovered->Flags & ImGuiViewportFlags_NoInputs)) - { - g.IO.MousePos = ConvertViewportPosToViewportPos(g.IO.MousePos, g.MouseRefViewport, viewport_hovered); g.MouseRefViewport = viewport_hovered; - } } IM_ASSERT(g.MouseRefViewport != NULL); @@ -3541,6 +3532,7 @@ void ImGui::UpdatePlatformWindows() IM_ASSERT(g.FrameCountEnded == g.FrameCount && "Forgot to call Render() or EndFrame() before UpdatePlatformWindows()?"); IM_ASSERT(g.FrameCountPlatformEnded < g.FrameCount); g.FrameCountPlatformEnded = g.FrameCount; + g.Viewports[0]->LastPos = g.Viewports[0]->Pos; if (!(g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) return; @@ -3549,6 +3541,7 @@ void ImGui::UpdatePlatformWindows() for (int i = 1; i < g.Viewports.Size; i++) { ImGuiViewportP* viewport = g.Viewports[i]; + viewport->LastPos = viewport->Pos; if (viewport->LastFrameActive < g.FrameCount) { if (viewport->LastFrameActive < g.FrameCount - 1) @@ -3587,7 +3580,7 @@ void ImGui::UpdatePlatformWindows() // Apply Position and Size (from ImGui to Platform Window) if (!viewport->PlatformRequestMove) - g.PlatformIO.Platform_SetWindowPos(viewport, viewport->PlatformPos); + g.PlatformIO.Platform_SetWindowPos(viewport, viewport->Pos); if (!viewport->PlatformRequestResize) g.PlatformIO.Platform_SetWindowSize(viewport, viewport->Size); @@ -3798,6 +3791,7 @@ void ImGui::NewFrame() IM_ASSERT(g.FrameCount == 0 || g.FrameCountPlatformEnded == g.FrameCount && "Forgot to call UpdatePlatformWindows() at the end of the previous frame?"); IM_ASSERT(g.PlatformIO.Platform_CreateWindow != NULL && "Platform init didn't install handlers?"); IM_ASSERT(g.PlatformIO.Platform_DestroyWindow != NULL && "Platform init didn't install handlers?"); + IM_ASSERT(g.PlatformIO.Platform_GetWindowPos != NULL && "Platform init didn't install handlers?"); IM_ASSERT((g.Viewports[0]->PlatformUserData != NULL || g.Viewports[0]->PlatformHandle != NULL) && "Platform init didn't setup main viewport."); #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS IM_ASSERT(g.IO.RenderDrawListsFn == NULL); // Call ImGui::Render() then pass ImGui::GetDrawData() yourself to your render function! @@ -4000,11 +3994,11 @@ static void SettingsHandlerWindow_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, float x, y; int i; ImU32 u1; - if (sscanf(line, "Pos=%f,%f", &x, &y) == 2) { settings->Pos = ImVec2(x, y); } - else if (sscanf(line, "Size=%f,%f", &x, &y) == 2) { settings->Size = ImMax(ImVec2(x, y), GImGui->Style.WindowMinSize); } - else if (sscanf(line, "ViewportId=0x%08X", &u1) == 1) { settings->ViewportId = u1; } - else if (sscanf(line, "ViewportPlatformPos=%f,%f", &x, &y)==2) { settings->ViewportPlatformPos = ImVec2(x, y); } - else if (sscanf(line, "Collapsed=%d", &i) == 1) { settings->Collapsed = (i != 0); } + if (sscanf(line, "Pos=%f,%f", &x, &y) == 2) { settings->Pos = ImVec2(x, y); } + else if (sscanf(line, "Size=%f,%f", &x, &y) == 2) { settings->Size = ImMax(ImVec2(x, y), GImGui->Style.WindowMinSize); } + else if (sscanf(line, "ViewportId=0x%08X", &u1) == 1) { settings->ViewportId = u1; } + else if (sscanf(line, "ViewportPos=%f,%f", &x, &y) == 2) { settings->ViewportPos = ImVec2(x, y); } + else if (sscanf(line, "Collapsed=%d", &i) == 1) { settings->Collapsed = (i != 0); } } static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf) @@ -4019,10 +4013,10 @@ static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSetting ImGuiWindowSettings* settings = ImGui::FindWindowSettings(window->ID); if (!settings) settings = AddWindowSettings(window->Name); - settings->Pos = window->Pos;// - window->Viewport->Pos; + settings->Pos = window->Pos - window->ViewportPos; settings->Size = window->SizeFull; settings->ViewportId = window->ViewportId; - settings->ViewportPlatformPos = window->ViewportPlatformPos; + settings->ViewportPos = window->ViewportPos; settings->Collapsed = window->Collapsed; } @@ -4032,20 +4026,18 @@ static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSetting for (int i = 0; i != g.SettingsWindows.Size; i++) { const ImGuiWindowSettings* settings = &g.SettingsWindows[i]; - if (settings->Pos.x == FLT_MAX) - continue; const char* name = settings->Name; if (const char* p = strstr(name, "###")) // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID() name = p; buf->appendf("[%s][%s]\n", handler->TypeName, name); - buf->appendf("Pos=%d,%d\n", (int)settings->Pos.x, (int)settings->Pos.y); - buf->appendf("Size=%d,%d\n", (int)settings->Size.x, (int)settings->Size.y); if (settings->ViewportId != 0 && settings->ViewportId != ImGui::IMGUI_VIEWPORT_DEFAULT_ID) { + buf->appendf("ViewportPos=%d,%d\n", (int)settings->ViewportPos.x, (int)settings->ViewportPos.y); buf->appendf("ViewportId=0x%08X\n", settings->ViewportId); - if (settings->ViewportPlatformPos.x != FLT_MAX && settings->ViewportPlatformPos.y != FLT_MAX) - buf->appendf("ViewportPlatformPos=%d,%d\n", (int)settings->ViewportPlatformPos.x, (int)settings->ViewportPlatformPos.y); } + if (settings->Pos.x != 0.0f || settings->Pos.y != 0.0f || settings->ViewportId == ImGui::IMGUI_VIEWPORT_DEFAULT_ID) + buf->appendf("Pos=%d,%d\n", (int)settings->Pos.x, (int)settings->Pos.y); + buf->appendf("Size=%d,%d\n", (int)settings->Size.x, (int)settings->Size.y); buf->appendf("Collapsed=%d\n", settings->Collapsed); buf->appendf("\n"); } @@ -4573,13 +4565,12 @@ void ImGui::Render() if (g.IO.MouseDrawCursor && g.IO.Fonts->GetMouseCursorTexData(g.MouseCursor, &offset, &size, &uv[0], &uv[2])) { // We need to account for the possibility of the mouse cursor straddling multiple viewports... - const ImVec2 main_pos = g.IO.MousePos - offset; + const ImVec2 pos = g.IO.MousePos - offset; const ImTextureID tex_id = g.IO.Fonts->TexID; const float sc = g.Style.MouseCursorScale; for (int viewport_n = 0; viewport_n < g.Viewports.Size; viewport_n++) { ImGuiViewportP* viewport = g.Viewports[viewport_n]; - ImVec2 pos = (g.MouseRefViewport == viewport) ? main_pos : ConvertViewportPosToViewportPos(main_pos, g.MouseRefViewport, viewport); if (viewport->GetRect().Overlaps(ImRect(pos, pos + ImVec2(2,2)*sc + size * sc))) { ImDrawList* draw_list = GetOverlayDrawList(viewport); @@ -4637,55 +4628,6 @@ ImGuiViewport* ImGui::FindViewportByPlatformHandle(void* platform_handle) return NULL; } -static void TranslateWindowX(ImGuiWindow* window, float dx) -{ - window->Pos.x += dx; - window->PosFloat.x += dx; - window->ClipRect.Translate(dx, 0.0f); - window->WindowRectClipped.Translate(dx, 0.0f); - window->InnerRect.Translate(dx, 0.0f); - window->DC.CursorPos.x += dx; - window->DC.CursorStartPos.x += dx; - window->DC.CursorMaxPos.x += dx; - window->DC.LastItemRect.Translate(dx, 0.0f); - window->DC.LastItemDisplayRect.Translate(dx, 0.0f); -} - -static void ImGui::TranslateOrEraseViewports(int viewport_idx_min, int viewport_idx_max, float delta_x, int delta_idx, ImGuiViewport* viewport_to_erase) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(delta_x != 0.0f || delta_idx != 0); - IM_ASSERT(g.CurrentViewport == NULL); // We only resize at the beginning of the frame - for (int n = 0; n < g.Windows.Size; n++) - { - ImGuiWindow* window = g.Windows[n]; - if (window->Viewport == viewport_to_erase) - window->Viewport = NULL; // Set to NULL, window->ViewportId becomes the master data - if (window->Viewport == NULL) - continue; - if (window->Viewport->Idx < viewport_idx_min || window->Viewport->Idx > viewport_idx_max) - continue; - TranslateWindowX(window, delta_x); - } - - for (int n = viewport_idx_min; n < viewport_idx_max; n++) - { - ImGuiViewportP* viewport = g.Viewports[n]; - viewport->Pos.x += delta_x; - viewport->Idx += delta_idx; - - // Patch mouse positions immediately so mouse delta will not appears to jump around - ImGuiID viewport_id = viewport->ID; - if (viewport_id == g.IO.MousePosViewport) // We are early in NewFrame and g.MousePosViewport hasn't been set, patch the source. - g.IO.MousePos.x += delta_x; - if (viewport == g.MouseRefPrevViewport) - g.IO.MousePosPrev.x += delta_x; - for (int mouse_n = 0; mouse_n < IM_ARRAYSIZE(g.MouseClickedPosViewportId); mouse_n++) - if (g.MouseClickedPosViewportId[mouse_n] == viewport_id) - g.IO.MouseClickedPos[mouse_n].x += delta_x; - } -} - void ImGui::SetCurrentViewport(ImGuiViewportP* viewport) { // Notify platform layer of viewport changes @@ -4708,8 +4650,7 @@ ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, ImGuiV ImGuiViewportP* viewport = (ImGuiViewportP*)FindViewportByID(id); if (viewport) { - // We defer translating windows to the beginning of the frame. - // Our viewport system already works with fully overlapped viewports, it's only certain user interactions that don't and they can't be performed while resizing. + viewport->Pos = platform_pos; viewport->Size = size; } else @@ -4718,7 +4659,7 @@ ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, ImGuiV viewport = IM_NEW(ImGuiViewportP)(); viewport->ID = id; viewport->Idx = g.Viewports.Size; - viewport->Pos = ImVec2(g.Viewports.back()->GetNextX(), 0.0f); + viewport->Pos = viewport->LastPos = platform_pos; viewport->Size = size; g.Viewports.push_back(viewport); @@ -4728,10 +4669,8 @@ ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, ImGuiV g.DrawListSharedData.ClipRectFullscreen.w = ImMax(g.DrawListSharedData.ClipRectFullscreen.w, viewport->Pos.y + viewport->Size.y); } - IM_ASSERT(viewport->Pos.y == 0.0f); viewport->Window = window; viewport->Flags = flags; - viewport->PlatformPos = platform_pos; viewport->LastFrameActive = g.FrameCount; if (window != NULL) @@ -5454,7 +5393,7 @@ void ImGui::OpenPopupEx(ImGuiID id) popup_ref.OpenFrameCount = g.FrameCount; popup_ref.OpenParentId = parent_window->IDStack.back(); popup_ref.OpenMousePos = g.IO.MousePos; - popup_ref.OpenPopupPos = NavCalcPreferredMousePos(NULL); + popup_ref.OpenPopupPos = NavCalcPreferredRefPos(); //printf("[%05d] OpenPopupEx(0x%08X)\n", g.FrameCount, id); if (g.OpenPopupStack.Size < current_stack_size + 1) @@ -5904,12 +5843,12 @@ static ImRect FindAllowedExtentRectForWindow(ImGuiWindow* window) { ImGuiContext& g = *GImGui; ImRect r_screen; - if (window->ViewportPlatformAllowMonitorExtend != -1) + if (window->ViewportAllowPlatformMonitorExtend != -1) { // Extent with be in the frame of reference of the given viewport (so Min is likely to be negative here) - const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[window->ViewportPlatformAllowMonitorExtend]; - r_screen.Min = ImGui::ConvertPlatformPosToViewportPos(monitor.Pos, window->Viewport); - r_screen.Max = ImGui::ConvertPlatformPosToViewportPos(monitor.Pos + monitor.Size, window->Viewport); + const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[window->ViewportAllowPlatformMonitorExtend]; + r_screen.Min = monitor.Pos; + r_screen.Max = monitor.Pos + monitor.Size; } else { @@ -5948,7 +5887,7 @@ static ImVec2 FindBestWindowPosForPopup(ImGuiWindow* window) { // Position tooltip (always follows mouse) float sc = g.Style.MouseCursorScale; - ImVec2 ref_pos = NavCalcPreferredMousePos(NULL); + ImVec2 ref_pos = NavCalcPreferredRefPos(); ImRect r_outer = FindAllowedExtentRectForWindow(window); ImRect r_avoid; if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos)) @@ -5992,18 +5931,23 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFl { // Retrieve settings from .ini file // Use SetWindowPos() or SetNextWindowPos() with the appropriate condition flag to change the initial position of a window. - window->Pos = window->PosFloat = ImVec2(60, 60); + ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + window->Pos = window->PosFloat = main_viewport->Pos + ImVec2(60, 60); if (ImGuiWindowSettings* settings = ImGui::FindWindowSettings(window->ID)) { SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false); - window->PosFloat = settings->Pos; - window->Pos = ImFloor(window->PosFloat); - window->Collapsed = settings->Collapsed; if (settings->ViewportId) + { window->ViewportId = settings->ViewportId; - if (settings->ViewportPlatformPos.x != FLT_MAX && settings->ViewportPlatformPos.y != FLT_MAX) - window->ViewportPlatformPos = settings->ViewportPlatformPos; + window->ViewportPos = settings->ViewportPos; + } + else + { + window->ViewportPos = main_viewport->Pos; + } + window->Pos = window->PosFloat = ImFloor(settings->Pos + window->ViewportPos); + window->Collapsed = settings->Collapsed; if (ImLengthSqr(settings->Size) > 0.00001f) size = settings->Size; } @@ -6081,7 +6025,7 @@ static ImVec2 CalcSizeAutoFit(ImGuiWindow* window, const ImVec2& size_contents) else { // Maximum window size is determined by the viewport size or monitor size - const int monitor_idx = window->ViewportPlatformAllowMonitorExtend; + const int monitor_idx = window->ViewportAllowPlatformMonitorExtend; ImVec2 avail_size = window->Viewport->Size; if (window->ViewportOwned) avail_size = (monitor_idx >= 0 && monitor_idx < g.PlatformIO.Monitors.Size) ? g.PlatformIO.Monitors[monitor_idx].Size : ImVec2(FLT_MAX, FLT_MAX); @@ -6149,26 +6093,7 @@ static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& co *out_size = size_constrained; } -static void ImGui::SetWindowViewportTranslateToPreservePlatformPos(ImGuiWindow* window, ImGuiViewportP* prev_viewport, ImGuiViewportP* curr_viewport) -{ - if (prev_viewport == curr_viewport) - return; - ImVec2 new_pos = ConvertViewportPosToViewportPos(window->PosFloat, prev_viewport, curr_viewport); - if ((window->FlagsPreviousFrame ^ window->Flags) & ImGuiWindowFlags_NoTitleBar) - { - // As a convenience, automatically adjust for client rect difference for the common use case of toggling the imgui title-bar when we move our tools to a separate OS window - float title_bar_height = GetFrameHeight(); - if (!(window->Flags & ImGuiWindowFlags_NoTitleBar)) - title_bar_height = -title_bar_height; - new_pos.y += title_bar_height; - window->SizeFull.y -= title_bar_height; - } - SetWindowPos(window, new_pos, ImGuiCond_Always); - window->Viewport = curr_viewport; - window->ViewportId = curr_viewport->ID; -} - -static int FindPlatformMonitorForPlatformPos(ImVec2 platform_pos) +static int FindPlatformMonitorForPos(ImVec2 platform_pos) { ImGuiContext& g = *GImGui; for (int monitor_n = 0; monitor_n < g.PlatformIO.Monitors.Size; monitor_n++) @@ -6186,7 +6111,7 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) { ImGuiContext& g = *GImGui; ImGuiWindowFlags flags = window->Flags; - window->ViewportPlatformAllowMonitorExtend = -1; + window->ViewportAllowPlatformMonitorExtend = -1; // Restore main viewport if multi-viewport is not supported by the back-end ImGuiViewportP* main_viewport = g.Viewports[0]; @@ -6221,8 +6146,8 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) if (window->Viewport == NULL && window->ViewportId != 0) { window->Viewport = FindViewportByID(window->ViewportId); - if (window->Viewport == NULL && window->ViewportPlatformPos.x != FLT_MAX && window->ViewportPlatformPos.y != FLT_MAX) - window->Viewport = AddUpdateViewport(window, window->ID, ImGuiViewportFlags_NoDecoration, window->ViewportPlatformPos, window->Size); + if (window->Viewport == NULL && window->ViewportPos.x != FLT_MAX && window->ViewportPos.y != FLT_MAX) + window->Viewport = AddUpdateViewport(window, window->ID, ImGuiViewportFlags_NoDecoration, window->ViewportPos, window->Size); } if (g.NextWindowData.ViewportCond) @@ -6246,14 +6171,9 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) // already have displaced the window outside of its viewport boundaries. While this is currently working it is very smelly. if (window->Viewport == NULL || !window->Viewport->GetRect().Contains(window->Rect())) { - // Calculate mouse position in OS/platform coordinates, create a Viewport at this position. - ImVec2 platform_pos = ConvertViewportPosToPlatformPos(g.IO.MousePos - g.ActiveIdClickOffset, g.MouseRefViewport); + ImVec2 viewport_pos = g.IO.MousePos - g.ActiveIdClickOffset; ImGuiViewportFlags viewport_flags = ImGuiViewportFlags_NoDecoration | ImGuiViewportFlags_NoFocusOnAppearing | ImGuiViewportFlags_NoInputs; - window->Viewport = AddUpdateViewport(window, window->ID, viewport_flags, platform_pos, window->Size); - } - else - { - window->Viewport = g.MouseRefViewport; + window->Viewport = AddUpdateViewport(window, window->ID, viewport_flags, viewport_pos, window->Size); } } @@ -6262,28 +6182,19 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) allow_protrude_on_whole_monitor |= (flags & ImGuiWindowFlags_Tooltip) != 0; allow_protrude_on_whole_monitor |= (flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) != 0; if (allow_protrude_on_whole_monitor) - { - ImGuiViewportP* ref_viewport; - ImVec2 ref_pos = NavCalcPreferredMousePos(&ref_viewport); - window->ViewportPlatformAllowMonitorExtend = FindPlatformMonitorForPlatformPos(ConvertViewportPosToPlatformPos(ref_pos, ref_viewport)); - } - if (window->ViewportTrySplit && window->ViewportPlatformAllowMonitorExtend == -1) - window->ViewportPlatformAllowMonitorExtend = FindPlatformMonitorForPlatformPos(window->Viewport->PlatformPos); + window->ViewportAllowPlatformMonitorExtend = FindPlatformMonitorForPos(NavCalcPreferredRefPos()); + if (window->ViewportTrySplit && window->ViewportAllowPlatformMonitorExtend == -1) + window->ViewportAllowPlatformMonitorExtend = FindPlatformMonitorForPos(window->Viewport->Pos); window->ViewportTrySplit = false; // Fallback to default viewport if (window->Viewport == NULL) window->Viewport = main_viewport; - // When we own the viewport update its size/position - if (window == window->Viewport->Window && window->Viewport->LastFrameActive != g.FrameCount) - { + // Update flags + window->ViewportOwned = (window == window->Viewport->Window); + if (window->ViewportOwned) window->Viewport->Flags |= ImGuiViewportFlags_NoDecoration; - if (!window->Viewport->PlatformRequestResize) - window->Viewport->Size = window->Size; - window->Viewport->PlatformPos = ConvertViewportPosToPlatformPos(window->Pos, window->Viewport); - window->ViewportOwned = true; - } // If the OS window has a title bar, hide our imgui title bar if (window->ViewportOwned && !(window->Viewport->Flags & ImGuiViewportFlags_NoDecoration)) @@ -6359,7 +6270,7 @@ static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au { // Resize from any of the four corners // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position - ImVec2 corner_target = ConvertViewportPosToViewportPos(g.IO.MousePos, g.MouseRefViewport, window->Viewport) - g.ActiveIdClickOffset + resize_rect.GetSize() * grip.CornerPos; // Corner of the window corresponding to our corner grip + ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + resize_rect.GetSize() * grip.CornerPos; // Corner of the window corresponding to our corner grip CalcResizePosSizeFromAnyCorner(window, corner_target, grip.CornerPos, &pos_target, &size_target); } if (resize_grip_n == 0 || held || hovered) @@ -6422,10 +6333,7 @@ static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au } if (pos_target.x != FLT_MAX) { - if (window->ViewportOwned) - window->Viewport->PlatformPos = ConvertViewportPosToPlatformPos(ImFloor(pos_target), window->Viewport); - else - window->Pos = window->PosFloat = ImFloor(pos_target); + window->Pos = window->PosFloat = ImFloor(pos_target); MarkIniSettingsDirty(window); } @@ -6716,16 +6624,14 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip) window->PosFloat = FindBestWindowPosForPopup(window); - if (window->ViewportPlatformAllowMonitorExtend != -1 && !window->ViewportOwned) + if (window->ViewportAllowPlatformMonitorExtend != -1 && !window->ViewportOwned) { if (!window->Viewport->GetRect().Contains(ImRect(window->PosFloat, window->PosFloat + window->Size))) { // Late create viewport, based on the assumption that with our calculations, the DPI will be known ahead (same as the DPI of the selection done in UpdateSelectWindowViewport) //ImGuiViewport* old_viewport = window->Viewport; ImGuiViewportFlags viewport_flags = ImGuiViewportFlags_NoDecoration | ImGuiViewportFlags_NoFocusOnAppearing | ((window->Flags & ImGuiWindowFlags_NoInputs) ? ImGuiViewportFlags_NoInputs : 0); - ImVec2 platform_pos = ConvertViewportPosToPlatformPos(window->PosFloat, window->Viewport); - window->Viewport = AddUpdateViewport(window, window->ID, viewport_flags, platform_pos, window->Size); - window->ViewportOwned = true; + window->Viewport = AddUpdateViewport(window, window->ID, viewport_flags, window->PosFloat, window->Size); // FIXME-DPI //IM_ASSERT(old_viewport->DpiScale == window->Viewport->DpiScale); // FIXME-DPI: Something went wrong @@ -6735,14 +6641,16 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) } } - // Position window to fit within viewport - // We can also tell the back-end that clearing the platform window won't be necessary, as our window is filling the viewport and we have disabled BgAlpha if (window->ViewportOwned) { - window->Viewport->Flags |= ImGuiViewportFlags_NoRendererClear; - window->PosFloat = window->Viewport->Pos; + // Synchronize viewport --> window + if (window->Viewport->PlatformRequestMove) + window->PosFloat = window->Viewport->Pos; if (window->Viewport->PlatformRequestResize) window->Size = window->SizeFull = window->Viewport->Size; + + // We also tell the back-end that clearing the platform window won't be necessary, as our window is filling the viewport and we have disabled BgAlpha + window->Viewport->Flags |= ImGuiViewportFlags_NoRendererClear; } // Clamp position so window stays visible within its viewport @@ -6788,10 +6696,15 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) UpdateManualResize(window, size_auto_fit, &border_held, resize_grip_count, &resize_grip_col[0]); // When a window is marked as owning its viewport, we immediately update the viewport after a resize - window->ViewportPlatformPos = window->Viewport->PlatformPos; - if (window->ViewportOwned && !window->Viewport->PlatformRequestResize && (window->SizeFull.x != window->Viewport->Size.x || window->SizeFull.y != window->Viewport->Size.y)) + window->ViewportPos = window->Viewport->Pos; + + // Synchronize window --> viewport + if (window->ViewportOwned) { - window->Viewport->Size = window->SizeFull; + if (!window->Viewport->PlatformRequestMove) + window->Viewport->Pos = window->PosFloat; + if (!window->Viewport->PlatformRequestResize) + window->Viewport->Size = window->Size; viewport_rect = window->Viewport->GetRect(); } @@ -12012,7 +11925,7 @@ bool ImGui::BeginMainMenuBar() { ImGuiContext& g = *GImGui; g.NextWindowData.MenuBarOffsetMinVal = ImVec2(g.Style.DisplaySafeAreaPadding.x, ImMax(g.Style.DisplaySafeAreaPadding.y - g.Style.FramePadding.y, 0.0f)); - SetNextWindowPos(ImVec2(0.0f, 0.0f)); + SetNextWindowPos(g.Viewports[0]->Pos); SetNextWindowSize(ImVec2(g.IO.DisplaySize.x, g.NextWindowData.MenuBarOffsetMinVal.y + g.FontBaseSize + g.Style.FramePadding.y)); PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0,0)); @@ -12233,8 +12146,7 @@ bool ImGui::BeginMenu(const char* label, bool enabled) if (menu_is_open) { // Sub-menus are ChildWindow so that mouse can be hovering across them (otherwise top-most popup menu would steal focus and not allow hovering on parent menu) - //SetNextWindowPos(popup_pos, ImGuiCond_Always); - SetNextWindowPos(popup_pos, ImGuiCond_Appearing); // FIXME-VIEWPORT: This needs to work with ImGuiCond_Always + SetNextWindowPos(popup_pos, ImGuiCond_Always); ImGuiWindowFlags flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings | ((window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) ? ImGuiWindowFlags_ChildMenu|ImGuiWindowFlags_ChildWindow : ImGuiWindowFlags_ChildMenu); menu_is_open = BeginPopupEx(id, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display) } @@ -14077,38 +13989,6 @@ static void ImeSetInputScreenPosFn_DefaultImpl(int, int) {} // HELP //----------------------------------------------------------------------------- -static void RenderViewportThumbnail(ImDrawList* draw_list, const ImRect& bb, const ImVec2& viewport_pos, const ImVec2& viewport_size) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - ImRect viewport_r(viewport_pos, viewport_pos + viewport_size); - ImVec2 scale = bb.GetSize() / viewport_size; - window->DrawList->AddRect(bb.Min, bb.Max, ImGui::GetColorU32(ImGuiCol_Border)); - for (int i = 0; i != g.Windows.Size; i++) - { - ImGuiWindow* thumb_window = g.Windows[i]; - if (!thumb_window->WasActive || ((thumb_window->Flags & ImGuiWindowFlags_ChildWindow))) - continue; - if (thumb_window->SkipItems && (thumb_window->Flags & ImGuiWindowFlags_ChildWindow)) // FIXME-DOCK: Skip hidden docked windows. Identify those betters. - continue; - if (!viewport_r.Overlaps(thumb_window->WindowRectClipped)) - continue; - - ImRect thumb_r = thumb_window->Rect(); - ImRect title_r = thumb_window->TitleBarRect(); - ImRect thumb_r_scaled = ImRect(ImFloor(bb.Min + (thumb_r.Min - viewport_pos) * scale), ImFloor(bb.Min + (thumb_r.Max - viewport_pos) * scale)); - ImRect title_r_scaled = ImRect(ImFloor(bb.Min + (title_r.Min - viewport_pos) * scale), ImFloor(bb.Min + (title_r.Max - viewport_pos) * scale)); - thumb_r_scaled.ClipWithFull(bb); - title_r_scaled.ClipWithFull(bb); - const bool window_is_focused = (g.NavWindow && thumb_window->RootWindowForTitleBarHighlight == g.NavWindow->RootWindowForTitleBarHighlight); - window->DrawList->AddRectFilled(thumb_r_scaled.Min, thumb_r_scaled.Max, ImGui::GetColorU32(ImGuiCol_WindowBg)); - window->DrawList->AddRectFilled(title_r_scaled.Min, title_r_scaled.Max, ImGui::GetColorU32(window_is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg)); - window->DrawList->AddRect(thumb_r_scaled.Min, thumb_r_scaled.Max, ImGui::GetColorU32(ImGuiCol_Border)); - } - draw_list->AddRect(bb.Min, bb.Max, ImGui::GetColorU32(ImGuiCol_Border)); -} - static void ScaleWindow(ImGuiWindow* window, float scale) { ImVec2 origin = window->Viewport->Pos; @@ -14148,26 +14028,59 @@ void ImGui::ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale) } } + +static void RenderViewportThumbnail(ImDrawList* draw_list, ImGuiViewportP* viewport, const ImRect& bb) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + + ImVec2 scale = bb.GetSize() / viewport->Size; + ImVec2 off = bb.Min - viewport->Pos * scale; + window->DrawList->AddRectFilled(bb.Min, bb.Max, ImGui::GetColorU32(ImGuiCol_Border, 0.40f)); + for (int i = 0; i != g.Windows.Size; i++) + { + ImGuiWindow* thumb_window = g.Windows[i]; + if (!thumb_window->WasActive || ((thumb_window->Flags & ImGuiWindowFlags_ChildWindow))) + continue; + if (thumb_window->SkipItems && (thumb_window->Flags & ImGuiWindowFlags_ChildWindow)) // FIXME-DOCK: Skip hidden docked windows. Identify those betters. + continue; + if (thumb_window->Viewport != viewport) + continue; + + ImRect thumb_r = thumb_window->Rect(); + ImRect title_r = thumb_window->TitleBarRect(); + ImRect thumb_r_scaled = ImRect(ImFloor(off + thumb_r.Min * scale), ImFloor(off + thumb_r.Max * scale)); + ImRect title_r_scaled = ImRect(ImFloor(off + title_r.Min * scale), ImFloor(off + ImVec2(title_r.Max.x, title_r.Min.y) * scale) + ImVec2(0,5)); // Exagerate title bar height + thumb_r_scaled.ClipWithFull(bb); + title_r_scaled.ClipWithFull(bb); + const bool window_is_focused = (g.NavWindow && thumb_window->RootWindowForTitleBarHighlight == g.NavWindow->RootWindowForTitleBarHighlight); + window->DrawList->AddRectFilled(thumb_r_scaled.Min, thumb_r_scaled.Max, ImGui::GetColorU32(ImGuiCol_WindowBg)); + window->DrawList->AddRectFilled(title_r_scaled.Min, title_r_scaled.Max, ImGui::GetColorU32(window_is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg)); + window->DrawList->AddRect(thumb_r_scaled.Min, thumb_r_scaled.Max, ImGui::GetColorU32(ImGuiCol_Border)); + window->DrawList->AddText(g.Font, g.FontSize * 1.0f, title_r_scaled.Min, ImGui::GetColorU32(ImGuiCol_Text), thumb_window->Name, ImGui::FindRenderedTextEnd(thumb_window->Name)); + } + draw_list->AddRect(bb.Min, bb.Max, ImGui::GetColorU32(ImGuiCol_Border)); +} + void ImGui::ShowViewportThumbnails() { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - float SCALE = 1.0f / 7.0f; - ImGui::NewLine(); // For labels + // We don't display full monitor bounds (we could, but it often looks awkward), instead we display just enough to cover all of our viewports. + float SCALE = 1.0f / 8.0f; + ImRect bb_full; + for (int n = 0; n < g.Viewports.Size; n++) + bb_full.Add(g.Viewports[n]->GetRect()); ImVec2 p = window->DC.CursorPos; + ImVec2 off = p - bb_full.Min * SCALE; for (int n = 0; n < g.Viewports.Size; n++) { ImGuiViewportP* viewport = g.Viewports[n]; - if (n > 0) - ImGui::SameLine(); - ImRect bb(p + (viewport->Pos) * SCALE, p + (viewport->Pos + viewport->Size) * SCALE); - RenderViewportThumbnail(window->DrawList, bb, viewport->Pos, viewport->Size); - char buf[64]; - ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f", viewport->Pos.x); - ImGui::RenderText(bb.Min + ImVec2(1, -g.FontSize), buf, NULL, false); - ImGui::Dummy(bb.GetSize()); + ImRect viewport_draw_bb(off + (viewport->Pos) * SCALE, off + (viewport->Pos + viewport->Size) * SCALE); + RenderViewportThumbnail(window->DrawList, viewport, viewport_draw_bb); } + ImGui::Dummy(bb_full.GetSize() * SCALE); } void ImGui::ShowMetricsWindow(bool* p_open) @@ -14282,7 +14195,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::BulletText("NavRectRel[0]: (%.1f,%.1f)(%.1f,%.1f)", window->NavRectRel[0].Min.x, window->NavRectRel[0].Min.y, window->NavRectRel[0].Max.x, window->NavRectRel[0].Max.y); else ImGui::BulletText("NavRectRel[0]: "); - ImGui::BulletText("Viewport: %d, ViewportId: 0x%08X, ViewportPlatformPos: (%.1f,%.1f)", window->Viewport ? window->Viewport->Idx : -1, window->ViewportId, window->ViewportPlatformPos.x, window->ViewportPlatformPos.y); + ImGui::BulletText("Viewport: %d, ViewportId: 0x%08X, ViewportPlatformPos: (%.1f,%.1f)", window->Viewport ? window->Viewport->Idx : -1, window->ViewportId, window->ViewportPos.x, window->ViewportPos.y); if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow"); if (window->DC.ChildWindows.Size > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows"); if (window->ColumnsStorage.Size > 0 && ImGui::TreeNode("Columns", "Columns sets (%d)", window->ColumnsStorage.Size)) @@ -14310,8 +14223,8 @@ void ImGui::ShowMetricsWindow(bool* p_open) if (ImGui::TreeNode((void*)(intptr_t)viewport->ID, "Viewport #%d, ID: 0x%08X, Window: \"%s\"", viewport->Idx, viewport->ID, viewport->Window ? viewport->Window->Name : "N/A")) { ImGuiWindowFlags flags = viewport->Flags; - ImGui::BulletText("Pos: (%.0f,%.0f), PlatformPos: (%.0f,%.0f)", viewport->Pos.x, viewport->Pos.y, viewport->PlatformPos.x, viewport->PlatformPos.y); - if (viewport->Idx > 0) { ImGui::SameLine(); if (ImGui::SmallButton("Reset")) viewport->PlatformPos = ImVec2(0, 0); } + ImGui::BulletText("Pos: (%.0f,%.0f)", viewport->Pos.x, viewport->Pos.y); + if (viewport->Idx > 0) { ImGui::SameLine(); if (ImGui::SmallButton("Reset")) { viewport->Pos = ImVec2(200,200); if (viewport->Window) viewport->Window->Pos = viewport->Window->PosFloat = ImVec2(200,200); } } ImGui::BulletText("Size: (%0.f,%.0f), DpiScale: %.0f%%", viewport->Size.x, viewport->Size.y, viewport->DpiScale * 100.0f); ImGui::BulletText("Flags: 0x%04X =%s%s%s%s%s", viewport->Flags, (flags & ImGuiViewportFlags_CanHostOtherWindows) ? " CanHostOtherWindows" : "", (flags & ImGuiViewportFlags_NoDecoration) ? " NoDecoration" : "", @@ -14372,8 +14285,6 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::Text("NavActivateId: 0x%08X, NavInputId: 0x%08X", g.NavActivateId, g.NavInputId); ImGui::Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover); ImGui::Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize); - ImVec2 mouse_platform_pos = ConvertViewportPosToPlatformPos(g.IO.MousePos, g.MouseRefViewport); - ImGui::Text("MousePlatformPos: (%.1f,%.1f)", mouse_platform_pos.x, mouse_platform_pos.y); ImGui::Text("MousePosViewport: 0x%08X, Hovered: 0x%08X -> Ref 0x%08X", g.IO.MousePosViewport, g.IO.MouseHoveredViewport, g.MouseRefViewport->ID); ImGui::TreePop(); } diff --git a/imgui.h b/imgui.h index e3e5ab31fe45..f207425cac26 100644 --- a/imgui.h +++ b/imgui.h @@ -1152,7 +1152,7 @@ namespace ImGui bool Begin(const char* name, bool* p_open, const ImVec2& size_on_first_use, float bg_alpha_override = -1.0f, ImGuiWindowFlags flags = 0); // Use SetNextWindowSize(size, ImGuiCond_FirstUseEver) + SetNextWindowBgAlpha() instead. static inline bool IsRootWindowOrAnyChildHovered() { return IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows); } static inline void AlignFirstTextHeightToWidgets() { AlignTextToFramePadding(); } - static inline void SetNextWindowPosCenter(ImGuiCond c=0) { ImGuiIO& io = GetIO(); SetNextWindowPos(ImVec2(io.DisplaySize.x * 0.5f, io.DisplaySize.y * 0.5f), c, ImVec2(0.5f, 0.5f)); } // FIXME-VIEWPORT: Select viewport based on mouse position + static inline void SetNextWindowPosCenter(ImGuiCond c=0) { ImGuiIO& io = GetIO(); SetNextWindowPos(ImVec2(io.DisplaySize.x * 0.5f, io.DisplaySize.y * 0.5f), c, ImVec2(0.5f, 0.5f)); } // FIXME-VIEWPORT-ABS: Select viewport based on mouse position // OBSOLETED in 1.51 (between Jun 2017 and Aug 2017) static inline bool IsItemHoveredRect() { return IsItemHovered(ImGuiHoveredFlags_RectOnly); } static inline bool IsPosHoveringAnyWindow(const ImVec2&) { IM_ASSERT(0); return false; } // This was misleading and partly broken. You probably want to use the ImGui::GetIO().WantCaptureMouse flag instead. @@ -1947,12 +1947,11 @@ struct ImGuiViewport { ImGuiID ID; ImGuiViewportFlags Flags; - ImVec2 Pos; // Position of viewport in imgui virtual space (all viewports Pos.y == 0.0f, main viewport Pos.x == 0.0f) + ImVec2 Pos; // Position of viewport both in imgui space and in OS desktop/native space ImVec2 Size; // Size of viewport in pixel float DpiScale; // 1.0f = 96 DPI = No extra scale ImDrawData* DrawData; // The ImDrawData corresponding to this viewport. Valid after Render() and until the next call to NewFrame(). - ImVec2 PlatformPos; // Position in OS desktop/native space void* PlatformUserData; // void* to hold custom data structure for the platform (e.g. windowing info, render context) void* PlatformHandle; // void* for FindViewportByPlatformHandle(). (e.g. HWND, GlfwWindow*, SDL_Window*) bool PlatformRequestClose; // Platform window requested closure @@ -1960,8 +1959,8 @@ struct ImGuiViewport bool PlatformRequestResize; // Platform window requested resize (e.g. window was resize using OS windowing facility) void* RendererUserData; // void* to hold custom data structure for the renderer (e.g. swap chain, frame-buffers etc.) - ImGuiViewport() { ID = 0; Flags = 0; DpiScale = 0.0f; DrawData = NULL; PlatformUserData = PlatformHandle = NULL; PlatformRequestClose = PlatformRequestMove = PlatformRequestResize = false; RendererUserData = NULL; } - ~ImGuiViewport() { IM_ASSERT(PlatformUserData == NULL && RendererUserData == NULL); } + ImGuiViewport() { ID = 0; Flags = 0; DpiScale = 0.0f; DrawData = NULL; PlatformUserData = PlatformHandle = NULL; PlatformRequestClose = PlatformRequestMove = PlatformRequestResize = false; RendererUserData = NULL; } + ~ImGuiViewport() { IM_ASSERT(PlatformUserData == NULL && RendererUserData == NULL); } }; #if defined(__clang__) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 8d6376603567..bcf0c02b5811 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2474,11 +2474,11 @@ static void ShowExampleAppConstrainedResize(bool* p_open) // Demonstrate creating a simple static window with no decoration + a context-menu to choose which corner of the screen to use. static void ShowExampleAppFixedOverlay(bool* p_open) { - // FIXME-VIEWPORT: Select a default viewport + // FIXME-VIEWPORT-ABS: Select a default viewport const float DISTANCE = 10.0f; static int corner = 0; - ImGuiIO& io = ImGui::GetIO(); - ImVec2 window_pos = ImVec2((corner & 1) ? io.DisplaySize.x - DISTANCE : DISTANCE, (corner & 2) ? io.DisplaySize.y - DISTANCE : DISTANCE); + ImGuiViewport* viewport = ImGui::GetMainViewport(); + ImVec2 window_pos = ImVec2((corner & 1) ? (viewport->Pos.x + viewport->Size.x - DISTANCE) : (viewport->Pos.x + DISTANCE), (corner & 2) ? (viewport->Pos.y + viewport->Size.y - DISTANCE) : (viewport->Pos.y + DISTANCE)); ImVec2 window_pos_pivot = ImVec2((corner & 1) ? 1.0f : 0.0f, (corner & 2) ? 1.0f : 0.0f); ImGui::SetNextWindowPos(window_pos, ImGuiCond_Always, window_pos_pivot); ImGui::SetNextWindowBgAlpha(0.3f); // Transparent background diff --git a/imgui_internal.h b/imgui_internal.h index edef57bbc582..f3377152517d 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -405,13 +405,13 @@ struct ImGuiWindowSettings { char* Name; ImGuiID Id; - ImVec2 Pos; + ImVec2 Pos; // NB: Settings position are stored RELATIVE to the viewport! Whereas runtime ones are absolute positions. ImVec2 Size; - ImVec2 ViewportPlatformPos; + ImVec2 ViewportPos; ImGuiID ViewportId; bool Collapsed; - ImGuiWindowSettings() { Name = NULL; Id = ViewportId = 0; Pos = Size = ImVec2(0,0); ViewportPlatformPos = ImVec2(FLT_MAX, FLT_MAX); Collapsed = false; } + ImGuiWindowSettings() { Name = NULL; Id = ViewportId = 0; Pos = Size = ViewportPos = ImVec2(0, 0); Collapsed = false; } }; struct ImGuiSettingsHandler @@ -518,6 +518,7 @@ struct ImGuiViewportP : public ImGuiViewport int LastFrameAsRefViewport; // Last frame number this viewport was io.MouseViewportRef int LastFrameOverlayDrawList; ImGuiID LastNameHash; + ImVec2 LastPos; float Alpha; // Window opacity (when dragging dockable windows/viewports we make them transparent) float LastAlpha; ImGuiWindow* Window; @@ -530,7 +531,6 @@ struct ImGuiViewportP : public ImGuiViewport ~ImGuiViewportP() { if (OverlayDrawList) IM_DELETE(OverlayDrawList); } ImRect GetRect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } ImVec2 GetCenter() const{ return ImVec2(Pos.x + Size.x * 0.5f, Pos.y + Size.y * 0.5f); } - float GetNextX() const { const float SPACING = 4.0f; return Pos.x + Size.x + SPACING; } }; struct ImGuiNavMoveResult @@ -940,9 +940,9 @@ struct IMGUI_API ImGuiWindow ImGuiID ID; // == ImHash(Name) ImGuiWindowFlags Flags, FlagsPreviousFrame; // See enum ImGuiWindowFlags_ ImGuiViewportP* Viewport; // Always set in Begin(), only inactive windows may have a NULL value here - ImGuiID ViewportId; // Inactive windows preserve their last viewport id (since the viewport may disappear with the window inactivity) - int ViewportPlatformAllowMonitorExtend; // Reset to -1 every frame (index is guaranteed to be valid between NewFrame..EndFrame), only used in the Appearing frame of a tooltip/popup to enforce clamping to a given monitor - ImVec2 ViewportPlatformPos; + ImGuiID ViewportId; // We backup the viewport id (since the viewport may disappear or never be created if the window is inactive) + ImVec2 ViewportPos; // We backup the viewport position (since the viewport may disappear or never be created if the window is inactive) + int ViewportAllowPlatformMonitorExtend; // Reset to -1 every frame (index is guaranteed to be valid between NewFrame..EndFrame), only used in the Appearing frame of a tooltip/popup to enforce clamping to a given monitor ImVec2 PosFloat; ImVec2 Pos; // Position rounded-up to nearest pixel ImVec2 Size; // Current size (==SizeFull or collapsed title bar size) From 4433ce4312ec1d6740cfffca0a7bc42687659cf6 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 23 Apr 2018 12:38:20 +0200 Subject: [PATCH 130/828] Viewport, Platform: Added work area in ImGuiPlatformMonitor. Renamed fields. (#1542) --- examples/imgui_impl_glfw.cpp | 11 +++++++---- examples/imgui_impl_sdl2.cpp | 29 +++++++++++++++++++---------- examples/imgui_impl_win32.cpp | 12 +++++++++--- imgui.cpp | 21 +++++++++++++++------ imgui.h | 6 +++--- 5 files changed, 53 insertions(+), 26 deletions(-) diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index b07f33f96b42..b826188b8457 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -521,25 +521,28 @@ static int ImGui_ImplGlfw_CreateVkSurface(ImGuiViewport* viewport, ImU64 vk_inst #endif // GLFW_HAS_VULKAN // FIXME-PLATFORM: Update when changed (using glfwSetMonitorCallback?) +// FIXME-PLATFORM: GLFW doesn't export work area (see https://github.com/glfw/glfw/pull/989) static void ImGui_ImplGlfw_UpdateMonitors() { ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); int monitors_count = 0; GLFWmonitor** glfw_monitors = glfwGetMonitors(&monitors_count); - platform_io.Monitors.resize(monitors_count, ImGuiPlatformMonitor()); + platform_io.Monitors.resize(0); for (int n = 0; n < monitors_count; n++) { + ImGuiPlatformMonitor monitor; int x, y; glfwGetMonitorPos(glfw_monitors[n], &x, &y); const GLFWvidmode* vid_mode = glfwGetVideoMode(glfw_monitors[n]); - platform_io.Monitors[n].Pos = ImVec2((float)x, (float)y); - platform_io.Monitors[n].Size = ImVec2((float)vid_mode->width, (float)vid_mode->height); + monitor.FullMin = monitor.WorkMin = ImVec2((float)x, (float)y); + monitor.FullMax = monitor.WorkMax = ImVec2((float)(x + vid_mode->width), (float)(y + vid_mode->height)); #if GLFW_HAS_PER_MONITOR_DPI // Warning: the validity of monitor DPI information on Windows depends on the application DPI awareness settings, which generally needs to be set in the manifest or at runtime. float x_scale, y_scale; glfwGetMonitorContentScale(glfw_monitors[n], &x_scale, &y_scale); - platform_io.Monitors[n].DpiScale = x_scale; + monitor.DpiScale = x_scale; #endif + platform_io.Monitors.push_back(monitor); } } diff --git a/examples/imgui_impl_sdl2.cpp b/examples/imgui_impl_sdl2.cpp index b7d801a8617b..9a67f0e1df2f 100644 --- a/examples/imgui_impl_sdl2.cpp +++ b/examples/imgui_impl_sdl2.cpp @@ -30,13 +30,15 @@ #include "imgui_impl_sdl2.h" // SDL +// (the multi-viewports feature requires SDL features suppoted from SDL 2.0.5+) #include #include -#define SDL_HAS_CAPTURE_MOUSE SDL_VERSION_ATLEAST(2,0,4) -#define SDL_HAS_WINDOW_OPACITY SDL_VERSION_ATLEAST(2,0,5) -#define SDL_HAS_ALWAYS_ON_TOP SDL_VERSION_ATLEAST(2,0,5) -#define SDL_HAS_PER_MONITOR_DPI SDL_VERSION_ATLEAST(2,0,4) -#define SDL_HAS_VULKAN SDL_VERSION_ATLEAST(2,0,6) +#define SDL_HAS_CAPTURE_MOUSE SDL_VERSION_ATLEAST(2,0,4) +#define SDL_HAS_WINDOW_OPACITY SDL_VERSION_ATLEAST(2,0,5) +#define SDL_HAS_ALWAYS_ON_TOP SDL_VERSION_ATLEAST(2,0,5) +#define SDL_HAS_USABLE_DISPLAY_BOUNDS SDL_VERSION_ATLEAST(2,0,5) +#define SDL_HAS_PER_MONITOR_DPI SDL_VERSION_ATLEAST(2,0,4) +#define SDL_HAS_VULKAN SDL_VERSION_ATLEAST(2,0,6) #if !SDL_HAS_VULKAN static const Uint32 SDL_WINDOW_VULKAN = 0x10000000; #endif @@ -446,20 +448,27 @@ static int ImGui_ImplSDL2_CreateVkSurface(ImGuiViewport* viewport, ImU64 vk_inst static void ImGui_ImplSDL2_UpdateMonitors() { ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.Monitors.resize(0); int display_count = SDL_GetNumVideoDisplays(); - platform_io.Monitors.resize(display_count, ImGuiPlatformMonitor()); for (int n = 0; n < display_count; n++) { // Warning: the validity of monitor DPI information on Windows depends on the application DPI awareness settings, which generally needs to be set in the manifest or at runtime. + ImGuiPlatformMonitor monitor; SDL_Rect r; SDL_GetDisplayBounds(n, &r); - platform_io.Monitors[n].Pos = ImVec2((float)r.x, (float)r.y); - platform_io.Monitors[n].Size = ImVec2((float)r.w, (float)r.h); + monitor.FullMin = monitor.WorkMin = ImVec2((float)(r.x), (float)(r.y)); + monitor.FullMax = monitor.WorkMax = ImVec2((float)(r.x + r.w), (float)(r.y + r.h)); +#if SDL_HAS_USABLE_DISPLAY_BOUNDS + SDL_GetDisplayUsableBounds(n, &r); + monitor.WorkMin = ImVec2((float)(r.x), (float)(r.y)); + monitor.WorkMax = ImVec2((float)(r.x + r.w), (float)(r.y + r.h)); +#endif #if SDL_HAS_PER_MONITOR_DPI float dpi = 0.0f; - SDL_GetDisplayDPI(n, &dpi, NULL, NULL); - platform_io.Monitors[n].DpiScale = dpi / 96.0f; + if (SDL_GetDisplayDPI(n, &dpi, NULL, NULL)) + monitor.DpiScale = dpi / 96.0f; #endif + platform_io.Monitors.push_back(monitor); } } diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index 47560a4e4768..c68dcc8b039f 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -550,11 +550,17 @@ static LRESULT CALLBACK ImGui_ImplWin32_WndProcHandler_PlatformWindow(HWND hWnd, return DefWindowProc(hWnd, msg, wParam, lParam); } -static BOOL CALLBACK ImGui_ImplWin32_UpdateMonitors_EnumFunc(HMONITOR monitor, HDC, LPRECT rect, LPARAM) +static BOOL CALLBACK ImGui_ImplWin32_UpdateMonitors_EnumFunc(HMONITOR monitor, HDC, LPRECT, LPARAM) { + MONITORINFO info = { 0 }; + info.cbSize = sizeof(MONITORINFO); + if (!::GetMonitorInfo(monitor, &info)) + return TRUE; ImGuiPlatformMonitor imgui_monitor; - imgui_monitor.Pos = ImVec2((float)rect->left, (float)rect->top); - imgui_monitor.Size = ImVec2((float)(rect->right - rect->left), (float)(rect->bottom - rect->top)); + imgui_monitor.FullMin = ImVec2((float)info.rcMonitor.left, (float)info.rcMonitor.top); + imgui_monitor.FullMax = ImVec2((float)info.rcMonitor.right, (float)info.rcMonitor.bottom); + imgui_monitor.WorkMin = ImVec2((float)info.rcWork.left, (float)info.rcWork.top); + imgui_monitor.WorkMax = ImVec2((float)info.rcWork.right, (float)info.rcWork.bottom); imgui_monitor.DpiScale = ImGui_ImplWin32_GetDpiScaleForMonitor(monitor); ImGui::GetPlatformIO().Monitors.push_back(imgui_monitor); return TRUE; diff --git a/imgui.cpp b/imgui.cpp index 59fdd2b0a942..b9b0a752b5f3 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3802,6 +3802,13 @@ void ImGui::NewFrame() // Disable feature, our back-ends do not support it g.IO.ConfigFlags &= ~ImGuiConfigFlags_ViewportsEnable; } + for (int monitor_n = 0; monitor_n < g.PlatformIO.Monitors.Size; monitor_n++) + { + ImGuiPlatformMonitor& mon = g.PlatformIO.Monitors[monitor_n]; + IM_ASSERT(mon.FullMin.x < mon.FullMax.x && mon.FullMin.y < mon.FullMax.y && "Monitor bounds not setup properly."); + IM_ASSERT(mon.WorkMin.x < mon.WorkMax.x && mon.WorkMin.y < mon.WorkMax.y && "Monitor bounds not setup properly. If you don't have work area information, just copy Min/Max into them."); + IM_ASSERT(mon.DpiScale != 0.0f); + } } // Load settings on first frame @@ -5847,8 +5854,8 @@ static ImRect FindAllowedExtentRectForWindow(ImGuiWindow* window) { // Extent with be in the frame of reference of the given viewport (so Min is likely to be negative here) const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[window->ViewportAllowPlatformMonitorExtend]; - r_screen.Min = monitor.Pos; - r_screen.Max = monitor.Pos + monitor.Size; + r_screen.Min = monitor.WorkMin; + r_screen.Max = monitor.WorkMax; } else { @@ -6028,7 +6035,7 @@ static ImVec2 CalcSizeAutoFit(ImGuiWindow* window, const ImVec2& size_contents) const int monitor_idx = window->ViewportAllowPlatformMonitorExtend; ImVec2 avail_size = window->Viewport->Size; if (window->ViewportOwned) - avail_size = (monitor_idx >= 0 && monitor_idx < g.PlatformIO.Monitors.Size) ? g.PlatformIO.Monitors[monitor_idx].Size : ImVec2(FLT_MAX, FLT_MAX); + avail_size = (monitor_idx >= 0 && monitor_idx < g.PlatformIO.Monitors.Size) ? (g.PlatformIO.Monitors[monitor_idx].WorkMax - g.PlatformIO.Monitors[monitor_idx].WorkMin) : ImVec2(FLT_MAX, FLT_MAX); ImVec2 size_auto_fit = ImClamp(size_contents, style.WindowMinSize, ImMax(style.WindowMinSize, avail_size - g.Style.DisplaySafeAreaPadding * 2.0f)); // When the window cannot fit all contents (either because of constraints, either because screen is too small), @@ -6099,8 +6106,7 @@ static int FindPlatformMonitorForPos(ImVec2 platform_pos) for (int monitor_n = 0; monitor_n < g.PlatformIO.Monitors.Size; monitor_n++) { const ImGuiPlatformMonitor* monitor = &g.PlatformIO.Monitors[monitor_n]; - ImRect monitor_rect(monitor->Pos, monitor->Pos + monitor->Size); - if (monitor_rect.Contains(platform_pos)) + if (ImRect(monitor->FullMin, monitor->FullMax).Contains(platform_pos)) return monitor_n; } return -1; @@ -14252,7 +14258,10 @@ void ImGui::ShowMetricsWindow(bool* p_open) for (int i = 0; i < g.PlatformIO.Monitors.Size; i++) { const ImGuiPlatformMonitor& mon = g.PlatformIO.Monitors[i]; - ImGui::BulletText("Monitor #%d: DPI %.0f%%, Min (%.0f,%.0f), Max (%.0f,%.0f), Size (%.0f,%.0f)", i, mon.DpiScale * 100.0f, mon.Pos.x, mon.Pos.y, mon.Pos.x + mon.Size.x, mon.Pos.y + mon.Size.y, mon.Size.x, mon.Size.y); + ImGui::BulletText("Monitor #%d: DPI %.0f%%\n FullMin (%.0f,%.0f), FullMax (%.0f,%.0f), FullSize (%.0f,%.0f)\n WorkMin (%.0f,%.0f), WorkMax (%.0f,%.0f), WorkSize (%.0f,%.0f)", + i, mon.DpiScale * 100.0f, + mon.FullMin.x, mon.FullMin.y, mon.FullMax.x, mon.FullMax.y, mon.FullMax.x - mon.FullMin.x, mon.FullMax.y - mon.FullMin.y, + mon.WorkMin.x, mon.WorkMin.y, mon.WorkMax.x, mon.WorkMax.y, mon.WorkMax.x - mon.WorkMin.x, mon.WorkMax.y - mon.WorkMin.y); } ImGui::TreePop(); } diff --git a/imgui.h b/imgui.h index f207425cac26..8c6e5b543847 100644 --- a/imgui.h +++ b/imgui.h @@ -1876,10 +1876,10 @@ struct ImFont // Dear ImGui only uses this to clamp the position of popups and tooltips so they don't straddle multiple monitors struct ImGuiPlatformMonitor { - ImVec2 Pos; - ImVec2 Size; + ImVec2 FullMin, FullMax; // Coordinates of the area displayed on this monitor (Min = upper left, Max = bottom right) + ImVec2 WorkMin, WorkMax; // (Optional) Coordinates without task bars / side bars / menu bars. imgui uses this to avoid positioning popups/tooltips inside this region. float DpiScale; - ImGuiPlatformMonitor() { Pos = ImVec2(0,0); Size = ImVec2(0,0); DpiScale = 1.0f; } + ImGuiPlatformMonitor() { FullMin = FullMax = WorkMin = WorkMax = ImVec2(0,0); DpiScale = 1.0f; } }; // (Optional) Setup required only if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) is enabled From f1c31ebc67a61810071e53144e40389790296856 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 23 Apr 2018 12:52:01 +0200 Subject: [PATCH 131/828] Viewport: Fixed initial popup positioning not using the monitor area properly. (#1542) --- imgui.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index b9b0a752b5f3..3ab3dd67a032 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6032,10 +6032,12 @@ static ImVec2 CalcSizeAutoFit(ImGuiWindow* window, const ImVec2& size_contents) else { // Maximum window size is determined by the viewport size or monitor size - const int monitor_idx = window->ViewportAllowPlatformMonitorExtend; ImVec2 avail_size = window->Viewport->Size; if (window->ViewportOwned) - avail_size = (monitor_idx >= 0 && monitor_idx < g.PlatformIO.Monitors.Size) ? (g.PlatformIO.Monitors[monitor_idx].WorkMax - g.PlatformIO.Monitors[monitor_idx].WorkMin) : ImVec2(FLT_MAX, FLT_MAX); + avail_size = ImVec2(FLT_MAX, FLT_MAX); + const int monitor_idx = window->ViewportAllowPlatformMonitorExtend; + if (monitor_idx >= 0 && monitor_idx < g.PlatformIO.Monitors.Size) + avail_size = (g.PlatformIO.Monitors[monitor_idx].WorkMax - g.PlatformIO.Monitors[monitor_idx].WorkMin); ImVec2 size_auto_fit = ImClamp(size_contents, style.WindowMinSize, ImMax(style.WindowMinSize, avail_size - g.Style.DisplaySafeAreaPadding * 2.0f)); // When the window cannot fit all contents (either because of constraints, either because screen is too small), From 423577e14edd93dd45b36028811c587d44a67b2e Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 23 Apr 2018 15:54:38 +0200 Subject: [PATCH 132/828] Viewport, Platforms: SDL: Fixed initial focus click being ignored as a mouse button. GLFW: Fixed mouse cursor support for multi-viewport with GLFW 3.3 (current master) - somehow doesn't appear to work with GLFW 3.2 but I'm not too fussed about it. (#1542) --- examples/imgui_impl_dx10.cpp | 1 - examples/imgui_impl_dx12.cpp | 2 +- examples/imgui_impl_glfw.cpp | 23 ++++++++++++++--------- examples/imgui_impl_sdl2.cpp | 27 ++++++++++++++++----------- examples/imgui_impl_win32.cpp | 2 +- 5 files changed, 32 insertions(+), 23 deletions(-) diff --git a/examples/imgui_impl_dx10.cpp b/examples/imgui_impl_dx10.cpp index 9d59724610ae..db6f99d6563e 100644 --- a/examples/imgui_impl_dx10.cpp +++ b/examples/imgui_impl_dx10.cpp @@ -521,7 +521,6 @@ static void ImGui_ImplDX10_CreateWindow(ImGuiViewport* viewport) ImGuiViewportDataDx10* data = IM_NEW(ImGuiViewportDataDx10)(); viewport->RendererUserData = data; - // FIXME-PLATFORM HWND hwnd = (HWND)viewport->PlatformHandle; IM_ASSERT(hwnd != 0); diff --git a/examples/imgui_impl_dx12.cpp b/examples/imgui_impl_dx12.cpp index 1aa0845b29bd..9ae3a0d3ffe4 100644 --- a/examples/imgui_impl_dx12.cpp +++ b/examples/imgui_impl_dx12.cpp @@ -605,7 +605,7 @@ bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FO } // Setup back-end capabilities flags - // FIXME-VIEWPORT: Actually unfinshed.. + // FIXME-VIEWPORT: Actually unfinished.. ImGuiIO& io = ImGui::GetIO(); io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional) if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index b826188b8457..19fa93d32d3a 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -242,17 +242,22 @@ static void ImGui_ImplGlfw_UpdateMouse() } // Update OS/hardware mouse cursor if imgui isn't drawing a software cursor + // FIXME-PLATFORM: Unfocused windows seems to fail changing the mouse cursor with GLFW 3.2, but 3.3 works here. if ((io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) == 0 && glfwGetInputMode(g_Window, GLFW_CURSOR) != GLFW_CURSOR_DISABLED) { ImGuiMouseCursor cursor = ImGui::GetMouseCursor(); - if (io.MouseDrawCursor || cursor == ImGuiMouseCursor_None) + for (int n = 0; n < platform_io.Viewports.Size; n++) { - glfwSetInputMode(g_Window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); - } - else - { - glfwSetCursor(g_Window, g_MouseCursors[cursor] ? g_MouseCursors[cursor] : g_MouseCursors[ImGuiMouseCursor_Arrow]); - glfwSetInputMode(g_Window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + GLFWwindow* window = (GLFWwindow*)platform_io.Viewports[n]->PlatformHandle; + if (io.MouseDrawCursor || cursor == ImGuiMouseCursor_None) + { + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); + } + else + { + glfwSetCursor(window, g_MouseCursors[cursor] ? g_MouseCursors[cursor] : g_MouseCursors[ImGuiMouseCursor_Arrow]); + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + } } } } @@ -520,8 +525,8 @@ static int ImGui_ImplGlfw_CreateVkSurface(ImGuiViewport* viewport, ImU64 vk_inst } #endif // GLFW_HAS_VULKAN -// FIXME-PLATFORM: Update when changed (using glfwSetMonitorCallback?) -// FIXME-PLATFORM: GLFW doesn't export work area (see https://github.com/glfw/glfw/pull/989) +// FIXME-PLATFORM: Update monitor list when changed (using glfwSetMonitorCallback?) +// FIXME-PLATFORM: GLFW doesn't export monitor work area (see https://github.com/glfw/glfw/pull/989) static void ImGui_ImplGlfw_UpdateMonitors() { ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); diff --git a/examples/imgui_impl_sdl2.cpp b/examples/imgui_impl_sdl2.cpp index 9a67f0e1df2f..4342afbb7527 100644 --- a/examples/imgui_impl_sdl2.cpp +++ b/examples/imgui_impl_sdl2.cpp @@ -30,15 +30,16 @@ #include "imgui_impl_sdl2.h" // SDL -// (the multi-viewports feature requires SDL features suppoted from SDL 2.0.5+) +// (the multi-viewports feature requires SDL features supported from SDL 2.0.5+) #include #include -#define SDL_HAS_CAPTURE_MOUSE SDL_VERSION_ATLEAST(2,0,4) -#define SDL_HAS_WINDOW_OPACITY SDL_VERSION_ATLEAST(2,0,5) -#define SDL_HAS_ALWAYS_ON_TOP SDL_VERSION_ATLEAST(2,0,5) -#define SDL_HAS_USABLE_DISPLAY_BOUNDS SDL_VERSION_ATLEAST(2,0,5) -#define SDL_HAS_PER_MONITOR_DPI SDL_VERSION_ATLEAST(2,0,4) -#define SDL_HAS_VULKAN SDL_VERSION_ATLEAST(2,0,6) +#define SDL_HAS_CAPTURE_MOUSE SDL_VERSION_ATLEAST(2,0,4) +#define SDL_HAS_WINDOW_OPACITY SDL_VERSION_ATLEAST(2,0,5) +#define SDL_HAS_ALWAYS_ON_TOP SDL_VERSION_ATLEAST(2,0,5) +#define SDL_HAS_USABLE_DISPLAY_BOUNDS SDL_VERSION_ATLEAST(2,0,5) +#define SDL_HAS_PER_MONITOR_DPI SDL_VERSION_ATLEAST(2,0,4) +#define SDL_HAS_VULKAN SDL_VERSION_ATLEAST(2,0,6) +#define SDL_HAS_MOUSE_FOCUS_CLICKTHROUGH SDL_VERSION_ATLEAST(2,0,5) #if !SDL_HAS_VULKAN static const Uint32 SDL_WINDOW_VULKAN = 0x10000000; #endif @@ -231,7 +232,7 @@ static void ImGui_ImplSDL2_UpdateMouse() io.MousePosViewport = viewport->ID; } - // We already retrieve global mouse position, SDL_CaptureMouse() also let the OS know our drag outside boundaries shouldn't trigger, e.g.: OS window resize cursor + // We already retrieve global mouse position, SDL_CaptureMouse() also let the OS know e.g. that our imgui drag outside the SDL window boundaries shouldn't trigger the OS window resize cursor // The function is only supported from SDL 2.0.4 (released Jan 2016) bool any_mouse_button_down = ImGui::IsAnyMouseDown(); SDL_CaptureMouse(any_mouse_button_down ? SDL_TRUE : SDL_FALSE); @@ -301,11 +302,10 @@ static void ImGui_ImplSDL2_CreateWindow(ImGuiViewport* viewport) ImGuiViewportDataSDL2* data = IM_NEW(ImGuiViewportDataSDL2)(); viewport->PlatformUserData = data; - // Share GL resources with main context - // FIXME-PLATFORM ImGuiViewport* main_viewport = ImGui::GetMainViewport(); ImGuiViewportDataSDL2* main_viewport_data = (ImGuiViewportDataSDL2*)main_viewport->PlatformUserData; + // Share GL resources with main context bool use_opengl = (main_viewport_data->GLContext != NULL); SDL_GLContext backup_context = NULL; if (use_opengl) @@ -444,7 +444,7 @@ static int ImGui_ImplSDL2_CreateVkSurface(ImGuiViewport* viewport, ImU64 vk_inst } #endif // SDL_HAS_VULKAN -// FIXME-PLATFORM: Update when changed? +// FIXME-PLATFORM: Update monitor list when changed? static void ImGui_ImplSDL2_UpdateMonitors() { ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); @@ -490,6 +490,11 @@ static void ImGui_ImplSDL2_InitPlatformInterface(SDL_Window* window, void* sdl_g platform_io.Platform_CreateVkSurface = ImGui_ImplSDL2_CreateVkSurface; #endif + // SDL2 by default doesn't pass mouse clicks to the application when the click focused a window. This is getting in the way of our interactions and we disable that behavior. +#if SDL_HAS_MOUSE_FOCUS_CLICKTHROUGH + SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1"); +#endif + ImGui_ImplSDL2_UpdateMonitors(); // Register main window handle (which is owned by the main application, not by us) diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index c68dcc8b039f..7cc6e5aeb489 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -566,7 +566,7 @@ static BOOL CALLBACK ImGui_ImplWin32_UpdateMonitors_EnumFunc(HMONITOR monitor, H return TRUE; } -// FIXME-PLATFORM: Update list when changed (WM_DISPLAYCHANGE?) +// FIXME-PLATFORM: Update monitor list when changed (WM_DISPLAYCHANGE?) static void ImGui_ImplWin32_UpdateMonitors() { ImGui::GetPlatformIO().Monitors.resize(0); From d268471285f65e23d6109dd918cca8a14e165bc0 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 23 Apr 2018 18:27:38 +0200 Subject: [PATCH 133/828] Viewport: Added ImGuiConfigFlags_ViewportsNoMerge flag (to enforce a platform window for all floating windows) + minor tidying up and addition of non-functional wip code. --- examples/directx11_example/main.cpp | 1 + examples/imgui_impl_win32.cpp | 17 +++++++++++ imgui.cpp | 45 +++++++++++++++-------------- imgui.h | 22 +++++++------- 4 files changed, 54 insertions(+), 31 deletions(-) diff --git a/examples/directx11_example/main.cpp b/examples/directx11_example/main.cpp index 308fb12b7f56..6a39d1437c8b 100644 --- a/examples/directx11_example/main.cpp +++ b/examples/directx11_example/main.cpp @@ -135,6 +135,7 @@ int main(int, char**) ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoTaskBarIcons; + //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoMerge; io.ConfigFlags |= ImGuiConfigFlags_DpiEnableScaleFonts; io.ConfigFlags |= ImGuiConfigFlags_DpiEnableScaleViewports; io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index 7cc6e5aeb489..0da56b633d81 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -518,6 +518,22 @@ static float ImGui_ImplWin32_GetWindowDpiScale(ImGuiViewport* viewport) (int)(viewport->Pos.x + viewport->Size.x), (int)(viewport->Pos.y + viewport->Size.y)); } +// FIXME-DPI: Testing DPI related ideas +static void ImGui_ImplWin32_OnChangedViewport(ImGuiViewport* viewport) +{ + (void)viewport; +#if 0 + ImGuiStyle default_style; + //default_style.WindowPadding = ImVec2(0, 0); + //default_style.WindowBorderSize = 0.0f; + //default_style.ItemSpacing.y = 3.0f; + //default_style.FramePadding = ImVec2(0, 0); + default_style.ScaleAllSizes(viewport->DpiScale); + ImGuiStyle& style = ImGui::GetStyle(); + style = default_style; +#endif +} + static LRESULT CALLBACK ImGui_ImplWin32_WndProcHandler_PlatformWindow(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam)) @@ -604,6 +620,7 @@ static void ImGui_ImplWin32_InitPlatformInterface() platform_io.Platform_SetWindowTitle = ImGui_ImplWin32_SetWindowTitle; platform_io.Platform_SetWindowAlpha = ImGui_ImplWin32_SetWindowAlpha; platform_io.Platform_GetWindowDpiScale = ImGui_ImplWin32_GetWindowDpiScale; + platform_io.Platform_OnChangedViewport = ImGui_ImplWin32_OnChangedViewport; // FIXME-DPI // Register main window handle (which is owned by the main application, not by us) ImGuiViewport* main_viewport = ImGui::GetMainViewport(); diff --git a/imgui.cpp b/imgui.cpp index 3ab3dd67a032..d423d42502a6 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -752,7 +752,7 @@ static void NavUpdate(); static void NavUpdateWindowing(); static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id); -static void NewFrameUpdateMovingWindow(); +static void UpdateMovingWindow(); static void UpdateManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4]); static void FocusFrontMostActiveWindow(ImGuiWindow* ignore_window); @@ -3305,6 +3305,16 @@ static void SetWindowViewport(ImGuiWindow* window, ImGuiViewportP* viewport) window->ViewportOwned = (viewport->Window == window); } +static bool GetWindowAlwaysWantOwnViewport(ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; + if ((g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) && (g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsNoMerge)) + //if (window->DockStatus == ImGuiDockStatus_Floating) + if ((window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_ChildMenu)) == 0) + return true; + return false; +} + static void ImGui::UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* viewport) { ImGuiContext& g = *GImGui; @@ -3314,6 +3324,8 @@ static void ImGui::UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImG return; if (!viewport->GetRect().Contains(window->Rect())) return; + if (GetWindowAlwaysWantOwnViewport(window)) + return; // Move to the existing viewport, Move child/hosted windows as well (FIXME-OPT: iterate child) ImGuiViewportP* old_viewport = window->Viewport; @@ -3324,7 +3336,7 @@ static void ImGui::UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImG SetWindowViewport(window, viewport); } -static void ImGui::NewFrameUpdateMovingWindow() +static void ImGui::UpdateMovingWindow() { ImGuiContext& g = *GImGui; if (g.MovingWindow && g.MovingWindow->MoveId == g.ActiveId && g.ActiveIdSource == ImGuiInputSource_Mouse) @@ -3474,9 +3486,11 @@ static void ImGui::UpdateViewports() //if (viewport == GetMainViewport()) // g.PlatformInterface.SetWindowSize(viewport, viewport->Size * scale_factor); - // FIXME-DPI: We need to preserve our pivots - //if (g.MovingWindow) - // g.ActiveIdClickOffset = g.ActiveIdClickOffset * scale_factor; + // Scale our window moving pivot so that the window will rescale roughly around the mouse position. + // FIXME-VIEWPORT: This currently creates a resizing feedback loop when a window is straddling a DPI transition border. + // (Minor: since our sizes do not perfectly linearly scale, deferring the click offset scale until we know the actual window scale ratio may get us slightly more precise mouse positioning.) + //if (g.MovingWindow != NULL && g.MovingWindow->Viewport == viewport) + // g.ActiveIdClickOffset = ImFloor(g.ActiveIdClickOffset * scale_factor); } viewport->DpiScale = new_dpi_scale; } @@ -3897,7 +3911,7 @@ void ImGui::NewFrame() g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame))) : FLT_MAX; // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering) - NewFrameUpdateMovingWindow(); + UpdateMovingWindow(); NewFrameUpdateHoveredWindowAndCaptureFlags(); if (GetFrontMostPopupModal() != NULL) @@ -6184,6 +6198,10 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) window->Viewport = AddUpdateViewport(window, window->ID, viewport_flags, viewport_pos, window->Size); } } + else if (!window->ViewportOwned && GetWindowAlwaysWantOwnViewport(window)) + { + window->Viewport = AddUpdateViewport(window, window->ID, ImGuiViewportFlags_NoDecoration, window->Pos, window->Size); + } // Mark window as allowed to protrude outside of its viewport and into the current monitor bool allow_protrude_on_whole_monitor = false; @@ -14010,20 +14028,6 @@ static void ScaleWindow(ImGuiWindow* window, float scale) void ImGui::ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale) { ImGuiContext& g = *GImGui; - - // FIXME-DPI: This is meant to have the window rescale around the mouse. It currently creates feedback loop when a window is straddling a DPI transition border. - // NB: since our sizes do not perfectly linearly scale, deferring the ClickOffset scale until we know the actual window scale ratio may get us slightly more precise mouse positioning. - //if (g.MovingWindow != NULL && g.MovingWindow->Viewport == viewport) - // g.ActiveIdClickOffset = ImFloor(g.ActiveIdClickOffset * scale); - - /* - if (g.IO.MousePosViewport == viewport->ID) - { - g.IO.MousePos = g.IO.MousePosPrev = ImFloor((g.IO.MousePos - viewport->Pos) * scale) + viewport->Pos; - g.IO.MouseDelta = ImVec2(0,0); - } - */ - if (viewport->Window) { ScaleWindow(viewport->Window, scale); @@ -14036,7 +14040,6 @@ void ImGui::ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale) } } - static void RenderViewportThumbnail(ImDrawList* draw_list, ImGuiViewportP* viewport, const ImRect& bb) { ImGuiContext& g = *GImGui; diff --git a/imgui.h b/imgui.h index 8c6e5b543847..f7e490482b65 100644 --- a/imgui.h +++ b/imgui.h @@ -787,22 +787,24 @@ enum ImGuiNavInput_ // Configuration flags stored in io.ConfigFlags. Set by user/application. enum ImGuiConfigFlags_ { - ImGuiConfigFlags_NavEnableKeyboard = 1 << 0, // Master keyboard navigation enable flag. NewFrame() will automatically fill io.NavInputs[] based on io.KeysDown[]. - ImGuiConfigFlags_NavEnableGamepad = 1 << 1, // Master gamepad navigation enable flag. This is mostly to instruct your imgui back-end to fill io.NavInputs[]. Back-end also needs to set ImGuiBackendFlags_HasGamepad. - ImGuiConfigFlags_NavEnableSetMousePos = 1 << 2, // Instruct navigation to move the mouse cursor. May be useful on TV/console systems where moving a virtual mouse is awkward. Will update io.MousePos and set io.WantSetMousePos=true. If enabled you MUST honor io.WantSetMousePos requests in your binding, otherwise ImGui will react as if the mouse is jumping around back and forth. - ImGuiConfigFlags_NavNoCaptureKeyboard = 1 << 3, // Instruct navigation to not set the io.WantCaptureKeyboard flag with io.NavActive is set. - ImGuiConfigFlags_NoMouse = 1 << 4, // Instruct imgui to clear mouse position/buttons in NewFrame(). This allows ignoring the mouse information back-end - ImGuiConfigFlags_NoMouseCursorChange = 1 << 5, // Instruct back-end to not alter mouse cursor shape and visibility. + ImGuiConfigFlags_NavEnableKeyboard = 1 << 0, // Master keyboard navigation enable flag. NewFrame() will automatically fill io.NavInputs[] based on io.KeysDown[]. + ImGuiConfigFlags_NavEnableGamepad = 1 << 1, // Master gamepad navigation enable flag. This is mostly to instruct your imgui back-end to fill io.NavInputs[]. Back-end also needs to set ImGuiBackendFlags_HasGamepad. + ImGuiConfigFlags_NavEnableSetMousePos = 1 << 2, // Instruct navigation to move the mouse cursor. May be useful on TV/console systems where moving a virtual mouse is awkward. Will update io.MousePos and set io.WantSetMousePos=true. If enabled you MUST honor io.WantSetMousePos requests in your binding, otherwise ImGui will react as if the mouse is jumping around back and forth. + ImGuiConfigFlags_NavNoCaptureKeyboard = 1 << 3, // Instruct navigation to not set the io.WantCaptureKeyboard flag with io.NavActive is set. + ImGuiConfigFlags_NoMouse = 1 << 4, // Instruct imgui to clear mouse position/buttons in NewFrame(). This allows ignoring the mouse information back-end + ImGuiConfigFlags_NoMouseCursorChange = 1 << 5, // Instruct back-end to not alter mouse cursor shape and visibility. // [BETA] Viewports ImGuiConfigFlags_ViewportsEnable = 1 << 10, // Viewport enable flags (require both ImGuiConfigFlags_PlatformHasViewports + ImGuiConfigFlags_RendererHasViewports set by the respective back-ends) ImGuiConfigFlags_ViewportsNoTaskBarIcons = 1 << 11, // Disable task bars icons for all secondary viewports (will set ImGuiViewportFlags_NoTaskBarIcon on them) - ImGuiConfigFlags_DpiEnableScaleViewports = 1 << 12, - ImGuiConfigFlags_DpiEnableScaleFonts = 1 << 13, + ImGuiConfigFlags_ViewportsNoMerge = 1 << 12, // All floating windows _always_ have create their own viewport and platform window. + + ImGuiConfigFlags_DpiEnableScaleViewports = 1 << 13, + ImGuiConfigFlags_DpiEnableScaleFonts = 1 << 14, // User storage (to allow your back-end/engine to communicate to code that may be shared between multiple projects. Those flags are not used by core ImGui) - ImGuiConfigFlags_IsSRGB = 1 << 20, // Application is SRGB-aware. - ImGuiConfigFlags_IsTouchScreen = 1 << 21 // Application is using a touch screen instead of a mouse. + ImGuiConfigFlags_IsSRGB = 1 << 20, // Application is SRGB-aware. + ImGuiConfigFlags_IsTouchScreen = 1 << 21 // Application is using a touch screen instead of a mouse. }; // Back-end capabilities flags stored in io.BackendFlags. Set by imgui_impl_xxx or custom back-end. From 010757266e587252774575d624f371e9db17cc35 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 23 Apr 2018 23:00:14 +0200 Subject: [PATCH 134/828] Viewports: Todos, Tooltips/menus not automatically forced into their own viewport when the NoMerge flag is set (however the multiplication of viewports makes them more likely to protude and create their own). Win32: try to make primary monitor the first tin the list. (#1542) --- TODO.txt | 8 ++++++++ imgui.cpp | 5 +++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/TODO.txt b/TODO.txt index 2ddd1cf908ea..28a1db8a7af1 100644 --- a/TODO.txt +++ b/TODO.txt @@ -258,6 +258,14 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - focus: SetKeyboardFocusHere() on with >= 0 offset could be done on same frame (else latch and modulate on beginning of next frame) - focus: unable to use SetKeyboardFocusHere() on clipped widgets. (#787) + - viewport: popup/tooltip glitches when appearing near the monitor limits (e.g. opening a menu). + - viewport: clamp windows position within monitors (especially important on monitor configuration change) -> clamp to closest rectangle. + - viewport: platform: introduce getfocus/setfocus api, so e.g. focus flags can be honored, imgui-side ctrl-tab can focus os window, OS alt-tab can focus imgui window etc. + - viewport: store per-viewport/monitor DPI in .ini file so an application reload or main window changing DPI on reload can be properly patched for. + - viewport: IME positioning are wrong. + - viewport: vulkan renderer implementation. + - viewport: need to clarify how to use GetMousePos() from a user point of view. + - inputs: we need an explicit flag about whether the imgui window is focused, to be able to distinguish focused key releases vs alt-tabbing all release behaviors. - inputs: rework IO system to be able to pass actual ordered/timestamped events. use an event queue? (~#335, #71) - inputs: support track pad style scrolling & slider edit. diff --git a/imgui.cpp b/imgui.cpp index d423d42502a6..e3aa2608b9da 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3307,10 +3307,11 @@ static void SetWindowViewport(ImGuiWindow* window, ImGuiViewportP* viewport) static bool GetWindowAlwaysWantOwnViewport(ImGuiWindow* window) { + // Tooltips and menus are not automatically forced into their own viewport when the NoMerge flag is set, however the multiplication of viewports makes them more likely to protude and create their own. ImGuiContext& g = *GImGui; - if ((g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) && (g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsNoMerge)) + if ((g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsNoMerge) && (g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) //if (window->DockStatus == ImGuiDockStatus_Floating) - if ((window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_ChildMenu)) == 0) + if ((window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip)) == 0) return true; return false; } From 376f2aec54fcce16a7555b574b628976af3dfb11 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 23 Apr 2018 23:01:37 +0200 Subject: [PATCH 135/828] Viewport: Clamp windows within monitors + fallback rescue window when it is out of sight (e.g. removed monitor, changed resolution) + Win32: declare primary monitor at the beginning of the list. (#1542) --- TODO.txt | 1 - examples/imgui_impl_win32.cpp | 6 +++- imgui.cpp | 64 +++++++++++++++++++++++++++++------ imgui.h | 2 +- 4 files changed, 60 insertions(+), 13 deletions(-) diff --git a/TODO.txt b/TODO.txt index 28a1db8a7af1..3cd9af464b4c 100644 --- a/TODO.txt +++ b/TODO.txt @@ -259,7 +259,6 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - focus: unable to use SetKeyboardFocusHere() on clipped widgets. (#787) - viewport: popup/tooltip glitches when appearing near the monitor limits (e.g. opening a menu). - - viewport: clamp windows position within monitors (especially important on monitor configuration change) -> clamp to closest rectangle. - viewport: platform: introduce getfocus/setfocus api, so e.g. focus flags can be honored, imgui-side ctrl-tab can focus os window, OS alt-tab can focus imgui window etc. - viewport: store per-viewport/monitor DPI in .ini file so an application reload or main window changing DPI on reload can be properly patched for. - viewport: IME positioning are wrong. diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index 0da56b633d81..132573607154 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -578,7 +578,11 @@ static BOOL CALLBACK ImGui_ImplWin32_UpdateMonitors_EnumFunc(HMONITOR monitor, H imgui_monitor.WorkMin = ImVec2((float)info.rcWork.left, (float)info.rcWork.top); imgui_monitor.WorkMax = ImVec2((float)info.rcWork.right, (float)info.rcWork.bottom); imgui_monitor.DpiScale = ImGui_ImplWin32_GetDpiScaleForMonitor(monitor); - ImGui::GetPlatformIO().Monitors.push_back(imgui_monitor); + ImGuiPlatformIO& io = ImGui::GetPlatformIO(); + if (info.dwFlags & MONITORINFOF_PRIMARY) + io.Monitors.push_front(imgui_monitor); + else + io.Monitors.push_back(imgui_monitor); return TRUE; } diff --git a/imgui.cpp b/imgui.cpp index e3aa2608b9da..95c2d2bc7fb8 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -832,7 +832,7 @@ ImGuiStyle::ImGuiStyle() GrabMinSize = 10.0f; // Minimum width/height of a grab box for slider/scrollbar GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs. ButtonTextAlign = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text. - DisplayWindowPadding = ImVec2(22,22); // Window positions are clamped to be visible within the display area by at least this amount. Only covers regular windows. + DisplayWindowPadding = ImVec2(20,20); // Window position are clamped to be visible within the display area or monitors by at least this amount. Only applies to regular windows. DisplaySafeAreaPadding = ImVec2(3,3); // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows. MouseCursorScale = 1.0f; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later. AntiAliasedLines = true; // Enable anti-aliasing on lines/borders. Disable if you are really short on CPU/GPU. @@ -3817,6 +3817,8 @@ void ImGui::NewFrame() // Disable feature, our back-ends do not support it g.IO.ConfigFlags &= ~ImGuiConfigFlags_ViewportsEnable; } + + // Perform simple checks on platform monitor data + compute a total bounding box for quick early outs for (int monitor_n = 0; monitor_n < g.PlatformIO.Monitors.Size; monitor_n++) { ImGuiPlatformMonitor& mon = g.PlatformIO.Monitors[monitor_n]; @@ -6122,13 +6124,37 @@ static int FindPlatformMonitorForPos(ImVec2 platform_pos) ImGuiContext& g = *GImGui; for (int monitor_n = 0; monitor_n < g.PlatformIO.Monitors.Size; monitor_n++) { - const ImGuiPlatformMonitor* monitor = &g.PlatformIO.Monitors[monitor_n]; - if (ImRect(monitor->FullMin, monitor->FullMax).Contains(platform_pos)) + const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[monitor_n]; + if (ImRect(monitor.FullMin, monitor.FullMax).Contains(platform_pos)) return monitor_n; } return -1; } +// Search for the monitor with the largest intersection area with the given rectangle +// We generally try to avoid searching loops but the monitor count should be very small here +static int FindPlatformMonitorForRect(const ImRect& rect) +{ + ImGuiContext& g = *GImGui; + float surface_threshold = rect.GetWidth() * rect.GetHeight() * 0.5f; + int best_monitor_n = -1; + float best_monitor_surface = 0.001f; + for (int monitor_n = 0; monitor_n < g.PlatformIO.Monitors.Size && best_monitor_surface < surface_threshold; monitor_n++) + { + const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[monitor_n]; + if (ImRect(monitor.FullMin, monitor.FullMax).Contains(rect)) + return monitor_n; + ImRect overlapping_rect = rect; + overlapping_rect.ClipWithFull(ImRect(monitor.FullMin, monitor.FullMax)); + float overlapping_surface = overlapping_rect.GetWidth() * overlapping_rect.GetHeight(); + if (overlapping_surface < best_monitor_surface) + continue; + best_monitor_surface = overlapping_surface; + best_monitor_n = monitor_n; + } + return best_monitor_n; +} + // FIXME-VIEWPORT: This is all super messy and ought to be clarified or rewritten. static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) { @@ -6367,6 +6393,11 @@ static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au window->Size = window->SizeFull; } +static inline void ClampWindowRect(ImGuiWindow* window, const ImRect& rect, const ImVec2& padding) +{ + window->PosFloat = ImMin(rect.Max - padding, ImMax(window->PosFloat + window->Size, rect.Min + padding) - window->Size); +} + // Push a new ImGui window to add widgets to. // - A default window called "Debug" is automatically stacked at the beginning of every frame so you can use widgets without explicitly calling a Begin/End pair. // - Begin/End can be called multiple times during the frame with the same window name to append content. @@ -6680,17 +6711,29 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->Viewport->Flags |= ImGuiViewportFlags_NoRendererClear; } - // Clamp position so window stays visible within its viewport + // Clamp position so window stays visible within its viewport or monitor // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing. ImRect viewport_rect = window->Viewport->GetRect(); - if (!window->ViewportOwned && !window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) - if (viewport_rect.GetWidth() > 0 && viewport_rect.GetHeight() > 0.0f) + if (!window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) + { + ImVec2 clamp_padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding); + if (!window->ViewportOwned && viewport_rect.GetWidth() > 0 && viewport_rect.GetHeight() > 0.0f) + ClampWindowRect(window, viewport_rect, clamp_padding); + else if (window->ViewportOwned && g.PlatformIO.Monitors.Size > 0) { - ImVec2 padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding); - window->PosFloat = ImMax(window->PosFloat + window->Size, viewport_rect.Min + padding) - window->Size; - window->PosFloat = ImMin(window->PosFloat, viewport_rect.Max - padding); + int monitor_n = FindPlatformMonitorForRect(viewport_rect); + if (monitor_n == -1) + { + // Fallback for "lost" window (e.g. a monitor disconnected): we move the window back over the main viewport + window->PosFloat = g.Viewports[0]->Pos + style.DisplayWindowPadding; + window->ViewportTryMerge = true; + } + else + { + ClampWindowRect(window, ImRect(g.PlatformIO.Monitors[monitor_n].WorkMin, g.PlatformIO.Monitors[monitor_n].WorkMax), clamp_padding); + } } - + } window->Pos = ImFloor(window->PosFloat); // Lock window rounding for the frame (so that altering them doesn't cause inconsistencies) @@ -14208,6 +14251,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) else ImGui::BulletText("NavRectRel[0]: "); ImGui::BulletText("Viewport: %d, ViewportId: 0x%08X, ViewportPlatformPos: (%.1f,%.1f)", window->Viewport ? window->Viewport->Idx : -1, window->ViewportId, window->ViewportPos.x, window->ViewportPos.y); + ImGui::BulletText("Monitor: %d", FindPlatformMonitorForRect(window->Rect())); if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow"); if (window->DC.ChildWindows.Size > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows"); if (window->ColumnsStorage.Size > 0 && ImGui::TreeNode("Columns", "Columns sets (%d)", window->ColumnsStorage.Size)) diff --git a/imgui.h b/imgui.h index f7e490482b65..95815a990eb6 100644 --- a/imgui.h +++ b/imgui.h @@ -1004,7 +1004,7 @@ struct ImGuiStyle float GrabMinSize; // Minimum width/height of a grab box for slider/scrollbar. float GrabRounding; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs. ImVec2 ButtonTextAlign; // Alignment of button text when button is larger than text. Defaults to (0.5f,0.5f) for horizontally+vertically centered. - ImVec2 DisplayWindowPadding; // Window positions are clamped to be visible within the display area by at least this amount. Only covers regular windows. + ImVec2 DisplayWindowPadding; // Window position are clamped to be visible within the display area or monitors by at least this amount. Only applies to regular windows. ImVec2 DisplaySafeAreaPadding; // If you cannot see the edges of your screen (e.g. on a TV) increase the safe area padding. Apply to popups/tooltips as well regular windows. NB: Prefer configuring your TV sets correctly! float MouseCursorScale; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later. bool AntiAliasedLines; // Enable anti-aliasing on lines/borders. Disable if you are really tight on CPU/GPU. From cb78e62df93732b64afcc9d4cd02e378730b32af Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 24 Apr 2018 12:40:38 +0200 Subject: [PATCH 136/828] Viewport, Platform: Fixed IME positioning for multi-viewport. Moved API from ImGuiIO to ImGuiPlatformIO. Because it is extremely unlikely to people redefined this API manually the moving-forward-breakage is ok. (#1542) SDL2 ime support under Win32 never worked properly because of SDL interferences. --- TODO.txt | 1 - examples/imgui_impl_allegro5.cpp | 4 --- examples/imgui_impl_glfw.cpp | 32 +++++++++++++++++-- examples/imgui_impl_sdl2.cpp | 7 ----- examples/imgui_impl_win32.cpp | 32 ++++++++++++++++--- imgui.cpp | 53 ++++++++------------------------ imgui.h | 12 +++----- imgui_internal.h | 8 +++-- 8 files changed, 79 insertions(+), 70 deletions(-) diff --git a/TODO.txt b/TODO.txt index 3cd9af464b4c..3fff67ed4584 100644 --- a/TODO.txt +++ b/TODO.txt @@ -261,7 +261,6 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - viewport: popup/tooltip glitches when appearing near the monitor limits (e.g. opening a menu). - viewport: platform: introduce getfocus/setfocus api, so e.g. focus flags can be honored, imgui-side ctrl-tab can focus os window, OS alt-tab can focus imgui window etc. - viewport: store per-viewport/monitor DPI in .ini file so an application reload or main window changing DPI on reload can be properly patched for. - - viewport: IME positioning are wrong. - viewport: vulkan renderer implementation. - viewport: need to clarify how to use GetMousePos() from a user point of view. diff --git a/examples/imgui_impl_allegro5.cpp b/examples/imgui_impl_allegro5.cpp index c82b7cc8c3c3..d9cde6d1e93c 100644 --- a/examples/imgui_impl_allegro5.cpp +++ b/examples/imgui_impl_allegro5.cpp @@ -199,10 +199,6 @@ bool ImGui_ImplAllegro5_Init(ALLEGRO_DISPLAY* display) io.KeyMap[ImGuiKey_Y] = ALLEGRO_KEY_Y; io.KeyMap[ImGuiKey_Z] = ALLEGRO_KEY_Z; -#ifdef _WIN32 - io.ImeWindowHandle = al_get_win_window_handle(g_Display); -#endif - return true; } diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index 19fa93d32d3a..0c74aa2caf8f 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -150,9 +150,6 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw io.SetClipboardTextFn = ImGui_ImplGlfw_SetClipboardText; io.GetClipboardTextFn = ImGui_ImplGlfw_GetClipboardText; io.ClipboardUserData = g_Window; -#ifdef _WIN32 - io.ImeWindowHandle = glfwGetWin32Window(g_Window); -#endif g_MouseCursors[ImGuiMouseCursor_Arrow] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); g_MouseCursors[ImGuiMouseCursor_TextInput] = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR); @@ -500,7 +497,33 @@ static void ImGui_ImplGlfw_SwapBuffers(ImGuiViewport* viewport, void*) glfwSwapBuffers(data->Window); } +//-------------------------------------------------------------------------------------------------------- +// IME (Input Method Editor) basic support for e.g. Asian language users +//-------------------------------------------------------------------------------------------------------- + +// We provide a Win32 implementation because this is such a common issue for IME users +#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS) && !defined(__GNUC__) +#define HAS_WIN32_IME 1 +#include +#ifdef _MSC_VER +#pragma comment(lib, "imm32") +#endif +static void ImGui_ImplWin32_SetImeInputPos(ImGuiViewport* viewport, ImVec2 pos) +{ + COMPOSITIONFORM cf = { CFS_FORCE_POSITION, { (LONG)(pos.x - viewport->Pos.x), (LONG)(pos.y - viewport->Pos.y) }, { 0, 0, 0, 0 } }; + if (ImGuiViewportDataGlfw* data = (ImGuiViewportDataGlfw*)viewport->PlatformUserData) + if (HWND hwnd = glfwGetWin32Window(data->Window)) + if (HIMC himc = ImmGetContext(hwnd)) + ImmSetCompositionWindow(himc, &cf); +} +#else +#define HAS_WIN32_IME 0 +#endif + +//-------------------------------------------------------------------------------------------------------- // Vulkan support (the Vulkan renderer needs to call a platform-side support function to create the surface) +//-------------------------------------------------------------------------------------------------------- + // Avoid including so we can build without it #if GLFW_HAS_VULKAN #ifndef VULKAN_H_ @@ -571,6 +594,9 @@ static void ImGui_ImplGlfw_InitPlatformInterface() #if GLFW_HAS_VULKAN platform_io.Platform_CreateVkSurface = ImGui_ImplGlfw_CreateVkSurface; #endif +#if HAS_WIN32_IME + platform_io.Platform_SetImeInputPos = ImGui_ImplWin32_SetImeInputPos; +#endif ImGui_ImplGlfw_UpdateMonitors(); diff --git a/examples/imgui_impl_sdl2.cpp b/examples/imgui_impl_sdl2.cpp index 4342afbb7527..6b25e9064a06 100644 --- a/examples/imgui_impl_sdl2.cpp +++ b/examples/imgui_impl_sdl2.cpp @@ -170,13 +170,6 @@ bool ImGui_ImplSDL2_Init(SDL_Window* window, void* sdl_gl_context) g_MouseCursors[ImGuiMouseCursor_ResizeNESW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENESW); g_MouseCursors[ImGuiMouseCursor_ResizeNWSE] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENWSE); -#ifdef _WIN32 - SDL_SysWMinfo wmInfo; - SDL_VERSION(&wmInfo.version); - SDL_GetWindowWMInfo(window, &wmInfo); - io.ImeWindowHandle = wmInfo.info.win.window; -#endif - // Our mouse update function expect PlatformHandle to be filled for the main viewport ImGuiViewport* main_viewport = ImGui::GetMainViewport(); main_viewport->PlatformHandle = (void*)window; diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index 132573607154..b90b7a267f9f 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -76,8 +76,6 @@ bool ImGui_ImplWin32_Init(void* hwnd) io.KeyMap[ImGuiKey_Y] = 'Y'; io.KeyMap[ImGuiKey_Z] = 'Z'; - io.ImeWindowHandle = g_hWnd; - return true; } @@ -279,7 +277,7 @@ IMGUI_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARAM wPa return 0; } -// -------------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------------- // DPI handling // Those in theory should be simple calls but Windows has multiple ways to handle DPI, and most of them // require recent Windows versions at runtime or recent Windows SDK at compile-time. Neither we want to depend on. @@ -370,9 +368,30 @@ float ImGui_ImplWin32_GetDpiScaleForRect(int x1, int y1, int x2, int y2) return ImGui_ImplWin32_GetDpiScaleForMonitor(monitor); } -// -------------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------------- +// IME (Input Method Editor) basic support for e.g. Asian language users +//-------------------------------------------------------------------------------------------------------- + +#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS) && !defined(__GNUC__) +#define HAS_WIN32_IME 1 +#include +#ifdef _MSC_VER +#pragma comment(lib, "imm32") +#endif +static void ImGui_ImplWin32_SetImeInputPos(ImGuiViewport* viewport, ImVec2 pos) +{ + COMPOSITIONFORM cf = { CFS_FORCE_POSITION,{ (LONG)(pos.x - viewport->Pos.x), (LONG)(pos.y - viewport->Pos.y) },{ 0, 0, 0, 0 } }; + if (HWND hwnd = (HWND)viewport->PlatformHandle) + if (HIMC himc = ImmGetContext(hwnd)) + ImmSetCompositionWindow(himc, &cf); +} +#else +#define HAS_WIN32_IME 0 +#endif + +//-------------------------------------------------------------------------------------------------------- // Platform Windows -// -------------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------------- struct ImGuiViewportDataWin32 { @@ -625,6 +644,9 @@ static void ImGui_ImplWin32_InitPlatformInterface() platform_io.Platform_SetWindowAlpha = ImGui_ImplWin32_SetWindowAlpha; platform_io.Platform_GetWindowDpiScale = ImGui_ImplWin32_GetWindowDpiScale; platform_io.Platform_OnChangedViewport = ImGui_ImplWin32_OnChangedViewport; // FIXME-DPI +#if HAS_WIN32_IME + platform_io.Platform_SetImeInputPos = ImGui_ImplWin32_SetImeInputPos; +#endif // Register main window handle (which is owned by the main application, not by us) ImGuiViewport* main_viewport = ImGui::GetMainViewport(); diff --git a/imgui.cpp b/imgui.cpp index 95c2d2bc7fb8..ca967ec1f132 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -262,6 +262,8 @@ Here is a change-log of API breaking changes, if you are using one of the functions listed, expect to have to fix some code. Also read releases logs https://github.com/ocornut/imgui/releases for more details. + - 2018/XX/XX (1.XX) - Moved IME support functions from io.ImeSetInputScreenPosFn, io.ImeWindowHandle to the PlatformIO api. + - 2018/04/09 (1.61) - IM_DELETE() helper function added in 1.60 doesn't clear the input _pointer_ reference, more consistent with expectation and allows passing r-value. - 2018/03/20 (1.60) - Renamed io.WantMoveMouse to io.WantSetMousePos for consistency and ease of understanding (was added in 1.52, _not_ used by core and only honored by some binding ahead of merging the Nav branch). - 2018/03/12 (1.60) - Removed ImGuiCol_CloseButton, ImGuiCol_CloseButtonActive, ImGuiCol_CloseButtonHovered as the closing cross uses regular button colors now. @@ -617,10 +619,7 @@ Otherwise you can convert yourself to UTF-8 or load text data from file already saved as UTF-8. Text input: it is up to your application to pass the right character code by calling - io.AddInputCharacter(). The applications in examples/ are doing that. For languages relying - on an Input Method Editor (IME), on Windows you can copy the Hwnd of your application in the - io.ImeWindowHandle field. The default implementation of io.ImeSetInputScreenPosFn() will set - your Microsoft IME position correctly. + io.AddInputCharacter(). The applications in examples/ are doing that. Q: How can I use the drawing facilities without an ImGui window? (using ImDrawList API) A: - You can create a dummy window. Call SetNextWindowBgAlpha(0.0f), call Begin() with NoTitleBar|NoResize|NoMove|NoScrollbar|NoSavedSettings|NoInputs flags. @@ -771,7 +770,6 @@ static void SetCurrentViewport(ImGuiViewportP* viewport); static const char* GetClipboardTextFn_DefaultImpl(void* user_data); static void SetClipboardTextFn_DefaultImpl(void* user_data, const char* text); -static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y); //----------------------------------------------------------------------------- // Context @@ -907,8 +905,6 @@ ImGuiIO::ImGuiIO() GetClipboardTextFn = GetClipboardTextFn_DefaultImpl; // Platform dependent default implementations SetClipboardTextFn = SetClipboardTextFn_DefaultImpl; ClipboardUserData = NULL; - ImeSetInputScreenPosFn = ImeSetInputScreenPosFn_DefaultImpl; - ImeWindowHandle = NULL; #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS RenderDrawListsFn = NULL; @@ -3924,7 +3920,8 @@ void ImGui::NewFrame() g.MouseCursor = ImGuiMouseCursor_Arrow; g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1; - g.OsImePosRequest = ImVec2(1.0f, 1.0f); // OS Input Method Editor showing on top-left of our window by default + g.PlatformImePos = ImVec2(1.0f, 1.0f); // OS Input Method Editor showing on top-left of our window by default + g.PlatformImePosViewport = NULL; // Mouse wheel scrolling, scale if (g.HoveredWindow && !g.HoveredWindow->Collapsed && (g.IO.MouseWheel != 0.0f || g.IO.MouseWheelH != 0.0f)) @@ -4464,10 +4461,11 @@ void ImGui::EndFrame() return; // Notify OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME) - if (g.IO.ImeSetInputScreenPosFn && ImLengthSqr(g.OsImePosRequest - g.OsImePosSet) > 0.0001f) + if (g.PlatformIO.Platform_SetImeInputPos && g.PlatformImePosViewport != NULL && ImLengthSqr(g.PlatformImePos - g.PlatformImeLastPos) > 0.0001f) { - g.IO.ImeSetInputScreenPosFn((int)g.OsImePosRequest.x, (int)g.OsImePosRequest.y); - g.OsImePosSet = g.OsImePosRequest; + g.PlatformIO.Platform_SetImeInputPos(g.PlatformImePosViewport, g.PlatformImePos); + g.PlatformImeLastPos = g.PlatformImePos; + g.PlatformImePosViewport = NULL; } // Hide implicit "Debug" window if it hasn't been used @@ -11316,7 +11314,10 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 // Notify OS of text input position for advanced IME (-1 x offset so that Windows IME can cover our cursor. Bit of an extra nicety.) if (is_editable) - g.OsImePosRequest = ImVec2(cursor_screen_pos.x - 1, cursor_screen_pos.y - g.FontSize); + { + g.PlatformImePos = ImVec2(cursor_screen_pos.x - 1, cursor_screen_pos.y - g.FontSize); + g.PlatformImePosViewport = window->Viewport; + } } else { @@ -14027,34 +14028,6 @@ static void SetClipboardTextFn_DefaultImpl(void*, const char* text) #endif -// Win32 API IME support (for Asian languages, etc.) -#if defined(_WIN32) && !defined(__GNUC__) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS) - -#include -#ifdef _MSC_VER -#pragma comment(lib, "imm32") -#endif - -static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y) -{ - // Notify OS Input Method Editor of text input position - if (HWND hwnd = (HWND)GImGui->IO.ImeWindowHandle) - if (HIMC himc = ImmGetContext(hwnd)) - { - COMPOSITIONFORM cf; - cf.ptCurrentPos.x = x; - cf.ptCurrentPos.y = y; - cf.dwStyle = CFS_FORCE_POSITION; - ImmSetCompositionWindow(himc, &cf); - } -} - -#else - -static void ImeSetInputScreenPosFn_DefaultImpl(int, int) {} - -#endif - //----------------------------------------------------------------------------- // HELP //----------------------------------------------------------------------------- diff --git a/imgui.h b/imgui.h index 95815a990eb6..3396d048595c 100644 --- a/imgui.h +++ b/imgui.h @@ -1059,11 +1059,6 @@ struct ImGuiIO void (*SetClipboardTextFn)(void* user_data, const char* text); void* ClipboardUserData; - // Optional: notify OS Input Method Editor of the screen position of your cursor for text input position (e.g. when using Japanese/Chinese IME in Windows) - // (default to use native imm32 api on Windows) - void (*ImeSetInputScreenPosFn)(int x, int y); - void* ImeWindowHandle; // (Windows) Set this to your HWND to get automatic IME cursor positioning. - #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS // [OBSOLETE] Rendering function, will be automatically called in Render(). Please call your rendering function yourself now! You can obtain the ImDrawData* by calling ImGui::GetDrawData() after Render(). // See example applications if you are unsure of how to implement this. @@ -1896,7 +1891,7 @@ struct ImGuiPlatformIO // Input - Back-end interface/functions + Monitor List //------------------------------------------------------------------ - // Platform functions (e.g. Win32, GLFW, SDL2) + // (Optional) Platform functions (e.g. Win32, GLFW, SDL2) void (*Platform_CreateWindow)(ImGuiViewport* vp); // Create a new platform window for the given viewport void (*Platform_DestroyWindow)(ImGuiViewport* vp); void (*Platform_ShowWindow)(ImGuiViewport* vp); // Newly created windows are initially hidden so SetWindowPos/Size/Title can be called on them first @@ -1910,16 +1905,17 @@ struct ImGuiPlatformIO void (*Platform_SwapBuffers)(ImGuiViewport* vp, void* render_arg); // (Optional) Call Present/SwapBuffers (platform side) float (*Platform_GetWindowDpiScale)(ImGuiViewport* vp); // (Optional) DPI handling: Return DPI scale for this viewport. 1.0f = 96 DPI. (FIXME-DPI) void (*Platform_OnChangedViewport)(ImGuiViewport* vp); // (Optional) DPI handling: Called during Begin() every time the viewport we are outputting into changes, so back-end has a chance to swap fonts to adjust style. + void (*Platform_SetImeInputPos)(ImGuiViewport* vp, ImVec2 pos); // (Optional) Set IME (Input Method Editor, e.g. for Asian languages) input position, so text preview appears over the imgui input box. int (*Platform_CreateVkSurface)(ImGuiViewport* vp, ImU64 vk_inst, const void* vk_allocators, ImU64* out_vk_surface); // (Optional) For Renderer to call into Platform code - // Renderer functions (e.g. DirectX, OpenGL3, Vulkan) + // (Optional) Renderer functions (e.g. DirectX, OpenGL3, Vulkan) void (*Renderer_CreateWindow)(ImGuiViewport* vp); // Create swap chains, frame buffers etc. void (*Renderer_DestroyWindow)(ImGuiViewport* vp); void (*Renderer_SetWindowSize)(ImGuiViewport* vp, ImVec2 size); // Resize swap chain, frame buffers etc. void (*Renderer_RenderWindow)(ImGuiViewport* vp, void* render_arg); // (Optional) Clear targets, Render viewport->DrawData void (*Renderer_SwapBuffers)(ImGuiViewport* vp, void* render_arg); // (Optional) Call Present/SwapBuffers (renderer side) - // List of monitors (updated by: app/back-end, used by: imgui to clamp popups/tooltips within same monitor and not have them straddle monitors) + // (Optional) List of monitors (updated by: app/back-end, used by: imgui to clamp popups/tooltips within same monitor and not have them straddle monitors) ImVector Monitors; //------------------------------------------------------------------ diff --git a/imgui_internal.h b/imgui_internal.h index f3377152517d..4dd7c9bac550 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -715,7 +715,10 @@ struct ImGuiContext ImVec2 ScrollbarClickDeltaToGrabCenter; // Distance between mouse and center of grab box, normalized in parent space. Use storage? int TooltipOverrideCount; ImVector PrivateClipboard; // If no custom clipboard handler is defined - ImVec2 OsImePosRequest, OsImePosSet; // Cursor position request & last passed to the OS Input Method Editor + + // Platform support + ImVec2 PlatformImePos, PlatformImeLastPos; // Cursor position request & last passed to the OS Input Method Editor + ImGuiViewport* PlatformImePosViewport; // Settings bool SettingsLoaded; @@ -822,7 +825,8 @@ struct ImGuiContext DragSpeedScaleFast = 10.0f; ScrollbarClickDeltaToGrabCenter = ImVec2(0.0f, 0.0f); TooltipOverrideCount = 0; - OsImePosRequest = OsImePosSet = ImVec2(-1.0f, -1.0f); + PlatformImePos = PlatformImeLastPos = ImVec2(FLT_MAX, FLT_MAX); + PlatformImePosViewport = 0; SettingsLoaded = false; SettingsDirtyTimer = 0.0f; From 679f4882a5d951256fe6038876bfc3ac15cbf0e9 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 24 Apr 2018 16:48:26 +0200 Subject: [PATCH 137/828] Removed presumably obsolete MovingWindow tests which prevent move/merge logic in viewport branch from working in all situations (e.g. docking away when ActiveId is the ID of a tab) --- imgui.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index ca967ec1f132..cbdc1fe1b66e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3336,10 +3336,10 @@ static void ImGui::UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImG static void ImGui::UpdateMovingWindow() { ImGuiContext& g = *GImGui; - if (g.MovingWindow && g.MovingWindow->MoveId == g.ActiveId && g.ActiveIdSource == ImGuiInputSource_Mouse) + if (g.MovingWindow != NULL) { // We actually want to move the root window. g.MovingWindow == window we clicked on (could be a child window). - // We track it to preserve Focus and so that ActiveIdWindow == MovingWindow and ActiveId == MovingWindow->MoveId for consistency. + // We track it to preserve Focus and so that generally ActiveIdWindow == MovingWindow and ActiveId == MovingWindow->MoveId for consistency. KeepAliveID(g.ActiveId); IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindow); ImGuiWindow* moving_window = g.MovingWindow->RootWindow; @@ -3373,10 +3373,6 @@ static void ImGui::UpdateMovingWindow() if (!g.IO.MouseDown[0]) ClearActiveID(); } - - if (g.MovingWindow != NULL) - g.MovingWindow->Viewport->Flags &= ~ImGuiViewportFlags_NoInputs; - g.MovingWindow = NULL; } } From e3e4b7bdf5dede380bc227c238cbb2026ca6a47d Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 24 Apr 2018 17:09:50 +0200 Subject: [PATCH 138/828] Viewport: Fixed mouse hover flicker on mouse button release frame after moving a window. (#1542) Platform: Clarifying some comments in back-ends. --- TODO.txt | 1 + examples/imgui_impl_dx10.cpp | 10 ++++------ examples/imgui_impl_dx11.cpp | 10 ++++------ examples/imgui_impl_dx12.cpp | 4 +++- examples/imgui_impl_glfw.cpp | 8 +++++--- examples/imgui_impl_opengl2.cpp | 4 +++- examples/imgui_impl_opengl3.cpp | 4 +++- examples/imgui_impl_sdl2.cpp | 4 +++- examples/imgui_impl_vulkan.cpp | 5 ++++- examples/imgui_impl_win32.cpp | 4 +++- imgui.cpp | 6 +++++- 11 files changed, 38 insertions(+), 22 deletions(-) diff --git a/TODO.txt b/TODO.txt index 3fff67ed4584..69f88df71845 100644 --- a/TODO.txt +++ b/TODO.txt @@ -262,6 +262,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - viewport: platform: introduce getfocus/setfocus api, so e.g. focus flags can be honored, imgui-side ctrl-tab can focus os window, OS alt-tab can focus imgui window etc. - viewport: store per-viewport/monitor DPI in .ini file so an application reload or main window changing DPI on reload can be properly patched for. - viewport: vulkan renderer implementation. + - viewport: fallback calculation of hovered window is very currently wrong without ImGuiBackendFlags_HasMouseHoveredViewport. typically affect half-overlapping viewported menus. - viewport: need to clarify how to use GetMousePos() from a user point of view. - inputs: we need an explicit flag about whether the imgui window is focused, to be able to distinguish focused key releases vs alt-tabbing all release behaviors. diff --git a/examples/imgui_impl_dx10.cpp b/examples/imgui_impl_dx10.cpp index db6f99d6563e..2201bb769b6a 100644 --- a/examples/imgui_impl_dx10.cpp +++ b/examples/imgui_impl_dx10.cpp @@ -504,7 +504,9 @@ void ImGui_ImplDX10_NewFrame() } //-------------------------------------------------------------------------------------------------------- -// Platform Interface (Optional, for multi-viewport support) +// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT +// This is an _advanced_ and _optional_ feature, allowing the back-end to create and handle multiple viewports simultaneously. +// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first.. //-------------------------------------------------------------------------------------------------------- struct ImGuiViewportDataDx10 @@ -581,11 +583,7 @@ static void ImGui_ImplDX10_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) ID3D10Texture2D* pBackBuffer = NULL; data->SwapChain->ResizeBuffers(0, (UINT)size.x, (UINT)size.y, DXGI_FORMAT_UNKNOWN, 0); data->SwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer)); - if (pBackBuffer == NULL) - { - fprintf(stderr, "ImGui_ImplDX10_SetWindowSize() can't created buffers.\n"); - return; - } + if (pBackBuffer == NULL) { fprintf(stderr, "ImGui_ImplDX10_SetWindowSize() failed creating buffers.\n"); return; } g_pd3dDevice->CreateRenderTargetView(pBackBuffer, NULL, &data->RTView); pBackBuffer->Release(); } diff --git a/examples/imgui_impl_dx11.cpp b/examples/imgui_impl_dx11.cpp index 9f5b3824996c..d9308e04005c 100644 --- a/examples/imgui_impl_dx11.cpp +++ b/examples/imgui_impl_dx11.cpp @@ -512,7 +512,9 @@ void ImGui_ImplDX11_NewFrame() } //-------------------------------------------------------------------------------------------------------- -// Platform Interface (Optional, for multi-viewport support) +// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT +// This is an _advanced_ and _optional_ feature, allowing the back-end to create and handle multiple viewports simultaneously. +// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first.. //-------------------------------------------------------------------------------------------------------- struct ImGuiViewportDataDx11 @@ -589,11 +591,7 @@ static void ImGui_ImplDX11_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) ID3D11Texture2D* pBackBuffer = NULL; data->SwapChain->ResizeBuffers(0, (UINT)size.x, (UINT)size.y, DXGI_FORMAT_UNKNOWN, 0); data->SwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer)); - if (pBackBuffer == NULL) - { - fprintf(stderr, "ImGui_ImplDX11_SetWindowSize() can't created buffers.\n"); - return; - } + if (pBackBuffer == NULL) { fprintf(stderr, "ImGui_ImplDX11_SetWindowSize() failed creating buffers.\n"); return; } g_pd3dDevice->CreateRenderTargetView(pBackBuffer, NULL, &data->RTView); pBackBuffer->Release(); } diff --git a/examples/imgui_impl_dx12.cpp b/examples/imgui_impl_dx12.cpp index 9ae3a0d3ffe4..76d17b855dbe 100644 --- a/examples/imgui_impl_dx12.cpp +++ b/examples/imgui_impl_dx12.cpp @@ -637,7 +637,9 @@ void ImGui_ImplDX12_NewFrame(ID3D12GraphicsCommandList* command_list) } //-------------------------------------------------------------------------------------------------------- -// Platform Interface (Optional, for multi-viewport support) +// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT +// This is an _advanced_ and _optional_ feature, allowing the back-end to create and handle multiple viewports simultaneously. +// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first.. //-------------------------------------------------------------------------------------------------------- struct ImGuiViewportDataDx12 diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index 0c74aa2caf8f..cd04bc485217 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -317,9 +317,11 @@ void ImGui_ImplGlfw_NewFrame() ImGui::NewFrame(); } -// -------------------------------------------------------------------------------------------------------- -// Platform Windows -// -------------------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------------------- +// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT +// This is an _advanced_ and _optional_ feature, allowing the back-end to create and handle multiple viewports simultaneously. +// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first.. +//-------------------------------------------------------------------------------------------------------- struct ImGuiViewportDataGlfw { diff --git a/examples/imgui_impl_opengl2.cpp b/examples/imgui_impl_opengl2.cpp index f4d848a8abbd..e43b20efad11 100644 --- a/examples/imgui_impl_opengl2.cpp +++ b/examples/imgui_impl_opengl2.cpp @@ -211,7 +211,9 @@ void ImGui_ImplOpenGL2_DestroyDeviceObjects() } //-------------------------------------------------------------------------------------------------------- -// Platform Interface (Optional, for multi-viewport support) +// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT +// This is an _advanced_ and _optional_ feature, allowing the back-end to create and handle multiple viewports simultaneously. +// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first.. //-------------------------------------------------------------------------------------------------------- static void ImGui_ImplOpenGL2_RenderWindow(ImGuiViewport* viewport, void*) diff --git a/examples/imgui_impl_opengl3.cpp b/examples/imgui_impl_opengl3.cpp index 99012c83e58a..5217408ffba5 100644 --- a/examples/imgui_impl_opengl3.cpp +++ b/examples/imgui_impl_opengl3.cpp @@ -330,7 +330,9 @@ void ImGui_ImplOpenGL3_DestroyDeviceObjects() } //-------------------------------------------------------------------------------------------------------- -// Platform Interface (Optional, for multi-viewport support) +// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT +// This is an _advanced_ and _optional_ feature, allowing the back-end to create and handle multiple viewports simultaneously. +// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first.. //-------------------------------------------------------------------------------------------------------- static void ImGui_ImplOpenGL3_RenderWindow(ImGuiViewport* viewport, void*) diff --git a/examples/imgui_impl_sdl2.cpp b/examples/imgui_impl_sdl2.cpp index 6b25e9064a06..089761bb2295 100644 --- a/examples/imgui_impl_sdl2.cpp +++ b/examples/imgui_impl_sdl2.cpp @@ -276,7 +276,9 @@ void ImGui_ImplSDL2_NewFrame(SDL_Window* window) } //-------------------------------------------------------------------------------------------------------- -// Platform Interface (Optional, for multi-viewport support) +// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT +// This is an _advanced_ and _optional_ feature, allowing the back-end to create and handle multiple viewports simultaneously. +// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first.. //-------------------------------------------------------------------------------------------------------- struct ImGuiViewportDataSDL2 diff --git a/examples/imgui_impl_vulkan.cpp b/examples/imgui_impl_vulkan.cpp index 4824b0b1cb23..9f46f04bbf7f 100644 --- a/examples/imgui_impl_vulkan.cpp +++ b/examples/imgui_impl_vulkan.cpp @@ -1059,7 +1059,10 @@ void ImGui_ImplVulkanH_DestroyWindowData(VkInstance instance, VkDevice device, I } //-------------------------------------------------------------------------------------------------------- -// Platform Interface (Optional, for multi-viewport support) +// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT +// This is an _advanced_ and _optional_ feature, allowing the back-end to create and handle multiple viewports simultaneously. +// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first.. +//-------------------------------------------------------------------------------------------------------- // FIXME-PLATFORM: Vulkan support unfinished //-------------------------------------------------------------------------------------------------------- diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index b90b7a267f9f..b4412d0ed32c 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -390,7 +390,9 @@ static void ImGui_ImplWin32_SetImeInputPos(ImGuiViewport* viewport, ImVec2 pos) #endif //-------------------------------------------------------------------------------------------------------- -// Platform Windows +// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT +// This is an _advanced_ and _optional_ feature, allowing the back-end to create and handle multiple viewports simultaneously. +// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first.. //-------------------------------------------------------------------------------------------------------- struct ImGuiViewportDataWin32 diff --git a/imgui.cpp b/imgui.cpp index cbdc1fe1b66e..064b1937fef2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3357,7 +3357,11 @@ static void ImGui::UpdateMovingWindow() { UpdateTryMergeWindowIntoHostViewport(moving_window, g.MouseRefViewport); - // Clear the NoInput flag set by the Viewport system + // Patch the mouse viewport so that we don't hover under the moved window during the mouse released frame + if (!IsDragDropPayloadBeingAccepted()) + g.MouseRefViewport = moving_window->Viewport; + + // Clear the NoInput window flag set by the Viewport system moving_window->Viewport->Flags &= ~ImGuiViewportFlags_NoInputs; ClearActiveID(); From 7684f53328dd9225909baa0fe4b9e45467014596 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 25 Apr 2018 10:54:38 +0200 Subject: [PATCH 139/828] Viewport: Fixed issues with popups drifting in particular when reference mouse position become invalid (e.g. changing app focus while viewported-menu is open). Storing monitor index in viewport at beginning of the frame. (#1542) --- imgui.cpp | 43 +++++++++++++++++++++++++++---------------- imgui_internal.h | 3 ++- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 064b1937fef2..41a68f4e3039 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -762,6 +762,8 @@ static void UpdateViewports(); static void UpdateSelectWindowViewport(ImGuiWindow* window); static void UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* host_viewport); static void SetCurrentViewport(ImGuiViewportP* viewport); +static int FindPlatformMonitorForPos(const ImVec2& pos); +static int FindPlatformMonitorForRect(const ImRect& r); } //----------------------------------------------------------------------------- @@ -2769,7 +2771,10 @@ static ImVec2 NavCalcPreferredRefPos() { ImGuiContext& g = *GImGui; if (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow) + { + IM_ASSERT(ImGui::IsMousePosValid()); // This will probably trigger at some point, please share your repro! return ImFloor(g.IO.MousePos); + } // When navigation is active and mouse is disabled, decide on an arbitrary position around the bottom left of the currently navigated item const ImRect& rect_rel = g.NavWindow->NavRectRel[g.NavLayer]; @@ -3469,6 +3474,9 @@ static void ImGui::UpdateViewports() if (g.Windows[window_n]->Viewport == viewport) TranslateWindow(g.Windows[window_n], delta); + // Update monitor + viewport->PlatformMonitor = FindPlatformMonitorForRect(viewport->GetRect()); + // Update DPI Scale float new_dpi_scale; if (g.PlatformIO.Platform_GetWindowDpiScale) @@ -6117,13 +6125,13 @@ static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& co *out_size = size_constrained; } -static int FindPlatformMonitorForPos(ImVec2 platform_pos) +static int ImGui::FindPlatformMonitorForPos(const ImVec2& pos) { ImGuiContext& g = *GImGui; for (int monitor_n = 0; monitor_n < g.PlatformIO.Monitors.Size; monitor_n++) { const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[monitor_n]; - if (ImRect(monitor.FullMin, monitor.FullMax).Contains(platform_pos)) + if (ImRect(monitor.FullMin, monitor.FullMax).Contains(pos)) return monitor_n; } return -1; @@ -6131,7 +6139,7 @@ static int FindPlatformMonitorForPos(ImVec2 platform_pos) // Search for the monitor with the largest intersection area with the given rectangle // We generally try to avoid searching loops but the monitor count should be very small here -static int FindPlatformMonitorForRect(const ImRect& rect) +static int ImGui::FindPlatformMonitorForRect(const ImRect& rect) { ImGuiContext& g = *GImGui; float surface_threshold = rect.GetWidth() * rect.GetHeight() * 0.5f; @@ -6189,7 +6197,7 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) if (window->Viewport == NULL && window->ParentWindow) window->Viewport = window->ParentWindow->Viewport; - // Attempt to restore saved viewport id (= window that hasn't been activated yet), try to restore the viewport based on saved 'window->ViewportPlatformPos' from .ini file + // Attempt to restore saved viewport id (= window that hasn't been activated yet), try to restore the viewport based on saved 'window->ViewportPos' restored from .ini file if (window->Viewport == NULL && window->ViewportId != 0) { window->Viewport = FindViewportByID(window->ViewportId); @@ -6205,7 +6213,7 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) } else if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_ChildMenu)) { - // Inherit viewport from parent window + // Always inherit viewport from parent window window->Viewport = window->ParentWindow->Viewport; } else if (flags & ImGuiWindowFlags_Tooltip) @@ -6214,8 +6222,6 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) } else if (g.MovingWindow && g.MovingWindow->RootWindow == window && IsMousePosValid()) { - // 2018-04-13: the if() below tends to succeed but for a misleading reason: when moving a window and hovering another, UpdateMovingWindow would - // already have displaced the window outside of its viewport boundaries. While this is currently working it is very smelly. if (window->Viewport == NULL || !window->Viewport->GetRect().Contains(window->Rect())) { ImVec2 viewport_pos = g.IO.MousePos - g.ActiveIdClickOffset; @@ -6229,13 +6235,18 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) } // Mark window as allowed to protrude outside of its viewport and into the current monitor - bool allow_protrude_on_whole_monitor = false; - allow_protrude_on_whole_monitor |= (flags & ImGuiWindowFlags_Tooltip) != 0; - allow_protrude_on_whole_monitor |= (flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) != 0; - if (allow_protrude_on_whole_monitor) - window->ViewportAllowPlatformMonitorExtend = FindPlatformMonitorForPos(NavCalcPreferredRefPos()); + // We need to take account of the possibility that mouse may become invalid. + const bool use_mouse_ref = (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow); + if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) + { + ImVec2 mouse_ref = (flags & ImGuiWindowFlags_Tooltip) ? g.IO.MousePos : g.CurrentPopupStack.back().OpenMousePos; + if (window->Appearing || (flags & ImGuiWindowFlags_Tooltip)) + window->ViewportAllowPlatformMonitorExtend = FindPlatformMonitorForPos((use_mouse_ref && IsMousePosValid(&mouse_ref)) ? mouse_ref : NavCalcPreferredRefPos()); + else + window->ViewportAllowPlatformMonitorExtend = window->Viewport->PlatformMonitor; + } if (window->ViewportTrySplit && window->ViewportAllowPlatformMonitorExtend == -1) - window->ViewportAllowPlatformMonitorExtend = FindPlatformMonitorForPos(window->Viewport->Pos); + window->ViewportAllowPlatformMonitorExtend = window->Viewport->PlatformMonitor; window->ViewportTrySplit = false; // Fallback to default viewport @@ -14223,8 +14234,8 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::BulletText("NavRectRel[0]: (%.1f,%.1f)(%.1f,%.1f)", window->NavRectRel[0].Min.x, window->NavRectRel[0].Min.y, window->NavRectRel[0].Max.x, window->NavRectRel[0].Max.y); else ImGui::BulletText("NavRectRel[0]: "); - ImGui::BulletText("Viewport: %d, ViewportId: 0x%08X, ViewportPlatformPos: (%.1f,%.1f)", window->Viewport ? window->Viewport->Idx : -1, window->ViewportId, window->ViewportPos.x, window->ViewportPos.y); - ImGui::BulletText("Monitor: %d", FindPlatformMonitorForRect(window->Rect())); + ImGui::BulletText("Viewport: %d, ViewportId: 0x%08X, ViewportPos: (%.1f,%.1f)", window->Viewport ? window->Viewport->Idx : -1, window->ViewportId, window->ViewportPos.x, window->ViewportPos.y); + ImGui::BulletText("ViewportMonitor: %d", window->Viewport->PlatformMonitor); if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow"); if (window->DC.ChildWindows.Size > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows"); if (window->ColumnsStorage.Size > 0 && ImGui::TreeNode("Columns", "Columns sets (%d)", window->ColumnsStorage.Size)) @@ -14252,7 +14263,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) if (ImGui::TreeNode((void*)(intptr_t)viewport->ID, "Viewport #%d, ID: 0x%08X, Window: \"%s\"", viewport->Idx, viewport->ID, viewport->Window ? viewport->Window->Name : "N/A")) { ImGuiWindowFlags flags = viewport->Flags; - ImGui::BulletText("Pos: (%.0f,%.0f)", viewport->Pos.x, viewport->Pos.y); + ImGui::BulletText("Pos: (%.0f,%.0f), Monitor: %d", viewport->Pos.x, viewport->Pos.y, viewport->PlatformMonitor); if (viewport->Idx > 0) { ImGui::SameLine(); if (ImGui::SmallButton("Reset")) { viewport->Pos = ImVec2(200,200); if (viewport->Window) viewport->Window->Pos = viewport->Window->PosFloat = ImVec2(200,200); } } ImGui::BulletText("Size: (%0.f,%.0f), DpiScale: %.0f%%", viewport->Size.x, viewport->Size.y, viewport->DpiScale * 100.0f); ImGui::BulletText("Flags: 0x%04X =%s%s%s%s%s", viewport->Flags, diff --git a/imgui_internal.h b/imgui_internal.h index 4dd7c9bac550..da6e42edddb7 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -521,13 +521,14 @@ struct ImGuiViewportP : public ImGuiViewport ImVec2 LastPos; float Alpha; // Window opacity (when dragging dockable windows/viewports we make them transparent) float LastAlpha; + int PlatformMonitor; ImGuiWindow* Window; ImDrawList* OverlayDrawList; // For convenience, a draw list we can render to that's always rendered last (we use it to draw software mouse cursor when io.MouseDrawCursor is set) ImDrawData DrawDataP; ImDrawDataBuilder DrawDataBuilder; ImVec2 RendererLastSize; - ImGuiViewportP() { Idx = 1; LastFrameActive = LastFrameAsRefViewport = LastFrameOverlayDrawList = -1; LastNameHash = 0; Alpha = LastAlpha = 1.0f; Window = NULL; OverlayDrawList = NULL; RendererLastSize = ImVec2(-1.0f,-1.0f); } + ImGuiViewportP() { Idx = 1; LastFrameActive = LastFrameAsRefViewport = LastFrameOverlayDrawList = -1; LastNameHash = 0; Alpha = LastAlpha = 1.0f; PlatformMonitor = -1; Window = NULL; OverlayDrawList = NULL; RendererLastSize = ImVec2(-1.0f,-1.0f); } ~ImGuiViewportP() { if (OverlayDrawList) IM_DELETE(OverlayDrawList); } ImRect GetRect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } ImVec2 GetCenter() const{ return ImVec2(Pos.x + Size.x * 0.5f, Pos.y + Size.y * 0.5f); } From 6d6580f6bea45c23e97642d68f0d0e1c2634dfe5 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 25 Apr 2018 11:23:34 +0200 Subject: [PATCH 140/828] Viewport: Fixed glitches with newly appearing menus creating viewport while the underlying imgui window should be hidden. (#1542) --- imgui.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 41a68f4e3039..9acfc3af496a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3578,6 +3578,8 @@ void ImGui::UpdatePlatformWindows() // New windows that appears directly in a new viewport won't always have a size on their frame if (viewport->Size.x <= 0 || viewport->Size.y <= 0) continue; + if (viewport->Window && viewport->Window->HiddenFrames > 0) + continue; // Update viewport flags if (viewport->Window != NULL) @@ -3640,7 +3642,7 @@ void ImGui::UpdatePlatformWindows() } // This is a default/basic function for performing the rendering/swap of multiple platform windows. -// Custom renderers may prefer to not call this function at all, and instead iterate the platform data and handle rendering/sync themselves. +// Custom renderers may prefer to not call this function at all, and instead iterate the publicly exposed platform data and handle rendering/sync themselves. // The Render/Swap functions stored in ImGuiPlatformIO are merely here to allow for this helper to exist, but you can do it yourself: // // ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); @@ -4539,6 +4541,8 @@ void ImGui::EndFrame() ImGuiViewportP* viewport = g.Viewports[i]; if (viewport->LastFrameActive < g.FrameCount || viewport->Size.x <= 0.0f || viewport->Size.y <= 0.0f) continue; + if (viewport->Window && viewport->Window->HiddenFrames > 0) + continue; if (i > 0) IM_ASSERT(viewport->Window != NULL); g.PlatformIO.Viewports.push_back(viewport); From cc882b07239b28b1dab6d46af72d3a463c92254a Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 25 Apr 2018 15:00:54 +0200 Subject: [PATCH 141/828] Viewport: Tidying up AddUpdateViewport() calls. Moved flags argument. Removed extraneous mid-frame call to FindPlatformMonitorFromRect(). (#1542) --- imgui.cpp | 39 ++++++++++++++++++--------------------- imgui_internal.h | 2 +- 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 9acfc3af496a..8cd1d25083c3 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -757,7 +757,7 @@ static void FocusFrontMostActiveWindow(ImGuiWindow* ignore_window); // Viewports const ImGuiID IMGUI_VIEWPORT_DEFAULT_ID = 0x11111111; // Using an arbitrary constant instead of e.g. ImHash("ViewportDefault", 0); so it's easier to spot in the debugger. The exact value doesn't matter. -static ImGuiViewportP* AddUpdateViewport(ImGuiWindow* window, ImGuiID id, ImGuiViewportFlags flags, const ImVec2& platform_pos, const ImVec2& size); +static ImGuiViewportP* AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const ImVec2& platform_pos, const ImVec2& size, ImGuiViewportFlags flags); static void UpdateViewports(); static void UpdateSelectWindowViewport(ImGuiWindow* window); static void UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* host_viewport); @@ -3431,7 +3431,7 @@ static void ImGui::UpdateViewports() ImVec2 main_viewport_platform_pos = ImVec2(0.0f, 0.0f); if ((g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) main_viewport_platform_pos = g.PlatformIO.Platform_GetWindowPos(main_viewport); - AddUpdateViewport(NULL, IMGUI_VIEWPORT_DEFAULT_ID, ImGuiViewportFlags_CanHostOtherWindows, main_viewport_platform_pos, g.IO.DisplaySize); + AddUpdateViewport(NULL, IMGUI_VIEWPORT_DEFAULT_ID, main_viewport_platform_pos, g.IO.DisplaySize, ImGuiViewportFlags_CanHostOtherWindows); g.CurrentViewport = NULL; for (int n = 0; n < g.Viewports.Size; n++) @@ -4676,7 +4676,7 @@ void ImGui::SetCurrentViewport(ImGuiViewportP* viewport) g.PlatformIO.Platform_OnChangedViewport(g.CurrentViewport); } -ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, ImGuiViewportFlags flags, const ImVec2& platform_pos, const ImVec2& size) +ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const ImVec2& pos, const ImVec2& size, ImGuiViewportFlags flags) { ImGuiContext& g = *GImGui; IM_ASSERT(id != 0); @@ -4684,7 +4684,7 @@ ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, ImGuiV ImGuiViewportP* viewport = (ImGuiViewportP*)FindViewportByID(id); if (viewport) { - viewport->Pos = platform_pos; + viewport->Pos = pos; viewport->Size = size; } else @@ -4693,8 +4693,9 @@ ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, ImGuiV viewport = IM_NEW(ImGuiViewportP)(); viewport->ID = id; viewport->Idx = g.Viewports.Size; - viewport->Pos = viewport->LastPos = platform_pos; + viewport->Pos = viewport->LastPos = pos; viewport->Size = size; + viewport->PlatformMonitor = FindPlatformMonitorForRect(viewport->GetRect()); g.Viewports.push_back(viewport); // We normally setup for all viewports in NewFrame() but here need to handle the mid-frame creation of a new viewport. @@ -5877,7 +5878,7 @@ static ImRect FindAllowedExtentRectForWindow(ImGuiWindow* window) { ImGuiContext& g = *GImGui; ImRect r_screen; - if (window->ViewportAllowPlatformMonitorExtend != -1) + if (window->ViewportAllowPlatformMonitorExtend >= 0) { // Extent with be in the frame of reference of the given viewport (so Min is likely to be negative here) const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[window->ViewportAllowPlatformMonitorExtend]; @@ -6206,7 +6207,7 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) { window->Viewport = FindViewportByID(window->ViewportId); if (window->Viewport == NULL && window->ViewportPos.x != FLT_MAX && window->ViewportPos.y != FLT_MAX) - window->Viewport = AddUpdateViewport(window, window->ID, ImGuiViewportFlags_NoDecoration, window->ViewportPos, window->Size); + window->Viewport = AddUpdateViewport(window, window->ID, window->ViewportPos, window->Size, ImGuiViewportFlags_NoDecoration); } if (g.NextWindowData.ViewportCond) @@ -6224,18 +6225,13 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) { window->Viewport = g.MouseRefViewport; } - else if (g.MovingWindow && g.MovingWindow->RootWindow == window && IsMousePosValid()) + else if (!window->ViewportOwned && g.MovingWindow && g.MovingWindow->RootWindow == window && IsMousePosValid()) { - if (window->Viewport == NULL || !window->Viewport->GetRect().Contains(window->Rect())) - { - ImVec2 viewport_pos = g.IO.MousePos - g.ActiveIdClickOffset; - ImGuiViewportFlags viewport_flags = ImGuiViewportFlags_NoDecoration | ImGuiViewportFlags_NoFocusOnAppearing | ImGuiViewportFlags_NoInputs; - window->Viewport = AddUpdateViewport(window, window->ID, viewport_flags, viewport_pos, window->Size); - } + window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoDecoration | ImGuiViewportFlags_NoFocusOnAppearing | ImGuiViewportFlags_NoInputs); } else if (!window->ViewportOwned && GetWindowAlwaysWantOwnViewport(window)) { - window->Viewport = AddUpdateViewport(window, window->ID, ImGuiViewportFlags_NoDecoration, window->Pos, window->Size); + window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoDecoration); } // Mark window as allowed to protrude outside of its viewport and into the current monitor @@ -6249,7 +6245,7 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) else window->ViewportAllowPlatformMonitorExtend = window->Viewport->PlatformMonitor; } - if (window->ViewportTrySplit && window->ViewportAllowPlatformMonitorExtend == -1) + if (window->ViewportTrySplit && window->ViewportAllowPlatformMonitorExtend < 0) window->ViewportAllowPlatformMonitorExtend = window->Viewport->PlatformMonitor; window->ViewportTrySplit = false; @@ -6695,14 +6691,14 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip) window->PosFloat = FindBestWindowPosForPopup(window); - if (window->ViewportAllowPlatformMonitorExtend != -1 && !window->ViewportOwned) + if (window->ViewportAllowPlatformMonitorExtend >= 0 && !window->ViewportOwned) { if (!window->Viewport->GetRect().Contains(ImRect(window->PosFloat, window->PosFloat + window->Size))) { // Late create viewport, based on the assumption that with our calculations, the DPI will be known ahead (same as the DPI of the selection done in UpdateSelectWindowViewport) //ImGuiViewport* old_viewport = window->Viewport; ImGuiViewportFlags viewport_flags = ImGuiViewportFlags_NoDecoration | ImGuiViewportFlags_NoFocusOnAppearing | ((window->Flags & ImGuiWindowFlags_NoInputs) ? ImGuiViewportFlags_NoInputs : 0); - window->Viewport = AddUpdateViewport(window, window->ID, viewport_flags, window->PosFloat, window->Size); + window->Viewport = AddUpdateViewport(window, window->ID, window->PosFloat, window->Size, viewport_flags); // FIXME-DPI //IM_ASSERT(old_viewport->DpiScale == window->Viewport->DpiScale); // FIXME-DPI: Something went wrong @@ -6734,8 +6730,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) ClampWindowRect(window, viewport_rect, clamp_padding); else if (window->ViewportOwned && g.PlatformIO.Monitors.Size > 0) { - int monitor_n = FindPlatformMonitorForRect(viewport_rect); - if (monitor_n == -1) + IM_ASSERT(window->Viewport->PlatformMonitor != INT_MIN); + if (window->Viewport->PlatformMonitor == -1) { // Fallback for "lost" window (e.g. a monitor disconnected): we move the window back over the main viewport window->PosFloat = g.Viewports[0]->Pos + style.DisplayWindowPadding; @@ -6743,7 +6739,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) } else { - ClampWindowRect(window, ImRect(g.PlatformIO.Monitors[monitor_n].WorkMin, g.PlatformIO.Monitors[monitor_n].WorkMax), clamp_padding); + ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[window->Viewport->PlatformMonitor]; + ClampWindowRect(window, ImRect(monitor.WorkMin, monitor.WorkMax), clamp_padding); } } } diff --git a/imgui_internal.h b/imgui_internal.h index da6e42edddb7..eb768a3cae6d 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -528,7 +528,7 @@ struct ImGuiViewportP : public ImGuiViewport ImDrawDataBuilder DrawDataBuilder; ImVec2 RendererLastSize; - ImGuiViewportP() { Idx = 1; LastFrameActive = LastFrameAsRefViewport = LastFrameOverlayDrawList = -1; LastNameHash = 0; Alpha = LastAlpha = 1.0f; PlatformMonitor = -1; Window = NULL; OverlayDrawList = NULL; RendererLastSize = ImVec2(-1.0f,-1.0f); } + ImGuiViewportP() { Idx = 1; LastFrameActive = LastFrameAsRefViewport = LastFrameOverlayDrawList = -1; LastNameHash = 0; Alpha = LastAlpha = 1.0f; PlatformMonitor = INT_MIN; Window = NULL; OverlayDrawList = NULL; RendererLastSize = ImVec2(-1.0f,-1.0f); } ~ImGuiViewportP() { if (OverlayDrawList) IM_DELETE(OverlayDrawList); } ImRect GetRect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } ImVec2 GetCenter() const{ return ImVec2(Pos.x + Size.x * 0.5f, Pos.y + Size.y * 0.5f); } From 7a41e0b1ea575ba72949a09b2fea3e2a1f76a71d Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 25 Apr 2018 15:15:14 +0200 Subject: [PATCH 142/828] Platform: Added platform_io.Platform_SetWindowFocus, Platform_GetWindowFocus function wrappers (unused yet). Exact specs tbd because our simplified concept of focus doesn't necessary match the more complex OS native concepts. (#1542) --- examples/directx11_example/main.cpp | 3 +-- examples/imgui_impl_glfw.cpp | 14 ++++++++++++++ examples/imgui_impl_sdl2.cpp | 15 +++++++++++++++ examples/imgui_impl_win32.cpp | 18 ++++++++++++++++++ examples/opengl3_example/main.cpp | 4 +++- imgui.h | 2 ++ 6 files changed, 53 insertions(+), 3 deletions(-) diff --git a/examples/directx11_example/main.cpp b/examples/directx11_example/main.cpp index 6a39d1437c8b..7fa16e98abd0 100644 --- a/examples/directx11_example/main.cpp +++ b/examples/directx11_example/main.cpp @@ -144,10 +144,9 @@ int main(int, char**) ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext); // Setup style + ImGui::GetStyle().WindowRounding = 0.0f; ImGui::StyleColorsDark(); //ImGui::StyleColorsClassic(); - ImGuiStyle& style = ImGui::GetStyle(); - style.WindowRounding = 0.0f; // Load Fonts // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index cd04bc485217..0d00b3aad7d2 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -477,6 +477,18 @@ static void ImGui_ImplGlfw_SetWindowTitle(ImGuiViewport* viewport, const char* t glfwSetWindowTitle(data->Window, title); } +static void ImGui_ImplGlfw_SetWindowFocus(ImGuiViewport* viewport) +{ + ImGuiViewportDataGlfw* data = (ImGuiViewportDataGlfw*)viewport->PlatformUserData; + glfwFocusWindow(data->Window); +} + +static bool ImGui_ImplGlfw_GetWindowFocus(ImGuiViewport* viewport) +{ + ImGuiViewportDataGlfw* data = (ImGuiViewportDataGlfw*)viewport->PlatformUserData; + return glfwGetWindowAttrib(data->Window, GLFW_FOCUSED) != 0; +} + #if GLFW_HAS_WINDOW_ALPHA static void ImGui_ImplGlfw_SetWindowAlpha(ImGuiViewport* viewport, float alpha) { @@ -587,6 +599,8 @@ static void ImGui_ImplGlfw_InitPlatformInterface() platform_io.Platform_GetWindowPos = ImGui_ImplGlfw_GetWindowPos; platform_io.Platform_SetWindowSize = ImGui_ImplGlfw_SetWindowSize; platform_io.Platform_GetWindowSize = ImGui_ImplGlfw_GetWindowSize; + platform_io.Platform_SetWindowFocus = ImGui_ImplGlfw_SetWindowFocus; + platform_io.Platform_GetWindowFocus = ImGui_ImplGlfw_GetWindowFocus; platform_io.Platform_SetWindowTitle = ImGui_ImplGlfw_SetWindowTitle; platform_io.Platform_RenderWindow = ImGui_ImplGlfw_RenderWindow; platform_io.Platform_SwapBuffers = ImGui_ImplGlfw_SwapBuffers; diff --git a/examples/imgui_impl_sdl2.cpp b/examples/imgui_impl_sdl2.cpp index 089761bb2295..e58f28cc670e 100644 --- a/examples/imgui_impl_sdl2.cpp +++ b/examples/imgui_impl_sdl2.cpp @@ -409,6 +409,19 @@ static void ImGui_ImplSDL2_SetWindowTitle(ImGuiViewport* viewport, const char* t SDL_SetWindowTitle(data->Window, title); } +static void ImGui_ImplSDL2_SetWindowFocus(ImGuiViewport* viewport) +{ + ImGuiViewportDataSDL2* data = (ImGuiViewportDataSDL2*)viewport->PlatformUserData; + SDL_RaiseWindow(data->Window); +} + +static bool ImGui_ImplSDL2_GetWindowFocus(ImGuiViewport* viewport) +{ + ImGuiViewportDataSDL2* data = (ImGuiViewportDataSDL2*)viewport->PlatformUserData; + bool focus = (SDL_GetWindowFlags(data->Window) & SDL_WINDOW_INPUT_FOCUS) != 0; + return focus; +} + static void ImGui_ImplSDL2_RenderWindow(ImGuiViewport* viewport, void*) { ImGuiViewportDataSDL2* data = (ImGuiViewportDataSDL2*)viewport->PlatformUserData; @@ -478,6 +491,8 @@ static void ImGui_ImplSDL2_InitPlatformInterface(SDL_Window* window, void* sdl_g platform_io.Platform_GetWindowPos = ImGui_ImplSDL2_GetWindowPos; platform_io.Platform_SetWindowSize = ImGui_ImplSDL2_SetWindowSize; platform_io.Platform_GetWindowSize = ImGui_ImplSDL2_GetWindowSize; + platform_io.Platform_SetWindowFocus = ImGui_ImplSDL2_SetWindowFocus; + platform_io.Platform_GetWindowFocus = ImGui_ImplSDL2_GetWindowFocus; platform_io.Platform_SetWindowTitle = ImGui_ImplSDL2_SetWindowTitle; platform_io.Platform_RenderWindow = ImGui_ImplSDL2_RenderWindow; platform_io.Platform_SwapBuffers = ImGui_ImplSDL2_SwapBuffers; diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index b4412d0ed32c..6f44060009e1 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -502,6 +502,22 @@ static void ImGui_ImplWin32_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) ::SetWindowPos(data->Hwnd, NULL, 0, 0, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE); } +static void ImGui_ImplWin32_SetWindowFocus(ImGuiViewport* viewport) +{ + ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData; + IM_ASSERT(data->Hwnd != 0); + ::BringWindowToTop(data->Hwnd); + ::SetForegroundWindow(data->Hwnd); + ::SetFocus(data->Hwnd); +} + +static bool ImGui_ImplWin32_GetWindowFocus(ImGuiViewport* viewport) +{ + ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData; + IM_ASSERT(data->Hwnd != 0); + return ::GetActiveWindow() == data->Hwnd; +} + static void ImGui_ImplWin32_SetWindowTitle(ImGuiViewport* viewport, const char* title) { ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData; @@ -642,6 +658,8 @@ static void ImGui_ImplWin32_InitPlatformInterface() platform_io.Platform_GetWindowPos = ImGui_ImplWin32_GetWindowPos; platform_io.Platform_SetWindowSize = ImGui_ImplWin32_SetWindowSize; platform_io.Platform_GetWindowSize = ImGui_ImplWin32_GetWindowSize; + platform_io.Platform_SetWindowFocus = ImGui_ImplWin32_SetWindowFocus; + platform_io.Platform_GetWindowFocus = ImGui_ImplWin32_GetWindowFocus; platform_io.Platform_SetWindowTitle = ImGui_ImplWin32_SetWindowTitle; platform_io.Platform_SetWindowAlpha = ImGui_ImplWin32_SetWindowAlpha; platform_io.Platform_GetWindowDpiScale = ImGui_ImplWin32_GetWindowDpiScale; diff --git a/examples/opengl3_example/main.cpp b/examples/opengl3_example/main.cpp index 6ae71d4f87ab..50b364c60658 100644 --- a/examples/opengl3_example/main.cpp +++ b/examples/opengl3_example/main.cpp @@ -37,13 +37,15 @@ int main(int, char**) ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoTaskBarIcons; - //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoMerge; + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls ImGui_ImplGlfw_InitForOpenGL(window, true); ImGui_ImplOpenGL3_Init(); // Setup style + ImGui::GetStyle().WindowRounding = 0.0f; ImGui::StyleColorsDark(); //ImGui::StyleColorsClassic(); diff --git a/imgui.h b/imgui.h index 3396d048595c..dbb59aa34891 100644 --- a/imgui.h +++ b/imgui.h @@ -1899,6 +1899,8 @@ struct ImGuiPlatformIO ImVec2 (*Platform_GetWindowPos)(ImGuiViewport* vp); void (*Platform_SetWindowSize)(ImGuiViewport* vp, ImVec2 size); ImVec2 (*Platform_GetWindowSize)(ImGuiViewport* vp); + void (*Platform_SetWindowFocus)(ImGuiViewport* vp); // Move window to front and set input focus + bool (*Platform_GetWindowFocus)(ImGuiViewport* vp); void (*Platform_SetWindowTitle)(ImGuiViewport* vp, const char* title); void (*Platform_SetWindowAlpha)(ImGuiViewport* vp, float alpha); // (Optional) Setup window transparency void (*Platform_RenderWindow)(ImGuiViewport* vp, void* render_arg); // (Optional) Setup for render (platform side) From 7b433605f93a368647e25d190a62faabf8be256d Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 25 Apr 2018 15:38:19 +0200 Subject: [PATCH 143/828] Viewport: Avoid creating viewport on drag (fix cc882b07239b28b1dab6d46af72d3a463c92254a) --- imgui.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index f0689f6668d4..19bdfe0d9cf0 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6229,7 +6229,8 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) } else if (!window->ViewportOwned && g.MovingWindow && g.MovingWindow->RootWindow == window && IsMousePosValid()) { - window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoDecoration | ImGuiViewportFlags_NoFocusOnAppearing | ImGuiViewportFlags_NoInputs); + if (!window->Viewport->GetRect().Contains(window->Rect())) + window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoDecoration | ImGuiViewportFlags_NoFocusOnAppearing | ImGuiViewportFlags_NoInputs); } else if (!window->ViewportOwned && GetWindowAlwaysWantOwnViewport(window)) { From 5979233a3c8a84cd5dee3f7ca6fdc845794a5fc9 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 25 Apr 2018 18:23:39 +0200 Subject: [PATCH 144/828] Misc: tweaks + minor changes merged from master to reduce branch drift. Removed code that ended up unused. --- imgui.cpp | 26 ++++++++------------------ imgui.h | 3 +-- imgui_internal.h | 4 +--- 3 files changed, 10 insertions(+), 23 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index b39848c6508a..76e8560aec99 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -262,14 +262,15 @@ Here is a change-log of API breaking changes, if you are using one of the functions listed, expect to have to fix some code. Also read releases logs https://github.com/ocornut/imgui/releases for more details. + (Viewport Branch) - 2018/XX/XX (1.XX) - Moved IME support functions from io.ImeSetInputScreenPosFn, io.ImeWindowHandle to the PlatformIO api. + - 2018/XX/XX (1.XX) - removed io.DisplayVisibleMin, io.DisplayVisibleMax settings (it was used to clip within the DisplayMin..DisplayMax range, I don't know of anyone using it) - 2018/04/09 (1.61) - IM_DELETE() helper function added in 1.60 doesn't clear the input _pointer_ reference, more consistent with expectation and allows passing r-value. - 2018/03/20 (1.60) - Renamed io.WantMoveMouse to io.WantSetMousePos for consistency and ease of understanding (was added in 1.52, _not_ used by core and only honored by some binding ahead of merging the Nav branch). - 2018/03/12 (1.60) - Removed ImGuiCol_CloseButton, ImGuiCol_CloseButtonActive, ImGuiCol_CloseButtonHovered as the closing cross uses regular button colors now. - 2018/03/08 (1.60) - Changed ImFont::DisplayOffset.y to default to 0 instead of +1. Fixed rounding of Ascent/Descent to match TrueType renderer. If you were adding or subtracting to ImFont::DisplayOffset check if your fonts are correctly aligned vertically. - 2018/03/03 (1.60) - Renamed ImGuiStyleVar_Count_ to ImGuiStyleVar_COUNT and ImGuiMouseCursor_Count_ to ImGuiMouseCursor_COUNT for consistency with other public enums. - - 2018/02/27 (1.XX) - removed io.DisplayVisibleMin, io.DisplayVisibleMax settings (it was used to clip within the DisplayMin..DisplayMax range, I don't know of anyone using it) - 2018/02/18 (1.60) - BeginDragDropSource(): temporarily removed the optional mouse_button=0 parameter because it is not really usable in many situations at the moment. - 2018/02/16 (1.60) - obsoleted the io.RenderDrawListsFn callback, you can call your graphics engine render function after ImGui::Render(). Use ImGui::GetDrawData() to retrieve the ImDrawData* to display. - 2018/02/07 (1.60) - reorganized context handling to be more explicit, @@ -4247,10 +4248,6 @@ static void LoadIniSettingsFromMemory(const char* buf_readonly) if (line[0] == '[' && line_end > line && line_end[-1] == ']') { - // Close last entry - if (entry_data && entry_handler && entry_handler->ReadCloseFn) - entry_handler->ReadCloseFn(&g, entry_handler, entry_data); - // Parse "[Type][Name]". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code. line_end[-1] = 0; const char* name_end = line_end - 1; @@ -4277,10 +4274,6 @@ static void LoadIniSettingsFromMemory(const char* buf_readonly) } } - // Close last entry - if (entry_data && entry_handler && entry_handler->ReadCloseFn) - entry_handler->ReadCloseFn(&g, entry_handler, entry_data); - ImGui::MemFree(buf); g.SettingsLoaded = true; } @@ -5819,7 +5812,7 @@ enum ImGuiPopupPositionPolicy ImGuiPopupPositionPolicy_Default, ImGuiPopupPositionPolicy_ComboBox }; - + // r_avoid = the rectangle to avoid (e.g. for tooltip it is a rectangle around the mouse cursor which we want to avoid. for popups it's a small point around the cursor.) // r_outer = the visible area rectangle, minus safe area padding. If our popup size won't fit because of safe area padding we ignore it. // (r_outer is usually equivalent to the viewport rectangle minus padding, but when multi-viewports are enabled and monitor @@ -5966,16 +5959,13 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFl g.WindowsById.SetVoidPtr(window->ID, window); // Default/arbitrary window position. Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window. - window->Pos = ImVec2(60, 60); + ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + window->Pos = main_viewport->Pos + ImVec2(60, 60); // User can disable loading and saving of settings. Tooltip and child windows also don't store settings. if (!(flags & ImGuiWindowFlags_NoSavedSettings)) { // Retrieve settings from .ini file - // Use SetWindowPos() or SetNextWindowPos() with the appropriate condition flag to change the initial position of a window. - ImGuiViewport* main_viewport = ImGui::GetMainViewport(); - window->Pos = main_viewport->Pos + ImVec2(60, 60); - if (ImGuiWindowSettings* settings = ImGui::FindWindowSettings(window->ID)) { SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false); @@ -6784,9 +6774,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if (!window->Collapsed) UpdateManualResize(window, size_auto_fit, &border_held, resize_grip_count, &resize_grip_col[0]); - // When a window is marked as owning its viewport, we immediately update the viewport after a resize - window->ViewportPos = window->Viewport->Pos; - // Synchronize window --> viewport if (window->ViewportOwned) { @@ -6797,6 +6784,9 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) viewport_rect = window->Viewport->GetRect(); } + // Save last knonw viewport position within the window itself (so it can be saved in .ini file and restored) + window->ViewportPos = window->Viewport->Pos; + // Default item width. Make it proportional to window size if window manually resizes if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize)) window->ItemWidthDefault = (float)(int)(window->Size.x * 0.65f); diff --git a/imgui.h b/imgui.h index 7075955b5c20..a6c50f5fc8c8 100644 --- a/imgui.h +++ b/imgui.h @@ -798,7 +798,6 @@ enum ImGuiConfigFlags_ ImGuiConfigFlags_ViewportsEnable = 1 << 10, // Viewport enable flags (require both ImGuiConfigFlags_PlatformHasViewports + ImGuiConfigFlags_RendererHasViewports set by the respective back-ends) ImGuiConfigFlags_ViewportsNoTaskBarIcons = 1 << 11, // Disable task bars icons for all secondary viewports (will set ImGuiViewportFlags_NoTaskBarIcon on them) ImGuiConfigFlags_ViewportsNoMerge = 1 << 12, // All floating windows _always_ have create their own viewport and platform window. - ImGuiConfigFlags_DpiEnableScaleViewports = 1 << 13, ImGuiConfigFlags_DpiEnableScaleFonts = 1 << 14, @@ -1953,7 +1952,7 @@ struct ImGuiViewport ImDrawData* DrawData; // The ImDrawData corresponding to this viewport. Valid after Render() and until the next call to NewFrame(). void* PlatformUserData; // void* to hold custom data structure for the platform (e.g. windowing info, render context) - void* PlatformHandle; // void* for FindViewportByPlatformHandle(). (e.g. HWND, GlfwWindow*, SDL_Window*) + void* PlatformHandle; // void* for FindViewportByPlatformHandle(). (e.g. suggested to use natural platform handle such as HWND, GlfwWindow*, SDL_Window*) bool PlatformRequestClose; // Platform window requested closure bool PlatformRequestMove; // Platform window requested move (e.g. window was moved using OS windowing facility) bool PlatformRequestResize; // Platform window requested resize (e.g. window was resize using OS windowing facility) diff --git a/imgui_internal.h b/imgui_internal.h index 9b0e64ed23a6..854ebff2c0c2 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -417,7 +417,6 @@ struct ImGuiSettingsHandler const char* TypeName; // Short description stored in .ini file. Disallowed characters: '[' ']' ImGuiID TypeHash; // == ImHash(TypeName, 0, 0) void* (*ReadOpenFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, const char* name); // Read: Called when entering into a new ini entry e.g. "[Window][Name]" - void (*ReadCloseFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, void* entry); // Read: Called when closing an existing entry, so code can validate overall data. [Optional] void (*ReadLineFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, void* entry, const char* line); // Read: Called for every line of text within an ini entry void (*WriteAllFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* out_buf); // Write: Output every entries into 'out_buf' void* UserData; @@ -499,7 +498,6 @@ struct ImDrawDataBuilder void Clear() { for (int n = 0; n < IM_ARRAYSIZE(Layers); n++) Layers[n].resize(0); } void ClearFreeMemory() { for (int n = 0; n < IM_ARRAYSIZE(Layers); n++) Layers[n].clear(); } - int GetDrawListCount() const { int count = 0; for (int n = 0; n < IM_ARRAYSIZE(Layers); n++) count += Layers[n].Size; return count; } IMGUI_API void FlattenIntoSingleLayer(); }; @@ -946,7 +944,7 @@ struct IMGUI_API ImGuiWindow ImGuiID ViewportId; // We backup the viewport id (since the viewport may disappear or never be created if the window is inactive) ImVec2 ViewportPos; // We backup the viewport position (since the viewport may disappear or never be created if the window is inactive) int ViewportAllowPlatformMonitorExtend; // Reset to -1 every frame (index is guaranteed to be valid between NewFrame..EndFrame), only used in the Appearing frame of a tooltip/popup to enforce clamping to a given monitor - ImVec2 Pos; // Position rounded-up to nearest pixel + ImVec2 Pos; // Position (always rounded-up to nearest pixel) ImVec2 Size; // Current size (==SizeFull or collapsed title bar size) ImVec2 SizeFull; // Size when non collapsed ImVec2 SizeFullAtLastBegin; // Copy of SizeFull at the end of Begin. This is the reference value we'll use on the next frame to decide if we need scrollbars. From d9cd494eaf7f5f441a01827490f88e4e45941da4 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 25 Apr 2018 19:12:16 +0200 Subject: [PATCH 145/828] Viewport: Moved ScaleWindow* code in a proper location. Various comments. --- imgui.cpp | 84 +++++++++++++++++++++++++++---------------------------- imgui.h | 2 +- 2 files changed, 42 insertions(+), 44 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 76e8560aec99..9c0e0c95b183 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -231,7 +231,7 @@ 0.0f= not held. 1.0f= fully held. Pass intermediate 0.0f..1.0f values for analog triggers/sticks. - We uses a simple >0.0f test for activation testing, and won't attempt to test for a dead-zone. Your code will probably need to transform your raw inputs (such as e.g. remapping your 0.2..0.9 raw input range to 0.0..1.0 imgui range, etc.). - - You can download PNG/PSD files depicting the gamepad controls for common controllers at: goo.gl/9LgVZW. + - You can download PNG/PSD files depicting the gamepad controls for common controllers at: http://goo.gl/9LgVZW. - If you need to share inputs between your game and the imgui parts, the easiest approach is to go all-or-nothing, with a buttons combo to toggle the target. Please reach out if you think the game vs navigation input sharing could be improved. - Keyboard: @@ -3421,6 +3421,31 @@ static void TranslateWindow(ImGuiWindow* window, const ImVec2& delta) window->DC.LastItemDisplayRect.Translate(delta); } +static void ScaleWindow(ImGuiWindow* window, float scale) +{ + ImVec2 origin = window->Viewport->Pos; + window->Pos = ImFloor((window->Pos - origin) * scale + origin); + window->Size = ImFloor(window->Size * scale); + window->SizeFull = ImFloor(window->SizeFull * scale); + window->SizeContents = ImFloor(window->SizeContents * scale); +} + +// Scale all windows (position, size). Use when e.g. changing DPI. (This is a lossy operation!) +void ImGui::ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale) +{ + ImGuiContext& g = *GImGui; + if (viewport->Window) + { + ScaleWindow(viewport->Window, scale); + } + else + { + for (int i = 0; i != g.Windows.Size; i++) + if (g.Windows[i]->Viewport == viewport) + ScaleWindow(g.Windows[i], scale); + } +} + static void ImGui::UpdateViewports() { ImGuiContext& g = *GImGui; @@ -3430,7 +3455,7 @@ static void ImGui::UpdateViewports() g.MouseRefPrevViewport = g.MouseRefViewport; g.MouseRefViewport = g.IO.MousePosViewport ? FindViewportByID(g.IO.MousePosViewport) : g.Viewports[0]; - // Update main viewport with current size (and OS window position, if known) + // Update main viewport with current platform position and size ImGuiViewportP* main_viewport = g.Viewports[0]; IM_ASSERT(main_viewport->ID == IMGUI_VIEWPORT_DEFAULT_ID); ImVec2 main_viewport_platform_pos = ImVec2(0.0f, 0.0f); @@ -3464,25 +3489,24 @@ static void ImGui::UpdateViewports() continue; } - // Apply Position and Size (from Platform Window to ImGui) if requested - // We do it here early in the frame instead of UpdatePlatformWindows() to allow the platform back-end to set PlatformRequestResize early - // (e.g. in their own message handler before NewFrame) and not have a frame of lag. + // Apply Position and Size (from Platform Window to ImGui) if requested. + // We do it early in the frame instead of waiting for UpdatePlatformWindows() to avoid a frame of lag when moving/resizing using OS facilities. if (viewport->PlatformRequestMove) viewport->Pos = g.PlatformIO.Platform_GetWindowPos(viewport); if (viewport->PlatformRequestResize) viewport->Size = g.PlatformIO.Platform_GetWindowSize(viewport); - // Translate resized viewports + // Translate imgui windows when a host viewport has been moved ImVec2 delta = viewport->Pos - viewport->LastPos; if ((viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) && (delta.x != 0.0f || delta.y != 0.0f)) for (int window_n = 0; window_n < g.Windows.Size; window_n++) if (g.Windows[window_n]->Viewport == viewport) TranslateWindow(g.Windows[window_n], delta); - // Update monitor + // Update monitor (we'll use this info to clamp windows and save windows lost in a removed monitor) viewport->PlatformMonitor = FindPlatformMonitorForRect(viewport->GetRect()); - // Update DPI Scale + // Update DPI scale float new_dpi_scale; if (g.PlatformIO.Platform_GetWindowDpiScale) new_dpi_scale = g.PlatformIO.Platform_GetWindowDpiScale(viewport); @@ -3520,7 +3544,7 @@ static void ImGui::UpdateViewports() viewport_hovered = g.IO.MouseHoveredViewport ? FindViewportByID(g.IO.MouseHoveredViewport) : NULL; if (viewport_hovered && (viewport_hovered->Flags & ImGuiViewportFlags_NoInputs)) { - // Back-end failed at honoring its contract + // Back-end failed at honoring its contract if it returned a viewport with the _NoInputs flag IM_ASSERT(0); viewport_hovered = FindViewportHoveredFromPlatformWindowStack(g.IO.MousePos); } @@ -3560,7 +3584,7 @@ void ImGui::UpdatePlatformWindows() if (!(g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) return; - // Create/resize/destroy platform windows to match each active viewport. Update the user-facing list. + // Create/resize/destroy platform windows to match each active viewport. // Skip the main viewport (index 0), which is always fully handled by the application! for (int i = 1; i < g.Viewports.Size; i++) { @@ -3595,6 +3619,7 @@ void ImGui::UpdatePlatformWindows() viewport->Flags = no_task_bar_icon ? (viewport->Flags | ImGuiViewportFlags_NoTaskBarIcon) : (viewport->Flags & ~ImGuiViewportFlags_NoTaskBarIcon); } + // Create window bool is_new_window = (viewport->PlatformHandle == NULL && viewport->PlatformUserData == NULL && viewport->RendererUserData == NULL); if (is_new_window && viewport->PlatformHandle == NULL && viewport->PlatformUserData == NULL) g.PlatformIO.Platform_CreateWindow(viewport); @@ -3604,28 +3629,26 @@ void ImGui::UpdatePlatformWindows() viewport->RendererLastSize = viewport->Size; } - // Apply Position and Size (from ImGui to Platform Window) + // Apply Position and Size (from ImGui to Platform/Renderer back-ends) if (!viewport->PlatformRequestMove) g.PlatformIO.Platform_SetWindowPos(viewport, viewport->Pos); if (!viewport->PlatformRequestResize) g.PlatformIO.Platform_SetWindowSize(viewport, viewport->Size); - - // Update Size for Renderer if (g.PlatformIO.Renderer_SetWindowSize && (viewport->RendererLastSize.x != viewport->Size.x || viewport->RendererLastSize.y != viewport->Size.y)) g.PlatformIO.Renderer_SetWindowSize(viewport, viewport->Size); viewport->RendererLastSize = viewport->Size; - // Update title bar + // Update title bar (if it changed) const char* title_begin = viewport->Window->Name; char* title_end = (char*)(intptr_t)ImGui::FindRenderedTextEnd(title_begin); const ImGuiID title_hash = ImHash(title_begin, (int)(title_end - title_begin)); if (viewport->LastNameHash != title_hash) { - viewport->LastNameHash = title_hash; char title_end_backup_c = *title_end; *title_end = 0; // Cut existing buffer short instead of doing an alloc/free g.PlatformIO.Platform_SetWindowTitle(viewport, title_begin); *title_end = title_end_backup_c; + viewport->LastNameHash = title_hash; } // Update alpha @@ -3681,7 +3704,7 @@ static void ImGui::UpdateMouseInputs() { ImGuiContext& g = *GImGui; - // If mouse just appeared or disappeared (usually denoted by -FLT_MAX component, but in reality we test for -256000.0f) we cancel out movement in MouseDelta + // If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MousePosPrev) && g.MouseRefViewport == g.MouseRefPrevViewport) g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev; else @@ -4529,7 +4552,7 @@ void ImGui::EndFrame() } } - // Update user-side viewport list + // Update user-facing viewport list g.PlatformIO.MainViewport = g.Viewports[0]; g.PlatformIO.Viewports.resize(0); for (int i = 0; i < g.Viewports.Size; i++) @@ -6707,9 +6730,9 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) } } + // Synchronize viewport --> window if (window->ViewportOwned) { - // Synchronize viewport --> window if (window->Viewport->PlatformRequestMove) window->Pos = window->Viewport->Pos; if (window->Viewport->PlatformRequestResize) @@ -14051,31 +14074,6 @@ static void SetClipboardTextFn_DefaultImpl(void*, const char* text) #endif -static void ScaleWindow(ImGuiWindow* window, float scale) -{ - ImVec2 origin = window->Viewport->Pos; - window->Pos = ImFloor((window->Pos - origin) * scale + origin); - window->Size = ImFloor(window->Size * scale); - window->SizeFull = ImFloor(window->SizeFull * scale); - window->SizeContents = ImFloor(window->SizeContents * scale); -} - -// Scale all windows (position, size). Use when e.g. changing DPI. (This is a lossy operation!) -void ImGui::ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale) -{ - ImGuiContext& g = *GImGui; - if (viewport->Window) - { - ScaleWindow(viewport->Window, scale); - } - else - { - for (int i = 0; i != g.Windows.Size; i++) - if (g.Windows[i]->Viewport == viewport) - ScaleWindow(g.Windows[i], scale); - } -} - //----------------------------------------------------------------------------- // HELP, METRICS //----------------------------------------------------------------------------- diff --git a/imgui.h b/imgui.h index a6c50f5fc8c8..17f6744941c0 100644 --- a/imgui.h +++ b/imgui.h @@ -752,7 +752,7 @@ enum ImGuiKey_ // [BETA] Gamepad/Keyboard directional navigation // Keyboard: Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable. NewFrame() will automatically fill io.NavInputs[] based on your io.KeysDown[] + io.KeyMap[] arrays. // Gamepad: Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable. Back-end: set ImGuiBackendFlags_HasGamepad and fill the io.NavInputs[] fields before calling NewFrame(). Note that io.NavInputs[] is cleared by EndFrame(). -// Read instructions in imgui.cpp for more details. Download PNG/PSD at goo.gl/9LgVZW. +// Read instructions in imgui.cpp for more details. Download PNG/PSD at http://goo.gl/9LgVZW. enum ImGuiNavInput_ { // Gamepad Mapping From f1ae07e5321014deaa9920b89e72b1faf9a95700 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 26 Apr 2018 11:52:38 +0200 Subject: [PATCH 146/828] Viewport, Platform: Using Platform_GetWindowFocus to provide a much stronger heuristic of platform z-order, in replacement for when the back-end cannot provide io.MouseHoveredViewport. The pressure for it to work well increased with the use of viewports by popups/menus. (#1542) --- TODO.txt | 1 - imgui.cpp | 29 +++++++++++++++++++++++------ imgui.h | 2 +- imgui_internal.h | 8 ++++++-- 4 files changed, 30 insertions(+), 10 deletions(-) diff --git a/TODO.txt b/TODO.txt index 69f88df71845..3fff67ed4584 100644 --- a/TODO.txt +++ b/TODO.txt @@ -262,7 +262,6 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - viewport: platform: introduce getfocus/setfocus api, so e.g. focus flags can be honored, imgui-side ctrl-tab can focus os window, OS alt-tab can focus imgui window etc. - viewport: store per-viewport/monitor DPI in .ini file so an application reload or main window changing DPI on reload can be properly patched for. - viewport: vulkan renderer implementation. - - viewport: fallback calculation of hovered window is very currently wrong without ImGuiBackendFlags_HasMouseHoveredViewport. typically affect half-overlapping viewported menus. - viewport: need to clarify how to use GetMousePos() from a user point of view. - inputs: we need an explicit flag about whether the imgui window is focused, to be able to distinguish focused key releases vs alt-tabbing all release behaviors. diff --git a/imgui.cpp b/imgui.cpp index 9c0e0c95b183..aade0c309d4d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3392,8 +3392,8 @@ static void ImGui::UpdateMovingWindow() } // If the back-end doesn't set MouseLastHoveredViewport or doesn't honor ImGuiViewportFlags_NoInputs, we do a search ourselves. -// This search won't take account of the possibility that non-imgui windows may be in-between our dragged window and our target window. -// FIXME-VIEWPORT: Need a proper notion of focus. At least use the equivalent of LastFrameAsRefViewport on a per-window basis. +// A) It won't take account of the possibility that non-imgui windows may be in-between our dragged window and our target window. +// B) It requires Platform_GetWindowFocus to be implemented by back-end. static ImGuiViewportP* FindViewportHoveredFromPlatformWindowStack(const ImVec2 mouse_platform_pos) { ImGuiContext& g = *GImGui; @@ -3402,7 +3402,7 @@ static ImGuiViewportP* FindViewportHoveredFromPlatformWindowStack(const ImVec2 m { ImGuiViewportP* viewport = g.Viewports[n]; if (!(viewport->Flags & ImGuiViewportFlags_NoInputs) && viewport->GetRect().Contains(mouse_platform_pos)) - if (best_candidate == NULL || best_candidate->LastFrameAsRefViewport < viewport->LastFrameAsRefViewport) + if (best_candidate == NULL || best_candidate->LastFrontMostStampCount < viewport->LastFrontMostStampCount) best_candidate = viewport; } return best_candidate; @@ -3532,10 +3532,8 @@ static void ImGui::UpdateViewports() if (!(g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) { g.MouseRefViewport = g.MouseRefPrevViewport = main_viewport; - g.MouseRefViewport->LastFrameAsRefViewport = g.FrameCount; return; } - g.MouseRefViewport->LastFrameAsRefViewport = g.FrameCount; // Mouse handling: decide on the actual mouse viewport for this frame between the active/focused viewport and the hovered viewport. ImGuiViewportP* viewport_hovered = NULL; @@ -3584,6 +3582,21 @@ void ImGui::UpdatePlatformWindows() if (!(g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) return; + // Update our implicit z-order knowledge of platform windows, which is used when the back-end cannot provide io.MouseHoveredViewport. + if (g.PlatformIO.Platform_GetWindowFocus) + { + ImGuiViewportP* focused_viewport = NULL; + for (int i = 0; i < g.Viewports.Size && focused_viewport == NULL; i++) + if (g.PlatformIO.Platform_GetWindowFocus(g.Viewports[i])) + focused_viewport = g.Viewports[i]; + if (focused_viewport && g.PlatformLastFocusedViewport != focused_viewport->ID) + { + if (focused_viewport->LastFrontMostStampCount != g.WindowsFrontMostStampCount) + focused_viewport->LastFrontMostStampCount = ++g.WindowsFrontMostStampCount; + g.PlatformLastFocusedViewport = focused_viewport->ID; + } + } + // Create/resize/destroy platform windows to match each active viewport. // Skip the main viewport (index 0), which is always fully handled by the application! for (int i = 1; i < g.Viewports.Size; i++) @@ -3656,7 +3669,7 @@ void ImGui::UpdatePlatformWindows() g.PlatformIO.Platform_SetWindowAlpha(viewport, viewport->Alpha); viewport->LastAlpha = viewport->Alpha; - // Show window. On startup ensure platform window don't get focus. + // Show window. On startup ensure platform window don't get focus if (is_new_window) { if (g.FrameCount < 2) @@ -3664,6 +3677,10 @@ void ImGui::UpdatePlatformWindows() g.PlatformIO.Platform_ShowWindow(viewport); } + // Even without focus, we assume the window becomes front-most. This is used by our platform z-order heuristic when io.MouseHoveredViewport is not available. + if (is_new_window && viewport->LastFrontMostStampCount != g.WindowsFrontMostStampCount) + viewport->LastFrontMostStampCount = ++g.WindowsFrontMostStampCount; + // Clear request flags viewport->PlatformRequestClose = viewport->PlatformRequestMove = viewport->PlatformRequestResize = false; } diff --git a/imgui.h b/imgui.h index 17f6744941c0..4aa47a74a82b 100644 --- a/imgui.h +++ b/imgui.h @@ -1073,7 +1073,7 @@ struct ImGuiIO float MouseWheel; // Mouse wheel Vertical: 1 unit scrolls about 5 lines text. float MouseWheelH; // Mouse wheel Horizontal. Most users don't have a mouse with an horizontal wheel, may not be filled by all back-ends. ImGuiID MousePosViewport; // (Optional) When using multiple viewports: viewport from which io.MousePos is based from (when dragging this is generally the captured/focused viewport, even though we can drag outside of it and then it's not hovered anymore). (0 == default viewport) - ImGuiID MouseHoveredViewport; // (Optional) When using multiple viewports: viewport the OS mouse cursor is hovering. (0 == default viewport) _IGNORING_ viewports with the ImGuiBackendFlags_HasMouseHoveredViewport flag, and _REGARDLESS_ of whether another viewport is focused. Set io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport if you can provide this information. + ImGuiID MouseHoveredViewport; // (Optional) When using multiple viewports: viewport the OS mouse cursor is hovering _IGNORING_ viewports with the ImGuiViewportFlags_NoInputs flag, and _REGARDLESS_ of whether another viewport is focused. Set io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport if you can provide this info. If you don't imgui will use a decent heuristic instead. bool MouseDrawCursor; // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). bool KeyCtrl; // Keyboard modifier pressed: Control bool KeyShift; // Keyboard modifier pressed: Shift diff --git a/imgui_internal.h b/imgui_internal.h index 854ebff2c0c2..8372e8bd3548 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -511,8 +511,8 @@ struct ImGuiViewportP : public ImGuiViewport { int Idx; int LastFrameActive; // Last frame number this viewport was activated by a window - int LastFrameAsRefViewport; // Last frame number this viewport was io.MouseViewportRef int LastFrameOverlayDrawList; + int LastFrontMostStampCount; // Last stamp number from when a window hosted by this viewport was made front-most (by comparing this value between two viewport we have an implicit viewport z-order ImGuiID LastNameHash; ImVec2 LastPos; float Alpha; // Window opacity (when dragging dockable windows/viewports we make them transparent) @@ -524,7 +524,7 @@ struct ImGuiViewportP : public ImGuiViewport ImDrawDataBuilder DrawDataBuilder; ImVec2 RendererLastSize; - ImGuiViewportP() { Idx = 1; LastFrameActive = LastFrameAsRefViewport = LastFrameOverlayDrawList = -1; LastNameHash = 0; Alpha = LastAlpha = 1.0f; PlatformMonitor = INT_MIN; Window = NULL; OverlayDrawList = NULL; RendererLastSize = ImVec2(-1.0f,-1.0f); } + ImGuiViewportP() { Idx = 1; LastFrameActive = LastFrameOverlayDrawList = LastFrontMostStampCount = -1; LastNameHash = 0; Alpha = LastAlpha = 1.0f; PlatformMonitor = INT_MIN; Window = NULL; OverlayDrawList = NULL; RendererLastSize = ImVec2(-1.0f,-1.0f); } ~ImGuiViewportP() { if (OverlayDrawList) IM_DELETE(OverlayDrawList); } ImRect GetRect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } ImVec2 GetCenter() const{ return ImVec2(Pos.x + Size.x * 0.5f, Pos.y + Size.y * 0.5f); } @@ -610,6 +610,7 @@ struct ImGuiContext ImVector CurrentWindowStack; ImGuiStorage WindowsById; int WindowsActiveCount; + int WindowsFrontMostStampCount; // Every time the front-most window changes, we stamp its viewport with an incrementing counter ImGuiWindow* CurrentWindow; // Being drawn into ImGuiWindow* HoveredWindow; // Will catch mouse inputs ImGuiWindow* HoveredRootWindow; // Will catch mouse inputs (for focus/move only) @@ -644,6 +645,7 @@ struct ImGuiContext ImGuiViewportP* MouseRefPrevViewport; ImGuiViewportP* MouseHoveredLastViewport; // Last viewport that was hovered by mouse (even if we are not hovering any viewport any more) ImGuiID MouseClickedPosViewportId[5]; // For rarely used fields we only compare to, store viewport ID only so we don't have to clean dangling pointers + ImGuiID PlatformLastFocusedViewport; // Record of last focused platform window/viewport, when this changes we stamp the viewport as front-most // Navigation data (for gamepad/keyboard) ImGuiWindow* NavWindow; // Focused window for navigation. Could be called 'FocusWindow' @@ -751,6 +753,7 @@ struct ImGuiContext FrameCount = 0; FrameCountEnded = FrameCountPlatformEnded = FrameCountRendered = -1; WindowsActiveCount = 0; + WindowsFrontMostStampCount = 0; CurrentWindow = NULL; HoveredWindow = NULL; HoveredRootWindow = NULL; @@ -776,6 +779,7 @@ struct ImGuiContext MouseRefViewport = NULL; MouseRefPrevViewport = MouseHoveredLastViewport = NULL; memset(MouseClickedPosViewportId, 0, sizeof(MouseClickedPosViewportId)); + PlatformLastFocusedViewport = 0; NavWindow = NULL; NavId = NavActivateId = NavActivateDownId = NavActivatePressedId = NavInputId = 0; From da70c837da3fd35b1fa4e786ba81a78ccfde0206 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 26 Apr 2018 14:32:52 +0200 Subject: [PATCH 147/828] Viewport: Removed unnecessary fields (now that the coordinate system is consistent accross viewports): MouseRefPrevViewport, MouseClickedPosViewportId. (#1542) --- TODO.txt | 3 +-- examples/imgui_impl_win32.cpp | 4 ++-- imgui.cpp | 18 +++++++----------- imgui_internal.h | 8 ++------ 4 files changed, 12 insertions(+), 21 deletions(-) diff --git a/TODO.txt b/TODO.txt index 3fff67ed4584..3a4e285f782f 100644 --- a/TODO.txt +++ b/TODO.txt @@ -258,8 +258,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - focus: SetKeyboardFocusHere() on with >= 0 offset could be done on same frame (else latch and modulate on beginning of next frame) - focus: unable to use SetKeyboardFocusHere() on clipped widgets. (#787) - - viewport: popup/tooltip glitches when appearing near the monitor limits (e.g. opening a menu). - - viewport: platform: introduce getfocus/setfocus api, so e.g. focus flags can be honored, imgui-side ctrl-tab can focus os window, OS alt-tab can focus imgui window etc. + - viewport: use getfocus/setfocus api to synchronize imgui<>platform focus better (e.g imgui-side ctrl-tab can focus os window, OS initial setup and alt-tab can focus imgui window etc.) - viewport: store per-viewport/monitor DPI in .ini file so an application reload or main window changing DPI on reload can be properly patched for. - viewport: vulkan renderer implementation. - viewport: need to clarify how to use GetMousePos() from a user point of view. diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index 6f44060009e1..f69067d080e5 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -117,9 +117,9 @@ static bool ImGui_ImplWin32_UpdateMouseCursor() } // This code supports multiple OS Windows mapped into different ImGui viewports, -// So it is a little more complicated than your typical binding code (which only needs to set io.MousePos in your WM_MOUSEMOVE handler) +// So it is a little more complicated than your typical single-viewport binding code (which only needs to set io.MousePos from the WM_MOUSEMOVE handler) // This is what imgui needs from the back-end to support multiple windows: -// - io.MousePos = mouse position (e.g. io.MousePos == viewport->Pos when we are on the upper-left of our viewport) +// - io.MousePos = mouse position in absolute coordinate (e.g. io.MousePos == ImVec2(0,0) when it is on the upper-left of the primary monitor) // - io.MousePosViewport = viewport which mouse position is based from (generally the focused/active/capturing viewport) // - io.MouseHoveredWindow = viewport which mouse is hovering, **regardless of it being the active/focused window**, **regardless of another window holding mouse captured**. [Optional] // This function overwrite the value of io.MousePos normally updated by the WM_MOUSEMOVE handler. diff --git a/imgui.cpp b/imgui.cpp index aade0c309d4d..93b4a30bb9d8 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3452,7 +3452,6 @@ static void ImGui::UpdateViewports() IM_ASSERT(g.PlatformIO.Viewports.Size <= g.Viewports.Size); // Update mouse reference viewport - g.MouseRefPrevViewport = g.MouseRefViewport; g.MouseRefViewport = g.IO.MousePosViewport ? FindViewportByID(g.IO.MousePosViewport) : g.Viewports[0]; // Update main viewport with current platform position and size @@ -3480,8 +3479,7 @@ static void ImGui::UpdateViewports() // Destroy if (viewport == g.MouseRefViewport) g.MouseRefViewport = main_viewport; - if (viewport == g.MouseRefPrevViewport) g.MouseRefPrevViewport = main_viewport; - if (viewport == g.MouseHoveredLastViewport) g.MouseHoveredLastViewport = NULL; + if (viewport == g.MouseLastHoveredViewport) g.MouseLastHoveredViewport = NULL; IM_ASSERT(viewport->RendererUserData == NULL && viewport->PlatformUserData == NULL && viewport->PlatformHandle == NULL); IM_ASSERT(g.PlatformIO.Viewports.contains(viewport) == false); IM_DELETE(viewport); @@ -3531,7 +3529,7 @@ static void ImGui::UpdateViewports() if (!(g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) { - g.MouseRefViewport = g.MouseRefPrevViewport = main_viewport; + g.MouseRefViewport = main_viewport; return; } @@ -3555,7 +3553,7 @@ static void ImGui::UpdateViewports() viewport_hovered = FindViewportHoveredFromPlatformWindowStack(g.IO.MousePos); } if (viewport_hovered != NULL) - g.MouseHoveredLastViewport = viewport_hovered; + g.MouseLastHoveredViewport = viewport_hovered; // When dragging something, always refer to the last hovered viewport. // (So when we are between viewports, our dragged preview will tend to show in the last viewport even if we don't have tooltips in viewports) @@ -3564,7 +3562,7 @@ static void ImGui::UpdateViewports() if (is_mouse_dragging_with_an_expected_destination || g.ActiveId == 0 || !ImGui::IsAnyMouseDown()) { if (is_mouse_dragging_with_an_expected_destination && viewport_hovered == NULL) - viewport_hovered = g.MouseHoveredLastViewport; + viewport_hovered = g.MouseLastHoveredViewport; if (viewport_hovered != NULL && viewport_hovered != g.MouseRefViewport && !(viewport_hovered->Flags & ImGuiViewportFlags_NoInputs)) g.MouseRefViewport = viewport_hovered; } @@ -3722,7 +3720,7 @@ static void ImGui::UpdateMouseInputs() ImGuiContext& g = *GImGui; // If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta - if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MousePosPrev) && g.MouseRefViewport == g.MouseRefPrevViewport) + if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MousePosPrev)) g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev; else g.IO.MouseDelta = ImVec2(0.0f, 0.0f); @@ -3752,7 +3750,6 @@ static void ImGui::UpdateMouseInputs() g.IO.MouseClickedPos[i] = g.IO.MousePos; g.IO.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f); g.IO.MouseDragMaxDistanceSqr[i] = 0.0f; - g.MouseClickedPosViewportId[i] = g.MouseRefViewport->ID; } else if (g.IO.MouseDown[i]) { @@ -4198,7 +4195,7 @@ void ImGui::Shutdown(ImGuiContext* context) g.FontStack.clear(); g.OpenPopupStack.clear(); g.CurrentPopupStack.clear(); - g.CurrentViewport = g.MouseRefViewport = g.MouseRefPrevViewport = g.MouseHoveredLastViewport = NULL; + g.CurrentViewport = g.MouseRefViewport = g.MouseLastHoveredViewport = NULL; for (int i = 0; i < g.Viewports.Size; i++) IM_DELETE(g.Viewports[i]); g.Viewports.clear(); @@ -5297,8 +5294,7 @@ ImVec2 ImGui::GetMouseDragDelta(int button, float lock_threshold) lock_threshold = g.IO.MouseDragThreshold; if (g.IO.MouseDown[button]) if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold) - if (g.MouseRefViewport->ID == g.MouseClickedPosViewportId[button]) - return g.IO.MousePos - g.IO.MouseClickedPos[button]; // Assume we can only get active with left-mouse button (at the moment). + return g.IO.MousePos - g.IO.MouseClickedPos[button]; // Assume we can only get active with left-mouse button (at the moment). return ImVec2(0.0f, 0.0f); } diff --git a/imgui_internal.h b/imgui_internal.h index 8372e8bd3548..603a31c73a26 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -642,9 +642,7 @@ struct ImGuiContext ImVector Viewports; // Active viewports (always 1+, and generally 1 unless multi-viewports are enabled). Each viewports hold their copy of ImDrawData. ImGuiViewportP* CurrentViewport; // We track changes of viewport (happening in Begin) so we can call Platform_OnChangedViewport() ImGuiViewportP* MouseRefViewport; - ImGuiViewportP* MouseRefPrevViewport; - ImGuiViewportP* MouseHoveredLastViewport; // Last viewport that was hovered by mouse (even if we are not hovering any viewport any more) - ImGuiID MouseClickedPosViewportId[5]; // For rarely used fields we only compare to, store viewport ID only so we don't have to clean dangling pointers + ImGuiViewportP* MouseLastHoveredViewport; // Last known viewport that was hovered by mouse (even if we are not hovering any viewport any more) ImGuiID PlatformLastFocusedViewport; // Record of last focused platform window/viewport, when this changes we stamp the viewport as front-most // Navigation data (for gamepad/keyboard) @@ -776,9 +774,7 @@ struct ImGuiContext NextTreeNodeOpenCond = 0; CurrentViewport = NULL; - MouseRefViewport = NULL; - MouseRefPrevViewport = MouseHoveredLastViewport = NULL; - memset(MouseClickedPosViewportId, 0, sizeof(MouseClickedPosViewportId)); + MouseRefViewport = MouseLastHoveredViewport = NULL; PlatformLastFocusedViewport = 0; NavWindow = NULL; From cd51f37fc04eb3b260940f7900afb84860119fc5 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 26 Apr 2018 16:07:54 +0200 Subject: [PATCH 148/828] Viewport, Platform: Refresh monitor list (win32, glfw) + avoid calling GetWindowFocus before platform window creation to not require of backend to null-check things inconsistently. (#1542) --- TODO.txt | 3 +++ examples/imgui_impl_glfw.cpp | 13 ++++++++++- examples/imgui_impl_sdl2.cpp | 5 ++--- examples/imgui_impl_win32.cpp | 12 ++++++++-- imgui.cpp | 42 ++++++++++++++++++----------------- imgui_internal.h | 3 ++- 6 files changed, 51 insertions(+), 27 deletions(-) diff --git a/TODO.txt b/TODO.txt index 3a4e285f782f..918f3b5809e8 100644 --- a/TODO.txt +++ b/TODO.txt @@ -262,6 +262,9 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - viewport: store per-viewport/monitor DPI in .ini file so an application reload or main window changing DPI on reload can be properly patched for. - viewport: vulkan renderer implementation. - viewport: need to clarify how to use GetMousePos() from a user point of view. + - platform: glfw: no support for ImGuiBackendFlags_HasMouseHoveredViewport. + - platform: sdl: no support for ImGuiBackendFlags_HasMouseHoveredViewport. maybe we could use SDL_GetMouseFocus() / SDL_WINDOW_MOUSE_FOCUS if imgui could fallback on its heuristic when NoInputs is set + - platform: sdl: no refresh of monitor/display (SDL doesn't seem to have an event for it). - inputs: we need an explicit flag about whether the imgui window is focused, to be able to distinguish focused key releases vs alt-tabbing all release behaviors. - inputs: rework IO system to be able to pass actual ordered/timestamped events. use an event queue? (~#335, #71) diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index 0d00b3aad7d2..af984041e218 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -53,10 +53,12 @@ static GlfwClientApi g_ClientApi = GlfwClientApi_Unknown; static double g_Time = 0.0f; static bool g_MouseJustPressed[5] = { false, false, false, false, false }; static GLFWcursor* g_MouseCursors[ImGuiMouseCursor_Count_] = { 0 }; +static bool g_WantUpdateMonitors = true; // Forward Declarations static void ImGui_ImplGlfw_InitPlatformInterface(); static void ImGui_ImplGlfw_ShutdownPlatformInterface(); +static void ImGui_ImplGlfw_UpdateMonitors(); static const char* ImGui_ImplGlfw_GetClipboardText(void* user_data) { @@ -271,6 +273,8 @@ void ImGui_ImplGlfw_NewFrame() glfwGetFramebufferSize(g_Window, &display_w, &display_h); io.DisplaySize = ImVec2((float)w, (float)h); io.DisplayFramebufferScale = ImVec2(w > 0 ? ((float)display_w / w) : 0, h > 0 ? ((float)display_h / h) : 0); + if (g_WantUpdateMonitors) + ImGui_ImplGlfw_UpdateMonitors(); // Setup time step double current_time = glfwGetTime(); @@ -562,7 +566,6 @@ static int ImGui_ImplGlfw_CreateVkSurface(ImGuiViewport* viewport, ImU64 vk_inst } #endif // GLFW_HAS_VULKAN -// FIXME-PLATFORM: Update monitor list when changed (using glfwSetMonitorCallback?) // FIXME-PLATFORM: GLFW doesn't export monitor work area (see https://github.com/glfw/glfw/pull/989) static void ImGui_ImplGlfw_UpdateMonitors() { @@ -586,6 +589,12 @@ static void ImGui_ImplGlfw_UpdateMonitors() #endif platform_io.Monitors.push_back(monitor); } + g_WantUpdateMonitors = false; +} + +static void ImGui_ImplGlfw_MonitorCallback(GLFWmonitor*, int) +{ + g_WantUpdateMonitors = true; } static void ImGui_ImplGlfw_InitPlatformInterface() @@ -614,7 +623,9 @@ static void ImGui_ImplGlfw_InitPlatformInterface() platform_io.Platform_SetImeInputPos = ImGui_ImplWin32_SetImeInputPos; #endif + // Note: monitor callback are broken GLFW 3.2 and earlier (see github.com/glfw/glfw/issues/784) ImGui_ImplGlfw_UpdateMonitors(); + glfwSetMonitorCallback(ImGui_ImplGlfw_MonitorCallback); // Register main window handle (which is owned by the main application, not by us) ImGuiViewport* main_viewport = ImGui::GetMainViewport(); diff --git a/examples/imgui_impl_sdl2.cpp b/examples/imgui_impl_sdl2.cpp index e58f28cc670e..e33c2a184b33 100644 --- a/examples/imgui_impl_sdl2.cpp +++ b/examples/imgui_impl_sdl2.cpp @@ -418,8 +418,7 @@ static void ImGui_ImplSDL2_SetWindowFocus(ImGuiViewport* viewport) static bool ImGui_ImplSDL2_GetWindowFocus(ImGuiViewport* viewport) { ImGuiViewportDataSDL2* data = (ImGuiViewportDataSDL2*)viewport->PlatformUserData; - bool focus = (SDL_GetWindowFlags(data->Window) & SDL_WINDOW_INPUT_FOCUS) != 0; - return focus; + return (SDL_GetWindowFlags(data->Window) & SDL_WINDOW_INPUT_FOCUS) != 0; } static void ImGui_ImplSDL2_RenderWindow(ImGuiViewport* viewport, void*) @@ -452,7 +451,7 @@ static int ImGui_ImplSDL2_CreateVkSurface(ImGuiViewport* viewport, ImU64 vk_inst } #endif // SDL_HAS_VULKAN -// FIXME-PLATFORM: Update monitor list when changed? +// FIXME-PLATFORM: SDL doesn't have an event to notify the application of display/monitor changes static void ImGui_ImplSDL2_UpdateMonitors() { ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index f69067d080e5..76c5b76b11b9 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -8,6 +8,7 @@ #include // CHANGELOG +// (minor and older changes stripped away, please see git history for details) // 2018-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. // 2018-03-20: Misc: Setup io.BackendFlags ImGuiBackendFlags_HasMouseCursors and ImGuiBackendFlags_HasSetMousePos flags + honor ImGuiConfigFlags_NoMouseCursorChange flag. // 2018-02-20: Inputs: Added support for mouse cursors (ImGui::GetMouseCursor() value and WM_SETCURSOR message handling). @@ -26,10 +27,12 @@ static HWND g_hWnd = 0; static INT64 g_Time = 0; static INT64 g_TicksPerSecond = 0; static ImGuiMouseCursor g_LastMouseCursor = ImGuiMouseCursor_Count_; +static bool g_WantUpdateMonitors = true; // Forward Declarations static void ImGui_ImplWin32_InitPlatformInterface(); static void ImGui_ImplWin32_ShutdownPlatformInterface(); +static void ImGui_ImplWin32_UpdateMonitors(); // Functions bool ImGui_ImplWin32_Init(void* hwnd) @@ -159,6 +162,8 @@ void ImGui_ImplWin32_NewFrame() RECT rect; ::GetClientRect(g_hWnd, &rect); io.DisplaySize = ImVec2((float)(rect.right - rect.left), (float)(rect.bottom - rect.top)); + if (g_WantUpdateMonitors) + ImGui_ImplWin32_UpdateMonitors(); // Setup time step INT64 current_time; @@ -251,7 +256,7 @@ IMGUI_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARAM wPa io.MouseWheelH += GET_WHEEL_DELTA_WPARAM(wParam) > 0 ? +1.0f : -1.0f; return 0; case WM_MOUSEMOVE: - io.MousePos.x = (signed short)(lParam); + io.MousePos.x = (signed short)(lParam); // Note: this is used for single-viewport support, but in reality the code in ImGui_ImplWin32_UpdateMousePos() overwrite this. io.MousePos.y = (signed short)(lParam >> 16); return 0; case WM_KEYDOWN: @@ -273,6 +278,9 @@ IMGUI_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARAM wPa if (LOWORD(lParam) == HTCLIENT && ImGui_ImplWin32_UpdateMouseCursor()) return 1; return 0; + case WM_DISPLAYCHANGE: + g_WantUpdateMonitors = true; + return 0; } return 0; } @@ -623,11 +631,11 @@ static BOOL CALLBACK ImGui_ImplWin32_UpdateMonitors_EnumFunc(HMONITOR monitor, H return TRUE; } -// FIXME-PLATFORM: Update monitor list when changed (WM_DISPLAYCHANGE?) static void ImGui_ImplWin32_UpdateMonitors() { ImGui::GetPlatformIO().Monitors.resize(0); ::EnumDisplayMonitors(NULL, NULL, ImGui_ImplWin32_UpdateMonitors_EnumFunc, NULL); + g_WantUpdateMonitors = false; } static void ImGui_ImplWin32_InitPlatformInterface() diff --git a/imgui.cpp b/imgui.cpp index 93b4a30bb9d8..1f624efa361b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3580,21 +3580,6 @@ void ImGui::UpdatePlatformWindows() if (!(g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) return; - // Update our implicit z-order knowledge of platform windows, which is used when the back-end cannot provide io.MouseHoveredViewport. - if (g.PlatformIO.Platform_GetWindowFocus) - { - ImGuiViewportP* focused_viewport = NULL; - for (int i = 0; i < g.Viewports.Size && focused_viewport == NULL; i++) - if (g.PlatformIO.Platform_GetWindowFocus(g.Viewports[i])) - focused_viewport = g.Viewports[i]; - if (focused_viewport && g.PlatformLastFocusedViewport != focused_viewport->ID) - { - if (focused_viewport->LastFrontMostStampCount != g.WindowsFrontMostStampCount) - focused_viewport->LastFrontMostStampCount = ++g.WindowsFrontMostStampCount; - g.PlatformLastFocusedViewport = focused_viewport->ID; - } - } - // Create/resize/destroy platform windows to match each active viewport. // Skip the main viewport (index 0), which is always fully handled by the application! for (int i = 1; i < g.Viewports.Size; i++) @@ -3631,13 +3616,14 @@ void ImGui::UpdatePlatformWindows() } // Create window - bool is_new_window = (viewport->PlatformHandle == NULL && viewport->PlatformUserData == NULL && viewport->RendererUserData == NULL); - if (is_new_window && viewport->PlatformHandle == NULL && viewport->PlatformUserData == NULL) - g.PlatformIO.Platform_CreateWindow(viewport); - if (is_new_window && viewport->RendererUserData == NULL && g.PlatformIO.Renderer_CreateWindow != NULL) + bool is_new_window = (viewport->CreatedPlatformWindow == false); + if (is_new_window) { - g.PlatformIO.Renderer_CreateWindow(viewport); + g.PlatformIO.Platform_CreateWindow(viewport); + if (g.PlatformIO.Renderer_CreateWindow != NULL) + g.PlatformIO.Renderer_CreateWindow(viewport); viewport->RendererLastSize = viewport->Size; + viewport->CreatedPlatformWindow = true; } // Apply Position and Size (from ImGui to Platform/Renderer back-ends) @@ -3682,6 +3668,22 @@ void ImGui::UpdatePlatformWindows() // Clear request flags viewport->PlatformRequestClose = viewport->PlatformRequestMove = viewport->PlatformRequestResize = false; } + + // Update our implicit z-order knowledge of platform windows, which is used when the back-end cannot provide io.MouseHoveredViewport. + if (g.PlatformIO.Platform_GetWindowFocus != NULL) + { + ImGuiViewportP* focused_viewport = NULL; + for (int i = 0; i < g.Viewports.Size && focused_viewport == NULL; i++) + if (g.Viewports[i]->PlatformUserData != NULL || g.Viewports[i]->PlatformHandle != NULL || g.Viewports[i]->CreatedPlatformWindow) + if (g.PlatformIO.Platform_GetWindowFocus(g.Viewports[i])) + focused_viewport = g.Viewports[i]; + if (focused_viewport && g.PlatformLastFocusedViewport != focused_viewport->ID) + { + if (focused_viewport->LastFrontMostStampCount != g.WindowsFrontMostStampCount) + focused_viewport->LastFrontMostStampCount = ++g.WindowsFrontMostStampCount; + g.PlatformLastFocusedViewport = focused_viewport->ID; + } + } } // This is a default/basic function for performing the rendering/swap of multiple platform windows. diff --git a/imgui_internal.h b/imgui_internal.h index 603a31c73a26..5cc91ba942aa 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -515,6 +515,7 @@ struct ImGuiViewportP : public ImGuiViewport int LastFrontMostStampCount; // Last stamp number from when a window hosted by this viewport was made front-most (by comparing this value between two viewport we have an implicit viewport z-order ImGuiID LastNameHash; ImVec2 LastPos; + bool CreatedPlatformWindow; float Alpha; // Window opacity (when dragging dockable windows/viewports we make them transparent) float LastAlpha; int PlatformMonitor; @@ -524,7 +525,7 @@ struct ImGuiViewportP : public ImGuiViewport ImDrawDataBuilder DrawDataBuilder; ImVec2 RendererLastSize; - ImGuiViewportP() { Idx = 1; LastFrameActive = LastFrameOverlayDrawList = LastFrontMostStampCount = -1; LastNameHash = 0; Alpha = LastAlpha = 1.0f; PlatformMonitor = INT_MIN; Window = NULL; OverlayDrawList = NULL; RendererLastSize = ImVec2(-1.0f,-1.0f); } + ImGuiViewportP() { Idx = 1; LastFrameActive = LastFrameOverlayDrawList = LastFrontMostStampCount = -1; LastNameHash = 0; CreatedPlatformWindow = false; Alpha = LastAlpha = 1.0f; PlatformMonitor = INT_MIN; Window = NULL; OverlayDrawList = NULL; RendererLastSize = ImVec2(-1.0f,-1.0f); } ~ImGuiViewportP() { if (OverlayDrawList) IM_DELETE(OverlayDrawList); } ImRect GetRect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } ImVec2 GetCenter() const{ return ImVec2(Pos.x + Size.x * 0.5f, Pos.y + Size.y * 0.5f); } From c47d34cf70fd1c6b8230477bb77e6c80c3b57a50 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 26 Apr 2018 16:36:39 +0200 Subject: [PATCH 149/828] Viewport: Exposing SetNextWindowViewport(). Used in demo and old/obsolete SetNextWindowPosCenter() (which is an interesting case). Fixed back-end likely crash calling GetFocus() after viewport destruction. Fixed metrics crash. --- imgui.cpp | 12 +++++++++++- imgui.h | 3 ++- imgui_demo.cpp | 9 ++++++--- imgui_internal.h | 1 - 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 1f624efa361b..cdc0f89d7d5b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3594,6 +3594,7 @@ void ImGui::UpdatePlatformWindows() g.PlatformIO.Renderer_DestroyWindow(viewport); if (g.PlatformIO.Platform_DestroyWindow) g.PlatformIO.Platform_DestroyWindow(viewport); + viewport->CreatedPlatformWindow = false; IM_ASSERT(viewport->RendererUserData == NULL); IM_ASSERT(viewport->PlatformUserData == NULL && viewport->PlatformHandle == NULL); } @@ -7916,6 +7917,15 @@ void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiCond cond, const ImVec2& pi g.NextWindowData.PosCond = cond ? cond : ImGuiCond_Always; } +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS +void ImGui::SetNextWindowPosCenter(ImGuiCond cond) +{ + ImGuiViewport* viewport = ImGui::GetMainViewport(); + SetNextWindowPos(viewport->Pos + viewport->Size * 0.5f, cond, ImVec2(0.5f, 0.5f)); + SetNextWindowViewport(viewport->ID); +} +#endif + void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiCond cond) { ImGuiContext& g = *GImGui; @@ -14260,7 +14270,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) else ImGui::BulletText("NavRectRel[0]: "); ImGui::BulletText("Viewport: %d, ViewportId: 0x%08X, ViewportPos: (%.1f,%.1f)", window->Viewport ? window->Viewport->Idx : -1, window->ViewportId, window->ViewportPos.x, window->ViewportPos.y); - ImGui::BulletText("ViewportMonitor: %d", window->Viewport->PlatformMonitor); + ImGui::BulletText("ViewportMonitor: %d", window->Viewport ? window->Viewport->PlatformMonitor : -1); if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow"); if (window->ParentWindow != NULL) NodeWindow(window->ParentWindow, "ParentWindow"); if (window->DC.ChildWindows.Size > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows"); diff --git a/imgui.h b/imgui.h index 4aa47a74a82b..5f57790d1726 100644 --- a/imgui.h +++ b/imgui.h @@ -206,6 +206,7 @@ namespace ImGui IMGUI_API void SetNextWindowCollapsed(bool collapsed, ImGuiCond cond = 0); // set next window collapsed state. call before Begin() IMGUI_API void SetNextWindowFocus(); // set next window to be focused / front-most. call before Begin() IMGUI_API void SetNextWindowBgAlpha(float alpha); // set next window background color alpha. helper to easily modify ImGuiCol_WindowBg/ChildBg/PopupBg. + IMGUI_API void SetNextWindowViewport(ImGuiID viewport_id); // set next window viewport IMGUI_API void SetWindowPos(const ImVec2& pos, ImGuiCond cond = 0); // (not recommended) set current window position - call within Begin()/End(). prefer using SetNextWindowPos(), as this may incur tearing and side-effects. IMGUI_API void SetWindowSize(const ImVec2& size, ImGuiCond cond = 0); // (not recommended) set current window size - call within Begin()/End(). set to ImVec2(0,0) to force an auto-fit. prefer using SetNextWindowSize(), as this may incur tearing and minor side-effects. IMGUI_API void SetWindowCollapsed(bool collapsed, ImGuiCond cond = 0); // (not recommended) set current window collapsed state. prefer using SetNextWindowCollapsed(). @@ -1148,7 +1149,7 @@ namespace ImGui bool Begin(const char* name, bool* p_open, const ImVec2& size_on_first_use, float bg_alpha_override = -1.0f, ImGuiWindowFlags flags = 0); // Use SetNextWindowSize(size, ImGuiCond_FirstUseEver) + SetNextWindowBgAlpha() instead. static inline bool IsRootWindowOrAnyChildHovered() { return IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows); } static inline void AlignFirstTextHeightToWidgets() { AlignTextToFramePadding(); } - static inline void SetNextWindowPosCenter(ImGuiCond c=0) { ImGuiIO& io = GetIO(); SetNextWindowPos(ImVec2(io.DisplaySize.x * 0.5f, io.DisplaySize.y * 0.5f), c, ImVec2(0.5f, 0.5f)); } // FIXME-VIEWPORT-ABS: Select viewport based on mouse position + void SetNextWindowPosCenter(ImGuiCond cond); // OBSOLETED in 1.51 (between Jun 2017 and Aug 2017) static inline bool IsItemHoveredRect() { return IsItemHovered(ImGuiHoveredFlags_RectOnly); } static inline bool IsPosHoveringAnyWindow(const ImVec2&) { IM_ASSERT(0); return false; } // This was misleading and partly broken. You probably want to use the ImGui::GetIO().WantCaptureMouse flag instead. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 3c68d6f001c8..cf5c95643dfa 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2477,11 +2477,14 @@ static void ShowExampleAppFixedOverlay(bool* p_open) // FIXME-VIEWPORT-ABS: Select a default viewport const float DISTANCE = 10.0f; static int corner = 0; - ImGuiViewport* viewport = ImGui::GetMainViewport(); - ImVec2 window_pos = ImVec2((corner & 1) ? (viewport->Pos.x + viewport->Size.x - DISTANCE) : (viewport->Pos.x + DISTANCE), (corner & 2) ? (viewport->Pos.y + viewport->Size.y - DISTANCE) : (viewport->Pos.y + DISTANCE)); - ImVec2 window_pos_pivot = ImVec2((corner & 1) ? 1.0f : 0.0f, (corner & 2) ? 1.0f : 0.0f); if (corner != -1) + { + ImGuiViewport* viewport = ImGui::GetMainViewport(); + ImVec2 window_pos = ImVec2((corner & 1) ? (viewport->Pos.x + viewport->Size.x - DISTANCE) : (viewport->Pos.x + DISTANCE), (corner & 2) ? (viewport->Pos.y + viewport->Size.y - DISTANCE) : (viewport->Pos.y + DISTANCE)); + ImVec2 window_pos_pivot = ImVec2((corner & 1) ? 1.0f : 0.0f, (corner & 2) ? 1.0f : 0.0f); ImGui::SetNextWindowPos(window_pos, ImGuiCond_Always, window_pos_pivot); + ImGui::SetNextWindowViewport(viewport->ID); + } ImGui::SetNextWindowBgAlpha(0.3f); // Transparent background if (ImGui::Begin("Example: Fixed Overlay", p_open, (corner != -1 ? ImGuiWindowFlags_NoMove : 0) | ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_NoFocusOnAppearing|ImGuiWindowFlags_NoNav)) { diff --git a/imgui_internal.h b/imgui_internal.h index 5cc91ba942aa..c7bdad913b56 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1080,7 +1080,6 @@ namespace ImGui // Viewports IMGUI_API ImGuiViewportP* FindViewportByID(ImGuiID id); - IMGUI_API void SetNextWindowViewport(ImGuiID id); IMGUI_API void ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale); IMGUI_API void ShowViewportThumbnails(); From c7687fc1d69a878a3598c53eaf4b175cd15cc0ee Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 7 May 2018 10:44:08 +0200 Subject: [PATCH 150/828] Viewports: Comments on ImGuiConfigFlags_DpiEnableScaleViewports and ImGuiConfigFlags_DpiEnableScaleFonts. (#1542) --- imgui.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/imgui.h b/imgui.h index 5f57790d1726..a9950da6ccb6 100644 --- a/imgui.h +++ b/imgui.h @@ -799,8 +799,8 @@ enum ImGuiConfigFlags_ ImGuiConfigFlags_ViewportsEnable = 1 << 10, // Viewport enable flags (require both ImGuiConfigFlags_PlatformHasViewports + ImGuiConfigFlags_RendererHasViewports set by the respective back-ends) ImGuiConfigFlags_ViewportsNoTaskBarIcons = 1 << 11, // Disable task bars icons for all secondary viewports (will set ImGuiViewportFlags_NoTaskBarIcon on them) ImGuiConfigFlags_ViewportsNoMerge = 1 << 12, // All floating windows _always_ have create their own viewport and platform window. - ImGuiConfigFlags_DpiEnableScaleViewports = 1 << 13, - ImGuiConfigFlags_DpiEnableScaleFonts = 1 << 14, + ImGuiConfigFlags_DpiEnableScaleViewports = 1 << 13, // Reposition and resize imgui windows when the DpiScale of a viewport changed (mostly useful for the main viewport hosting other window). Note that resizing the main window itself is up to your application. + ImGuiConfigFlags_DpiEnableScaleFonts = 1 << 14, // FIXME-DPI: Request bitmap-scaled fonts to match DpiScale. This is a very low-quality workaround. The correct way to handle DPI is _currently_ to replace the atlas and/or fonts in the Platform_OnChangedViewport callback, but this is all early work in progress. // User storage (to allow your back-end/engine to communicate to code that may be shared between multiple projects. Those flags are not used by core ImGui) ImGuiConfigFlags_IsSRGB = 1 << 20, // Application is SRGB-aware. From 6eacddb50f267546d8b0f5bc9a51d239fe369603 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 10 May 2018 12:10:10 +0200 Subject: [PATCH 151/828] Viewport: Changed Monitor field to use Pos+Size (more consistent), changed FullMin,FullMax to MainPos,MainSize. Made main viewport accessible in PlatformIO on first frame. Fixed casing of ImGuiViewportFlags_TopMost flag. (#1542) --- examples/imgui_impl_glfw.cpp | 6 +++--- examples/imgui_impl_sdl2.cpp | 10 +++++----- examples/imgui_impl_win32.cpp | 10 +++++----- imgui.cpp | 28 +++++++++++++++------------- imgui.h | 9 +++++---- 5 files changed, 33 insertions(+), 30 deletions(-) diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index af984041e218..a8e9c8c8eb35 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -364,7 +364,7 @@ static void ImGui_ImplGlfw_CreateWindow(ImGuiViewport* viewport) glfwWindowHint(GLFW_FOCUSED, false); glfwWindowHint(GLFW_DECORATED, (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? false : true); #if GLFW_HAS_WINDOW_TOPMOST - glfwWindowHint(GLFW_FLOATING, (viewport->Flags & imGuiViewportFlags_TopMost) ? true : false); + glfwWindowHint(GLFW_FLOATING, (viewport->Flags & ImGuiViewportFlags_TopMost) ? true : false); #endif GLFWwindow* share_window = (g_ClientApi == GlfwClientApi_OpenGL) ? g_Window : NULL; data->Window = glfwCreateWindow((int)viewport->Size.x, (int)viewport->Size.y, "No Title Yet", NULL, share_window); @@ -579,8 +579,8 @@ static void ImGui_ImplGlfw_UpdateMonitors() int x, y; glfwGetMonitorPos(glfw_monitors[n], &x, &y); const GLFWvidmode* vid_mode = glfwGetVideoMode(glfw_monitors[n]); - monitor.FullMin = monitor.WorkMin = ImVec2((float)x, (float)y); - monitor.FullMax = monitor.WorkMax = ImVec2((float)(x + vid_mode->width), (float)(y + vid_mode->height)); + monitor.MainPos = monitor.WorkPos = ImVec2((float)x, (float)y); + monitor.MainSize = monitor.WorkSize = ImVec2((float)vid_mode->width, (float)vid_mode->height); #if GLFW_HAS_PER_MONITOR_DPI // Warning: the validity of monitor DPI information on Windows depends on the application DPI awareness settings, which generally needs to be set in the manifest or at runtime. float x_scale, y_scale; diff --git a/examples/imgui_impl_sdl2.cpp b/examples/imgui_impl_sdl2.cpp index e33c2a184b33..8234fcf85526 100644 --- a/examples/imgui_impl_sdl2.cpp +++ b/examples/imgui_impl_sdl2.cpp @@ -317,7 +317,7 @@ static void ImGui_ImplSDL2_CreateWindow(ImGuiViewport* viewport) sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? SDL_WINDOW_BORDERLESS : 0; sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? 0 : SDL_WINDOW_RESIZABLE; #if SDL_HAS_ALWAYS_ON_TOP - sdl_flags |= (viewport->Flags & imGuiViewportFlags_TopMost) ? SDL_WINDOW_ALWAYS_ON_TOP : 0; + sdl_flags |= (viewport->Flags & ImGuiViewportFlags_TopMost) ? SDL_WINDOW_ALWAYS_ON_TOP : 0; #endif data->Window = SDL_CreateWindow("No Title Yet", (int)viewport->Pos.x, (int)viewport->Pos.y, (int)viewport->Size.x, (int)viewport->Size.y, sdl_flags); data->WindowOwned = true; @@ -463,12 +463,12 @@ static void ImGui_ImplSDL2_UpdateMonitors() ImGuiPlatformMonitor monitor; SDL_Rect r; SDL_GetDisplayBounds(n, &r); - monitor.FullMin = monitor.WorkMin = ImVec2((float)(r.x), (float)(r.y)); - monitor.FullMax = monitor.WorkMax = ImVec2((float)(r.x + r.w), (float)(r.y + r.h)); + monitor.MainPos = monitor.WorkPos = ImVec2((float)r.x, (float)r.y); + monitor.MainSize = monitor.WorkSize = ImVec2((float)r.w, (float)r.h); #if SDL_HAS_USABLE_DISPLAY_BOUNDS SDL_GetDisplayUsableBounds(n, &r); - monitor.WorkMin = ImVec2((float)(r.x), (float)(r.y)); - monitor.WorkMax = ImVec2((float)(r.x + r.w), (float)(r.y + r.h)); + monitor.WorkPos = ImVec2((float)r.x, (float)r.y); + monitor.WorkSize = ImVec2((float)r.w, (float)r.h); #endif #if SDL_HAS_PER_MONITOR_DPI float dpi = 0.0f; diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index 76c5b76b11b9..097c5dbc3670 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -431,7 +431,7 @@ static void ImGui_ImplWin32_CreateWindow(ImGuiViewport* viewport) data->DwStyle = WS_OVERLAPPEDWINDOW; data->DwExStyle = no_task_bar_icon ? WS_EX_TOOLWINDOW : WS_EX_APPWINDOW; } - if (viewport->Flags & imGuiViewportFlags_TopMost) + if (viewport->Flags & ImGuiViewportFlags_TopMost) data->DwExStyle |= WS_EX_TOPMOST; // Create window @@ -618,10 +618,10 @@ static BOOL CALLBACK ImGui_ImplWin32_UpdateMonitors_EnumFunc(HMONITOR monitor, H if (!::GetMonitorInfo(monitor, &info)) return TRUE; ImGuiPlatformMonitor imgui_monitor; - imgui_monitor.FullMin = ImVec2((float)info.rcMonitor.left, (float)info.rcMonitor.top); - imgui_monitor.FullMax = ImVec2((float)info.rcMonitor.right, (float)info.rcMonitor.bottom); - imgui_monitor.WorkMin = ImVec2((float)info.rcWork.left, (float)info.rcWork.top); - imgui_monitor.WorkMax = ImVec2((float)info.rcWork.right, (float)info.rcWork.bottom); + imgui_monitor.MainPos = ImVec2((float)info.rcMonitor.left, (float)info.rcMonitor.top); + imgui_monitor.MainSize = ImVec2((float)(info.rcMonitor.right - info.rcMonitor.left), (float)(info.rcMonitor.bottom - info.rcMonitor.top)); + imgui_monitor.WorkPos = ImVec2((float)info.rcWork.left, (float)info.rcWork.top); + imgui_monitor.WorkSize = ImVec2((float)(info.rcWork.right - info.rcWork.left), (float)(info.rcWork.bottom - info.rcWork.top)); imgui_monitor.DpiScale = ImGui_ImplWin32_GetDpiScaleForMonitor(monitor); ImGuiPlatformIO& io = ImGui::GetPlatformIO(); if (info.dwFlags & MONITORINFOF_PRIMARY) diff --git a/imgui.cpp b/imgui.cpp index 832108c144b1..b18277461536 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3620,7 +3620,7 @@ void ImGui::UpdatePlatformWindows() { bool topmost = (viewport->Window->Flags & ImGuiWindowFlags_Tooltip) != 0; bool no_task_bar_icon = (g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsNoTaskBarIcons) != 0 || (viewport->Window->Flags & (ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) != 0; - viewport->Flags = topmost ? (viewport->Flags | imGuiViewportFlags_TopMost) : (viewport->Flags & ~imGuiViewportFlags_TopMost); + viewport->Flags = topmost ? (viewport->Flags | ImGuiViewportFlags_TopMost) : (viewport->Flags & ~ImGuiViewportFlags_TopMost); viewport->Flags = no_task_bar_icon ? (viewport->Flags | ImGuiViewportFlags_NoTaskBarIcon) : (viewport->Flags & ~ImGuiViewportFlags_NoTaskBarIcon); } @@ -3881,8 +3881,8 @@ void ImGui::NewFrame() for (int monitor_n = 0; monitor_n < g.PlatformIO.Monitors.Size; monitor_n++) { ImGuiPlatformMonitor& mon = g.PlatformIO.Monitors[monitor_n]; - IM_ASSERT(mon.FullMin.x < mon.FullMax.x && mon.FullMin.y < mon.FullMax.y && "Monitor bounds not setup properly."); - IM_ASSERT(mon.WorkMin.x < mon.WorkMax.x && mon.WorkMin.y < mon.WorkMax.y && "Monitor bounds not setup properly. If you don't have work area information, just copy Min/Max into them."); + IM_ASSERT(mon.MainSize.x > 0.0f && mon.MainSize.y > 0.0f && "Monitor bounds not setup properly."); + IM_ASSERT(mon.WorkSize.x > 0.0f && mon.WorkSize.y > 0.0f && "Monitor bounds not setup properly. If you don't have work area information, just copy Min/Max into them."); IM_ASSERT(mon.DpiScale != 0.0f); } } @@ -4152,6 +4152,8 @@ void ImGui::Initialize(ImGuiContext* context) viewport->ID = IMGUI_VIEWPORT_DEFAULT_ID; viewport->Idx = 0; g.Viewports.push_back(viewport); + g.PlatformIO.MainViewport = g.Viewports[0]; // Make it accessible in public-facing GetPlatformIO() immediately (before the first call to EndFrame) + g.PlatformIO.Viewports.push_back(g.Viewports[0]); g.Initialized = true; } @@ -5936,8 +5938,8 @@ static ImRect FindAllowedExtentRectForWindow(ImGuiWindow* window) { // Extent with be in the frame of reference of the given viewport (so Min is likely to be negative here) const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[window->ViewportAllowPlatformMonitorExtend]; - r_screen.Min = monitor.WorkMin; - r_screen.Max = monitor.WorkMax; + r_screen.Min = monitor.WorkPos; + r_screen.Max = monitor.WorkPos + monitor.WorkSize; } else { @@ -6119,7 +6121,7 @@ static ImVec2 CalcSizeAutoFit(ImGuiWindow* window, const ImVec2& size_contents) avail_size = ImVec2(FLT_MAX, FLT_MAX); const int monitor_idx = window->ViewportAllowPlatformMonitorExtend; if (monitor_idx >= 0 && monitor_idx < g.PlatformIO.Monitors.Size) - avail_size = (g.PlatformIO.Monitors[monitor_idx].WorkMax - g.PlatformIO.Monitors[monitor_idx].WorkMin); + avail_size = g.PlatformIO.Monitors[monitor_idx].WorkSize; ImVec2 size_auto_fit = ImClamp(size_contents, style.WindowMinSize, ImMax(style.WindowMinSize, avail_size - g.Style.DisplaySafeAreaPadding * 2.0f)); // When the window cannot fit all contents (either because of constraints, either because screen is too small), @@ -6190,7 +6192,7 @@ static int ImGui::FindPlatformMonitorForPos(const ImVec2& pos) for (int monitor_n = 0; monitor_n < g.PlatformIO.Monitors.Size; monitor_n++) { const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[monitor_n]; - if (ImRect(monitor.FullMin, monitor.FullMax).Contains(pos)) + if (ImRect(monitor.MainPos, monitor.MainPos + monitor.MainSize).Contains(pos)) return monitor_n; } return -1; @@ -6207,10 +6209,10 @@ static int ImGui::FindPlatformMonitorForRect(const ImRect& rect) for (int monitor_n = 0; monitor_n < g.PlatformIO.Monitors.Size && best_monitor_surface < surface_threshold; monitor_n++) { const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[monitor_n]; - if (ImRect(monitor.FullMin, monitor.FullMax).Contains(rect)) + if (ImRect(monitor.MainPos, monitor.MainPos + monitor.MainSize).Contains(rect)) return monitor_n; ImRect overlapping_rect = rect; - overlapping_rect.ClipWithFull(ImRect(monitor.FullMin, monitor.FullMax)); + overlapping_rect.ClipWithFull(ImRect(monitor.MainPos, monitor.MainPos + monitor.MainSize)); float overlapping_surface = overlapping_rect.GetWidth() * overlapping_rect.GetHeight(); if (overlapping_surface < best_monitor_surface) continue; @@ -6796,7 +6798,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) else { ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[window->Viewport->PlatformMonitor]; - ClampWindowRect(window, ImRect(monitor.WorkMin, monitor.WorkMax), clamp_padding); + ClampWindowRect(window, ImRect(monitor.WorkPos, monitor.WorkPos + monitor.WorkSize), clamp_padding); } } } @@ -14401,10 +14403,10 @@ void ImGui::ShowMetricsWindow(bool* p_open) for (int i = 0; i < g.PlatformIO.Monitors.Size; i++) { const ImGuiPlatformMonitor& mon = g.PlatformIO.Monitors[i]; - ImGui::BulletText("Monitor #%d: DPI %.0f%%\n FullMin (%.0f,%.0f), FullMax (%.0f,%.0f), FullSize (%.0f,%.0f)\n WorkMin (%.0f,%.0f), WorkMax (%.0f,%.0f), WorkSize (%.0f,%.0f)", + ImGui::BulletText("Monitor #%d: DPI %.0f%%\n MainMin (%.0f,%.0f), MainMax (%.0f,%.0f), MainSize (%.0f,%.0f)\n WorkMin (%.0f,%.0f), WorkMax (%.0f,%.0f), WorkSize (%.0f,%.0f)", i, mon.DpiScale * 100.0f, - mon.FullMin.x, mon.FullMin.y, mon.FullMax.x, mon.FullMax.y, mon.FullMax.x - mon.FullMin.x, mon.FullMax.y - mon.FullMin.y, - mon.WorkMin.x, mon.WorkMin.y, mon.WorkMax.x, mon.WorkMax.y, mon.WorkMax.x - mon.WorkMin.x, mon.WorkMax.y - mon.WorkMin.y); + mon.MainPos.x, mon.MainPos.y, mon.MainPos.x + mon.MainSize.x, mon.MainPos.y + mon.MainSize.y, mon.MainSize.x, mon.MainSize.y, + mon.WorkPos.x, mon.WorkPos.y, mon.WorkPos.x + mon.WorkSize.x, mon.WorkPos.y + mon.WorkSize.y, mon.WorkSize.x, mon.WorkSize.y); } ImGui::TreePop(); } diff --git a/imgui.h b/imgui.h index 38b776a3c151..d2b5faa34db0 100644 --- a/imgui.h +++ b/imgui.h @@ -74,6 +74,7 @@ struct ImGuiListClipper; // Helper to manually clip large list of ite struct ImGuiPayload; // User data payload for drag and drop operations struct ImGuiViewport; // Viewport (generally ~1 per window to output to at the OS level. Need per-platform support to use multiple viewports) struct ImGuiPlatformIO; // Multi-viewport support: interface for Platform/Renderer back-ends + viewports to render +struct ImGuiPlatformMonitor; // Multi-viewport support: user-provided bounds for each connected monitor/display. Used when positioning popups and tooltips to avoid them straddling monitors struct ImGuiContext; // ImGui context (opaque) #ifndef ImTextureID @@ -1890,10 +1891,10 @@ struct ImFont // Dear ImGui only uses this to clamp the position of popups and tooltips so they don't straddle multiple monitors struct ImGuiPlatformMonitor { - ImVec2 FullMin, FullMax; // Coordinates of the area displayed on this monitor (Min = upper left, Max = bottom right) - ImVec2 WorkMin, WorkMax; // (Optional) Coordinates without task bars / side bars / menu bars. imgui uses this to avoid positioning popups/tooltips inside this region. + ImVec2 MainPos, MainSize; // Coordinates of the area displayed on this monitor (Min = upper left, Max = bottom right) + ImVec2 WorkPos, WorkSize; // (Optional) Coordinates without task bars / side bars / menu bars. imgui uses this to avoid positioning popups/tooltips inside this region. float DpiScale; - ImGuiPlatformMonitor() { FullMin = FullMax = WorkMin = WorkMax = ImVec2(0,0); DpiScale = 1.0f; } + ImGuiPlatformMonitor() { MainPos = MainSize = WorkPos = WorkSize = ImVec2(0,0); DpiScale = 1.0f; } }; // (Optional) Setup required only if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) is enabled @@ -1956,7 +1957,7 @@ enum ImGuiViewportFlags_ ImGuiViewportFlags_NoInputs = 1 << 2, // Platform Window: Make mouse pass through so we can drag this window while peaking behind it. ImGuiViewportFlags_NoTaskBarIcon = 1 << 3, // Platform Window: Disable platform task bar icon (for popups, menus, or all windows if ImGuiConfigFlags_ViewportsNoTaskBarIcons if set) ImGuiViewportFlags_NoRendererClear = 1 << 4, // Platform Window: Renderer doesn't need to clear the framebuffer ahead. - imGuiViewportFlags_TopMost = 1 << 5 // Platform Window: Display on top (for tooltips only) + ImGuiViewportFlags_TopMost = 1 << 5 // Platform Window: Display on top (for tooltips only) }; // The viewports created and managed by imgui. The role of the platform back-end is to create the platform/OS windows corresponding to each viewport. From d574604a5de563eb60a990f5117de0ba488e3cb9 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 11 May 2018 14:05:40 +0200 Subject: [PATCH 152/828] Viewport, Platform: Win32: Fixed handling of io.WantSetMousePos + added a bunch of comments. GLFW, SDL2: Added handling of io.WantSetMousePos. (#1542) --- examples/imgui_impl_glfw.cpp | 22 ++++++------ examples/imgui_impl_sdl2.cpp | 17 +++++++++ examples/imgui_impl_win32.cpp | 65 +++++++++++++++++++---------------- 3 files changed, 64 insertions(+), 40 deletions(-) diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index a8e9c8c8eb35..88f61d1e9b3b 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -120,7 +120,7 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw // Setup back-end capabilities flags ImGuiIO& io = ImGui::GetIO(); io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) - //io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) + io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional) #if GLFW_HAS_GLFW_HOVERED io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can set io.MouseHoveredViewport correctly (optional, not easy) @@ -198,13 +198,8 @@ void ImGui_ImplGlfw_Shutdown() static void ImGui_ImplGlfw_UpdateMouse() { -#if 0 - // Set OS mouse position if requested (only used when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user) - if (io.WantSetMousePos) - glfwSetCursorPos(g_Window, (double)io.MousePos.x, (double)io.MousePos.y); -#endif - ImGuiIO& io = ImGui::GetIO(); + const ImVec2 mouse_pos_backup = io.MousePos; io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); io.MousePosViewport = 0; io.MouseHoveredViewport = 0; @@ -225,9 +220,16 @@ static void ImGui_ImplGlfw_UpdateMouse() IM_ASSERT(window != NULL); if (glfwGetWindowAttrib(window, GLFW_FOCUSED)) { - double mouse_x, mouse_y; - glfwGetCursorPos(window, &mouse_x, &mouse_y); - io.MousePos = ImVec2((float)mouse_x + viewport->Pos.x, (float)mouse_y + viewport->Pos.y); + if (io.WantSetMousePos) + { + glfwSetCursorPos(window, (double)(mouse_pos_backup.x - viewport->Pos.x), (double)(mouse_pos_backup.y - viewport->Pos.y)); + } + else + { + double mouse_x, mouse_y; + glfwGetCursorPos(window, &mouse_x, &mouse_y); + io.MousePos = ImVec2((float)mouse_x + viewport->Pos.x, (float)mouse_y + viewport->Pos.y); + } io.MousePosViewport = viewport->ID; for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) io.MouseDown[i] |= glfwGetMouseButton(window, i) != 0; diff --git a/examples/imgui_impl_sdl2.cpp b/examples/imgui_impl_sdl2.cpp index 8234fcf85526..5d1532494618 100644 --- a/examples/imgui_impl_sdl2.cpp +++ b/examples/imgui_impl_sdl2.cpp @@ -33,6 +33,7 @@ // (the multi-viewports feature requires SDL features supported from SDL 2.0.5+) #include #include +#define SDL_HAS_WARP_MOUSE_GLOBAL SDL_VERSION_ATLEAST(2,0,4) #define SDL_HAS_CAPTURE_MOUSE SDL_VERSION_ATLEAST(2,0,4) #define SDL_HAS_WINDOW_OPACITY SDL_VERSION_ATLEAST(2,0,5) #define SDL_HAS_ALWAYS_ON_TOP SDL_VERSION_ATLEAST(2,0,5) @@ -131,6 +132,9 @@ bool ImGui_ImplSDL2_Init(SDL_Window* window, void* sdl_gl_context) // Setup back-end capabilities flags ImGuiIO& io = ImGui::GetIO(); io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) +#if SDL_HAS_WARP_MOUSE_GLOBAL + io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) +#endif #if SDL_HAS_CAPTURE_MOUSE io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional) #endif @@ -196,10 +200,23 @@ void ImGui_ImplSDL2_Shutdown() static void ImGui_ImplSDL2_UpdateMouse() { ImGuiIO& io = ImGui::GetIO(); + const ImVec2 mouse_pos_backup = io.MousePos; io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); io.MousePosViewport = 0; io.MouseHoveredViewport = 0; + // Set OS mouse position if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user) + // (When multi-viewports are enabled, all imgui positions are same as OS positions.) +#if SDL_HAS_WARP_MOUSE_GLOBAL + if (io.WantSetMousePos) + { + if ((io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) == 0) + SDL_WarpMouseInWindow(g_Window, (int)mouse_pos_backup.x, (int)mouse_pos_backup.y); + else + SDL_WarpMouseGlobal((int)mouse_pos_backup.x, (int)mouse_pos_backup.y); + } +#endif + int mx, my; Uint32 mouse_buttons = SDL_GetMouseState(&mx, &my); io.MouseDown[0] = g_MousePressed[0] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_LEFT)) != 0; // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame. diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index 097c5dbc3670..95c92e8e0b77 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -120,38 +120,53 @@ static bool ImGui_ImplWin32_UpdateMouseCursor() } // This code supports multiple OS Windows mapped into different ImGui viewports, -// So it is a little more complicated than your typical single-viewport binding code (which only needs to set io.MousePos from the WM_MOUSEMOVE handler) -// This is what imgui needs from the back-end to support multiple windows: -// - io.MousePos = mouse position in absolute coordinate (e.g. io.MousePos == ImVec2(0,0) when it is on the upper-left of the primary monitor) -// - io.MousePosViewport = viewport which mouse position is based from (generally the focused/active/capturing viewport) -// - io.MouseHoveredWindow = viewport which mouse is hovering, **regardless of it being the active/focused window**, **regardless of another window holding mouse captured**. [Optional] +// Because of that, it is a little more complicated than your typical single-viewport binding code. +// A) In Single-viewport mode imgui needs: +// - io.MousePos ............... mouse position, in client window coordinates (what you'd get from GetCursorPos+ScreenToClient() or from WM_MOUSEMOVE) +// io.MousePos is (0,0) when the mouse is on the upper-left corner of the application window. +// B) In Multi-viewport mode imgui needs: (when ImGuiConfigFlags_ViewportsEnable is set) +// - io.MousePos ............... mouse position, in OS absolute coordinates (what you'd get from GetCursorPos(), or from WM_MOUSEMOVE+viewport->Pos). +// io.MousePos is (0,0) when the mouse is on the upper-left of the primary monitor. +// - io.MousePosViewport ....... viewport which mouse position is based from (generally the focused/active/capturing viewport) +// - io.MouseHoveredViewport ... [optional] viewport which mouse is hovering, with _very_ specific/strict conditions (Read comments next to io.MouseHoveredViewport. This is _NOT_ easy to provide in many high-level engine because of how we handle the ImGuiViewportFlags_NoInputs flag) // This function overwrite the value of io.MousePos normally updated by the WM_MOUSEMOVE handler. -// We keep the WM_MOUSEMOVE handling code so that WndProc function can be copied as-in in applications which do not need multiple OS windows support. +// We keep the WM_MOUSEMOVE handling code so that WndProc function can be copied as-in in applications which do not need multi-viewport support. static void ImGui_ImplWin32_UpdateMousePos() { ImGuiIO& io = ImGui::GetIO(); + + // Set OS mouse position if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user) + // (When multi-viewports are enabled, all imgui positions are same as OS positions.) + if (io.WantSetMousePos) + { + POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y }; + if ((io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) == 0) + ::ClientToScreen(g_hWnd, &pos); + ::SetCursorPos(pos.x, pos.y); + } + io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); io.MousePosViewport = 0; io.MouseHoveredViewport = 0; + // Set mouse position and viewport + // (Note that ScreenToClient() and adding +viewport->Pos are mutually cancelling each others when we have multi-viewport enabled. In single-viewport mode, viewport->Pos will be zero) POINT pos; if (!::GetCursorPos(&pos)) return; - - // Our back-end can tell which window is under the mouse cursor (not every back-end can), so pass that info to imgui - HWND hovered_hwnd = ::WindowFromPoint(pos); - if (hovered_hwnd) - if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle((void*)hovered_hwnd)) - io.MouseHoveredViewport = viewport->ID; - - // Convert mouse from screen position to window client position - HWND focused_hwnd = ::GetActiveWindow(); - if (focused_hwnd != 0 && ::ScreenToClient(focused_hwnd, &pos)) + if (HWND focused_hwnd = ::GetActiveWindow()) if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle((void*)focused_hwnd)) { - io.MousePos = ImVec2(viewport->Pos.x + (float)pos.x, viewport->Pos.y + (float)pos.y); + POINT client_pos = pos; + ::ScreenToClient(focused_hwnd, &client_pos); + io.MousePos = ImVec2(viewport->Pos.x + (float)client_pos.x, viewport->Pos.y + (float)client_pos.y); io.MousePosViewport = viewport->ID; } + + // Our back-end can tell which window is under the mouse cursor (not every back-end can), so pass that info to imgui + if (HWND hovered_hwnd = ::WindowFromPoint(pos)) + if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle((void*)hovered_hwnd)) + io.MouseHoveredViewport = viewport->ID; } void ImGui_ImplWin32_NewFrame() @@ -176,18 +191,10 @@ void ImGui_ImplWin32_NewFrame() io.KeyShift = (::GetKeyState(VK_SHIFT) & 0x8000) != 0; io.KeyAlt = (::GetKeyState(VK_MENU) & 0x8000) != 0; io.KeySuper = false; - // io.KeysDown : filled by WM_KEYDOWN/WM_KEYUP events - // io.MousePos : filled by WM_MOUSEMOVE events - // io.MouseDown : filled by WM_*BUTTON* events - // io.MouseWheel : filled by WM_MOUSEWHEEL events + // io.KeysDown[], io.MousePos, io.MouseDown[], io.MouseWheel: filled by the WndProc handler below. - // Set OS mouse position if requested (only used when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user) - if (io.WantSetMousePos) - { - POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y }; - ::ClientToScreen(g_hWnd, &pos); - ::SetCursorPos(pos.x, pos.y); - } + // Update OS mouse position + ImGui_ImplWin32_UpdateMousePos(); // Update OS mouse cursor with the cursor requested by imgui ImGuiMouseCursor mouse_cursor = io.MouseDrawCursor ? ImGuiMouseCursor_None : ImGui::GetMouseCursor(); @@ -197,8 +204,6 @@ void ImGui_ImplWin32_NewFrame() ImGui_ImplWin32_UpdateMouseCursor(); } - ImGui_ImplWin32_UpdateMousePos(); - // Start the frame. This call will update the io.WantCaptureMouse, io.WantCaptureKeyboard flag that you can use to dispatch inputs (or not) to your application. ImGui::NewFrame(); } From 090eb437ed973603fb99beb25e4500c04499bbfb Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 11 May 2018 16:41:32 +0200 Subject: [PATCH 153/828] Viewport, Platform: Cleaned up xxx_UpdateMouseCursor() functions to make them more consistent. (#1542) --- examples/imgui_impl_glfw.cpp | 41 ++++++++++++++++++++--------------- examples/imgui_impl_sdl2.cpp | 34 +++++++++++++++++------------ examples/imgui_impl_win32.cpp | 6 ++--- 3 files changed, 47 insertions(+), 34 deletions(-) diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index 88f61d1e9b3b..36d500ff6740 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -196,7 +196,7 @@ void ImGui_ImplGlfw_Shutdown() g_ClientApi = GlfwClientApi_Unknown; } -static void ImGui_ImplGlfw_UpdateMouse() +static void ImGui_ImplGlfw_UpdateMousePosAndButtons() { ImGuiIO& io = ImGui::GetIO(); const ImVec2 mouse_pos_backup = io.MousePos; @@ -241,24 +241,30 @@ static void ImGui_ImplGlfw_UpdateMouse() io.MouseHoveredViewport = viewport->ID; #endif } +} + +static void ImGui_ImplGlfw_UpdateMouseCursor() +{ + ImGuiIO& io = ImGui::GetIO(); + if ((io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) || glfwGetInputMode(g_Window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED) + return; - // Update OS/hardware mouse cursor if imgui isn't drawing a software cursor - // FIXME-PLATFORM: Unfocused windows seems to fail changing the mouse cursor with GLFW 3.2, but 3.3 works here. - if ((io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) == 0 && glfwGetInputMode(g_Window, GLFW_CURSOR) != GLFW_CURSOR_DISABLED) + ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor(); + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + for (int n = 0; n < platform_io.Viewports.Size; n++) { - ImGuiMouseCursor cursor = ImGui::GetMouseCursor(); - for (int n = 0; n < platform_io.Viewports.Size; n++) + GLFWwindow* window = (GLFWwindow*)platform_io.Viewports[n]->PlatformHandle; + if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor) { - GLFWwindow* window = (GLFWwindow*)platform_io.Viewports[n]->PlatformHandle; - if (io.MouseDrawCursor || cursor == ImGuiMouseCursor_None) - { - glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); - } - else - { - glfwSetCursor(window, g_MouseCursors[cursor] ? g_MouseCursors[cursor] : g_MouseCursors[ImGuiMouseCursor_Arrow]); - glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); - } + // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); + } + else + { + // Show OS mouse cursor + // FIXME-PLATFORM: Unfocused windows seems to fail changing the mouse cursor with GLFW 3.2, but 3.3 works here. + glfwSetCursor(window, g_MouseCursors[imgui_cursor] ? g_MouseCursors[imgui_cursor] : g_MouseCursors[ImGuiMouseCursor_Arrow]); + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); } } } @@ -283,7 +289,8 @@ void ImGui_ImplGlfw_NewFrame() io.DeltaTime = g_Time > 0.0 ? (float)(current_time - g_Time) : (float)(1.0f/60.0f); g_Time = current_time; - ImGui_ImplGlfw_UpdateMouse(); + ImGui_ImplGlfw_UpdateMousePosAndButtons(); + ImGui_ImplGlfw_UpdateMouseCursor(); // Gamepad navigation mapping [BETA] memset(io.NavInputs, 0, sizeof(io.NavInputs)); diff --git a/examples/imgui_impl_sdl2.cpp b/examples/imgui_impl_sdl2.cpp index 5d1532494618..abf034e7393d 100644 --- a/examples/imgui_impl_sdl2.cpp +++ b/examples/imgui_impl_sdl2.cpp @@ -197,7 +197,7 @@ void ImGui_ImplSDL2_Shutdown() memset(g_MouseCursors, 0, sizeof(g_MouseCursors)); } -static void ImGui_ImplSDL2_UpdateMouse() +static void ImGui_ImplSDL2_UpdateMousePosAndButtons() { ImGuiIO& io = ImGui::GetIO(); const ImVec2 mouse_pos_backup = io.MousePos; @@ -250,20 +250,25 @@ static void ImGui_ImplSDL2_UpdateMouse() if (SDL_GetWindowFlags(g_Window) & SDL_WINDOW_INPUT_FOCUS) io.MousePos = ImVec2((float)mx, (float)my); #endif +} + +static void ImGui_ImplSDL2_UpdateMouseCursor() +{ + ImGuiIO& io = ImGui::GetIO(); + if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) + return; - // Update OS/hardware mouse cursor if imgui isn't drawing a software cursor - if ((io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) == 0) + ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor(); + if (io.MouseDrawCursor || imgui_cursor == ImGuiMouseCursor_None) { - ImGuiMouseCursor cursor = ImGui::GetMouseCursor(); - if (io.MouseDrawCursor || cursor == ImGuiMouseCursor_None) - { - SDL_ShowCursor(SDL_FALSE); - } - else - { - SDL_SetCursor(g_MouseCursors[cursor] ? g_MouseCursors[cursor] : g_MouseCursors[ImGuiMouseCursor_Arrow]); - SDL_ShowCursor(SDL_TRUE); - } + // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor + SDL_ShowCursor(SDL_FALSE); + } + else + { + // Show OS mouse cursor + SDL_SetCursor(g_MouseCursors[imgui_cursor] ? g_MouseCursors[imgui_cursor] : g_MouseCursors[ImGuiMouseCursor_Arrow]); + SDL_ShowCursor(SDL_TRUE); } } @@ -286,7 +291,8 @@ void ImGui_ImplSDL2_NewFrame(SDL_Window* window) io.DeltaTime = g_Time > 0 ? (float)((double)(current_time - g_Time) / frequency) : (float)(1.0f / 60.0f); g_Time = current_time; - ImGui_ImplSDL2_UpdateMouse(); + ImGui_ImplSDL2_UpdateMousePosAndButtons(); + ImGui_ImplSDL2_UpdateMouseCursor(); // Start the frame. This call will update the io.WantCaptureMouse, io.WantCaptureKeyboard flag that you can use to dispatch inputs (or not) to your application. ImGui::NewFrame(); diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index 95c92e8e0b77..f43ebd0c2b92 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -94,15 +94,15 @@ static bool ImGui_ImplWin32_UpdateMouseCursor() if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) return false; - ImGuiMouseCursor imgui_cursor = io.MouseDrawCursor ? ImGuiMouseCursor_None : ImGui::GetMouseCursor(); - if (imgui_cursor == ImGuiMouseCursor_None) + ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor(); + if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor) { // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor ::SetCursor(NULL); } else { - // Hardware cursor type + // Show OS mouse cursor LPTSTR win32_cursor = IDC_ARROW; switch (imgui_cursor) { From 1cafdb5b46a366d566e4f430e8b093b5c2bd1352 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 15 May 2018 11:04:07 +0200 Subject: [PATCH 154/828] Viewport: Added GetWindowViewport() to query the current viewport for the current window. Comments. (#1542) --- imgui.cpp | 12 ++++++++++++ imgui.h | 8 +++++--- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 1898d9280edf..5c50f7201938 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -264,6 +264,11 @@ You can read releases logs https://github.com/ocornut/imgui/releases for more details. (Viewport Branch) + - 2018/XX/XX (1.XX) - when multi-viewports are enabled, all positions will be in your natural OS coordinates space. It means that: + - reference to hard-coded positions such as in SetNextWindowPos(ImVec2(0,0)) are probably not what you want anymore. + you may use GetMainViewport()->Pos to offset hard-coded positions, e.g. SetNextWindowPos(GetMainViewport()->Pos) + - likewise io.MousePos and GetMousePos() will use OS coordinates coordinates. + If you query mouse positions to interact with non-imgui coordinates you will need to offset them, e.g. subtract GetWindowViewport()->Pos. - 2018/XX/XX (1.XX) - Moved IME support functions from io.ImeSetInputScreenPosFn, io.ImeWindowHandle to the PlatformIO api. - 2018/XX/XX (1.XX) - removed io.DisplayVisibleMin, io.DisplayVisibleMax settings (it was used to clip within the DisplayMin..DisplayMax range, I don't know of anyone using it) @@ -8125,6 +8130,13 @@ ImDrawList* ImGui::GetWindowDrawList() return window->DrawList; } +ImGuiViewport* ImGui::GetWindowViewport() +{ + ImGuiWindow* window = GetCurrentWindow(); + IM_ASSERT(window->Viewport != NULL); + return window->Viewport; +} + ImFont* ImGui::GetFont() { return GImGui->Font; diff --git a/imgui.h b/imgui.h index 6aa9b8a96d7f..8c07ed33e88a 100644 --- a/imgui.h +++ b/imgui.h @@ -196,7 +196,8 @@ namespace ImGui IMGUI_API bool IsWindowCollapsed(); IMGUI_API bool IsWindowFocused(ImGuiFocusedFlags flags=0); // is current window focused? or its root/child, depending on flags. see flags for options. IMGUI_API bool IsWindowHovered(ImGuiHoveredFlags flags=0); // is current window hovered (and typically: not blocked by a popup/modal)? see flags for options. NB: If you are trying to check whether your mouse should be dispatched to imgui or to your app, you should use the 'io.WantCaptureMouse' boolean for that! Please read the FAQ! - IMGUI_API ImDrawList* GetWindowDrawList(); // get draw list associated to the window, to append your own drawing primitives + IMGUI_API ImDrawList* GetWindowDrawList(); // get draw list associated to the current window, to append your own drawing primitives + IMGUI_API ImGuiViewport*GetWindowViewport(); // get viewport currently associated to the current window. IMGUI_API ImVec2 GetWindowPos(); // get current window position in screen space (useful if you want to do your own drawing via the DrawList API) IMGUI_API ImVec2 GetWindowSize(); // get current window size IMGUI_API float GetWindowWidth(); // get current window width (shortcut for GetWindowSize().x) @@ -580,8 +581,9 @@ namespace ImGui IMGUI_API void MemFree(void* ptr); // (Optional) Platform/OS interface for multi-viewport support + // Note: You may use GetWindowViewport() to get the current viewport of the current window. IMGUI_API ImGuiPlatformIO& GetPlatformIO(); // platform/renderer functions, for back-end to setup + viewports list. - IMGUI_API ImGuiViewport* GetMainViewport(); // shortcut to == GetPlatformIO().MainViewport == GetPlatformIO().Viewports[0] + IMGUI_API ImGuiViewport* GetMainViewport(); // main viewport. same as GetPlatformIO().MainViewport == GetPlatformIO().Viewports[0]. IMGUI_API void UpdatePlatformWindows(); // call in main loop. will call CreateWindow/ResizeWindow/etc. platform functions for each secondary viewport, and DestroyWindow for each inactive viewport. IMGUI_API void RenderPlatformWindowsDefault(void* platform_arg = NULL, void* renderer_arg = NULL); // call in main loop. will call RenderWindow/SwapBuffers platform functions for each secondary viewport. may be reimplemented by user for custom rendering needs. IMGUI_API void DestroyPlatformWindows(); // call DestroyWindow platform functions for all viewports. call from back-end Shutdown() if you need to close platform windows before imgui shutdown. otherwise will be called by DestroyContext(). @@ -1971,7 +1973,7 @@ struct ImGuiPlatformIO // Output - List of viewports to render into platform windows //------------------------------------------------------------------ - // List of viewports (updated by ImGui::UpdatePlatformWindows() along when calling the Platform/Renderer functions) + // List of viewports (the list is updated by calling ImGui::EndFrame or ImGui::Render) ImGuiViewport* MainViewport; // Guaranteed to be == Viewports[0] ImVector Viewports; // Main viewports, followed by all secondary viewports. From 7f960616e0b7bee6ccadea26d92726dbab3ec7b8 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 16 May 2018 14:34:46 +0200 Subject: [PATCH 155/828] Viewport: When resizing/moving a window using the host OS/WM we attempt to merge back into host viewport. (#1542) --- imgui.cpp | 10 ++++++++-- imgui.h | 4 ++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 5c50f7201938..22c07fbf1b09 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3553,7 +3553,7 @@ static void ImGui::UpdateViewports() if (viewport->PlatformRequestResize) viewport->Size = g.PlatformIO.Platform_GetWindowSize(viewport); - // Translate imgui windows when a host viewport has been moved + // Translate imgui windows when a Host Viewport has been moved ImVec2 delta = viewport->Pos - viewport->LastPos; if ((viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) && (delta.x != 0.0f || delta.y != 0.0f)) for (int window_n = 0; window_n < g.Windows.Size; window_n++) @@ -6827,13 +6827,19 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) } } - // Synchronize viewport --> window + // Synchronize viewport --> window in case the platform window has been moved or resized from the OS/WM if (window->ViewportOwned) { if (window->Viewport->PlatformRequestMove) + { window->Pos = window->Viewport->Pos; + window->ViewportTryMerge = true; + } if (window->Viewport->PlatformRequestResize) + { window->Size = window->SizeFull = window->Viewport->Size; + window->ViewportTryMerge = true; + } // We also tell the back-end that clearing the platform window won't be necessary, as our window is filling the viewport and we have disabled BgAlpha window->Viewport->Flags |= ImGuiViewportFlags_NoRendererClear; diff --git a/imgui.h b/imgui.h index 8c07ed33e88a..7d86b2879b66 100644 --- a/imgui.h +++ b/imgui.h @@ -2004,8 +2004,8 @@ struct ImGuiViewport void* PlatformUserData; // void* to hold custom data structure for the platform (e.g. windowing info, render context) void* PlatformHandle; // void* for FindViewportByPlatformHandle(). (e.g. suggested to use natural platform handle such as HWND, GlfwWindow*, SDL_Window*) bool PlatformRequestClose; // Platform window requested closure - bool PlatformRequestMove; // Platform window requested move (e.g. window was moved using OS windowing facility) - bool PlatformRequestResize; // Platform window requested resize (e.g. window was resize using OS windowing facility) + bool PlatformRequestMove; // Platform window requested move (e.g. window was moved by the OS / host window manager) + bool PlatformRequestResize; // Platform window requested resize (e.g. window was resize by the OS / host window manager) void* RendererUserData; // void* to hold custom data structure for the renderer (e.g. swap chain, frame-buffers etc.) ImGuiViewport() { ID = 0; Flags = 0; DpiScale = 0.0f; DrawData = NULL; PlatformUserData = PlatformHandle = NULL; PlatformRequestClose = PlatformRequestMove = PlatformRequestResize = false; RendererUserData = NULL; } From 4c35e00f49203bc00ebe97d0a3a9d543af355909 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 16 May 2018 22:48:54 +0200 Subject: [PATCH 156/828] Viewport: Made GetWindowViewport() not flag the window as written to. --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 22c07fbf1b09..7f8d8283ee3a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -8138,7 +8138,7 @@ ImDrawList* ImGui::GetWindowDrawList() ImGuiViewport* ImGui::GetWindowViewport() { - ImGuiWindow* window = GetCurrentWindow(); + ImGuiWindow* window = GetCurrentWindowRead(); IM_ASSERT(window->Viewport != NULL); return window->Viewport; } From 1176460e4476b2d77f75cd4388ec92af0a93f6c7 Mon Sep 17 00:00:00 2001 From: omar Date: Sat, 19 May 2018 20:45:49 +0200 Subject: [PATCH 157/828] Viewport: Fixed using ImGuiConfigFlags_ViewportsNoMerge always showing the Debug window by testing the Active flag as well. (#1542) --- imgui.cpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 7d9008fb8014..7dea35e03671 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3638,6 +3638,11 @@ static void ImGui::UpdateViewports() IM_ASSERT(g.MouseRefViewport != NULL); } +static bool IsWindowActiveAndVisible(ImGuiWindow* window) +{ + return (window->HiddenFrames == 0) && (window->Active); +} + void ImGui::UpdatePlatformWindows() { ImGuiContext& g = *GImGui; @@ -3672,7 +3677,9 @@ void ImGui::UpdatePlatformWindows() // New windows that appears directly in a new viewport won't always have a size on their frame if (viewport->Size.x <= 0 || viewport->Size.y <= 0) continue; - if (viewport->Window && viewport->Window->HiddenFrames > 0) + + // Ignore viewport that are hosting a hidden window (also check the Active flag, as the implicit Debug window will be registering its viewport then immediately disabled) + if (viewport->Window && !IsWindowActiveAndVisible(viewport->Window)) continue; // Update viewport flags @@ -4514,7 +4521,7 @@ static void AddWindowToDrawData(ImGuiWindow* window, int layer) for (int i = 0; i < window->DC.ChildWindows.Size; i++) { ImGuiWindow* child = window->DC.ChildWindows[i]; - if (child->Active && child->HiddenFrames == 0) // Clipped children may have been marked not active + if (IsWindowActiveAndVisible(child)) // Clipped children may have been marked not active AddWindowToDrawData(child, layer); } } @@ -4657,7 +4664,7 @@ void ImGui::EndFrame() ImGuiViewportP* viewport = g.Viewports[i]; if (viewport->LastFrameActive < g.FrameCount || viewport->Size.x <= 0.0f || viewport->Size.y <= 0.0f) continue; - if (viewport->Window && viewport->Window->HiddenFrames > 0) + if (viewport->Window && !IsWindowActiveAndVisible(viewport->Window)) continue; if (i > 0) IM_ASSERT(viewport->Window != NULL); @@ -4704,10 +4711,10 @@ void ImGui::Render() for (int n = 0; n != g.Windows.Size; n++) { ImGuiWindow* window = g.Windows[n]; - if (window->Active && window->HiddenFrames == 0 && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != window_to_render_front_most) + if (IsWindowActiveAndVisible(window) && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != window_to_render_front_most) AddRootWindowToDrawData(window); } - if (window_to_render_front_most && window_to_render_front_most->Active && window_to_render_front_most->HiddenFrames == 0) // NavWindowingTarget is always temporarily displayed as the front-most window + if (window_to_render_front_most && IsWindowActiveAndVisible(window_to_render_front_most)) // NavWindowingTarget is always temporarily displayed as the front-most window AddRootWindowToDrawData(window_to_render_front_most); // Draw software mouse cursor if requested From ed84b2aaebb2c0c9a83d3f82cad0c516d87a8a71 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 23 May 2018 18:01:50 +0200 Subject: [PATCH 158/828] Viewport, Platform: Fixed a crash if the back-end set the PlatformRequestMove/PlatformRequestSize flags while viewports were disabled (it happened in the SDL back-end, and generally we want to tolerate it to make back-end implementation simpler). (#1542) --- imgui.cpp | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 7dea35e03671..5b53041f2b10 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3555,22 +3555,25 @@ static void ImGui::UpdateViewports() continue; } - // Apply Position and Size (from Platform Window to ImGui) if requested. - // We do it early in the frame instead of waiting for UpdatePlatformWindows() to avoid a frame of lag when moving/resizing using OS facilities. - if (viewport->PlatformRequestMove) - viewport->Pos = g.PlatformIO.Platform_GetWindowPos(viewport); - if (viewport->PlatformRequestResize) - viewport->Size = g.PlatformIO.Platform_GetWindowSize(viewport); - - // Translate imgui windows when a Host Viewport has been moved - ImVec2 delta = viewport->Pos - viewport->LastPos; - if ((viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) && (delta.x != 0.0f || delta.y != 0.0f)) - for (int window_n = 0; window_n < g.Windows.Size; window_n++) - if (g.Windows[window_n]->Viewport == viewport) - TranslateWindow(g.Windows[window_n], delta); + if ((g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) + { + // Apply Position and Size (from Platform Window to ImGui) if requested. + // We do it early in the frame instead of waiting for UpdatePlatformWindows() to avoid a frame of lag when moving/resizing using OS facilities. + if (viewport->PlatformRequestMove) + viewport->Pos = g.PlatformIO.Platform_GetWindowPos(viewport); + if (viewport->PlatformRequestResize) + viewport->Size = g.PlatformIO.Platform_GetWindowSize(viewport); - // Update monitor (we'll use this info to clamp windows and save windows lost in a removed monitor) - viewport->PlatformMonitor = FindPlatformMonitorForRect(viewport->GetRect()); + // Translate imgui windows when a Host Viewport has been moved + ImVec2 delta = viewport->Pos - viewport->LastPos; + if ((viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) && (delta.x != 0.0f || delta.y != 0.0f)) + for (int window_n = 0; window_n < g.Windows.Size; window_n++) + if (g.Windows[window_n]->Viewport == viewport) + TranslateWindow(g.Windows[window_n], delta); + + // Update monitor (we'll use this info to clamp windows and save windows lost in a removed monitor) + viewport->PlatformMonitor = FindPlatformMonitorForRect(viewport->GetRect()); + } // Update DPI scale float new_dpi_scale; From 002e513b82be5cc55e9f76bf7fc4e95df446b45f Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 23 May 2018 18:23:58 +0200 Subject: [PATCH 159/828] Added float GetWindowDpiScale(). (#1542, #1676) --- TODO.txt | 2 ++ imgui.cpp | 13 ++++++++++--- imgui.h | 1 + 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/TODO.txt b/TODO.txt index f3984e2bd0bc..dcef7d350b42 100644 --- a/TODO.txt +++ b/TODO.txt @@ -264,6 +264,8 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - focus: SetKeyboardFocusHere() on with >= 0 offset could be done on same frame (else latch and modulate on beginning of next frame) - focus: unable to use SetKeyboardFocusHere() on clipped widgets. (#787) + - examples: move ImGui::NewFrame() out of the backend _NewFrame() ? + - viewport: make it possible to have no main/hosting viewport - viewport: use getfocus/setfocus api to synchronize imgui<>platform focus better (e.g imgui-side ctrl-tab can focus os window, OS initial setup and alt-tab can focus imgui window etc.) - viewport: store per-viewport/monitor DPI in .ini file so an application reload or main window changing DPI on reload can be properly patched for. - viewport: vulkan renderer implementation. diff --git a/imgui.cpp b/imgui.cpp index 5b53041f2b10..5f53bdf5ddf3 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -8192,11 +8192,18 @@ ImDrawList* ImGui::GetWindowDrawList() return window->DrawList; } +float ImGui::GetWindowDpiScale() +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(g.CurrentViewport != NULL); + return g.CurrentViewport->DpiScale; +} + ImGuiViewport* ImGui::GetWindowViewport() { - ImGuiWindow* window = GetCurrentWindowRead(); - IM_ASSERT(window->Viewport != NULL); - return window->Viewport; + ImGuiContext& g = *GImGui; + IM_ASSERT(g.CurrentViewport != NULL && g.CurrentViewport == g.CurrentWindow->Viewport); + return g.CurrentViewport; } ImFont* ImGui::GetFont() diff --git a/imgui.h b/imgui.h index 05ea570fbcea..070031fbbd18 100644 --- a/imgui.h +++ b/imgui.h @@ -197,6 +197,7 @@ namespace ImGui IMGUI_API bool IsWindowFocused(ImGuiFocusedFlags flags=0); // is current window focused? or its root/child, depending on flags. see flags for options. IMGUI_API bool IsWindowHovered(ImGuiHoveredFlags flags=0); // is current window hovered (and typically: not blocked by a popup/modal)? see flags for options. NB: If you are trying to check whether your mouse should be dispatched to imgui or to your app, you should use the 'io.WantCaptureMouse' boolean for that! Please read the FAQ! IMGUI_API ImDrawList* GetWindowDrawList(); // get draw list associated to the current window, to append your own drawing primitives + IMGUI_API float GetWindowDpiScale(); // get DPI scale currently associated to the current window's viewport. IMGUI_API ImGuiViewport*GetWindowViewport(); // get viewport currently associated to the current window. IMGUI_API ImVec2 GetWindowPos(); // get current window position in screen space (useful if you want to do your own drawing via the DrawList API) IMGUI_API ImVec2 GetWindowSize(); // get current window size From 77d51ebf248efb3bc8dd4bd461c78ed660eca4d5 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 7 Jun 2018 19:20:04 +0200 Subject: [PATCH 160/828] Examples: Added .. folder in include path to remove the awkward #include "../imgui_impl_xxx.h" statements. --- examples/allegro5_example/README.md | 4 ++-- examples/allegro5_example/main.cpp | 2 +- examples/directx10_example/directx10_example.vcxproj | 8 ++++---- examples/directx10_example/main.cpp | 4 ++-- examples/directx11_example/directx11_example.vcxproj | 8 ++++---- examples/directx11_example/main.cpp | 4 ++-- examples/directx12_example/directx12_example.vcxproj | 8 ++++---- examples/directx12_example/main.cpp | 4 ++-- examples/directx9_example/directx9_example.vcxproj | 8 ++++---- examples/directx9_example/main.cpp | 4 ++-- examples/imgui_examples.sln | 10 ++++++++++ examples/marmalade_example/main.cpp | 2 +- examples/marmalade_example/marmalade_example.mkb | 1 + examples/opengl2_example/main.cpp | 4 ++-- examples/opengl2_example/opengl2_example.vcxproj | 8 ++++---- examples/opengl3_example/main.cpp | 4 ++-- examples/opengl3_example/opengl3_example.vcxproj | 8 ++++---- examples/sdl_opengl2_example/main.cpp | 4 ++-- .../sdl_opengl2_example/sdl_opengl2_example.vcxproj | 8 ++++---- examples/sdl_opengl3_example/main.cpp | 4 ++-- .../sdl_opengl3_example/sdl_opengl3_example.vcxproj | 8 ++++---- examples/sdl_vulkan_example/main.cpp | 4 ++-- examples/sdl_vulkan_example/sdl_vulkan_example.vcxproj | 8 ++++---- examples/vulkan_example/main.cpp | 4 ++-- examples/vulkan_example/vulkan_example.vcxproj | 8 ++++---- 25 files changed, 75 insertions(+), 64 deletions(-) diff --git a/examples/allegro5_example/README.md b/examples/allegro5_example/README.md index 4f18f2aba311..f15b5fda983d 100644 --- a/examples/allegro5_example/README.md +++ b/examples/allegro5_example/README.md @@ -12,12 +12,12 @@ Note that the back-end supports _BOTH_ 16-bit and 32-bit indices, but 32-bit ind - On Ubuntu 14.04+ ```bash -g++ -DIMGUI_USER_CONFIG=\"examples/allegro5_example/imconfig_allegro5.h\" -I ../.. main.cpp imgui_impl_allegro5.cpp ../../imgui*.cpp -lallegro -lallegro_primitives -o allegro5_example +g++ -DIMGUI_USER_CONFIG=\"examples/allegro5_example/imconfig_allegro5.h\" -I .. -I ../.. main.cpp imgui_impl_allegro5.cpp ../../imgui*.cpp -lallegro -lallegro_primitives -o allegro5_example ``` - On Windows with Visual Studio's CLI ``` set ALLEGRODIR=path_to_your_allegro5_folder -cl /Zi /MD /I %ALLEGRODIR%\include /DIMGUI_USER_CONFIG=\"examples/allegro5_example/imconfig_allegro5.h\" /I ..\.. main.cpp imgui_impl_allegro5.cpp ..\..\imgui*.cpp /link /LIBPATH:%ALLEGRODIR%\lib allegro-5.0.10-monolith-md.lib user32.lib +cl /Zi /MD /I %ALLEGRODIR%\include /DIMGUI_USER_CONFIG=\"examples/allegro5_example/imconfig_allegro5.h\" /I .. /I ..\.. main.cpp imgui_impl_allegro5.cpp ..\..\imgui*.cpp /link /LIBPATH:%ALLEGRODIR%\lib allegro-5.0.10-monolith-md.lib user32.lib ``` diff --git a/examples/allegro5_example/main.cpp b/examples/allegro5_example/main.cpp index 055d352dab7e..c04e17728879 100644 --- a/examples/allegro5_example/main.cpp +++ b/examples/allegro5_example/main.cpp @@ -5,7 +5,7 @@ #include #include #include "imgui.h" -#include "../imgui_impl_allegro5.h" +#include "imgui_impl_allegro5.h" int main(int, char**) { diff --git a/examples/directx10_example/directx10_example.vcxproj b/examples/directx10_example/directx10_example.vcxproj index 2013d0e7af17..dc08d182a889 100644 --- a/examples/directx10_example/directx10_example.vcxproj +++ b/examples/directx10_example/directx10_example.vcxproj @@ -81,7 +81,7 @@ Level4 Disabled - ..\..;%(AdditionalIncludeDirectories);$(DXSDK_DIR)Include; + ..\..;..;%(AdditionalIncludeDirectories);$(DXSDK_DIR)Include; true @@ -94,7 +94,7 @@ Level4 Disabled - ..\..;%(AdditionalIncludeDirectories);$(DXSDK_DIR)Include; + ..\..;..;%(AdditionalIncludeDirectories);$(DXSDK_DIR)Include; true @@ -109,7 +109,7 @@ MaxSpeed true true - ..\..;%(AdditionalIncludeDirectories);$(DXSDK_DIR)Include; + ..\..;..;%(AdditionalIncludeDirectories);$(DXSDK_DIR)Include; false @@ -127,7 +127,7 @@ MaxSpeed true true - ..\..;%(AdditionalIncludeDirectories);$(DXSDK_DIR)Include; + ..\..;..;%(AdditionalIncludeDirectories);$(DXSDK_DIR)Include; false diff --git a/examples/directx10_example/main.cpp b/examples/directx10_example/main.cpp index cd59bcfe5860..704c8bd9c10c 100644 --- a/examples/directx10_example/main.cpp +++ b/examples/directx10_example/main.cpp @@ -2,8 +2,8 @@ // If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp. #include "imgui.h" -#include "../imgui_impl_win32.h" -#include "../imgui_impl_dx10.h" +#include "imgui_impl_win32.h" +#include "imgui_impl_dx10.h" #include #include #define DIRECTINPUT_VERSION 0x0800 diff --git a/examples/directx11_example/directx11_example.vcxproj b/examples/directx11_example/directx11_example.vcxproj index e38bb7a95cdc..77f56fead14a 100644 --- a/examples/directx11_example/directx11_example.vcxproj +++ b/examples/directx11_example/directx11_example.vcxproj @@ -81,7 +81,7 @@ Level4 Disabled - ..\..;%(AdditionalIncludeDirectories);$(DXSDK_DIR)Include; + ..\..;..;%(AdditionalIncludeDirectories);$(DXSDK_DIR)Include; true @@ -94,7 +94,7 @@ Level4 Disabled - ..\..;%(AdditionalIncludeDirectories);$(DXSDK_DIR)Include; + ..\..;..;%(AdditionalIncludeDirectories);$(DXSDK_DIR)Include; true @@ -109,7 +109,7 @@ MaxSpeed true true - ..\..;%(AdditionalIncludeDirectories);$(DXSDK_DIR)Include; + ..\..;..;%(AdditionalIncludeDirectories);$(DXSDK_DIR)Include; false @@ -127,7 +127,7 @@ MaxSpeed true true - ..\..;%(AdditionalIncludeDirectories);$(DXSDK_DIR)Include; + ..\..;..;%(AdditionalIncludeDirectories);$(DXSDK_DIR)Include; false diff --git a/examples/directx11_example/main.cpp b/examples/directx11_example/main.cpp index 1137b15a92ca..ad3f697a5e3c 100644 --- a/examples/directx11_example/main.cpp +++ b/examples/directx11_example/main.cpp @@ -2,8 +2,8 @@ // If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp. #include "imgui.h" -#include "../imgui_impl_win32.h" -#include "../imgui_impl_dx11.h" +#include "imgui_impl_win32.h" +#include "imgui_impl_dx11.h" #include #define DIRECTINPUT_VERSION 0x0800 #include diff --git a/examples/directx12_example/directx12_example.vcxproj b/examples/directx12_example/directx12_example.vcxproj index 1ee7e343555d..f27664e58c7f 100644 --- a/examples/directx12_example/directx12_example.vcxproj +++ b/examples/directx12_example/directx12_example.vcxproj @@ -86,7 +86,7 @@ Level4 Disabled - ..\..;%(AdditionalIncludeDirectories) + ..\..;..;%(AdditionalIncludeDirectories) true @@ -99,7 +99,7 @@ Level4 Disabled - ..\..;%(AdditionalIncludeDirectories) + ..\..;..;%(AdditionalIncludeDirectories) true @@ -114,7 +114,7 @@ MaxSpeed true true - ..\..;%(AdditionalIncludeDirectories) + ..\..;..;%(AdditionalIncludeDirectories) true @@ -131,7 +131,7 @@ MaxSpeed true true - ..\..;%(AdditionalIncludeDirectories) + ..\..;..;%(AdditionalIncludeDirectories) true diff --git a/examples/directx12_example/main.cpp b/examples/directx12_example/main.cpp index d96ad08c45bd..89230e5dba92 100644 --- a/examples/directx12_example/main.cpp +++ b/examples/directx12_example/main.cpp @@ -3,8 +3,8 @@ // FIXME: 64-bit only for now! (Because sizeof(ImTextureId) == sizeof(void*)) #include "imgui.h" -#include "../imgui_impl_win32.h" -#include "../imgui_impl_dx12.h" +#include "imgui_impl_win32.h" +#include "imgui_impl_dx12.h" #include #include #include diff --git a/examples/directx9_example/directx9_example.vcxproj b/examples/directx9_example/directx9_example.vcxproj index 0b7bfa6808f2..83d06f603049 100644 --- a/examples/directx9_example/directx9_example.vcxproj +++ b/examples/directx9_example/directx9_example.vcxproj @@ -81,7 +81,7 @@ Level4 Disabled - ..\..;%(AdditionalIncludeDirectories);$(DXSDK_DIR)Include; + ..\..;..;%(AdditionalIncludeDirectories);$(DXSDK_DIR)Include; true @@ -94,7 +94,7 @@ Level4 Disabled - ..\..;%(AdditionalIncludeDirectories);$(DXSDK_DIR)Include; + ..\..;..;%(AdditionalIncludeDirectories);$(DXSDK_DIR)Include; true @@ -109,7 +109,7 @@ MaxSpeed true true - ..\..;%(AdditionalIncludeDirectories);$(DXSDK_DIR)Include; + ..\..;..;%(AdditionalIncludeDirectories);$(DXSDK_DIR)Include; false @@ -127,7 +127,7 @@ MaxSpeed true true - ..\..;%(AdditionalIncludeDirectories);$(DXSDK_DIR)Include; + ..\..;..;%(AdditionalIncludeDirectories);$(DXSDK_DIR)Include; false diff --git a/examples/directx9_example/main.cpp b/examples/directx9_example/main.cpp index 8dd66814e9d0..a7333a5d3d48 100644 --- a/examples/directx9_example/main.cpp +++ b/examples/directx9_example/main.cpp @@ -2,8 +2,8 @@ // If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp. #include "imgui.h" -#include "../imgui_impl_dx9.h" -#include "../imgui_impl_win32.h" +#include "imgui_impl_dx9.h" +#include "imgui_impl_win32.h" #include #define DIRECTINPUT_VERSION 0x0800 #include diff --git a/examples/imgui_examples.sln b/examples/imgui_examples.sln index 0d6bbd6c8e97..1310b77619bf 100644 --- a/examples/imgui_examples.sln +++ b/examples/imgui_examples.sln @@ -21,6 +21,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vulkan_example", "vulkan_ex EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sdl_vulkan_example", "sdl_vulkan_example\sdl_vulkan_example.vcxproj", "{BAE3D0B5-9695-4EB1-AD0F-75890EB4A3B3}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "directx12_example", "directx12_example\directx12_example.vcxproj", "{B4CF9797-519D-4AFE-A8F4-5141A6B521D3}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -101,6 +103,14 @@ Global {BAE3D0B5-9695-4EB1-AD0F-75890EB4A3B3}.Release|Win32.Build.0 = Release|Win32 {BAE3D0B5-9695-4EB1-AD0F-75890EB4A3B3}.Release|x64.ActiveCfg = Release|x64 {BAE3D0B5-9695-4EB1-AD0F-75890EB4A3B3}.Release|x64.Build.0 = Release|x64 + {B4CF9797-519D-4AFE-A8F4-5141A6B521D3}.Debug|Win32.ActiveCfg = Debug|Win32 + {B4CF9797-519D-4AFE-A8F4-5141A6B521D3}.Debug|Win32.Build.0 = Debug|Win32 + {B4CF9797-519D-4AFE-A8F4-5141A6B521D3}.Debug|x64.ActiveCfg = Debug|x64 + {B4CF9797-519D-4AFE-A8F4-5141A6B521D3}.Debug|x64.Build.0 = Debug|x64 + {B4CF9797-519D-4AFE-A8F4-5141A6B521D3}.Release|Win32.ActiveCfg = Release|Win32 + {B4CF9797-519D-4AFE-A8F4-5141A6B521D3}.Release|Win32.Build.0 = Release|Win32 + {B4CF9797-519D-4AFE-A8F4-5141A6B521D3}.Release|x64.ActiveCfg = Release|x64 + {B4CF9797-519D-4AFE-A8F4-5141A6B521D3}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/examples/marmalade_example/main.cpp b/examples/marmalade_example/main.cpp index b8ac728bdb50..3fcf15263a87 100644 --- a/examples/marmalade_example/main.cpp +++ b/examples/marmalade_example/main.cpp @@ -5,7 +5,7 @@ // This file is part of ImGui #include "imgui.h" -#include "../imgui_impl_marmalade.h" +#include "imgui_impl_marmalade.h" #include #include diff --git a/examples/marmalade_example/marmalade_example.mkb b/examples/marmalade_example/marmalade_example.mkb index 11b0392f9701..f605078e6281 100644 --- a/examples/marmalade_example/marmalade_example.mkb +++ b/examples/marmalade_example/marmalade_example.mkb @@ -17,6 +17,7 @@ options includepaths { + .. ../.. } diff --git a/examples/opengl2_example/main.cpp b/examples/opengl2_example/main.cpp index a0f86a3c7b51..eff0f300da1a 100644 --- a/examples/opengl2_example/main.cpp +++ b/examples/opengl2_example/main.cpp @@ -7,8 +7,8 @@ // See imgui_impl_glfw.cpp for details. #include "imgui.h" -#include "../imgui_impl_glfw.h" -#include "../imgui_impl_opengl2.h" +#include "imgui_impl_glfw.h" +#include "imgui_impl_opengl2.h" #include #include diff --git a/examples/opengl2_example/opengl2_example.vcxproj b/examples/opengl2_example/opengl2_example.vcxproj index 501d73c00ad3..927ddfa54184 100644 --- a/examples/opengl2_example/opengl2_example.vcxproj +++ b/examples/opengl2_example/opengl2_example.vcxproj @@ -85,7 +85,7 @@ Level4 Disabled - $(SolutionDir)\libs\glfw\include;..\..;%(AdditionalIncludeDirectories) + ..\..;..;$(SolutionDir)\libs\glfw\include;%(AdditionalIncludeDirectories) true @@ -99,7 +99,7 @@ Level4 Disabled - $(SolutionDir)\libs\glfw\include;..\..;%(AdditionalIncludeDirectories) + ..\..;..;$(SolutionDir)\libs\glfw\include;%(AdditionalIncludeDirectories) true @@ -115,7 +115,7 @@ MaxSpeed true true - $(SolutionDir)\libs\glfw\include;..\..;%(AdditionalIncludeDirectories) + ..\..;..;$(SolutionDir)\libs\glfw\include;%(AdditionalIncludeDirectories) false @@ -135,7 +135,7 @@ MaxSpeed true true - $(SolutionDir)\libs\glfw\include;..\..;%(AdditionalIncludeDirectories) + ..\..;..;$(SolutionDir)\libs\glfw\include;%(AdditionalIncludeDirectories) false diff --git a/examples/opengl3_example/main.cpp b/examples/opengl3_example/main.cpp index 0563e1daeba0..be6c03579c39 100644 --- a/examples/opengl3_example/main.cpp +++ b/examples/opengl3_example/main.cpp @@ -4,8 +4,8 @@ // (GL3W is a helper library to access OpenGL functions since there is no standard header to access modern OpenGL functions easily. Alternatives are GLEW, Glad, etc.) #include "imgui.h" -#include "../imgui_impl_glfw.h" -#include "../imgui_impl_opengl3.h" +#include "imgui_impl_glfw.h" +#include "imgui_impl_opengl3.h" #include #include // This example is using gl3w to access OpenGL functions (because it is small). You may use glew/glad/glLoadGen/etc. whatever already works for you. #include diff --git a/examples/opengl3_example/opengl3_example.vcxproj b/examples/opengl3_example/opengl3_example.vcxproj index e0ff1ba3f7a8..34bae2511c34 100644 --- a/examples/opengl3_example/opengl3_example.vcxproj +++ b/examples/opengl3_example/opengl3_example.vcxproj @@ -85,7 +85,7 @@ Level4 Disabled - $(SolutionDir)\libs\glfw\include;$(SolutionDir)\libs\gl3w;..\..;%(AdditionalIncludeDirectories) + ..\..;..;$(SolutionDir)\libs\glfw\include;$(SolutionDir)\libs\gl3w;%(AdditionalIncludeDirectories) true @@ -99,7 +99,7 @@ Level4 Disabled - $(SolutionDir)\libs\glfw\include;$(SolutionDir)\libs\gl3w;..\..;%(AdditionalIncludeDirectories) + ..\..;..;$(SolutionDir)\libs\glfw\include;$(SolutionDir)\libs\gl3w;%(AdditionalIncludeDirectories) true @@ -115,7 +115,7 @@ MaxSpeed true true - $(SolutionDir)\libs\glfw\include;$(SolutionDir)\libs\gl3w;..\..;%(AdditionalIncludeDirectories) + ..\..;..;$(SolutionDir)\libs\glfw\include;$(SolutionDir)\libs\gl3w;%(AdditionalIncludeDirectories) false @@ -135,7 +135,7 @@ MaxSpeed true true - $(SolutionDir)\libs\glfw\include;$(SolutionDir)\libs\gl3w;..\..;%(AdditionalIncludeDirectories) + ..\..;..;$(SolutionDir)\libs\glfw\include;$(SolutionDir)\libs\gl3w;%(AdditionalIncludeDirectories) false diff --git a/examples/sdl_opengl2_example/main.cpp b/examples/sdl_opengl2_example/main.cpp index 1ba1bf0618d0..36c8ecf02547 100644 --- a/examples/sdl_opengl2_example/main.cpp +++ b/examples/sdl_opengl2_example/main.cpp @@ -7,8 +7,8 @@ // See imgui_impl_sdl.cpp for details. #include "imgui.h" -#include "../imgui_impl_sdl2.h" -#include "../imgui_impl_opengl2.h" +#include "imgui_impl_sdl2.h" +#include "imgui_impl_opengl2.h" #include #include #include diff --git a/examples/sdl_opengl2_example/sdl_opengl2_example.vcxproj b/examples/sdl_opengl2_example/sdl_opengl2_example.vcxproj index 3e3ea79051fd..6bc24caa8ebf 100644 --- a/examples/sdl_opengl2_example/sdl_opengl2_example.vcxproj +++ b/examples/sdl_opengl2_example/sdl_opengl2_example.vcxproj @@ -85,7 +85,7 @@ Level4 Disabled - %SDL2_DIR%\include;$(SolutionDir)\libs\gl3w;..\..;%(AdditionalIncludeDirectories) + ..\..;..;%SDL2_DIR%\include;$(SolutionDir)\libs\gl3w;%(AdditionalIncludeDirectories) true @@ -99,7 +99,7 @@ Level4 Disabled - %SDL2_DIR%\include;$(SolutionDir)\libs\gl3w;..\..;%(AdditionalIncludeDirectories) + ..\..;..;%SDL2_DIR%\include;$(SolutionDir)\libs\gl3w;%(AdditionalIncludeDirectories) true @@ -115,7 +115,7 @@ MaxSpeed true true - %SDL2_DIR%\include;$(SolutionDir)\libs\gl3w;..\..;%(AdditionalIncludeDirectories) + ..\..;..;%SDL2_DIR%\include;$(SolutionDir)\libs\gl3w;%(AdditionalIncludeDirectories) false @@ -135,7 +135,7 @@ MaxSpeed true true - %SDL2_DIR%\include;$(SolutionDir)\libs\gl3w;..\..;%(AdditionalIncludeDirectories) + ..\..;..;%SDL2_DIR%\include;$(SolutionDir)\libs\gl3w;%(AdditionalIncludeDirectories) false diff --git a/examples/sdl_opengl3_example/main.cpp b/examples/sdl_opengl3_example/main.cpp index 1314f8596a06..69044527b4d1 100644 --- a/examples/sdl_opengl3_example/main.cpp +++ b/examples/sdl_opengl3_example/main.cpp @@ -4,8 +4,8 @@ // (GL3W is a helper library to access OpenGL functions since there is no standard header to access modern OpenGL functions easily. Alternatives are GLEW, Glad, etc.) #include "imgui.h" -#include "../imgui_impl_sdl2.h" -#include "../imgui_impl_opengl3.h" +#include "imgui_impl_sdl2.h" +#include "imgui_impl_opengl3.h" #include #include // This example is using gl3w to access OpenGL functions (because it is small). You may use glew/glad/glLoadGen/etc. whatever already works for you. #include diff --git a/examples/sdl_opengl3_example/sdl_opengl3_example.vcxproj b/examples/sdl_opengl3_example/sdl_opengl3_example.vcxproj index 7ef3fd96e586..c8ebb094a27b 100644 --- a/examples/sdl_opengl3_example/sdl_opengl3_example.vcxproj +++ b/examples/sdl_opengl3_example/sdl_opengl3_example.vcxproj @@ -85,7 +85,7 @@ Level4 Disabled - %SDL2_DIR%\include;$(SolutionDir)\libs\gl3w;..\..;%(AdditionalIncludeDirectories) + ..\..;..;%SDL2_DIR%\include;$(SolutionDir)\libs\gl3w;%(AdditionalIncludeDirectories) true @@ -99,7 +99,7 @@ Level4 Disabled - %SDL2_DIR%\include;$(SolutionDir)\libs\gl3w;..\..;%(AdditionalIncludeDirectories) + ..\..;..;%SDL2_DIR%\include;$(SolutionDir)\libs\gl3w;%(AdditionalIncludeDirectories) true @@ -115,7 +115,7 @@ MaxSpeed true true - %SDL2_DIR%\include;$(SolutionDir)\libs\gl3w;..\..;%(AdditionalIncludeDirectories) + ..\..;..;%SDL2_DIR%\include;$(SolutionDir)\libs\gl3w;%(AdditionalIncludeDirectories) false @@ -135,7 +135,7 @@ MaxSpeed true true - %SDL2_DIR%\include;$(SolutionDir)\libs\gl3w;..\..;%(AdditionalIncludeDirectories) + ..\..;..;%SDL2_DIR%\include;$(SolutionDir)\libs\gl3w;%(AdditionalIncludeDirectories) false diff --git a/examples/sdl_vulkan_example/main.cpp b/examples/sdl_vulkan_example/main.cpp index f22c1612c034..50cdb1022cc8 100644 --- a/examples/sdl_vulkan_example/main.cpp +++ b/examples/sdl_vulkan_example/main.cpp @@ -2,8 +2,8 @@ // If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp. #include "imgui.h" -#include "../imgui_impl_sdl2.h" -#include "../imgui_impl_vulkan.h" +#include "imgui_impl_sdl2.h" +#include "imgui_impl_vulkan.h" #include #include #include diff --git a/examples/sdl_vulkan_example/sdl_vulkan_example.vcxproj b/examples/sdl_vulkan_example/sdl_vulkan_example.vcxproj index 3f7733a80f58..8b4d648c8698 100644 --- a/examples/sdl_vulkan_example/sdl_vulkan_example.vcxproj +++ b/examples/sdl_vulkan_example/sdl_vulkan_example.vcxproj @@ -85,7 +85,7 @@ Level4 Disabled - %VULKAN_SDK%\include;%SDL2_DIR%\include;..\..;%(AdditionalIncludeDirectories) + ..\..;..;%VULKAN_SDK%\include;%SDL2_DIR%\include;%(AdditionalIncludeDirectories) true @@ -99,7 +99,7 @@ Level4 Disabled - %VULKAN_SDK%\include;%SDL2_DIR%\include;..\..;%(AdditionalIncludeDirectories) + ..\..;..;%VULKAN_SDK%\include;%SDL2_DIR%\include;%(AdditionalIncludeDirectories) true @@ -115,7 +115,7 @@ MaxSpeed true true - %VULKAN_SDK%\include;%SDL2_DIR%\include;..\..;%(AdditionalIncludeDirectories) + ..\..;..;%VULKAN_SDK%\include;%SDL2_DIR%\include;%(AdditionalIncludeDirectories) false @@ -135,7 +135,7 @@ MaxSpeed true true - %VULKAN_SDK%\include;%SDL2_DIR%\include;..\..;%(AdditionalIncludeDirectories) + ..\..;..;%VULKAN_SDK%\include;%SDL2_DIR%\include;%(AdditionalIncludeDirectories) false diff --git a/examples/vulkan_example/main.cpp b/examples/vulkan_example/main.cpp index 6a4950e9d3eb..2b39fa83cd69 100644 --- a/examples/vulkan_example/main.cpp +++ b/examples/vulkan_example/main.cpp @@ -2,8 +2,8 @@ // If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp. #include "imgui.h" -#include "../imgui_impl_glfw.h" -#include "../imgui_impl_vulkan.h" +#include "imgui_impl_glfw.h" +#include "imgui_impl_vulkan.h" #include // printf, fprintf #include // abort diff --git a/examples/vulkan_example/vulkan_example.vcxproj b/examples/vulkan_example/vulkan_example.vcxproj index 0523bb18d8bf..3a0a994715c5 100644 --- a/examples/vulkan_example/vulkan_example.vcxproj +++ b/examples/vulkan_example/vulkan_example.vcxproj @@ -85,7 +85,7 @@ Level4 Disabled - %VULKAN_SDK%\include;$(SolutionDir)\libs\glfw\include;..\..;%(AdditionalIncludeDirectories) + ..\..;..;%VULKAN_SDK%\include;$(SolutionDir)\libs\glfw\include;%(AdditionalIncludeDirectories) true @@ -99,7 +99,7 @@ Level4 Disabled - %VULKAN_SDK%\include;$(SolutionDir)\libs\glfw\include;..\..;%(AdditionalIncludeDirectories) + ..\..;..;%VULKAN_SDK%\include;$(SolutionDir)\libs\glfw\include;%(AdditionalIncludeDirectories) true @@ -115,7 +115,7 @@ MaxSpeed true true - %VULKAN_SDK%\include;$(SolutionDir)\libs\glfw\include;..\..;%(AdditionalIncludeDirectories) + ..\..;..;%VULKAN_SDK%\include;$(SolutionDir)\libs\glfw\include;%(AdditionalIncludeDirectories) false @@ -135,7 +135,7 @@ MaxSpeed true true - %VULKAN_SDK%\include;$(SolutionDir)\libs\glfw\include;..\..;%(AdditionalIncludeDirectories) + ..\..;..;%VULKAN_SDK%\include;$(SolutionDir)\libs\glfw\include;%(AdditionalIncludeDirectories) false From 8c374512fdaa1918c155c1e65a81e5f5e08ed094 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 7 Jun 2018 22:10:31 +0200 Subject: [PATCH 161/828] Examples, Platform: Removed the call to ImGui::NewFrame() from the platform _NewFrame() function e.g. ImGui_ImplWin32_NewFrame(), ImGui_ImplSDL2_NewFrame(), ImGui_ImplGlfw_NewFrame(), etc. Moved to main.cpp for consistency. (#1542) --- examples/allegro5_example/main.cpp | 6 +++++- examples/directx10_example/main.cpp | 6 +++++- examples/directx11_example/main.cpp | 6 +++++- examples/directx12_example/main.cpp | 6 +++++- examples/directx9_example/main.cpp | 4 ++++ examples/imgui_impl_allegro5.cpp | 3 --- examples/imgui_impl_glfw.cpp | 3 --- examples/imgui_impl_marmalade.cpp | 3 --- examples/imgui_impl_sdl2.cpp | 3 --- examples/imgui_impl_win32.cpp | 3 --- examples/marmalade_example/main.cpp | 6 +++++- examples/opengl2_example/main.cpp | 4 ++++ examples/opengl3_example/main.cpp | 4 ++++ examples/sdl_opengl2_example/main.cpp | 6 +++++- examples/sdl_opengl3_example/main.cpp | 4 ++++ examples/sdl_vulkan_example/main.cpp | 4 ++++ examples/vulkan_example/main.cpp | 4 +++- imgui.cpp | 2 ++ 18 files changed, 55 insertions(+), 22 deletions(-) diff --git a/examples/allegro5_example/main.cpp b/examples/allegro5_example/main.cpp index c04e17728879..3c0d8f62b8a6 100644 --- a/examples/allegro5_example/main.cpp +++ b/examples/allegro5_example/main.cpp @@ -56,6 +56,7 @@ int main(int, char**) bool running = true; while (running) { + // Poll and handle events (inputs, window resize, etc.) // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. @@ -73,7 +74,10 @@ int main(int, char**) ImGui_ImplAllegro5_CreateDeviceObjects(); } } + + // Start the ImGui frame ImGui_ImplAllegro5_NewFrame(); + ImGui::NewFrame(); // 1. Show a simple window. // Tip: if we don't call ImGui::Begin()/ImGui::End() the widgets automatically appears in a window called "Debug". @@ -113,8 +117,8 @@ int main(int, char**) } // Rendering - al_clear_to_color(al_map_rgba_f(clear_color.x, clear_color.y, clear_color.z, clear_color.w)); ImGui::Render(); + al_clear_to_color(al_map_rgba_f(clear_color.x, clear_color.y, clear_color.z, clear_color.w)); ImGui_ImplAllegro5_RenderDrawData(ImGui::GetDrawData()); al_flip_display(); } diff --git a/examples/directx10_example/main.cpp b/examples/directx10_example/main.cpp index 704c8bd9c10c..3cca82396eb4 100644 --- a/examples/directx10_example/main.cpp +++ b/examples/directx10_example/main.cpp @@ -153,6 +153,7 @@ int main(int, char**) ZeroMemory(&msg, sizeof(msg)); while (msg.message != WM_QUIT) { + // Poll and handle messages (inputs, window resize, etc.) // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. @@ -163,8 +164,11 @@ int main(int, char**) DispatchMessage(&msg); continue; } + + // Start the ImGui frame ImGui_ImplDX10_NewFrame(); ImGui_ImplWin32_NewFrame(); + ImGui::NewFrame(); // 1. Show a simple window. // Tip: if we don't call ImGui::Begin()/ImGui::End() the widgets automatically appears in a window called "Debug". @@ -204,9 +208,9 @@ int main(int, char**) } // Rendering + ImGui::Render(); g_pd3dDevice->OMSetRenderTargets(1, &g_mainRenderTargetView, NULL); g_pd3dDevice->ClearRenderTargetView(g_mainRenderTargetView, (float*)&clear_color); - ImGui::Render(); ImGui_ImplDX10_RenderDrawData(ImGui::GetDrawData()); // Update and Render additional Platform Windows diff --git a/examples/directx11_example/main.cpp b/examples/directx11_example/main.cpp index ad3f697a5e3c..f36567e7e56a 100644 --- a/examples/directx11_example/main.cpp +++ b/examples/directx11_example/main.cpp @@ -173,6 +173,7 @@ int main(int, char**) ZeroMemory(&msg, sizeof(msg)); while (msg.message != WM_QUIT) { + // Poll and handle messages (inputs, window resize, etc.) // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. @@ -183,8 +184,11 @@ int main(int, char**) DispatchMessage(&msg); continue; } + + // Start the ImGui frame ImGui_ImplDX11_NewFrame(); ImGui_ImplWin32_NewFrame(); + ImGui::NewFrame(); // 1. Show a simple window. // Tip: if we don't call ImGui::Begin()/ImGui::End() the widgets automatically appears in a window called "Debug". @@ -224,9 +228,9 @@ int main(int, char**) } // Rendering + ImGui::Render(); g_pd3dDeviceContext->OMSetRenderTargets(1, &g_mainRenderTargetView, NULL); g_pd3dDeviceContext->ClearRenderTargetView(g_mainRenderTargetView, (float*)&clear_color); - ImGui::Render(); ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData()); // Update and Render additional Platform Windows diff --git a/examples/directx12_example/main.cpp b/examples/directx12_example/main.cpp index 89230e5dba92..1eec041fc77c 100644 --- a/examples/directx12_example/main.cpp +++ b/examples/directx12_example/main.cpp @@ -290,7 +290,7 @@ int main(int, char**) IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; + //io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls ImGui_ImplWin32_Init(hwnd); @@ -325,6 +325,7 @@ int main(int, char**) ZeroMemory(&msg, sizeof(msg)); while (msg.message != WM_QUIT) { + // Poll and handle messages (inputs, window resize, etc.) // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. @@ -335,8 +336,11 @@ int main(int, char**) DispatchMessage(&msg); continue; } + + // Start the ImGui frame ImGui_ImplDX12_NewFrame(g_pd3dCommandList); ImGui_ImplWin32_NewFrame(); + ImGui::NewFrame(); // 1. Show a simple window. // Tip: if we don't call ImGui::Begin()/ImGui::End() the widgets automatically appears in a window called "Debug". diff --git a/examples/directx9_example/main.cpp b/examples/directx9_example/main.cpp index a7333a5d3d48..458f516a910c 100644 --- a/examples/directx9_example/main.cpp +++ b/examples/directx9_example/main.cpp @@ -113,6 +113,7 @@ int main(int, char**) UpdateWindow(hwnd); while (msg.message != WM_QUIT) { + // Poll and handle messages (inputs, window resize, etc.) // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. @@ -123,8 +124,11 @@ int main(int, char**) DispatchMessage(&msg); continue; } + + // Start the ImGui frame ImGui_ImplDX9_NewFrame(); ImGui_ImplWin32_NewFrame(); + ImGui::NewFrame(); // 1. Show a simple window. // Tip: if we don't call ImGui::Begin()/ImGui::End() the widgets automatically appears in a window called "Debug". diff --git a/examples/imgui_impl_allegro5.cpp b/examples/imgui_impl_allegro5.cpp index 3a2a88a88bfd..93ae78f86338 100644 --- a/examples/imgui_impl_allegro5.cpp +++ b/examples/imgui_impl_allegro5.cpp @@ -307,7 +307,4 @@ void ImGui_ImplAllegro5_NewFrame() } al_set_system_mouse_cursor(g_Display, cursor_id); } - - // Start the frame. This call will update the io.WantCaptureMouse, io.WantCaptureKeyboard flag that you can use to dispatch inputs (or not) to your application. - ImGui::NewFrame(); } diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index e03e93bcbfc1..b33c6990ba7f 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -326,9 +326,6 @@ void ImGui_ImplGlfw_NewFrame() else io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; } - - // Start the frame. This call will update the io.WantCaptureMouse, io.WantCaptureKeyboard flag that you can use to dispatch inputs (or not) to your application. - ImGui::NewFrame(); } //-------------------------------------------------------------------------------------------------------- diff --git a/examples/imgui_impl_marmalade.cpp b/examples/imgui_impl_marmalade.cpp index 680c16e15b7f..58c70be24771 100644 --- a/examples/imgui_impl_marmalade.cpp +++ b/examples/imgui_impl_marmalade.cpp @@ -289,9 +289,6 @@ void ImGui_Marmalade_NewFrame() // TODO: Hide OS mouse cursor if ImGui is drawing it // s3ePointerSetInt(S3E_POINTER_HIDE_CURSOR,(io.MouseDrawCursor ? 0 : 1)); - // Start the frame. This call will update the io.WantCaptureMouse, io.WantCaptureKeyboard flag that you can use to dispatch inputs (or not) to your application. - ImGui::NewFrame(); - // Show/hide OSD keyboard if (io.WantTextInput) { diff --git a/examples/imgui_impl_sdl2.cpp b/examples/imgui_impl_sdl2.cpp index eee40265b891..c21f0319f7fc 100644 --- a/examples/imgui_impl_sdl2.cpp +++ b/examples/imgui_impl_sdl2.cpp @@ -302,9 +302,6 @@ void ImGui_ImplSDL2_NewFrame(SDL_Window* window) ImGui_ImplSDL2_UpdateMousePosAndButtons(); ImGui_ImplSDL2_UpdateMouseCursor(); - - // Start the frame. This call will update the io.WantCaptureMouse, io.WantCaptureKeyboard flag that you can use to dispatch inputs (or not) to your application. - ImGui::NewFrame(); } //-------------------------------------------------------------------------------------------------------- diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index f43ebd0c2b92..9debebc5f9d1 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -203,9 +203,6 @@ void ImGui_ImplWin32_NewFrame() g_LastMouseCursor = mouse_cursor; ImGui_ImplWin32_UpdateMouseCursor(); } - - // Start the frame. This call will update the io.WantCaptureMouse, io.WantCaptureKeyboard flag that you can use to dispatch inputs (or not) to your application. - ImGui::NewFrame(); } // Allow compilation with old Windows SDK. MinGW doesn't have default _WIN32_WINNT/WINVER versions. diff --git a/examples/marmalade_example/main.cpp b/examples/marmalade_example/main.cpp index 3fcf15263a87..c09ab7ddb0c6 100644 --- a/examples/marmalade_example/main.cpp +++ b/examples/marmalade_example/main.cpp @@ -52,13 +52,17 @@ int main(int, char**) if (s3eDeviceCheckQuitRequest()) break; + // Poll and handle inputs // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. s3eKeyboardUpdate(); s3ePointerUpdate(); + + // Start the ImGui frame ImGui_Marmalade_NewFrame(); + ImGui::NewFrame(); // 1. Show a simple window. // Tip: if we don't call ImGui::Begin()/ImGui::End() the widgets automatically appears in a window called "Debug". @@ -98,9 +102,9 @@ int main(int, char**) } // Rendering + ImGui::Render(); IwGxSetColClear(clear_color.x * 255, clear_color.y * 255, clear_color.z * 255, clear_color.w * 255); IwGxClear(); - ImGui::Render(); ImGui_Marmalade_RenderDrawData(ImGui::GetDrawData()); IwGxSwapBuffers(); diff --git a/examples/opengl2_example/main.cpp b/examples/opengl2_example/main.cpp index eff0f300da1a..e73e0afab13c 100644 --- a/examples/opengl2_example/main.cpp +++ b/examples/opengl2_example/main.cpp @@ -63,13 +63,17 @@ int main(int, char**) // Main loop while (!glfwWindowShouldClose(window)) { + // Poll and handle events (inputs, window resize, etc.) // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. glfwPollEvents(); + + // Start the ImGui frame ImGui_ImplOpenGL2_NewFrame(); ImGui_ImplGlfw_NewFrame(); + ImGui::NewFrame(); // 1. Show a simple window. // Tip: if we don't call ImGui::Begin()/ImGui::End() the widgets automatically appears in a window called "Debug". diff --git a/examples/opengl3_example/main.cpp b/examples/opengl3_example/main.cpp index be6c03579c39..06414b0cf974 100644 --- a/examples/opengl3_example/main.cpp +++ b/examples/opengl3_example/main.cpp @@ -72,13 +72,17 @@ int main(int, char**) // Main loop while (!glfwWindowShouldClose(window)) { + // Poll and handle events (inputs, window resize, etc.) // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. glfwPollEvents(); + + // Start the ImGui frame ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplGlfw_NewFrame(); + ImGui::NewFrame(); // 1. Show a simple window. // Tip: if we don't call ImGui::Begin()/ImGui::End() the widgets automatically appears in a window called "Debug". diff --git a/examples/sdl_opengl2_example/main.cpp b/examples/sdl_opengl2_example/main.cpp index 36c8ecf02547..133ecb2b2bd7 100644 --- a/examples/sdl_opengl2_example/main.cpp +++ b/examples/sdl_opengl2_example/main.cpp @@ -70,6 +70,7 @@ int main(int, char**) bool done = false; while (!done) { + // Poll and handle events (inputs, window resize, etc.) // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. @@ -81,8 +82,11 @@ int main(int, char**) if (event.type == SDL_QUIT) done = true; } + + // Start the ImGui frame ImGui_ImplOpenGL2_NewFrame(); ImGui_ImplSDL2_NewFrame(window); + ImGui::NewFrame(); // 1. Show a simple window. // Tip: if we don't call ImGui::Begin()/ImGui::End() the widgets automatically appears in a window called "Debug". @@ -122,11 +126,11 @@ int main(int, char**) } // Rendering + ImGui::Render(); glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y); glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w); glClear(GL_COLOR_BUFFER_BIT); //glUseProgram(0); // You may want this if using this code in an OpenGL 3+ context where shaders may be bound - ImGui::Render(); ImGui_ImplOpenGL2_RenderDrawData(ImGui::GetDrawData()); SDL_GL_SwapWindow(window); } diff --git a/examples/sdl_opengl3_example/main.cpp b/examples/sdl_opengl3_example/main.cpp index 69044527b4d1..f2a0e5a80df1 100644 --- a/examples/sdl_opengl3_example/main.cpp +++ b/examples/sdl_opengl3_example/main.cpp @@ -72,6 +72,7 @@ int main(int, char**) bool done = false; while (!done) { + // Poll and handle events (inputs, window resize, etc.) // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. @@ -85,8 +86,11 @@ int main(int, char**) if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window)) done = true; } + + // Start the ImGui frame ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplSDL2_NewFrame(window); + ImGui::NewFrame(); // 1. Show a simple window. // Tip: if we don't call ImGui::Begin()/ImGui::End() the widgets automatically appears in a window called "Debug". diff --git a/examples/sdl_vulkan_example/main.cpp b/examples/sdl_vulkan_example/main.cpp index 50cdb1022cc8..a88c9c1bbb7f 100644 --- a/examples/sdl_vulkan_example/main.cpp +++ b/examples/sdl_vulkan_example/main.cpp @@ -411,6 +411,7 @@ int main(int, char**) bool done = false; while (!done) { + // Poll and handle events (inputs, window resize, etc.) // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. @@ -424,8 +425,11 @@ int main(int, char**) if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_RESIZED && event.window.windowID == SDL_GetWindowID(window)) ImGui_ImplVulkanH_CreateWindowDataSwapChainAndFramebuffer(g_PhysicalDevice, g_Device, &g_WindowData, g_Allocator, (int)event.window.data1, (int)event.window.data2); } + + // Start the ImGui frame ImGui_ImplVulkan_NewFrame(); ImGui_ImplSDL2_NewFrame(window); + ImGui::NewFrame(); // 1. Show a simple window. // Tip: if we don't call ImGui::Begin()/ImGui::End() the widgets automatically appears in a window called "Debug". diff --git a/examples/vulkan_example/main.cpp b/examples/vulkan_example/main.cpp index 2b39fa83cd69..13588c46de29 100644 --- a/examples/vulkan_example/main.cpp +++ b/examples/vulkan_example/main.cpp @@ -423,20 +423,22 @@ int main(int, char**) // Main loop while (!glfwWindowShouldClose(window)) { + // Poll and handle events (inputs, window resize, etc.) // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. glfwPollEvents(); - if (g_ResizeWanted) { ImGui_ImplVulkanH_CreateWindowDataSwapChainAndFramebuffer(g_PhysicalDevice, g_Device, &g_WindowData, g_Allocator, g_ResizeWidth, g_ResizeHeight); g_ResizeWanted = false; } + // Start the ImGui frame ImGui_ImplVulkan_NewFrame(); ImGui_ImplGlfw_NewFrame(); + ImGui::NewFrame(); // 1. Show a simple window. // Tip: if we don't call ImGui::Begin()/ImGui::End() the widgets automatically appears in a window called "Debug". diff --git a/imgui.cpp b/imgui.cpp index 28af3212eb94..0f7edbe641a1 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -264,6 +264,8 @@ You can read releases logs https://github.com/ocornut/imgui/releases for more details. (Viewport Branch) + - 2018/XX/XX (1.XX) - examples: the examples imgui_impl_xxx files have been split to separate platform (Win32, Glfw, SDL2, etc.) from renderer (DX11, OpenGL, Vulkan, etc.) + when adopting new bindings follow the code in examples/ to know which functions to call. - 2018/XX/XX (1.XX) - when multi-viewports are enabled, all positions will be in your natural OS coordinates space. It means that: - reference to hard-coded positions such as in SetNextWindowPos(ImVec2(0,0)) are probably not what you want anymore. you may use GetMainViewport()->Pos to offset hard-coded positions, e.g. SetNextWindowPos(GetMainViewport()->Pos) From 06aa9d8d9a0e4724b3b79ee1154a41f38a197387 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 7 Jun 2018 22:30:10 +0200 Subject: [PATCH 162/828] Examples: Fixed Makefile, batch files. --- examples/directx10_example/build_win32.bat | 2 +- examples/directx11_example/build_win32.bat | 2 +- examples/directx12_example/build_win32.bat | 2 +- examples/directx9_example/build_win32.bat | 2 +- examples/opengl2_example/Makefile | 9 +++++---- examples/opengl2_example/build_win32.bat | 2 +- examples/opengl3_example/Makefile | 9 +++++---- examples/sdl_opengl2_example/build_win32.bat | 2 +- examples/sdl_opengl3_example/Makefile | 9 +++++---- examples/sdl_opengl3_example/build_win32.bat | 2 +- examples/vulkan_example/build_win32.bat | 4 ++-- examples/vulkan_example/build_win64.bat | 4 ++-- 12 files changed, 26 insertions(+), 23 deletions(-) diff --git a/examples/directx10_example/build_win32.bat b/examples/directx10_example/build_win32.bat index e7f4bb2cf2b7..9d806ab74334 100644 --- a/examples/directx10_example/build_win32.bat +++ b/examples/directx10_example/build_win32.bat @@ -1,4 +1,4 @@ @REM Build for Visual Studio compiler. Run your copy of vcvars32.bat or vcvarsall.bat to setup command-line compiler. mkdir Debug -cl /nologo /Zi /MD /I ..\.. /I "%WindowsSdkDir%Include\um" /I "%WindowsSdkDir%Include\shared" /I "%DXSDK_DIR%Include" /D UNICODE /D _UNICODE *.cpp ..\imgui_impl_win32.cpp ..\imgui_impl_dx10.cpp ..\..\*.cpp /FeDebug/directx10_example.exe /FoDebug/ /link /LIBPATH:"%DXSDK_DIR%/Lib/x86" d3d10.lib d3dcompiler.lib +cl /nologo /Zi /MD /I .. /I ..\.. /I "%WindowsSdkDir%Include\um" /I "%WindowsSdkDir%Include\shared" /I "%DXSDK_DIR%Include" /D UNICODE /D _UNICODE *.cpp ..\imgui_impl_win32.cpp ..\imgui_impl_dx10.cpp ..\..\imgui*.cpp /FeDebug/directx10_example.exe /FoDebug/ /link /LIBPATH:"%DXSDK_DIR%/Lib/x86" d3d10.lib d3dcompiler.lib diff --git a/examples/directx11_example/build_win32.bat b/examples/directx11_example/build_win32.bat index b448a5798147..eefeed9859f3 100644 --- a/examples/directx11_example/build_win32.bat +++ b/examples/directx11_example/build_win32.bat @@ -1,4 +1,4 @@ @REM Build for Visual Studio compiler. Run your copy of vcvars32.bat or vcvarsall.bat to setup command-line compiler. mkdir Debug -cl /nologo /Zi /MD /I ..\.. /I "%WindowsSdkDir%Include\um" /I "%WindowsSdkDir%Include\shared" /I "%DXSDK_DIR%Include" /D UNICODE /D _UNICODE *.cpp ..\imgui_impl_win32.cpp ..\imgui_impl_dx11.cpp ..\..\*.cpp /FeDebug/directx11_example.exe /FoDebug/ /link /LIBPATH:"%DXSDK_DIR%/Lib/x86" d3d11.lib d3dcompiler.lib +cl /nologo /Zi /MD /I .. /I ..\.. /I "%WindowsSdkDir%Include\um" /I "%WindowsSdkDir%Include\shared" /I "%DXSDK_DIR%Include" /D UNICODE /D _UNICODE *.cpp ..\imgui_impl_dx11.cpp ..\imgui_impl_win32.cpp ..\..\imgui*.cpp /FeDebug/directx11_example.exe /FoDebug/ /link /LIBPATH:"%DXSDK_DIR%/Lib/x86" d3d11.lib d3dcompiler.lib diff --git a/examples/directx12_example/build_win32.bat b/examples/directx12_example/build_win32.bat index 04fbd5d01691..066e37cbf24e 100644 --- a/examples/directx12_example/build_win32.bat +++ b/examples/directx12_example/build_win32.bat @@ -1,4 +1,4 @@ @REM Build for Visual Studio compiler. Run your copy of vcvars32.bat or vcvarsall.bat to setup command-line compiler. mkdir Debug -cl /nologo /Zi /MD /I ..\.. /I "%WindowsSdkDir%Include\um" /I "%WindowsSdkDir%Include\shared" /D UNICODE /D _UNICODE *.cpp ..\imgui_impl_win32.cpp ..\imgui_impl_dx12.cpp ..\..\*.cpp /FeDebug/directx12_example.exe /FoDebug/ /link d3d12.lib d3dcompiler.lib dxgi.lib +cl /nologo /Zi /MD /I .. /I ..\.. /I "%WindowsSdkDir%Include\um" /I "%WindowsSdkDir%Include\shared" /D UNICODE /D _UNICODE *.cpp ..\imgui_impl_dx12.cpp ..\imgui_impl_win32.cpp ..\..\imgui*.cpp /FeDebug/directx12_example.exe /FoDebug/ /link d3d12.lib d3dcompiler.lib dxgi.lib diff --git a/examples/directx9_example/build_win32.bat b/examples/directx9_example/build_win32.bat index e8edc27d2252..767438650e76 100644 --- a/examples/directx9_example/build_win32.bat +++ b/examples/directx9_example/build_win32.bat @@ -1,3 +1,3 @@ @REM Build for Visual Studio compiler. Run your copy of vcvars32.bat or vcvarsall.bat to setup command-line compiler. mkdir Debug -cl /nologo /Zi /MD /I ..\.. /I "%DXSDK_DIR%/Include" /D UNICODE /D _UNICODE *.cpp ..\imgui_impl_win32.cpp ..\imgui_impl_dx9.cpp ..\..\*.cpp /FeDebug/directx9_example.exe /FoDebug/ /link /LIBPATH:"%DXSDK_DIR%/Lib/x86" d3d9.lib +cl /nologo /Zi /MD /I .. /I ..\.. /I "%DXSDK_DIR%/Include" /D UNICODE /D _UNICODE *.cpp ..\imgui_impl_dx9.cpp ..\imgui_impl_win32.cpp ..\..\imgui*.cpp /FeDebug/directx9_example.exe /FoDebug/ /link /LIBPATH:"%DXSDK_DIR%/Lib/x86" d3d9.lib diff --git a/examples/opengl2_example/Makefile b/examples/opengl2_example/Makefile index cf126b734048..36f8baa59c61 100644 --- a/examples/opengl2_example/Makefile +++ b/examples/opengl2_example/Makefile @@ -15,7 +15,8 @@ #CXX = clang++ EXE = opengl2_example -SOURCES = main.cpp ../imgui_impl_glfw.cpp ../imgui_impl_opengl2.cpp +SOURCES = main.cpp +SOURCES += ../imgui_impl_glfw.cpp ../imgui_impl_opengl2.cpp SOURCES += ../../imgui.cpp ../../imgui_demo.cpp ../../imgui_draw.cpp OBJS = $(addsuffix .o, $(basename $(notdir $(SOURCES)))) @@ -26,7 +27,7 @@ ifeq ($(UNAME_S), Linux) #LINUX ECHO_MESSAGE = "Linux" LIBS = -lGL `pkg-config --static --libs glfw3` - CXXFLAGS = -I../../ `pkg-config --cflags glfw3` + CXXFLAGS = -I../ -I../../ `pkg-config --cflags glfw3` CXXFLAGS += -Wall -Wformat CFLAGS = $(CXXFLAGS) endif @@ -37,7 +38,7 @@ ifeq ($(UNAME_S), Darwin) #APPLE #LIBS += -L/usr/local/lib -lglfw3 LIBS += -L/usr/local/lib -lglfw - CXXFLAGS = -I../../ -I/usr/local/include + CXXFLAGS = -I../ -I../../ -I/usr/local/include CXXFLAGS += -Wall -Wformat CFLAGS = $(CXXFLAGS) endif @@ -46,7 +47,7 @@ ifeq ($(findstring MINGW,$(UNAME_S)),MINGW) ECHO_MESSAGE = "Windows" LIBS = -lglfw3 -lgdi32 -lopengl32 -limm32 - CXXFLAGS = -I../../ -I../libs/gl3w `pkg-config --cflags glfw3` + CXXFLAGS = -I../ -I../../ -I../libs/gl3w `pkg-config --cflags glfw3` CXXFLAGS += -Wall -Wformat CFLAGS = $(CXXFLAGS) endif diff --git a/examples/opengl2_example/build_win32.bat b/examples/opengl2_example/build_win32.bat index fba5030666a9..c7a8073a8500 100644 --- a/examples/opengl2_example/build_win32.bat +++ b/examples/opengl2_example/build_win32.bat @@ -1,3 +1,3 @@ @REM Build for Visual Studio compiler. Run your copy of vcvars32.bat or vcvarsall.bat to setup command-line compiler. mkdir Debug -cl /nologo /Zi /MD /I ..\.. /I ..\libs\glfw\include *.cpp ..\imgui_impl_glfw.cpp ..\imgui_impl_opengl2.cpp ..\..\*.cpp /FeDebug/opengl2_example.exe /FoDebug/ /link /LIBPATH:..\libs\glfw\lib-vc2010-32 glfw3.lib opengl32.lib gdi32.lib shell32.lib +cl /nologo /Zi /MD /I .. /I ..\.. /I ..\libs\glfw\include *.cpp ..\imgui_impl_opengl2.cpp ..\imgui_impl_glfw.cpp ..\..\imgui*.cpp /FeDebug/opengl2_example.exe /FoDebug/ /link /LIBPATH:..\libs\glfw\lib-vc2010-32 glfw3.lib opengl32.lib gdi32.lib shell32.lib diff --git a/examples/opengl3_example/Makefile b/examples/opengl3_example/Makefile index 313ba09ba100..c6ce9a488f81 100644 --- a/examples/opengl3_example/Makefile +++ b/examples/opengl3_example/Makefile @@ -15,7 +15,8 @@ #CXX = clang++ EXE = opengl3_example -SOURCES = main.cpp ../imgui_impl_glfw.cpp ../imgui_impl_opengl3.cpp +SOURCES = main.cpp +SOURCES += ../imgui_impl_glfw.cpp ../imgui_impl_opengl3.cpp SOURCES += ../../imgui.cpp ../../imgui_demo.cpp ../../imgui_draw.cpp SOURCES += ../libs/gl3w/GL/gl3w.c OBJS = $(addsuffix .o, $(basename $(notdir $(SOURCES)))) @@ -27,7 +28,7 @@ ifeq ($(UNAME_S), Linux) #LINUX ECHO_MESSAGE = "Linux" LIBS = -lGL `pkg-config --static --libs glfw3` - CXXFLAGS = -I../../ -I../libs/gl3w `pkg-config --cflags glfw3` + CXXFLAGS = -I../ -I../../ -I../libs/gl3w `pkg-config --cflags glfw3` CXXFLAGS += -Wall -Wformat CFLAGS = $(CXXFLAGS) endif @@ -38,7 +39,7 @@ ifeq ($(UNAME_S), Darwin) #APPLE #LIBS += -L/usr/local/lib -lglfw3 LIBS += -L/usr/local/lib -lglfw - CXXFLAGS = -I../../ -I../libs/gl3w -I/usr/local/include + CXXFLAGS = -I../ -I../../ -I../libs/gl3w -I/usr/local/include CXXFLAGS += -Wall -Wformat CFLAGS = $(CXXFLAGS) endif @@ -47,7 +48,7 @@ ifeq ($(findstring MINGW,$(UNAME_S)),MINGW) ECHO_MESSAGE = "Windows" LIBS = -lglfw3 -lgdi32 -lopengl32 -limm32 - CXXFLAGS = -I../../ -I../libs/gl3w `pkg-config --cflags glfw3` + CXXFLAGS = -I../ -I../../ -I../libs/gl3w `pkg-config --cflags glfw3` CXXFLAGS += -Wall -Wformat CFLAGS = $(CXXFLAGS) endif diff --git a/examples/sdl_opengl2_example/build_win32.bat b/examples/sdl_opengl2_example/build_win32.bat index 7844dc1ff2fd..be0f75a72ce7 100644 --- a/examples/sdl_opengl2_example/build_win32.bat +++ b/examples/sdl_opengl2_example/build_win32.bat @@ -1,3 +1,3 @@ @REM Build for Visual Studio compiler. Run your copy of vcvars32.bat or vcvarsall.bat to setup command-line compiler. mkdir Debug -cl /nologo /Zi /MD /I ..\.. /I ..\libs\gl3w /I %SDL2_DIR%\include *.cpp ..\imgui_impl_sdl2.cpp ..\imgui_impl_opengl2.cpp ..\..\imgui*.cpp ..\libs\gl3w\GL\gl3w.c /FeDebug/sdl_opengl2_example.exe /FoDebug/ /link /libpath:%SDL2_DIR%\lib\x86 SDL2.lib SDL2main.lib opengl32.lib /subsystem:console +cl /nologo /Zi /MD /I .. /I ..\.. /I ..\libs\gl3w /I %SDL2_DIR%\include *.cpp ..\imgui_impl_opengl2.cpp ..\imgui_impl_sdl2.cpp ..\..\imgui*.cpp ..\libs\gl3w\GL\gl3w.c /FeDebug/sdl_opengl2_example.exe /FoDebug/ /link /libpath:%SDL2_DIR%\lib\x86 SDL2.lib SDL2main.lib opengl32.lib /subsystem:console diff --git a/examples/sdl_opengl3_example/Makefile b/examples/sdl_opengl3_example/Makefile index b37d3336d5d3..3870ec86c6fb 100644 --- a/examples/sdl_opengl3_example/Makefile +++ b/examples/sdl_opengl3_example/Makefile @@ -15,7 +15,8 @@ #CXX = clang++ EXE = sdl_opengl3_example -SOURCES = main.cpp ../imgui_impl_sdl2.cpp ../imgui_impl_opengl3.cpp +SOURCES = main.cpp +SOURCES += ../imgui_impl_sdl2.cpp ../imgui_impl_opengl3.cpp SOURCES += ../../imgui.cpp ../../imgui_demo.cpp ../../imgui_draw.cpp SOURCES += ../libs/gl3w/GL/gl3w.c OBJS = $(addsuffix .o, $(basename $(notdir $(SOURCES)))) @@ -27,7 +28,7 @@ ifeq ($(UNAME_S), Linux) #LINUX ECHO_MESSAGE = "Linux" LIBS = -lGL -ldl `sdl2-config --libs` - CXXFLAGS = -I../../ -I../libs/gl3w `sdl2-config --cflags` + CXXFLAGS = -I../ -I../../ -I../libs/gl3w `sdl2-config --cflags` CXXFLAGS += -Wall -Wformat CFLAGS = $(CXXFLAGS) endif @@ -36,7 +37,7 @@ ifeq ($(UNAME_S), Darwin) #APPLE ECHO_MESSAGE = "Mac OS X" LIBS = -framework OpenGL -framework Cocoa -framework IOKit -framework CoreVideo `sdl2-config --libs` - CXXFLAGS = -I../../ -I../libs/gl3w -I/usr/local/include `sdl2-config --cflags` + CXXFLAGS = -I../ -I../../ -I../libs/gl3w -I/usr/local/include `sdl2-config --cflags` CXXFLAGS += -Wall -Wformat CFLAGS = $(CXXFLAGS) endif @@ -45,7 +46,7 @@ ifeq ($(findstring MINGW,$(UNAME_S)),MINGW) ECHO_MESSAGE = "Windows" LIBS = -lgdi32 -lopengl32 -limm32 `pkg-config --static --libs sdl2` - CXXFLAGS = -I../../ -I../libs/gl3w `pkg-config --cflags sdl2` + CXXFLAGS = -I../ -I../../ -I../libs/gl3w `pkg-config --cflags sdl2` CXXFLAGS += -Wall -Wformat CFLAGS = $(CXXFLAGS) endif diff --git a/examples/sdl_opengl3_example/build_win32.bat b/examples/sdl_opengl3_example/build_win32.bat index c6c87c4b0162..e933a86223dd 100644 --- a/examples/sdl_opengl3_example/build_win32.bat +++ b/examples/sdl_opengl3_example/build_win32.bat @@ -1,3 +1,3 @@ @REM Build for Visual Studio compiler. Run your copy of vcvars32.bat or vcvarsall.bat to setup command-line compiler. mkdir Debug -cl /nologo /Zi /MD /I ..\.. /I ..\libs\gl3w /I %SDL2_DIR%\include *.cpp ..\imgui_impl_sdl2.cpp ..\imgui_impl_opengl3.cpp ..\..\imgui*.cpp ..\libs\gl3w\GL\gl3w.c /FeDebug/sdl_opengl3_example.exe /FoDebug/ /link /libpath:%SDL2_DIR%\lib\x86 SDL2.lib SDL2main.lib opengl32.lib /subsystem:console +cl /nologo /Zi /MD /I .. /I ..\.. /I ..\libs\gl3w /I %SDL2_DIR%\include *.cpp ..\imgui_impl_opengl3.cpp ..\imgui_impl_sdl2.cpp ..\..\imgui*.cpp ..\libs\gl3w\GL\gl3w.c /FeDebug/sdl_opengl3_example.exe /FoDebug/ /link /libpath:%SDL2_DIR%\lib\x86 SDL2.lib SDL2main.lib opengl32.lib /subsystem:console diff --git a/examples/vulkan_example/build_win32.bat b/examples/vulkan_example/build_win32.bat index c09ac34ef65f..e578930167f6 100644 --- a/examples/vulkan_example/build_win32.bat +++ b/examples/vulkan_example/build_win32.bat @@ -1,7 +1,7 @@ @REM Build for Visual Studio compiler. Run your copy of vcvars32.bat or vcvarsall.bat to setup command-line compiler. mkdir Debug -cl /nologo /Zi /MD /I ..\.. /I ..\libs\glfw\include /I %VULKAN_SDK%\include *.cpp ..\imgui_impl_glfw.cpp ..\imgui_impl_vulkan.cpp ..\..\*.cpp /FeDebug/vulkan_example.exe /FoDebug/ /link /LIBPATH:..\libs\glfw\lib-vc2010-32 /libpath:%VULKAN_SDK%\lib32 glfw3.lib opengl32.lib gdi32.lib shell32.lib vulkan-1.lib +cl /nologo /Zi /MD /I .. /I ..\.. /I ..\libs\glfw\include /I %VULKAN_SDK%\include *.cpp ..\imgui_impl_vulkan.cpp ..\imgui_impl_glfw.cpp ..\..\imgui*.cpp /FeDebug/vulkan_example.exe /FoDebug/ /link /LIBPATH:..\libs\glfw\lib-vc2010-32 /libpath:%VULKAN_SDK%\lib32 glfw3.lib opengl32.lib gdi32.lib shell32.lib vulkan-1.lib mkdir Release -cl /nologo /Zi /MD /Ox /Oi /I ..\.. /I ..\libs\glfw\include /I %VULKAN_SDK%\include *.cpp ..\imgui_impl_glfw.cpp ..\imgui_impl_vulkan.cpp ..\..\*.cpp /FeRelease/vulkan_example.exe /FoRelease/ /link /LIBPATH:..\libs\glfw\lib-vc2010-32 /libpath:%VULKAN_SDK%\lib32 glfw3.lib opengl32.lib gdi32.lib shell32.lib vulkan-1.lib +cl /nologo /Zi /MD /Ox /Oi /I .. /I ..\.. /I ..\libs\glfw\include /I %VULKAN_SDK%\include *.cpp ..\imgui_impl_vulkan.cpp ..\imgui_impl_glfw.cpp ..\..\imgui*.cpp /FeRelease/vulkan_example.exe /FoRelease/ /link /LIBPATH:..\libs\glfw\lib-vc2010-32 /libpath:%VULKAN_SDK%\lib32 glfw3.lib opengl32.lib gdi32.lib shell32.lib vulkan-1.lib diff --git a/examples/vulkan_example/build_win64.bat b/examples/vulkan_example/build_win64.bat index f7b50247843b..871370c5a268 100644 --- a/examples/vulkan_example/build_win64.bat +++ b/examples/vulkan_example/build_win64.bat @@ -1,7 +1,7 @@ @REM Build for Visual Studio compiler. Run your copy of amd64/vcvars32.bat to setup 64-bit command-line compiler. mkdir Debug -cl /nologo /Zi /MD /I ..\.. /I ..\libs\glfw\include /I %VULKAN_SDK%\include *.cpp ..\imgui_impl_glfw.cpp ..\imgui_impl_vulkan.cpp ..\..\*.cpp /FeDebug/vulkan_example.exe /FoDebug/ /link /LIBPATH:..\libs\glfw\lib-vc2010-64 /libpath:%VULKAN_SDK%\lib glfw3.lib opengl32.lib gdi32.lib shell32.lib vulkan-1.lib +cl /nologo /Zi /MD /I .. /I ..\.. /I ..\libs\glfw\include /I %VULKAN_SDK%\include *.cpp ..\imgui_impl_vulkan.cpp ..\imgui_impl_glfw.cpp ..\..\imgui*.cpp /FeDebug/vulkan_example.exe /FoDebug/ /link /LIBPATH:..\libs\glfw\lib-vc2010-64 /libpath:%VULKAN_SDK%\lib glfw3.lib opengl32.lib gdi32.lib shell32.lib vulkan-1.lib mkdir Release -cl /nologo /Zi /MD /Ox /Oi /I ..\.. /I ..\libs\glfw\include /I %VULKAN_SDK%\include *.cpp ..\imgui_impl_glfw.cpp ..\imgui_impl_vulkan.cpp ..\..\*.cpp /FeRelease/vulkan_example.exe /FoRelease/ /link /LIBPATH:..\libs\glfw\lib-vc2010-64 /libpath:%VULKAN_SDK%\lib glfw3.lib opengl32.lib gdi32.lib shell32.lib vulkan-1.lib +cl /nologo /Zi /MD /Ox /Oi /I .. /I ..\.. /I ..\libs\glfw\include /I %VULKAN_SDK%\include *.cpp ..\imgui_impl_vulkan.cpp ..\imgui_impl_glfw.cpp ..\..\imgui*.cpp /FeRelease/vulkan_example.exe /FoRelease/ /link /LIBPATH:..\libs\glfw\lib-vc2010-64 /libpath:%VULKAN_SDK%\lib glfw3.lib opengl32.lib gdi32.lib shell32.lib vulkan-1.lib From 24fc7c30dd0761014c744d0027e83ce4941c061b Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 7 Jun 2018 23:48:34 +0200 Subject: [PATCH 163/828] Examples: Documentation --- examples/README.txt | 267 ++++++++++++++++++++++++++++++++------------ imgui.h | 2 +- 2 files changed, 195 insertions(+), 74 deletions(-) diff --git a/examples/README.txt b/examples/README.txt index 171c338150b3..960c17d7c2ab 100644 --- a/examples/README.txt +++ b/examples/README.txt @@ -1,108 +1,229 @@ -Those are standalone ready-to-build applications to demonstrate Dear ImGui. -Binaries of some of those demos: http://www.miracleworld.net/imgui/binaries +--------------------------------------- + README FIRST +--------------------------------------- -Third party languages and frameworks bindings: - https://github.com/ocornut/imgui/wiki/Links -(languages: C, C#, ChaiScript, D, Go, Haxe, Odin, Python, Rust, Lua, Pascal) -(other frameworks: OpenGLES, FreeGlut, Cinder, Cocos2d-x, SFML, GML/GameMaker Studio, Irrlicht, - Ogre, OpenSceneGraph, openFrameworks, LOVE, NanoRT, Qt3d, SFML, Unreal Engine 4, etc.) -(extras: RemoteImGui, ImWindow, imgui_wm, etc.) +Dear ImGui is highly portable and only requires a few things to run and render: + + - Providing mouse/keyboard inputs + - Uploading the font atlas texture into graphics memory + - Providing a render function to render indexed textured triangles + - Optional: clipboard support, mouse cursor supports, Windows IME support, etc. + - Optional (Advanced,Beta): platform window API to use multi-viewport. + +This is essentially what the example bindings in this folder are providing + obligatory portability cruft. + +It is important to understand the difference between the core Dear ImGui library (files in the root folder) +and examples bindings which we are describing here (examples/ folder). +You should be able to write bindings for pretty much any platform and any 3D graphics API. With some extra +effort you can even perform the rendering remotely, on a different machine than the one running the logic. + +This folder contains two things: + + - Example bindings for popular platforms/graphics API, which you can use as is or adapt for your own use. + They are the imgui_impl_XXXX files found in the examples/ folder. + + - Example applications (standalone, ready-to-build) using the aforementioned bindings. + They are the in the XXXX_example/ sub-folders. + +You can find binaries of some of those example applications at: + http://www.miracleworld.net/imgui/binaries -TL;DR; +--------------------------------------- + MISC COMMENTS AND SUGGESTIONS +--------------------------------------- + - Newcomers, read 'PROGRAMMER GUIDE' in imgui.cpp for notes on how to setup ImGui in your codebase. + + - Please read the comments and instruction at the top of each file. + - If you are using of the backend provided here, so you can copy the imgui_impl_xxx.cpp/h files - to your project and use them unmodified. - - To LEARN how to setup imgui, you may refer to 'opengl2_example' because is the simplest one to read. - However, do NOT USE the 'opengl2_example' if your code is using any modern GL3+ calls. + to your project and use them unmodified. Each imgui_impl_xxxx.cpp comes with its own individual + ChangeLog at the top of the .cpp files, so if you want to update them later it will be easier to + catch up with what changed. + + - To LEARN how to setup imgui, you may refer to 'opengl2_example/' because is the simplest one to read. + However, do NOT USE the OpenGL2 renderer if your code is using any modern GL3+ calls. Mixing old fixed-pipeline OpenGL2 and modern OpenGL3+ is going to make everything more complicated. - Read comments below for details. If you are not sure, in doubt, use 'opengl3_example'. - - If you have your own engine, you probably want to read a few of the examples first then adapt it to - your engine. Please note that if your engine is based on OpenGL/DirectX you can perfectly use the - existing rendering backends, don't feel forced to rewrite them with your own engine API, or you can - do that later when you already got things to work. + Read comments below for details. If you are not sure, in doubt, use the OpenGL3 renderer. -Dear ImGui is highly portable and only requires a few things to run and render. - - Providing mouse/keyboard inputs - - Load the font atlas texture into graphics memory - - Providing a render function to render indexed textured triangles - - Optional: clipboard support, mouse cursor supports, Windows IME support, etc. -So this is essentially what those examples are doing + the obligatory cruft for portability. - -Unfortunately in 2018 it is still tedious to create and maintain portable build files using external -libraries (the kind we're using here to create a window and render 3D triangles) without relying on -third party software. For most examples here I choose to provide: - - Makefiles for Linux/OSX - - Batch files for Visual Studio 2008+ - - A .sln project file for Visual Studio 2010+ -Please let me know if they don't work with your setup! -You can probably just import the imgui_impl_xxx.cpp/.h files into your own codebase or compile those -directly with a command-line compiler. - -Dear ImGui has zero to one frame of lag for most behaviors, at 60 FPS your experience should be pleasant. -Consider that OS mouse cursors are typically drawn through a specific hardware accelerated route and may -feel smoother than other GPU rendered contents. You may experiment with the io.MouseDrawCursor flag to -request ImGui to draw a mouse cursor itself, to visualize the lag between a hardware cursor and a software -cursor. It might be beneficial to the user experience to switch to a software rendered cursor when an -interactive drag is in progress. -Also note that some setup or GPU drivers may be causing extra lag (possibly by enforcing triple buffering), -leaving you with little option but sadness (Intel GPU drivers were reported as such). + - Dear ImGui has 0 to 1 frame of lag for most behaviors, at 60 FPS your experience should be pleasant. + However, consider that OS mouse cursors are typically drawn through a specific hardware accelerated path + and will feel smoother than common GPU rendered contents (including Dear ImGui windows). + You may experiment with the io.MouseDrawCursor flag to request ImGui to draw a mouse cursor itself, + to visualize the lag between a hardware cursor and a software cursor. However, rendering a mouse cursor + at 60 FPS will feel slow. It might be beneficial to the user experience to switch to a software rendered + cursor only when an interactive drag is in progress. + Note that some setup or GPU drivers are likely to be causing extra lag depending on their settings. + If you are not sure who to blame if you feeling that dragging something is laggy, try to build an + application drawing a shape directly under the mouse cursor. -opengl2_example/ - **DO NOT USE THIS CODE IF YOUR CODE/ENGINE IS USING MODERN OPENGL (SHADERS, VBO, VAO, etc.)** - **Prefer using the code in the opengl3_example/ folder** - GLFW + OpenGL example (legacy, fixed pipeline). - This code is mostly provided as a reference to learn how ImGui integration works, because it is shorter. - If your code is using GL3+ context or any semi modern OpenGL calls, using this renderer is likely to - make things more complicated, will require your code to several OpenGL attributes to their initial state, - and might confuse your GPU driver. +--------------------------------------- + EXAMPLE BINDINGS +--------------------------------------- + +Most the example bindings are split in 2 parts: + + - The "Platform" bindings, in charge of: mouse/keyboard/gamepad inputs, cursor shape, timing, windowing. + Examples: Windows (imgui_impl_win32.cpp), GLFW (imgui_impl_glfw.cpp), SDL2 (imgui_impl_sdl2.cpp) + + - The "Renderer" bindings, in charge of: creating the main font texture, rendering imgui draw data. + Examples: DirectX11 (imgui_impl_dx11.cpp), GL3 (imgui_impl_opengl3.cpp), Vulkan (imgui_impl_vulkan.cpp) + + - The example _applications_ usually combine 1 platform + 1 renderer binding to create a working program. + Examples: the directx11_example/ application combines imgui_impl_win32.cpp + imgui_impl_dx11.cpp. + + - Some bindings for higher level frameworks carry both "Platform" and "Renderer" parts in one file. + This is the case for Allegro 5 (imgui_impl_allegro5.cpp), Marmalade (imgui_impl_marmalade5.cpp). + + - If you use your own engine, you may decide to use some of existing bindings and/or rewrite some using + your own API. As a recommendation, if you are new to Dear ImGui, try using the existing binding as-is + first, before moving on to rewrite some of the code. Although it is tempting to rewrite both of the + imgui_impl_xxxx files to fit under your coding style, consider that it is not necessary! + In fact, if you are new to Dear ImGui, rewriting them will almost always be harder. + + Example: your engine is built over Windows + DirectX11 but you have your own high-level rendering system + layered over DirectX11. + Suggestion: step 1: try using imgui_impl_win32.cpp + imgui_impl_dx11.cpp first. + Once this work, _if_ you want you can replace the imgui_impl_dx11.cpp code with a custom renderer + using your own functions, etc. + Please consider using the bindings to the lower-level platform/graphics API as-is. + + Example: your engine is multi-platform (consoles, phones, etc.), you have high-level systems everywhere. + Suggestion: step 1: try using a non-portable binding first (e.g. win32 + underlying graphics API)! + This is counter-intuitive, but this will get you running faster! Once you better understand how imgui + works and is bound, you can rewrite the code using your own systems. + + - From Dear ImGui 1.XX we added an (optional) feature called "viewport" which allows imgui windows to be + seamlessly detached from the main application window. This is achieved using an extra layer to the + platform and renderer bindings, which allows imgui to communicate platform-specific requests such as + "create an additional OS window", "create a render context", "get the OS position of this window" etc. + When using this feature, the coupling with your OS/renderer becomes much tighter than a regular imgui + integration. It is also much more complicated and require more work to integrate correctly. + If you are new to imgui and you are trying to integrate it into your application, first try to ignore + everything related to Viewport and Platform Windows. You'll be able to come back to it later! + Note that if you decide to use unmodified imgui_impl_xxxx.cpp files, you will automatically benefit from + improvements and fixes related to viewports and platform windows without extra work on your side. + See 'ImGuiPlatformIO' for details. + +List of officially maintained Platforms Bindings: + + imgui_impl_glfw.cpp + imgui_impl_sdl2.cpp + imgui_impl_win32.cpp + +List of officially maintained Renderer Bindings: + + imgui_impl_dx9.cpp + imgui_impl_dx10.cpp + imgui_impl_dx11.cpp + imgui_impl_dx12.cpp + imgui_impl_opengl2.cpp + imgui_impl_opengl3.cpp + imgui_impl_vulkan.cpp + +List of officially maintained high-level Frameworks Bindings (combine Platform + Renderer) + + imgui_impl_allegro5.cpp + imgui_impl_marmalade.cpp + +Third-party framework, graphics API and languages bindings: + + https://github.com/ocornut/imgui/wiki/Links + + Languages: C, C#, ChaiScript, D, Go, Haxe, Java, Lua, Odin, Pascal, PureBasic, Python, Rust, Swift... + Frameworks: FreeGlut, Cinder, Cocos2d-x, Emscripten, SFML, GML/GameMaker Studio, Irrlicht, Ogre, + OpenSceneGraph, openFrameworks, LOVE, NanoRT, Nim Game Lib, Qt3d, SFML, Unreal Engine 4... + Miscellaneous: Software Renderer, RemoteImgui, etc. + + +--------------------------------------- + EXAMPLE APPLICATIONS +--------------------------------------- + +Building: + Unfortunately in 2018 it is still tedious to create and maintain portable build files using external + libraries (the kind we're using here to create a window and render 3D triangles) without relying on + third party software. For most examples here I choose to provide: + - Makefiles for Linux/OSX + - Batch files for Visual Studio 2008+ + - A .sln project file for Visual Studio 2010+ + Please let me know if they don't work with your setup! + You can probably just import the imgui_impl_xxx.cpp/.h files into your own codebase or compile those + directly with a command-line compiler. -opengl3_example/ - GLFW + OpenGL example (programmable pipeline, binding modern functions with GL3W). - This uses more modern OpenGL calls and custom shaders. - Prefer using that if you are using modern OpenGL in your application (anything with shaders). directx9_example/ DirectX9 example, Windows only. - + = main.cpp + imgui_impl_win32.cpp + imgui_impl_dx9.cpp + directx10_example/ DirectX10 example, Windows only. - This is quite long and tedious, because: DirectX10. + = main.cpp + imgui_impl_win32.cpp + imgui_impl_dx10.cpp directx11_example/ DirectX11 example, Windows only. - This is quite long and tedious, because: DirectX11. - + = main.cpp + imgui_impl_win32.cpp + imgui_impl_dx11.cpp + directx12_example/ DirectX12 example, Windows only. - This is quite longer and tedious, because: DirectX12. + This is quite long and tedious, because: DirectX12. + = main.cpp + imgui_impl_win32.cpp + imgui_impl_dx12.cpp + +opengl2_example/ + **DO NOT USE THIS CODE IF YOUR CODE/ENGINE IS USING MODERN OPENGL (SHADERS, VBO, VAO, etc.)** + **Prefer using the code in the opengl3_example/ folder** + GLFW + OpenGL example (legacy, fixed pipeline). + This code is mostly provided as a reference to learn about ImGui integration, because it is shorter. + If your code is using GL3+ context or any semi modern OpenGL calls, using this renderer is likely to + make things more complicated, will require your code to reset many OpenGL attributes to their initial + state, and might confuse your GPU driver. One star, not recommended. + = main.cpp + imgui_impl_glfw.cpp + imgui_impl_opengl2.cpp + +opengl3_example/ + GLFW (Win32, Mac, Linux) + OpenGL example (programmable pipeline, binding modern functions with GL3W). + This uses more modern OpenGL calls and custom shaders. + Prefer using that if you are using modern OpenGL in your application (anything with shaders). + = main.cpp + imgui_impl_glfw.cpp + imgui_impl_opengl3.cpp -apple_example/ - OSX & iOS example. - On iOS, Using Synergy to access keyboard/mouse data from server computer. - Synergy keyboard integration is rather hacky. +vulkan_example/ + Vulkan example. + This is quite long and tedious, because: Vulkan. + = main.cpp + imgui_impl_glfw.cpp + imgui_impl_vulkan.cpp sdl_opengl2_example/ **DO NOT USE THIS CODE IF YOUR CODE/ENGINE IS USING MODERN OPENGL (SHADERS, VBO, VAO, etc.)** **Prefer using the code in the sdl_opengl3_example/ folder** - SDL2 + OpenGL example (legacy, fixed pipeline). - This code is mostly provided as a reference to learn how ImGui integration works, because it is shorter. + SDL2 (Win32, Mac, Linux etc.) + OpenGL example (legacy, fixed pipeline). + This code is mostly provided as a reference to learn about ImGui integration, because it is shorter. If your code is using GL3+ context or any semi modern OpenGL calls, using this renderer is likely to - make things more complicated, will require your code to several OpenGL attributes to their initial state, - and might confuse your GPU driver. + make things more complicated, will require your code to reset many OpenGL attributes to their initial + state, and might confuse your GPU driver. One star, not recommended. + = main.cpp + imgui_impl_sdl2.cpp + imgui_impl_opengl2.cpp sdl_opengl3_example/ - SDL2 + OpenGL3 example. + SDL2 (Win32, Mac, Linux, etc.) + OpenGL3 example. This uses more modern OpenGL calls and custom shaders. Prefer using that if you are using modern OpenGL in your application (anything with shaders). + = main.cpp + imgui_impl_sdl2.cpp + imgui_impl_opengl3.cpp + +sdl_vulkan_example/ + SDL2 (Win32, Mac, Linux, etc.) + Vulkan example. + This is quite long and tedious, because: Vulkan. + = main.cpp + imgui_impl_glfw.cpp + imgui_impl_vulkan.cpp + +apple_example/ + OSX & iOS example + OpenGL2. + THIS EXAMPLE HAS NOT BEEN MAINTAINED PROPERLY AND NEEDS A MAINTAINER. + Consider using the opengl3_example/ instead. + On iOS, Using Synergy to access keyboard/mouse data from server computer. + Synergy keyboard integration is rather hacky. allegro5_example/ Allegro 5 example. - + = main.cpp + imgui_impl_allegro5.cpp + marmalade_example/ Marmalade example using IwGx - -vulkan_example/ - Vulkan example. - This is quite longer and tedious, because: Vulkan. + = main.cpp + imgui_impl_marmalade.cpp diff --git a/imgui.h b/imgui.h index a520c7424d62..a953475f038c 100644 --- a/imgui.h +++ b/imgui.h @@ -1935,7 +1935,7 @@ struct ImGuiPlatformMonitor ImGuiPlatformMonitor() { MainPos = MainSize = WorkPos = WorkSize = ImVec2(0,0); DpiScale = 1.0f; } }; -// (Optional) Setup required only if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) is enabled +// (Optional) Setup required only if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) is enabled. Access via ImGui::GetPlatformIO(). // This is designed so we can mix and match two imgui_impl_xxxx files, one for the Platform (~window handling), one for Renderer. // Custom engine back-ends will often provide both Platform and Renderer interfaces and thus may not need to use all functions. // Platform functions are typically called before their Renderer counterpart, apart from Destroy which are called the other way. From 087ab2acbf938c0a17dba54d6dbe61993f2e9f80 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 13 Jun 2018 15:59:21 +0200 Subject: [PATCH 164/828] Examples: Fixed example vcproj names in Viewport branch. --- examples/imgui_examples.sln | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/imgui_examples.sln b/examples/imgui_examples.sln index dd27ca13197b..488496de9978 100644 --- a/examples/imgui_examples.sln +++ b/examples/imgui_examples.sln @@ -9,19 +9,19 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_win32_directx10", " EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_win32_directx11", "example_win32_directx11\example_win32_directx11.vcxproj", "{9F316E83-5AE5-4939-A723-305A94F48005}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_win32_directx12", "example_win32_directx12\example_win32_directx12.vcxproj", "{B4CF9797-519D-4AFE-A8F4-5141A6B521D3}" +EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_glfw_opengl2", "example_glfw_opengl2\example_glfw_opengl2.vcxproj", "{9CDA7840-B7A5-496D-A527-E95571496D18}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_glfw_opengl3", "example_glfw_opengl3\example_glfw_opengl3.vcxproj", "{4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sdl_opengl2_example", "sdl_opengl2_example\sdl_opengl2_example.vcxproj", "{2AE17FDE-F7F3-4CAC-ADAB-0710EDA4F741}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sdl_opengl3_example", "sdl_opengl3_example\sdl_opengl3_example.vcxproj", "{BBAEB705-1669-40F3-8567-04CF6A991F4C}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_glfw_vulkan", "example_glfw_vulkan\example_glfw_vulkan.vcxproj", "{57E2DF5A-6FC8-45BB-99DD-91A18C646E80}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vulkan_example", "vulkan_example\vulkan_example.vcxproj", "{57E2DF5A-6FC8-45BB-99DD-91A18C646E80}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_sdl_opengl2", "example_sdl_opengl2\example_sdl_opengl2.vcxproj", "{2AE17FDE-F7F3-4CAC-ADAB-0710EDA4F741}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sdl_vulkan_example", "sdl_vulkan_example\sdl_vulkan_example.vcxproj", "{BAE3D0B5-9695-4EB1-AD0F-75890EB4A3B3}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_sdl_opengl3", "example_sdl_opengl3\example_sdl_opengl3.vcxproj", "{BBAEB705-1669-40F3-8567-04CF6A991F4C}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "directx12_example", "directx12_example\directx12_example.vcxproj", "{B4CF9797-519D-4AFE-A8F4-5141A6B521D3}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_sdl_vulkan", "example_sdl_vulkan\example_sdl_vulkan.vcxproj", "{BAE3D0B5-9695-4EB1-AD0F-75890EB4A3B3}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution From c71522adc5f56317c2d5f679bb39a98e69a32953 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 15 Jun 2018 19:20:09 +0200 Subject: [PATCH 165/828] Viewport: Comments. Removed misleading test for !window->ViewportOwned in UpdateSelectWindowViewport(). (#1541) --- imgui.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index b90308219821..fa2de1edf55b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3617,9 +3617,11 @@ static void ImGui::UpdateMovingWindow() } else { + // Try to merge the window back into the main viewport. + // This works because MouseRefViewport shouldn't be == MovingWindow->Viewport which should have the NoInputs flag during moving. UpdateTryMergeWindowIntoHostViewport(moving_window, g.MouseRefViewport); - // Patch the mouse viewport so that we don't hover under the moved window during the mouse released frame + // Restore the mouse viewport so that we don't hover the viewport _under_ the moved window during the frame we released the mouse button. if (!IsDragDropPayloadBeingAccepted()) g.MouseRefViewport = moving_window->Viewport; @@ -6608,12 +6610,13 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) { window->Viewport = g.MouseRefViewport; } - else if (!window->ViewportOwned && g.MovingWindow && g.MovingWindow->RootWindow == window && IsMousePosValid()) + else if (g.MovingWindow && g.MovingWindow->RootWindow == window && IsMousePosValid()) { + // Transition to a standalone viewport if (!window->Viewport->GetRect().Contains(window->Rect())) window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoDecoration | ImGuiViewportFlags_NoFocusOnAppearing | ImGuiViewportFlags_NoInputs); } - else if (!window->ViewportOwned && GetWindowAlwaysWantOwnViewport(window)) + else if (GetWindowAlwaysWantOwnViewport(window)) { window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoDecoration); } @@ -7176,7 +7179,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) viewport_rect = window->Viewport->GetRect(); } - // Save last knonw viewport position within the window itself (so it can be saved in .ini file and restored) + // Save last known viewport position within the window itself (so it can be saved in .ini file and restored) window->ViewportPos = window->Viewport->Pos; // Default item width. Make it proportional to window size if window manually resizes @@ -14881,9 +14884,8 @@ void ImGui::ShowMetricsWindow(bool* p_open) if (ImGui::TreeNode((void*)(intptr_t)viewport->ID, "Viewport #%d, ID: 0x%08X, Window: \"%s\"", viewport->Idx, viewport->ID, viewport->Window ? viewport->Window->Name : "N/A")) { ImGuiWindowFlags flags = viewport->Flags; - ImGui::BulletText("Pos: (%.0f,%.0f), Monitor: %d", viewport->Pos.x, viewport->Pos.y, viewport->PlatformMonitor); - if (viewport->Idx > 0) { ImGui::SameLine(); if (ImGui::SmallButton("Reset")) { viewport->Pos = ImVec2(200,200); if (viewport->Window) viewport->Window->Pos = ImVec2(200,200); } } - ImGui::BulletText("Size: (%0.f,%.0f), DpiScale: %.0f%%", viewport->Size.x, viewport->Size.y, viewport->DpiScale * 100.0f); + ImGui::BulletText("Pos: (%.0f,%.0f), Size: (%.0f, %.0f), Monitor: %d, DpiScale: %.0f%%", viewport->Pos.x, viewport->Pos.y, viewport->Size.x, viewport->Size.y, viewport->PlatformMonitor, viewport->DpiScale * 100.0f); + if (viewport->Idx > 0) { ImGui::SameLine(); if (ImGui::SmallButton("Reset Pos")) { viewport->Pos = ImVec2(200,200); if (viewport->Window) viewport->Window->Pos = ImVec2(200,200); } } ImGui::BulletText("Flags: 0x%04X =%s%s%s%s%s", viewport->Flags, (flags & ImGuiViewportFlags_CanHostOtherWindows) ? " CanHostOtherWindows" : "", (flags & ImGuiViewportFlags_NoDecoration) ? " NoDecoration" : "", (flags & ImGuiViewportFlags_NoFocusOnAppearing) ? " NoFocusOnAppearing" : "", (flags & ImGuiViewportFlags_NoInputs) ? " NoInputs" : "", From 7e6700d261388150f1eaff9dd41f75ba68370b5f Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 15 Jun 2018 19:38:17 +0200 Subject: [PATCH 166/828] Viewport: Fix lagging overlay clipping rectangle on viewport owning window (affecting sync of multi-layered docking overlays). This was extremely tricky to find and fix (*). (#1541) (*) Merely assigning viewport->Pos = pos in UpdateMovingWindow() broke a series of thing because the code that assign viewports and viewport flags relied on moving window leaving its own viewport the first time to set the NoInputs flag. --- imgui.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index fa2de1edf55b..5cfcdc83cc2a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3612,6 +3612,8 @@ static void ImGui::UpdateMovingWindow() { MarkIniSettingsDirty(moving_window); SetWindowPos(moving_window, pos, ImGuiCond_Always); + if (moving_window->ViewportOwned) // Synchronize viewport immediately because some overlays may relies on clipping rectangle before we Begin() into the window. + moving_window->Viewport->Pos = pos; } FocusWindow(g.MovingWindow); } @@ -6612,8 +6614,12 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) } else if (g.MovingWindow && g.MovingWindow->RootWindow == window && IsMousePosValid()) { - // Transition to a standalone viewport - if (!window->Viewport->GetRect().Contains(window->Rect())) + // Transition to our own viewport when leaving our host boundaries + set the NoInputs flag (which will be cleared in UpdateMovingWindow when releasing the mouse) + // If we are already in our own viewport, if need to set the NoInputs flag. + bool own_viewport = window->Viewport->Window == window; // We test window->Viewport->Window because window->ViewportOwned is not valid during this function. + bool leave_host_viewport = !own_viewport && !window->Viewport->GetRect().Contains(window->Rect()); + bool move_from_own_viewport = own_viewport && !(window->Viewport->Flags & ImGuiViewportFlags_NoInputs); + if (leave_host_viewport || move_from_own_viewport) window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoDecoration | ImGuiViewportFlags_NoFocusOnAppearing | ImGuiViewportFlags_NoInputs); } else if (GetWindowAlwaysWantOwnViewport(window)) From 9a5f742e63f0ed98f1c34b7b771614620336f036 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 15 Jun 2018 19:48:51 +0200 Subject: [PATCH 167/828] Fixed merge. --- examples/imgui_impl_opengl2.cpp | 6 +----- examples/imgui_impl_opengl2.h | 6 +----- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/examples/imgui_impl_opengl2.cpp b/examples/imgui_impl_opengl2.cpp index e39b0f5f5ed2..cb55187ce78b 100644 --- a/examples/imgui_impl_opengl2.cpp +++ b/examples/imgui_impl_opengl2.cpp @@ -2,12 +2,8 @@ // This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..) // Implemented features: -<<<<<<< HEAD -// [X] Renderer: User texture binding. Use 'GLUint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. -// [X] Renderer: Multi-viewport support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. -======= // [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. ->>>>>>> master +// [X] Renderer: Multi-viewport support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. // **DO NOT USE THIS CODE IF YOUR CODE/ENGINE IS USING MODERN OPENGL (SHADERS, VBO, VAO, etc.)** // **Prefer using the code in imgui_impl_opengl3.cpp** diff --git a/examples/imgui_impl_opengl2.h b/examples/imgui_impl_opengl2.h index f873424cfcf1..7a0011de605d 100644 --- a/examples/imgui_impl_opengl2.h +++ b/examples/imgui_impl_opengl2.h @@ -2,12 +2,8 @@ // This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..) // Implemented features: -<<<<<<< HEAD -// [X] Renderer: User texture binding. Use 'GLUint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. -// [X] Renderer: Multi-viewport support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. -======= // [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. ->>>>>>> master +// [X] Renderer: Multi-viewport support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. // **DO NOT USE THIS CODE IF YOUR CODE/ENGINE IS USING MODERN OPENGL (SHADERS, VBO, VAO, etc.)** // **Prefer using the code in imgui_impl_opengl3.cpp** From d3ee3e7ab51cf73dd4214b9cc4523eac04d306ec Mon Sep 17 00:00:00 2001 From: omar Date: Sat, 23 Jun 2018 09:39:05 +0200 Subject: [PATCH 168/828] Fixed merge. --- examples/imgui_impl_vulkan.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/imgui_impl_vulkan.cpp b/examples/imgui_impl_vulkan.cpp index aa01884e5d0c..aaca353256e6 100644 --- a/examples/imgui_impl_vulkan.cpp +++ b/examples/imgui_impl_vulkan.cpp @@ -1175,7 +1175,7 @@ static void ImGui_ImplVulkan_RenderWindow(ImGuiViewport* viewport, void*) } } - ImGui_ImplVulkan_RenderDrawData(wd->Frames[wd->FrameIndex].CommandBuffer, viewport->DrawData); + ImGui_ImplVulkan_RenderDrawData(viewport->DrawData, wd->Frames[wd->FrameIndex].CommandBuffer); { ImGui_ImplVulkanH_FrameData* fd = &wd->Frames[wd->FrameIndex]; From dd61c4802a0afdc043d1c3ac1cbe7ed3a0b81e7f Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 9 Jul 2018 19:16:48 +0200 Subject: [PATCH 169/828] Nav: Added a CTRL+TAB window list and changed the highlight system accordingly. (#787) --- CHANGELOG.txt | 2 + imgui.cpp | 129 +++++++++++++++++++++++++++++++++++++---------- imgui.h | 6 ++- imgui_demo.cpp | 2 +- imgui_draw.cpp | 15 +++--- imgui_internal.h | 7 +-- 6 files changed, 122 insertions(+), 39 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 86bdb8647fff..5f3f08819c40 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -37,11 +37,13 @@ Breaking Changes: - Removed per-window ImGuiWindowFlags_ResizeFromAnySide beta flag in favor `io.OptResizeWindowsFromEdges=true` to enable the feature globally. (#1495) The feature is not currently enabled by default because it is not satisfying enough. + - Style: Renamed ImGuiCol_ModalWindowDarkening to ImGuiCol_ModalWindowDimBg for consistency with other features. Kept redirection enum (will obsolete). Other Changes: - ArrowButton: Fixed to honor PushButtonRepeat() setting (and internals' ImGuiItemFlags_ButtonRepeat). - ArrowButton: Setup current line text baseline so that ArrowButton() + SameLine() + Text() are aligned properly. + - Nav: Added a CTRL+TAB window list and changed the highlight system accordingly. This is designed to allow CTRL+TAB between Tabs in the future. - Window: Allow menu and popups windows from ignoring the style.WindowMinSize values so short menus/popups are not padded. (#1909) - Window: Added global io.OptResizeWindowsFromEdges option to enable resizing windows from their edges and from the lower-left corner. (#1495) - Drag and Drop: Fixed an incorrect assert when dropping a source that is submitted after the target (bug introduced with 1.62 changes diff --git a/imgui.cpp b/imgui.cpp index 70b81eaba2cd..b269b1406018 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -316,6 +316,7 @@ - 2018/XX/XX (1.XX) - Moved IME support functions from io.ImeSetInputScreenPosFn, io.ImeWindowHandle to the PlatformIO api. - 2018/XX/XX (1.XX) - removed io.DisplayVisibleMin, io.DisplayVisibleMax settings (it was used to clip within the DisplayMin..DisplayMax range, I don't know of anyone using it) + - 2018/07/08 (1.63) - style: renamed ImGuiCol_ModalWindowDarkening to ImGuiCol_ModalWindowDimBg for consistency with other features. Kept redirection enum (will obsolete). - 2018/07/06 (1.63) - removed per-window ImGuiWindowFlags_ResizeFromAnySide beta flag in favor of a global io.OptResizeWindowsFromEdges to enable the feature. - 2018/06/06 (1.62) - renamed GetGlyphRangesChinese() to GetGlyphRangesChineseFull() to distinguish other variants and discourage using the full set. - 2018/06/06 (1.62) - TreeNodeEx()/TreeNodeBehavior(): the ImGuiTreeNodeFlags_CollapsingHeader helper now include the ImGuiTreeNodeFlags_NoTreePushOnOpen flag. See Changelog for details. @@ -901,6 +902,7 @@ static bool BeginChildEx(const char* name, ImGuiID id, const ImVec2& static void NavUpdate(); static void NavUpdateWindowing(); +static void NavUpdateWindowingList(); static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id); static void UpdateMouseInputs(); @@ -3104,7 +3106,8 @@ static void NavUpdateWindowingHighlightWindow(int focus_change_dir) ImGuiWindow* window_target = FindWindowNavigable(i_current + focus_change_dir, -INT_MAX, focus_change_dir); if (!window_target) window_target = FindWindowNavigable((focus_change_dir < 0) ? (g.Windows.Size - 1) : 0, i_current, focus_change_dir); - g.NavWindowingTarget = window_target; + if (window_target) // Don't reset windowing target if there's a single window in the list + g.NavWindowingTarget = window_target; g.NavWindowingToggleLayer = false; } @@ -3189,7 +3192,7 @@ static void ImGui::NavUpdateWindowing() { const float NAV_MOVE_SPEED = 800.0f; const float move_speed = ImFloor(NAV_MOVE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y)); // FIXME: Doesn't code variable framerate very well - g.NavWindowingTarget->Pos += move_delta * move_speed; + g.NavWindowingTarget->RootWindow->Pos += move_delta * move_speed; g.NavDisableMouseHover = true; MarkIniSettingsDirty(g.NavWindowingTarget); } @@ -3231,6 +3234,47 @@ static void ImGui::NavUpdateWindowing() } } +// Window has already passed the IsWindowNavFocusable() +static const char* GetFallbackWindowNameForWindowingList(ImGuiWindow* window) +{ + if (window->Flags & ImGuiWindowFlags_Popup) + return "(Popup)"; + if ((window->Flags & ImGuiWindowFlags_MenuBar) && strcmp(window->Name, "##MainMenuBar") == 0) + return "(Main menu bar)"; + return "(Untitled)"; +} + +// Overlay displayed when using CTRL+TAB. Called by EndFrame(). +void ImGui::NavUpdateWindowingList() +{ + ImGuiContext& g = *GImGui; + if (!g.NavWindowingTarget) + { + g.NavWindowingList = NULL; + return; + } + + if (g.NavWindowingList == NULL) + g.NavWindowingList = FindWindowByName("###NavWindowList"); + ImGuiViewportP* viewport = /*g.NavWindow ? g.NavWindow->Viewport :*/ (ImGuiViewportP*)GetMainViewport(); + SetNextWindowSizeConstraints(ImVec2(viewport->Size.x * 0.20f, viewport->Size.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX)); + SetNextWindowPos(viewport->Pos + viewport->Size * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f)); + PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f); + Begin("###NavWindowList", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize); + for (int n = g.Windows.Size - 1; n >= 0; n--) + { + ImGuiWindow* window = g.Windows[n]; + if (!IsWindowNavFocusable(window)) + continue; + const char* label = window->Name; + if (label == FindRenderedTextEnd(label)) + label = GetFallbackWindowNameForWindowingList(window); + Selectable(label, g.NavWindowingTarget == window); + } + End(); + PopStyleVar(); +} + // Scroll to keep newly navigated item fully into view // NB: We modify rect_rel by the amount we scrolled for, so it is immediately updated. static void NavScrollToBringItemIntoView(ImGuiWindow* window, const ImRect& item_rect) @@ -3951,16 +3995,19 @@ void ImGui::UpdatePlatformWindows() viewport->RendererLastSize = viewport->Size; // Update title bar (if it changed) - const char* title_begin = viewport->Window->Name; - char* title_end = (char*)(intptr_t)ImGui::FindRenderedTextEnd(title_begin); - const ImGuiID title_hash = ImHash(title_begin, (int)(title_end - title_begin)); - if (viewport->LastNameHash != title_hash) + if (ImGuiWindow* window_for_title = viewport->Window) { - char title_end_backup_c = *title_end; - *title_end = 0; // Cut existing buffer short instead of doing an alloc/free - g.PlatformIO.Platform_SetWindowTitle(viewport, title_begin); - *title_end = title_end_backup_c; - viewport->LastNameHash = title_hash; + const char* title_begin = window_for_title->Name; + char* title_end = (char*)(intptr_t)ImGui::FindRenderedTextEnd(title_begin); + const ImGuiID title_hash = ImHash(title_begin, (int)(title_end - title_begin)); + if (viewport->LastNameHash != title_hash) + { + char title_end_backup_c = *title_end; + *title_end = 0; // Cut existing buffer short instead of doing an alloc/free + g.PlatformIO.Platform_SetWindowTitle(viewport, title_begin); + *title_end = title_end_backup_c; + viewport->LastNameHash = title_hash; + } } // Update alpha @@ -4300,10 +4347,10 @@ void ImGui::NewFrame() UpdateHoveredWindowAndCaptureFlags(); // Background darkening/whitening - if (GetFrontMostPopupModal() != NULL) - g.ModalWindowDarkeningRatio = ImMin(g.ModalWindowDarkeningRatio + g.IO.DeltaTime * 6.0f, 1.0f); + if (GetFrontMostPopupModal() != NULL || g.NavWindowingTarget != NULL) + g.DimBgRatio = ImMin(g.DimBgRatio + g.IO.DeltaTime * 6.0f, 1.0f); else - g.ModalWindowDarkeningRatio = 0.0f; + g.DimBgRatio = 0.0f; g.MouseCursor = ImGuiMouseCursor_Arrow; g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1; @@ -4856,6 +4903,27 @@ void ImGui::EndFrame() g.PlatformImePosViewport = NULL; } + // Draw modal whitening background on _other_ viewports than the one the modal is one + ImGuiWindow* modal_window = GetFrontMostPopupModal(); + const bool dim_bg_for_modal = (modal_window != NULL); + const bool dim_bg_for_window_list = (g.NavWindowingTarget != NULL); + if (dim_bg_for_modal || dim_bg_for_window_list) + for (int viewport_n = 0; viewport_n < g.Viewports.Size; viewport_n++) + { + ImGuiViewportP* viewport = g.Viewports[viewport_n]; + if (modal_window && viewport == modal_window->Viewport) + continue; + if (g.NavWindowingList && viewport == g.NavWindowingList->Viewport) + continue; + if (g.NavWindowingTarget && viewport == g.NavWindowingTarget->Viewport) + continue; + ImDrawList* draw_list = GetOverlayDrawList(viewport); + const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowListDimBg, g.DimBgRatio); + draw_list->AddRectFilled(viewport->Pos, viewport->Pos + viewport->Size, dim_bg_col); + } + + NavUpdateWindowingList(); + // Hide implicit "Debug" window if it hasn't been used IM_ASSERT(g.CurrentWindowStack.Size == 1); // Mismatched Begin()/End() calls, did you forget to call end on g.CurrentWindow->Name? if (g.CurrentWindow && !g.CurrentWindow->WriteAccessed) @@ -4962,15 +5030,18 @@ void ImGui::Render() g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = g.IO.MetricsActiveWindows = 0; for (int n = 0; n != g.Viewports.Size; n++) g.Viewports[n]->DrawDataBuilder.Clear(); - ImGuiWindow* window_to_render_front_most = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget : NULL; + ImGuiWindow* windows_to_render_front_most[2]; + windows_to_render_front_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindow : NULL; + windows_to_render_front_most[1] = (g.NavWindowingList); for (int n = 0; n != g.Windows.Size; n++) { ImGuiWindow* window = g.Windows[n]; - if (IsWindowActiveAndVisible(window) && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != window_to_render_front_most) + if (IsWindowActiveAndVisible(window) && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != windows_to_render_front_most[0] && window != windows_to_render_front_most[1]) AddRootWindowToDrawData(window); } - if (window_to_render_front_most && IsWindowActiveAndVisible(window_to_render_front_most)) // NavWindowingTarget is always temporarily displayed as the front-most window - AddRootWindowToDrawData(window_to_render_front_most); + for (int n = 0; n < IM_ARRAYSIZE(windows_to_render_front_most); n++) + if (windows_to_render_front_most[n] && IsWindowActiveAndVisible(windows_to_render_front_most[n])) // NavWindowingTarget is always temporarily displayed as the front-most window + AddRootWindowToDrawData(windows_to_render_front_most[n]); // Draw software mouse cursor if requested ImVec2 offset, size, uv[4]; @@ -6843,7 +6914,7 @@ static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au PopID(); // Navigation resize (keyboard/gamepad) - if (g.NavWindowingTarget == window) + if (g.NavWindowingTarget && g.NavWindowingTarget->RootWindow == window) { ImVec2 nav_resize_delta; if (g.NavInputSource == ImGuiInputSource_NavKeyboard && g.IO.KeyShift) @@ -7294,27 +7365,31 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) PushClipRect(viewport_rect.Min, viewport_rect.Max, true); // Draw modal window background (darkens what is behind them, all viewports) - if ((flags & ImGuiWindowFlags_Modal) != 0 && window == GetFrontMostPopupModal() && window->HiddenFrames <= 0) + const bool dim_bg_for_modal = (flags & ImGuiWindowFlags_Modal) && window == GetFrontMostPopupModal() && window->HiddenFrames <= 0; + const bool dim_bg_for_window_list = g.NavWindowingTarget && ((window == g.NavWindowingTarget->RootWindow) || (g.NavWindowingList && (window == g.NavWindowingList) && g.NavWindowingList->Viewport != g.NavWindowingTarget->Viewport)); + if (dim_bg_for_modal || dim_bg_for_window_list) for (int viewport_n = 0; viewport_n < g.Viewports.Size; viewport_n++) { ImGuiViewportP* viewport = g.Viewports[viewport_n]; ImDrawList* draw_list = (viewport == window->Viewport) ? window->DrawList : GetOverlayDrawList(viewport); - draw_list->AddRectFilled(viewport->Pos, viewport->Pos + viewport->Size, GetColorU32(ImGuiCol_ModalWindowDarkening, g.ModalWindowDarkeningRatio)); + const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowListDimBg, g.DimBgRatio); + draw_list->AddRectFilled(viewport->Pos, viewport->Pos + viewport->Size, dim_bg_col); } // Draw navigation selection/windowing rectangle background - if (g.NavWindowingTarget == window) + if (dim_bg_for_window_list && window == g.NavWindowingTarget->RootWindow) { ImRect bb = window->Rect(); bb.Expand(g.FontSize); if (!bb.Contains(viewport_rect)) // Avoid drawing if the window covers all the viewport anyway - window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha * 0.25f), g.Style.WindowRounding); + window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowListHighlight, g.NavWindowingHighlightAlpha * 0.25f), g.Style.WindowRounding); } // Draw window + handle manual resize const float window_rounding = window->WindowRounding; const float window_border_size = window->WindowBorderSize; - const bool title_bar_is_highlight = want_focus || (g.NavWindow && window->RootWindowForTitleBarHighlight == g.NavWindow->RootWindowForTitleBarHighlight); + const ImGuiWindow* window_to_highlight = g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow; + const bool title_bar_is_highlight = want_focus || (window_to_highlight && window->RootWindowForTitleBarHighlight == window_to_highlight->RootWindowForTitleBarHighlight); const ImRect title_bar_rect = window->TitleBarRect(); if (window->Collapsed) { @@ -7396,7 +7471,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) bb.Expand(-g.FontSize - 1.0f); rounding = window->WindowRounding; } - window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), rounding, ~0, 3.0f); + window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowListHighlight, g.NavWindowingHighlightAlpha), rounding, ~0, 3.0f); } // Store a backup of SizeFull which we will use next frame to decide if we need scrollbars. @@ -8105,10 +8180,10 @@ const char* ImGui::GetStyleColorName(ImGuiCol idx) case ImGuiCol_PlotHistogram: return "PlotHistogram"; case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered"; case ImGuiCol_TextSelectedBg: return "TextSelectedBg"; - case ImGuiCol_ModalWindowDarkening: return "ModalWindowDarkening"; case ImGuiCol_DragDropTarget: return "DragDropTarget"; case ImGuiCol_NavHighlight: return "NavHighlight"; - case ImGuiCol_NavWindowingHighlight: return "NavWindowingHighlight"; + case ImGuiCol_NavWindowListDimBg: return "NavWindowListDimBg"; + case ImGuiCol_ModalWindowDimBg: return "ModalWindowDimBg"; } IM_ASSERT(0); return "Unknown"; diff --git a/imgui.h b/imgui.h index ada7478ac292..159e73205350 100644 --- a/imgui.h +++ b/imgui.h @@ -921,15 +921,17 @@ enum ImGuiCol_ ImGuiCol_PlotHistogram, ImGuiCol_PlotHistogramHovered, ImGuiCol_TextSelectedBg, - ImGuiCol_ModalWindowDarkening, // Darken/colorize entire screen behind a modal window, when one is active ImGuiCol_DragDropTarget, ImGuiCol_NavHighlight, // Gamepad/keyboard: current highlighted item - ImGuiCol_NavWindowingHighlight, // Gamepad/keyboard: when holding NavMenu to focus/move/resize windows + ImGuiCol_NavWindowListHighlight,// Highlight window when using CTRL+TAB + ImGuiCol_NavWindowListDimBg, // Darken/colorize entire screen behind the CTRL+TAB window list, when active + ImGuiCol_ModalWindowDimBg, // Darken/colorize entire screen behind a modal window, when one is active ImGuiCol_COUNT // Obsolete names (will be removed) #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS , ImGuiCol_ChildWindowBg = ImGuiCol_ChildBg, ImGuiCol_Column = ImGuiCol_Separator, ImGuiCol_ColumnHovered = ImGuiCol_SeparatorHovered, ImGuiCol_ColumnActive = ImGuiCol_SeparatorActive + , ImGuiCol_ModalWindowDarkening = ImGuiCol_ModalWindowDimBg //ImGuiCol_CloseButton, ImGuiCol_CloseButtonActive, ImGuiCol_CloseButtonHovered, // [unused since 1.60+] the close button now uses regular button colors. //ImGuiCol_ComboBg, // [unused since 1.53+] ComboBg has been merged with PopupBg, so a redirect isn't accurate. #endif diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 8eee311fa40e..0d8b309f716a 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1860,7 +1860,7 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::OpenPopup("Stacked 1"); if (ImGui::BeginPopupModal("Stacked 1")) { - ImGui::Text("Hello from Stacked The First\nUsing style.Colors[ImGuiCol_ModalWindowDarkening] for darkening."); + ImGui::Text("Hello from Stacked The First\nUsing style.Colors[ImGuiCol_ModalWindowDimBg] behind it."); static int item = 1; ImGui::Combo("Combo", &item, "aaaa\0bbbb\0cccc\0dddd\0eeee\0\0"); static float color[4] = { 0.4f,0.7f,0.0f,0.5f }; diff --git a/imgui_draw.cpp b/imgui_draw.cpp index dfb8bb4b8bfe..5d7418197559 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -194,10 +194,11 @@ void ImGui::StyleColorsDark(ImGuiStyle* dst) colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f); colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f); - colors[ImGuiCol_ModalWindowDarkening] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f); colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f); colors[ImGuiCol_NavHighlight] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); - colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f); + colors[ImGuiCol_NavWindowListHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f); + colors[ImGuiCol_NavWindowListDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f); + colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f); } void ImGui::StyleColorsClassic(ImGuiStyle* dst) @@ -243,10 +244,11 @@ void ImGui::StyleColorsClassic(ImGuiStyle* dst) colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f); colors[ImGuiCol_TextSelectedBg] = ImVec4(0.00f, 0.00f, 1.00f, 0.35f); - colors[ImGuiCol_ModalWindowDarkening] = ImVec4(0.20f, 0.20f, 0.20f, 0.35f); colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f); colors[ImGuiCol_NavHighlight] = colors[ImGuiCol_HeaderHovered]; - colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f); + colors[ImGuiCol_NavWindowListHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f); + colors[ImGuiCol_NavWindowListDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f); + colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.20f, 0.20f, 0.20f, 0.35f); } // Those light colors are better suited with a thicker font than the default one + FrameBorder @@ -293,10 +295,11 @@ void ImGui::StyleColorsLight(ImGuiStyle* dst) colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.45f, 0.00f, 1.00f); colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f); - colors[ImGuiCol_ModalWindowDarkening] = ImVec4(0.20f, 0.20f, 0.20f, 0.35f); colors[ImGuiCol_DragDropTarget] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f); colors[ImGuiCol_NavHighlight] = colors[ImGuiCol_HeaderHovered]; - colors[ImGuiCol_NavWindowingHighlight] = ImVec4(0.70f, 0.70f, 0.70f, 0.70f); + colors[ImGuiCol_NavWindowListHighlight] = ImVec4(0.70f, 0.70f, 0.70f, 0.70f); + colors[ImGuiCol_NavWindowListDimBg] = ImVec4(0.20f, 0.20f, 0.20f, 0.20f); + colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.20f, 0.20f, 0.20f, 0.35f); } //----------------------------------------------------------------------------- diff --git a/imgui_internal.h b/imgui_internal.h index f6514c8614b5..d13a9631ef61 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -704,6 +704,7 @@ struct ImGuiContext ImRect NavScoringRectScreen; // Rectangle used for scoring, in screen space. Based of window->DC.NavRefRectRel[], modified for directional navigation scoring. int NavScoringCount; // Metrics for debugging ImGuiWindow* NavWindowingTarget; // When selecting a window (holding Menu+FocusPrev/Next, or equivalent of CTRL-TAB) this window is temporarily displayed front-most. + ImGuiWindow* NavWindowingList; float NavWindowingHighlightTimer; float NavWindowingHighlightAlpha; bool NavWindowingToggleLayer; @@ -729,7 +730,7 @@ struct ImGuiContext ImGuiNavMoveResult NavMoveResultOther; // Best move request candidate within NavWindow's flattened hierarchy (when using ImGuiWindowFlags_NavFlattened flag) // Render - float ModalWindowDarkeningRatio; + float DimBgRatio; // 0.0..1.0 animation when fading in a dimming background (for modal window and CTRL+TAB list) ImGuiMouseCursor MouseCursor; // Drag and Drop @@ -837,7 +838,7 @@ struct ImGuiContext NavInputSource = ImGuiInputSource_None; NavScoringRectScreen = ImRect(); NavScoringCount = 0; - NavWindowingTarget = NULL; + NavWindowingTarget = NavWindowingList = NULL; NavWindowingHighlightTimer = NavWindowingHighlightAlpha = 0.0f; NavWindowingToggleLayer = false; NavLayer = 0; @@ -856,7 +857,7 @@ struct ImGuiContext NavMoveRequestForward = ImGuiNavForward_None; NavMoveDir = NavMoveDirLast = NavMoveClipDir = ImGuiDir_None; - ModalWindowDarkeningRatio = 0.0f; + DimBgRatio = 0.0f; MouseCursor = ImGuiMouseCursor_Arrow; DragDropActive = DragDropWithinSourceOrTarget = false; From 574185426c6f9ad6c98f0d4e9dded43aae6c1ae9 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 9 Jul 2018 21:06:46 +0200 Subject: [PATCH 170/828] Internals: Misc/shallow merge from Docking branch. --- imgui.cpp | 86 +++++++++++++++++++++++++++--------------------- imgui.h | 3 +- imgui_internal.h | 9 ++++- 3 files changed, 59 insertions(+), 39 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index b269b1406018..c707ef009a95 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -401,7 +401,7 @@ - 2016/05/07 (1.49) - removed confusing set of GetInternalState(), GetInternalStateSize(), SetInternalState() functions. Now using CreateContext(), DestroyContext(), GetCurrentContext(), SetCurrentContext(). - 2016/05/02 (1.49) - renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(), no redirection. - 2016/05/01 (1.49) - obsoleted old signature of CollapsingHeader(const char* label, const char* str_id = NULL, bool display_frame = true, bool default_open = false) as extra parameters were badly designed and rarely used. You can replace the "default_open = true" flag in new API with CollapsingHeader(label, ImGuiTreeNodeFlags_DefaultOpen). - - 2016/04/26 (1.49) - changed ImDrawList::PushClipRect(ImVec4 rect) to ImDraw::PushClipRect(Imvec2 min,ImVec2 max,bool intersect_with_current_clip_rect=false). Note that higher-level ImGui::PushClipRect() is preferable because it will clip at logic/widget level, whereas ImDrawList::PushClipRect() only affect your renderer. + - 2016/04/26 (1.49) - changed ImDrawList::PushClipRect(ImVec4 rect) to ImDrawList::PushClipRect(Imvec2 min,ImVec2 max,bool intersect_with_current_clip_rect=false). Note that higher-level ImGui::PushClipRect() is preferable because it will clip at logic/widget level, whereas ImDrawList::PushClipRect() only affect your renderer. - 2016/04/03 (1.48) - removed style.WindowFillAlphaDefault setting which was redundant. Bake default BG alpha inside style.Colors[ImGuiCol_WindowBg] and all other Bg color values. (ref github issue #337). - 2016/04/03 (1.48) - renamed ImGuiCol_TooltipBg to ImGuiCol_PopupBg, used by popups/menus and tooltips. popups/menus were previously using ImGuiCol_WindowBg. (ref github issue #337) - 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete). @@ -850,13 +850,6 @@ #endif #endif -// Enforce cdecl calling convention for functions called by the standard library, in case compilation settings changed the default to e.g. __vectorcall -#ifdef _MSC_VER -#define IMGUI_CDECL __cdecl -#else -#define IMGUI_CDECL -#endif - static const ImS32 IM_S32_MIN = 0x80000000; // INT_MIN; static const ImS32 IM_S32_MAX = 0x7FFFFFFF; // INT_MAX; static const ImU32 IM_U32_MIN = 0; @@ -879,7 +872,7 @@ static void SetWindowScrollY(ImGuiWindow* window, float new_scroll_y static void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond); static void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond); static void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond); -static ImGuiWindow* FindHoveredWindow(); +static void FindHoveredWindow(); static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags); static void CheckStacksSize(ImGuiWindow* window, bool write); static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window, bool snap_on_edges); @@ -3219,9 +3212,11 @@ static void ImGui::NavUpdateWindowing() // Apply menu/layer toggle if (apply_toggle_layer && g.NavWindow) { + // Move to parent menu if necessary ImGuiWindow* new_nav_window = g.NavWindow; while ((new_nav_window->DC.NavLayerActiveMask & (1 << 1)) == 0 && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0) new_nav_window = new_nav_window->ParentWindow; + if (new_nav_window != g.NavWindow) { ImGuiWindow* old_nav_window = g.NavWindow; @@ -3998,7 +3993,7 @@ void ImGui::UpdatePlatformWindows() if (ImGuiWindow* window_for_title = viewport->Window) { const char* title_begin = window_for_title->Name; - char* title_end = (char*)(intptr_t)ImGui::FindRenderedTextEnd(title_begin); + char* title_end = (char*)(intptr_t)FindRenderedTextEnd(title_begin); const ImGuiID title_hash = ImHash(title_begin, (int)(title_end - title_begin)); if (viewport->LastNameHash != title_hash) { @@ -4138,8 +4133,7 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags() // - Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow. // - When moving a window we can skip the search, which also conveniently bypasses the fact that window->WindowRectClipped is lagging as this point of the frame. // - We also support the moved window toggling the NoInputs flag after moving has started in order to be able to detect windows below it, which is useful for e.g. docking mechanisms. - g.HoveredWindow = (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoInputs)) ? g.MovingWindow : FindHoveredWindow(); - g.HoveredRootWindow = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL; + FindHoveredWindow(); IM_ASSERT(g.HoveredWindow == NULL || g.HoveredWindow == g.MovingWindow || g.HoveredWindow->Viewport == g.MouseRefViewport); // Modal windows prevents cursor from hovering behind them. @@ -5523,10 +5517,15 @@ void ImGui::CalcListClipping(int items_count, float items_height, int* out_items // Find window given position, search front-to-back // FIXME: Note that we have a lag here because WindowRectClipped is updated in Begin() so windows moved by user via SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is called, aka before the next Begin(). Moving window thankfully isn't affected. -static ImGuiWindow* FindHoveredWindow() +static void FindHoveredWindow() { ImGuiContext& g = *GImGui; - for (int i = g.Windows.Size - 1; i >= 0; i--) + + ImGuiWindow* hovered_window = NULL; + if (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoInputs)) + hovered_window = g.MovingWindow; + + for (int i = g.Windows.Size - 1; i >= 0 && hovered_window == NULL; i--) { ImGuiWindow* window = g.Windows[i]; if (!window->Active) @@ -5540,9 +5539,17 @@ static ImGuiWindow* FindHoveredWindow() // Using the clipped AABB, a child window will typically be clipped by its parent (not always) ImRect bb(window->OuterRectClipped.Min - g.Style.TouchExtraPadding, window->OuterRectClipped.Max + g.Style.TouchExtraPadding); if (bb.Contains(g.IO.MousePos)) - return window; + { + if (hovered_window == NULL) + hovered_window = window; + if (hovered_window) + break; + } } - return NULL; + + g.HoveredWindow = hovered_window; + g.HoveredRootWindow = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL; + } // Test if mouse cursor is hovering given rectangle @@ -6492,10 +6499,9 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFl // User can disable loading and saving of settings. Tooltip and child windows also don't store settings. if (!(flags & ImGuiWindowFlags_NoSavedSettings)) - { - // Retrieve settings from .ini file if (ImGuiWindowSettings* settings = ImGui::FindWindowSettings(window->ID)) { + // Retrieve settings from .ini file window->SettingsIdx = g.SettingsWindows.index_from_pointer(settings); SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false); if (settings->ViewportId) @@ -6512,7 +6518,6 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFl if (ImLengthSqr(settings->Size) > 0.00001f) size = ImFloor(settings->Size); } - } window->Size = window->SizeFull = window->SizeFullAtLastBegin = size; if ((flags & ImGuiWindowFlags_AlwaysAutoResize) != 0) @@ -7007,6 +7012,12 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) flags = window->Flags; } + // Parent window is latched only on the first call to Begin() of the frame, so further append-calls can be done from a different window stack + ImGuiWindow* parent_window_in_stack = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back(); + ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow; + IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow)); + window->HasCloseButton = (p_open != NULL); + // Update the Appearing flag bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFrames > 0); @@ -7017,15 +7028,9 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window_just_activated_by_user |= (window != popup_ref.Window); } window->Appearing = (window_just_activated_by_user || window_just_appearing_after_hidden_for_resize); - window->HasCloseButton = (p_open != NULL); if (window->Appearing) SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true); - // Parent window is latched only on the first call to Begin() of the frame, so further append-calls can be done from a different window stack - ImGuiWindow* parent_window_in_stack = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back(); - ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow; - IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow)); - // Add to stack // We intentionally set g.CurrentWindow to NULL to prevent usage until when the viewport is set, then will call SetCurrentWindow() g.CurrentWindowStack.push_back(window); @@ -7310,7 +7315,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if (window->ViewportOwned) window->WindowRounding = 0.0f; - // Prepare for focus requests + // Prepare for item focus requests window->FocusIdxAllRequestCurrent = (window->FocusIdxAllRequestNext == INT_MAX || window->FocusIdxAllCounter == -1) ? INT_MAX : (window->FocusIdxAllRequestNext + (window->FocusIdxAllCounter+1)) % (window->FocusIdxAllCounter+1); window->FocusIdxTabRequestCurrent = (window->FocusIdxTabRequestNext == INT_MAX || window->FocusIdxTabCounter == -1) ? INT_MAX : (window->FocusIdxTabRequestNext + (window->FocusIdxTabCounter+1)) % (window->FocusIdxTabCounter+1); window->FocusIdxAllCounter = window->FocusIdxTabCounter = -1; @@ -7320,7 +7325,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window, true); window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX); - // Apply focus, new windows appears in front + // Apply window focus (new and reactivated windows are moved to front) bool want_focus = false; if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing)) if (!(flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) || (flags & ImGuiWindowFlags_Popup)) @@ -7411,7 +7416,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) //window->Viewport->Alpha = ((bg_col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT) / 255.0f; bg_col = (bg_col | IM_COL32_A_MASK); } - window->DrawList->AddRectFilled(window->Pos+ImVec2(0,window->TitleBarHeight()), window->Pos+window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Bot); + window->DrawList->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight()), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Bot); // Title bar ImU32 title_bar_col = GetColorU32(window->Collapsed ? ImGuiCol_TitleBgCollapsed : title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg); @@ -7457,7 +7462,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DrawList->AddLine(border.Min, border.Max, GetColorU32(ImGuiCol_SeparatorActive), ImMax(1.0f, window_border_size)); } if (style.FrameBorderSize > 0 && !(flags & ImGuiWindowFlags_NoTitleBar)) - window->DrawList->AddLine(title_bar_rect.GetBL() + ImVec2(style.WindowBorderSize, -1), title_bar_rect.GetBR() + ImVec2(-style.WindowBorderSize,-1), GetColorU32(ImGuiCol_Border), style.FrameBorderSize); + window->DrawList->AddLine(title_bar_rect.GetBL() + ImVec2(style.WindowBorderSize, -1), title_bar_rect.GetBR() + ImVec2(-style.WindowBorderSize, -1), GetColorU32(ImGuiCol_Border), style.FrameBorderSize); } // Draw navigation selection/windowing rectangle border @@ -7640,10 +7645,11 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->BeginCount++; g.NextWindowData.Clear(); - // Child window can be out of sight and have "negative" clip windows. - // Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar). + if (flags & ImGuiWindowFlags_ChildWindow) { + // Child window can be out of sight and have "negative" clip windows. + // Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar). IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0); window->Collapsed = parent_window && parent_window->Collapsed; @@ -7655,6 +7661,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if (window->Collapsed) window->Active = false; } + + // Don't render if style alpha is 0.0 at the time of Begin(). This is arbitrary and inconsistent but has been there for a long while (may remove at some point) if (style.Alpha <= 0.0f) window->Active = false; @@ -14922,7 +14930,8 @@ void ImGui::ShowMetricsWindow(bool* p_open) static bool show_draw_cmd_clip_rects = true; static bool show_window_begin_order = false; ImGui::Checkbox("Show clipping rectangles when hovering draw commands", &show_draw_cmd_clip_rects); - ImGui::Checkbox("Show window begin order", &show_window_begin_order); + ImGui::Checkbox("Ctrl shows window begin order", &show_window_begin_order); + ImGui::Separator(); struct Funcs @@ -15012,12 +15021,13 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGuiWindowFlags flags = window->Flags; NodeDrawList(window, window->Viewport, window->DrawList, "DrawList"); ImGui::BulletText("Pos: (%.1f,%.1f), Size: (%.1f,%.1f), SizeContents (%.1f,%.1f)", window->Pos.x, window->Pos.y, window->Size.x, window->Size.y, window->SizeContents.x, window->SizeContents.y); - ImGui::BulletText("Flags: 0x%08X (%s%s%s%s%s%s%s..)", flags, + ImGui::BulletText("Flags: 0x%08X (%s%s%s%s%s%s%s%s..)", flags, (flags & ImGuiWindowFlags_ChildWindow) ? "Child " : "", (flags & ImGuiWindowFlags_Tooltip) ? "Tooltip " : "", (flags & ImGuiWindowFlags_Popup) ? "Popup " : "", (flags & ImGuiWindowFlags_Modal) ? "Modal " : "", (flags & ImGuiWindowFlags_ChildMenu) ? "ChildMenu " : "", (flags & ImGuiWindowFlags_NoSavedSettings) ? "NoSavedSettings " : "", - (flags & ImGuiWindowFlags_AlwaysAutoResize) ? "AlwaysAutoResize" : ""); + (flags & ImGuiWindowFlags_NoInputs) ? "NoInputs":"", (flags & ImGuiWindowFlags_AlwaysAutoResize) ? "AlwaysAutoResize" : ""); ImGui::BulletText("Scroll: (%.2f/%.2f,%.2f/%.2f)", window->Scroll.x, GetScrollMaxX(window), window->Scroll.y, GetScrollMaxY(window)); - ImGui::BulletText("Active: %d, WriteAccessed: %d, BeginOrderWithinContext: %d", window->Active || window->WasActive, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1); + ImGui::BulletText("Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", window->Active, window->WasActive, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1); + ImGui::BulletText("NavLastIds: 0x%08X,0x%08X, NavLayerActiveMask: %X", window->NavLastIds[0], window->NavLastIds[1], window->DC.NavLayerActiveMask); ImGui::BulletText("NavLastChildNavWindow: %s", window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL"); if (!window->NavRectRel[0].IsInverted()) @@ -15093,7 +15103,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) Funcs::NodeViewport(g.Viewports[i]); ImGui::TreePop(); } - if (ImGui::TreeNode("Popups", "Open Popups Stack (%d)", g.OpenPopupStack.Size)) + if (ImGui::TreeNode("Popups", "Popups (%d)", g.OpenPopupStack.Size)) { for (int i = 0; i < g.OpenPopupStack.Size; i++) { @@ -15117,11 +15127,13 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible); ImGui::Text("NavActivateId: 0x%08X, NavInputId: 0x%08X", g.NavActivateId, g.NavInputId); ImGui::Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover); + ImGui::Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL"); ImGui::Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize); ImGui::Text("MousePosViewport: 0x%08X, Hovered: 0x%08X -> Ref 0x%08X", g.IO.MousePosViewport, g.IO.MouseHoveredViewport, g.MouseRefViewport->ID); ImGui::TreePop(); } - if (show_window_begin_order) + + if (g.IO.KeyCtrl && show_window_begin_order) { for (int n = 0; n < g.Windows.Size; n++) { diff --git a/imgui.h b/imgui.h index 159e73205350..1aea01df12ed 100644 --- a/imgui.h +++ b/imgui.h @@ -724,7 +724,8 @@ enum ImGuiFocusedFlags_ }; // Flags for ImGui::IsItemHovered(), ImGui::IsWindowHovered() -// Note: If you are trying to check whether your mouse should be dispatched to imgui or to your app, you should use the 'io.WantCaptureMouse' boolean for that. Please read the FAQ! +// Note: if you are trying to check whether your mouse should be dispatched to imgui or to your app, you should use the 'io.WantCaptureMouse' boolean for that. Please read the FAQ! +// Note: windows with the ImGuiWindowFlags_NoInputs flag are ignored by IsWindowHovered() calls. enum ImGuiHoveredFlags_ { ImGuiHoveredFlags_None = 0, // Return true if directly over the item/window, not obstructed by another window, not obstructed by an active popup or modal blocking inputs under them. diff --git a/imgui_internal.h b/imgui_internal.h index d13a9631ef61..50318b0481de 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -98,6 +98,13 @@ extern IMGUI_API ImGuiContext* GImGui; // Current implicit ImGui context pointe #define IM_NEWLINE "\n" #endif +// Enforce cdecl calling convention for functions called by the standard library, in case compilation settings changed the default to e.g. __vectorcall +#ifdef _MSC_VER +#define IMGUI_CDECL __cdecl +#else +#define IMGUI_CDECL +#endif + // Helpers: UTF-8 <> wchar IMGUI_API int ImTextStrToUtf8(char* buf, int buf_size, const ImWchar* in_text, const ImWchar* in_text_end); // return output UTF-8 bytes count IMGUI_API int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end); // return input UTF-8 bytes count @@ -564,7 +571,7 @@ struct ImGuiViewportP : public ImGuiViewport ImDrawDataBuilder DrawDataBuilder; ImVec2 RendererLastSize; - ImGuiViewportP() { Idx = 1; LastFrameActive = LastFrameOverlayDrawList = LastFrontMostStampCount = -1; LastNameHash = 0; CreatedPlatformWindow = false; Alpha = LastAlpha = 1.0f; PlatformMonitor = INT_MIN; Window = NULL; OverlayDrawList = NULL; RendererLastSize = ImVec2(-1.0f,-1.0f); } + ImGuiViewportP() { Idx = -1; LastFrameActive = LastFrameOverlayDrawList = LastFrontMostStampCount = -1; LastNameHash = 0; CreatedPlatformWindow = false; Alpha = LastAlpha = 1.0f; PlatformMonitor = INT_MIN; Window = NULL; OverlayDrawList = NULL; RendererLastSize = ImVec2(-1.0f,-1.0f); } ~ImGuiViewportP() { if (OverlayDrawList) IM_DELETE(OverlayDrawList); } ImRect GetRect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } ImVec2 GetCenter() const{ return ImVec2(Pos.x + Size.x * 0.5f, Pos.y + Size.y * 0.5f); } From 7abf72ec7856a40ab86c69bf86eb8ad73f2bce97 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 26 Jun 2018 20:51:44 +0200 Subject: [PATCH 171/828] Viewport: Reapply/recover ownership of viewport which is convenient for docking but also can recover from faulty .ini files. (#1542) --- imgui.cpp | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index c707ef009a95..5da147ef72b6 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -908,7 +908,7 @@ static ImGuiViewportP* AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const static void UpdateViewports(); static void UpdateSelectWindowViewport(ImGuiWindow* window); static void UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* host_viewport); -static void SetCurrentViewport(ImGuiViewportP* viewport); +static void SetCurrentViewport(ImGuiWindow* window, ImGuiViewportP* viewport); static int FindPlatformMonitorForPos(const ImVec2& pos); static int FindPlatformMonitorForRect(const ImRect& r); @@ -4924,7 +4924,7 @@ void ImGui::EndFrame() g.CurrentWindow->Active = false; End(); - SetCurrentViewport(NULL); + SetCurrentViewport(NULL, NULL); if (g.ActiveId == 0 && g.HoveredId == 0) { @@ -5105,16 +5105,26 @@ ImGuiViewport* ImGui::FindViewportByPlatformHandle(void* platform_handle) return NULL; } -void ImGui::SetCurrentViewport(ImGuiViewportP* viewport) +void ImGui::SetCurrentViewport(ImGuiWindow* current_window, ImGuiViewportP* viewport) { - // Notify platform layer of viewport changes - // FIXME-DPI: This is only currently used for experimenting with handling of multiple DPI ImGuiContext& g = *GImGui; if (viewport) + { + // First window submitted gets viewport ownership + if (viewport->LastFrameActive < g.FrameCount && viewport->Window != current_window && viewport->Window != NULL && current_window != NULL) + { + //printf("[%05d] Window '%s' steal Viewport %08X from Window '%s'\n", g.FrameCount, current_window->Name, viewport->ID, viewport->Window->Name); + viewport->Window = current_window; + viewport->ID = current_window->ID; + } viewport->LastFrameActive = g.FrameCount; + } if (g.CurrentViewport == viewport) return; g.CurrentViewport = viewport; + + // Notify platform layer of viewport changes + // FIXME-DPI: This is only currently used for experimenting with handling of multiple DPI if (g.CurrentViewport && g.PlatformIO.Platform_OnChangedViewport) g.PlatformIO.Platform_OnChangedViewport(g.CurrentViewport); } @@ -5150,6 +5160,7 @@ ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const viewport->Window = window; viewport->Flags = flags; viewport->LastFrameActive = g.FrameCount; + IM_ASSERT(window == NULL || viewport->ID == window->ID); if (window != NULL) window->ViewportOwned = true; @@ -7142,7 +7153,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // We need to do this before using any style/font sizes, as viewport with a different DPI may affect font sizes. UpdateSelectWindowViewport(window); - SetCurrentViewport(window->Viewport); + SetCurrentViewport(window, window->Viewport); window->FontDpiScale = (g.IO.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleFonts) ? window->Viewport->DpiScale : 1.0f; SetCurrentWindow(window); flags = window->Flags; @@ -7260,7 +7271,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // FIXME-DPI //IM_ASSERT(old_viewport->DpiScale == window->Viewport->DpiScale); // FIXME-DPI: Something went wrong - SetCurrentViewport(window->Viewport); + SetCurrentViewport(window, window->Viewport); window->FontDpiScale = (g.IO.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleFonts) ? window->Viewport->DpiScale : 1.0f; SetCurrentWindow(window); } @@ -7632,7 +7643,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) else { // Append - SetCurrentViewport(window->Viewport); + SetCurrentViewport(window, window->Viewport); SetCurrentWindow(window); } @@ -7707,7 +7718,7 @@ void ImGui::End() CheckStacksSize(window, false); SetCurrentWindow(g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back()); if (g.CurrentWindow) - SetCurrentViewport(g.CurrentWindow->Viewport); + SetCurrentViewport(g.CurrentWindow, g.CurrentWindow->Viewport); } // Vertical scrollbar From 140ece0aebf442f55abb700509d74a913e339787 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 26 Jun 2018 19:05:02 +0200 Subject: [PATCH 172/828] Internals: HoveredWindowUnderMovingWindow special handling for drag and drop of window without altering the _NoInputs window flag, which worked but messed up the IsWindowHovered() user-facing flags. --- imgui.cpp | 34 +++++++++++++++++++++------------- imgui_internal.h | 1 + 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 5da147ef72b6..e6a8b33b7d8d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2333,7 +2333,8 @@ static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFla // Filter by viewport if (window->Viewport != g.MouseRefViewport) - return false; + if (g.MovingWindow == NULL || window->RootWindow != g.MovingWindow->RootWindow) + return false; return true; } @@ -4140,11 +4141,11 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags() ImGuiWindow* modal_window = GetFrontMostPopupModal(); if (modal_window) if (g.HoveredRootWindow && !IsWindowChildOf(g.HoveredRootWindow, modal_window)) - g.HoveredRootWindow = g.HoveredWindow = NULL; + g.HoveredWindow = g.HoveredRootWindow = g.HoveredWindowUnderMovingWindow = NULL; // Disabled mouse? if (g.IO.ConfigFlags & ImGuiConfigFlags_NoMouse) - g.HoveredWindow = g.HoveredRootWindow = NULL; + g.HoveredWindow = g.HoveredRootWindow = g.HoveredWindowUnderMovingWindow = NULL; // We track click ownership. When clicked outside of a window the click is owned by the application and won't report hovering nor request capture even while dragging over our windows afterward. int mouse_earliest_button_down = -1; @@ -4164,7 +4165,7 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags() // FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02) const bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0; if (!mouse_avail_to_imgui && !mouse_dragging_extern_payload) - g.HoveredWindow = g.HoveredRootWindow = NULL; + g.HoveredWindow = g.HoveredRootWindow = g.HoveredWindowUnderMovingWindow = NULL; // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to imgui, false = dispatch mouse info to imgui + app) if (g.WantCaptureMouseNextFrame != -1) @@ -4336,9 +4337,12 @@ void ImGui::NewFrame() g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame); g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame))) : FLT_MAX; + // Find hovered window + // (needs to be before UpdateMovingWindow so we fill HoveredWindowUnderMovingWindow on the mouse release frame) + UpdateHoveredWindowAndCaptureFlags(); + // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering) UpdateMovingWindow(); - UpdateHoveredWindowAndCaptureFlags(); // Background darkening/whitening if (GetFrontMostPopupModal() != NULL || g.NavWindowingTarget != NULL) @@ -4567,8 +4571,7 @@ void ImGui::Shutdown(ImGuiContext* context) g.CurrentWindowStack.clear(); g.WindowsById.Clear(); g.NavWindow = NULL; - g.HoveredWindow = NULL; - g.HoveredRootWindow = NULL; + g.HoveredWindow = g.HoveredRootWindow = g.HoveredWindowUnderMovingWindow = NULL; g.ActiveIdWindow = g.ActiveIdPreviousFrameWindow = NULL; g.MovingWindow = NULL; g.ColorModifiers.clear(); @@ -5527,16 +5530,18 @@ void ImGui::CalcListClipping(int items_count, float items_height, int* out_items } // Find window given position, search front-to-back -// FIXME: Note that we have a lag here because WindowRectClipped is updated in Begin() so windows moved by user via SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is called, aka before the next Begin(). Moving window thankfully isn't affected. +// FIXME: Note that we have an inconsequential lag here: OuterRectClipped is updated in Begin(), so windows moved programatically with SetWindowPos() and not SetNextWindowPos() +// will have that rectangle lagging by a frame at the time FindHoveredWindow() is called, aka before the next Begin(). Moving window isn't affected. static void FindHoveredWindow() { ImGuiContext& g = *GImGui; ImGuiWindow* hovered_window = NULL; + ImGuiWindow* hovered_window_ignoring_moving_window = NULL; if (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoInputs)) hovered_window = g.MovingWindow; - for (int i = g.Windows.Size - 1; i >= 0 && hovered_window == NULL; i--) + for (int i = g.Windows.Size - 1; i >= 0; i--) { ImGuiWindow* window = g.Windows[i]; if (!window->Active) @@ -5553,14 +5558,16 @@ static void FindHoveredWindow() { if (hovered_window == NULL) hovered_window = window; - if (hovered_window) + if (hovered_window_ignoring_moving_window == NULL && (!g.MovingWindow || window->RootWindow != g.MovingWindow->RootWindow)) + hovered_window_ignoring_moving_window = window; + if (hovered_window && hovered_window_ignoring_moving_window) break; } } g.HoveredWindow = hovered_window; g.HoveredRootWindow = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL; - + g.HoveredWindowUnderMovingWindow = hovered_window_ignoring_moving_window; } // Test if mouse cursor is hovering given rectangle @@ -14685,7 +14692,7 @@ bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id) return false; ImGuiWindow* window = g.CurrentWindow; - if (g.HoveredWindow == NULL || window->RootWindow != g.HoveredWindow->RootWindow) + if (g.HoveredWindowUnderMovingWindow == NULL || window->RootWindow != g.HoveredWindowUnderMovingWindow->RootWindow) return false; IM_ASSERT(id != 0); if (!IsMouseHoveringRect(bb.Min, bb.Max) || (id == g.DragDropPayload.SourceId)) @@ -14711,7 +14718,7 @@ bool ImGui::BeginDragDropTarget() ImGuiWindow* window = g.CurrentWindow; if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect)) return false; - if (g.HoveredWindow == NULL || window->RootWindow != g.HoveredWindow->RootWindow) + if (g.HoveredWindowUnderMovingWindow == NULL || window->RootWindow != g.HoveredWindowUnderMovingWindow->RootWindow) return false; const ImRect& display_rect = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? window->DC.LastItemDisplayRect : window->DC.LastItemRect; @@ -15128,6 +15135,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) const char* input_source_names[] = { "None", "Mouse", "Nav", "NavKeyboard", "NavGamepad" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT); ImGui::Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL"); ImGui::Text("HoveredRootWindow: '%s'", g.HoveredRootWindow ? g.HoveredRootWindow->Name : "NULL"); + ImGui::Text("HoveredWindowUnderMovingWindow: '%s'", g.HoveredWindowUnderMovingWindow ? g.HoveredWindowUnderMovingWindow->Name : "NULL"); ImGui::Text("HoveredId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d", g.HoveredId, g.HoveredIdPreviousFrame, g.HoveredIdTimer, g.HoveredIdAllowOverlap); // Data is "in-flight" so depending on when the Metrics window is called we may see current frame information or not ImGui::Text("ActiveId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d, Source: %s", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdTimer, g.ActiveIdAllowOverlap, input_source_names[g.ActiveIdSource]); ImGui::Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL"); diff --git a/imgui_internal.h b/imgui_internal.h index 50318b0481de..7c754f42b7d4 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -660,6 +660,7 @@ struct ImGuiContext ImGuiWindow* CurrentWindow; // Being drawn into ImGuiWindow* HoveredWindow; // Will catch mouse inputs ImGuiWindow* HoveredRootWindow; // Will catch mouse inputs (for focus/move only) + ImGuiWindow* HoveredWindowUnderMovingWindow; ImGuiID HoveredId; // Hovered widget bool HoveredIdAllowOverlap; ImGuiID HoveredIdPreviousFrame; From 9dea27b273afa79fee320c38217265d1772f7a87 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 27 Jun 2018 17:27:27 +0200 Subject: [PATCH 173/828] Viewport: Made it possible for the moving window to detach and recreate its own Viewport (used by Docking) --- imgui.cpp | 40 +++++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index e6a8b33b7d8d..df05a24082d7 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5536,6 +5536,11 @@ static void FindHoveredWindow() { ImGuiContext& g = *GImGui; + // Special handling for the window being moved: Ignore the mouse viewport check (because it may reset/lose its viewport during the undocking frame) + ImGuiViewportP* moving_window_viewport = g.MovingWindow ? g.MovingWindow->Viewport : NULL; + if (g.MovingWindow) + g.MovingWindow->Viewport = g.MouseRefViewport; + ImGuiWindow* hovered_window = NULL; ImGuiWindow* hovered_window_ignoring_moving_window = NULL; if (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoInputs)) @@ -5568,6 +5573,9 @@ static void FindHoveredWindow() g.HoveredWindow = hovered_window; g.HoveredRootWindow = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL; g.HoveredWindowUnderMovingWindow = hovered_window_ignoring_moving_window; + + if (g.MovingWindow) + g.MovingWindow->Viewport = moving_window_viewport; } // Test if mouse cursor is hovering given rectangle @@ -6764,16 +6772,19 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) window->ViewportId = 0; } - // By default inherit from parent window - if (window->Viewport == NULL && window->ParentWindow) - window->Viewport = window->ParentWindow->Viewport; - - // Attempt to restore saved viewport id (= window that hasn't been activated yet), try to restore the viewport based on saved 'window->ViewportPos' restored from .ini file - if (window->Viewport == NULL && window->ViewportId != 0) + if (!g.NextWindowData.ViewportCond) { - window->Viewport = FindViewportByID(window->ViewportId); - if (window->Viewport == NULL && window->ViewportPos.x != FLT_MAX && window->ViewportPos.y != FLT_MAX) - window->Viewport = AddUpdateViewport(window, window->ID, window->ViewportPos, window->Size, ImGuiViewportFlags_NoDecoration); + // By default inherit from parent window + if (window->Viewport == NULL && window->ParentWindow) + window->Viewport = window->ParentWindow->Viewport; + + // Attempt to restore saved viewport id (= window that hasn't been activated yet), try to restore the viewport based on saved 'window->ViewportPos' restored from .ini file + if (window->Viewport == NULL && window->ViewportId != 0) + { + window->Viewport = FindViewportByID(window->ViewportId); + if (window->Viewport == NULL && window->ViewportPos.x != FLT_MAX && window->ViewportPos.y != FLT_MAX) + window->Viewport = AddUpdateViewport(window, window->ID, window->ViewportPos, window->Size, ImGuiViewportFlags_NoDecoration); + } } if (g.NextWindowData.ViewportCond) @@ -6795,10 +6806,13 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) { // Transition to our own viewport when leaving our host boundaries + set the NoInputs flag (which will be cleared in UpdateMovingWindow when releasing the mouse) // If we are already in our own viewport, if need to set the NoInputs flag. - bool own_viewport = window->Viewport->Window == window; // We test window->Viewport->Window because window->ViewportOwned is not valid during this function. - bool leave_host_viewport = !own_viewport && !window->Viewport->GetRect().Contains(window->Rect()); - bool move_from_own_viewport = own_viewport && !(window->Viewport->Flags & ImGuiViewportFlags_NoInputs); - if (leave_host_viewport || move_from_own_viewport) + // If we have no viewport (which happens when detaching a docked node) immediately create one. + // We test for 'window->Viewport->Window == window' instead of 'window->ViewportOwned' because ViewportOwned is not valid during this function. + bool has_viewport = (window->Viewport != NULL); + bool own_viewport = has_viewport && (window->Viewport->Window == window); + bool leave_host_viewport = has_viewport && !own_viewport && !window->Viewport->GetRect().Contains(window->Rect()); + bool move_from_own_viewport = has_viewport && own_viewport && !(window->Viewport->Flags & ImGuiViewportFlags_NoInputs); + if (!has_viewport || leave_host_viewport || move_from_own_viewport) window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoDecoration | ImGuiViewportFlags_NoFocusOnAppearing | ImGuiViewportFlags_NoInputs); } else if (GetWindowAlwaysWantOwnViewport(window)) From ce18371d1bd331fc03c7ff22686bff1453b733f4 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 6 Jul 2018 19:24:54 +0200 Subject: [PATCH 174/828] Internals: Fixed CalcSizeContents() returning negative value on first run (inconsequential afaik, but fixing for sanity) --- imgui.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/imgui.cpp b/imgui.cpp index df05a24082d7..18df9d1495c1 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6545,6 +6545,7 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFl size = ImFloor(settings->Size); } window->Size = window->SizeFull = window->SizeFullAtLastBegin = size; + window->DC.CursorMaxPos = window->Pos; // So first call to CalcSizeContents() doesn't return crazy values if ((flags & ImGuiWindowFlags_AlwaysAutoResize) != 0) { From d10714668740c33b5743626014c08aed075c8221 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 12 Jul 2018 11:19:17 +0200 Subject: [PATCH 175/828] Viewport: Fixed CTRL+TAB windowing list displaying too many dimming layers (fix dd61c48 for viewport branch) --- imgui.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 709a59db80d3..3521878b0a02 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7405,17 +7405,14 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) else PushClipRect(viewport_rect.Min, viewport_rect.Max, true); - // Draw modal window background (darkens what is behind them, all viewports) + // Draw modal or window list full viewport dimming background (for other viewports we'll render them in EndFrame) const bool dim_bg_for_modal = (flags & ImGuiWindowFlags_Modal) && window == GetFrontMostPopupModal() && window->HiddenFrames <= 0; const bool dim_bg_for_window_list = g.NavWindowingTarget && ((window == g.NavWindowingTarget->RootWindow) || (g.NavWindowingList && (window == g.NavWindowingList) && g.NavWindowingList->Viewport != g.NavWindowingTarget->Viewport)); if (dim_bg_for_modal || dim_bg_for_window_list) - for (int viewport_n = 0; viewport_n < g.Viewports.Size; viewport_n++) - { - ImGuiViewportP* viewport = g.Viewports[viewport_n]; - ImDrawList* draw_list = (viewport == window->Viewport) ? window->DrawList : GetOverlayDrawList(viewport); - const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio); - draw_list->AddRectFilled(viewport->Pos, viewport->Pos + viewport->Size, dim_bg_col); - } + { + const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio); + window->DrawList->AddRectFilled(viewport_rect.Min, viewport_rect.Max, dim_bg_col); + } // Draw navigation selection/windowing rectangle background if (dim_bg_for_window_list && window == g.NavWindowingTarget->RootWindow) From 16c6734bcbbd793f6b88eb219aa6692d2821f3cf Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 16 Jul 2018 23:39:14 +0200 Subject: [PATCH 176/828] Viewport: Revert part of 7abf72e, the viewport ownership stealing is problematic. (#1542). Will rework in the context of docking. (+1 squashed commits) + Fixed unused prototype warning (left-over from a merge) --- imgui.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 9da860d4a488..86c694c8c28a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -878,7 +878,6 @@ static void CheckStacksSize(ImGuiWindow* window, bool write); static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window, bool snap_on_edges); static void AddDrawListToDrawData(ImVector* out_list, ImDrawList* draw_list); -static void AddWindowToDrawData(ImVector* out_list, ImGuiWindow* window); static void AddWindowToSortedBuffer(ImVector* out_sorted_windows, ImGuiWindow* window); static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data); @@ -5117,12 +5116,16 @@ void ImGui::SetCurrentViewport(ImGuiWindow* current_window, ImGuiViewportP* view if (viewport) { // First window submitted gets viewport ownership + // [2018-07-18] This is problematic: e.g. a drag and drop tooltip may steal viewport of the window it is hovered. Disabling, will rework in Docking branch. + (void)current_window; + /* if (viewport->LastFrameActive < g.FrameCount && viewport->Window != current_window && viewport->Window != NULL && current_window != NULL) { //printf("[%05d] Window '%s' steal Viewport %08X from Window '%s'\n", g.FrameCount, current_window->Name, viewport->ID, viewport->Window->Name); viewport->Window = current_window; viewport->ID = current_window->ID; } + */ viewport->LastFrameActive = g.FrameCount; } if (g.CurrentViewport == viewport) From defbf1c4b301955b36727a01d52e93d0582aa6c8 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 17 Jul 2018 15:56:02 +0200 Subject: [PATCH 177/828] Viewport: when moving window we use the moving window viewport unless drag and dropping. This is to fix e.g. IsItemHovered() + Tooltip pattern that may be active while moving a window. (#1542) --- imgui.cpp | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 86c694c8c28a..9d689e8acc02 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3808,9 +3808,6 @@ static void ImGui::UpdateViewports() ImGuiContext& g = *GImGui; IM_ASSERT(g.PlatformIO.Viewports.Size <= g.Viewports.Size); - // Update mouse reference viewport - g.MouseRefViewport = g.IO.MousePosViewport ? FindViewportByID(g.IO.MousePosViewport) : g.Viewports[0]; - // Update main viewport with current platform position and size ImGuiViewportP* main_viewport = g.Viewports[0]; IM_ASSERT(main_viewport->ID == IMGUI_VIEWPORT_DEFAULT_ID); @@ -3820,6 +3817,7 @@ static void ImGui::UpdateViewports() AddUpdateViewport(NULL, IMGUI_VIEWPORT_DEFAULT_ID, main_viewport_platform_pos, g.IO.DisplaySize, ImGuiViewportFlags_CanHostOtherWindows); g.CurrentViewport = NULL; + g.MouseRefViewport = NULL; for (int n = 0; n < g.Viewports.Size; n++) { // Erase unused viewports @@ -3835,7 +3833,6 @@ static void ImGui::UpdateViewports() g.Viewports.erase(g.Viewports.Data + n); // Destroy - if (viewport == g.MouseRefViewport) g.MouseRefViewport = main_viewport; if (viewport == g.MouseLastHoveredViewport) g.MouseLastHoveredViewport = NULL; IM_ASSERT(viewport->RendererUserData == NULL && viewport->PlatformUserData == NULL && viewport->PlatformHandle == NULL); IM_ASSERT(g.PlatformIO.Viewports.contains(viewport) == false); @@ -3894,13 +3891,14 @@ static void ImGui::UpdateViewports() } // Mouse handling: decide on the actual mouse viewport for this frame between the active/focused viewport and the hovered viewport. + // Note that 'viewport_hovered' should skip over any viewport that has the ImGuiViewportFlags_NoInputs flags set. ImGuiViewportP* viewport_hovered = NULL; if (g.IO.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport) { viewport_hovered = g.IO.MouseHoveredViewport ? FindViewportByID(g.IO.MouseHoveredViewport) : NULL; if (viewport_hovered && (viewport_hovered->Flags & ImGuiViewportFlags_NoInputs)) { - // Back-end failed at honoring its contract if it returned a viewport with the _NoInputs flag + // Back-end failed at honoring its contract if it returned a viewport with the _NoInputs flag. IM_ASSERT(0); viewport_hovered = FindViewportHoveredFromPlatformWindowStack(g.IO.MousePos); } @@ -3915,17 +3913,23 @@ static void ImGui::UpdateViewports() if (viewport_hovered != NULL) g.MouseLastHoveredViewport = viewport_hovered; + // Update mouse reference viewport + g.MouseRefViewport = g.IO.MousePosViewport ? FindViewportByID(g.IO.MousePosViewport) : g.Viewports[0]; + + // When moving a window we aim at its viewport + if (g.MovingWindow) + g.MouseRefViewport = g.MovingWindow->Viewport; + // When dragging something, always refer to the last hovered viewport. - // (So when we are between viewports, our dragged preview will tend to show in the last viewport even if we don't have tooltips in viewports) - // Also consider the case of holding on a menu item to browse child menus: even thought a mouse button is held, there's no active id because menu items only react on mouse release. - const bool is_mouse_dragging_with_an_expected_destination = g.DragDropActive || (g.MovingWindow != NULL); + // - when releasing a moving window we will revert to aiming behind (at viewport_hovered) + // - when we are between viewports, our dragged preview will tend to show in the last viewport _even_ if we don't have tooltips in their viewports (when lacking monitor info) + // - consider the case of holding on a menu item to browse child menus: even thou a mouse button is held, there's no active id because menu items only react on mouse release. + const bool is_mouse_dragging_with_an_expected_destination = g.DragDropActive;// || (g.MovingWindow != NULL); + if (is_mouse_dragging_with_an_expected_destination && viewport_hovered == NULL) + viewport_hovered = g.MouseLastHoveredViewport; if (is_mouse_dragging_with_an_expected_destination || g.ActiveId == 0 || !ImGui::IsAnyMouseDown()) - { - if (is_mouse_dragging_with_an_expected_destination && viewport_hovered == NULL) - viewport_hovered = g.MouseLastHoveredViewport; if (viewport_hovered != NULL && viewport_hovered != g.MouseRefViewport && !(viewport_hovered->Flags & ImGuiViewportFlags_NoInputs)) g.MouseRefViewport = viewport_hovered; - } IM_ASSERT(g.MouseRefViewport != NULL); } From b8b74970d741c9f5f1973c8fe8fddaa7228afdb0 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 17 Jul 2018 15:59:09 +0200 Subject: [PATCH 178/828] Internals: Viewport: Renamed g.MouseRefViewport to g.MouseViewport. (#1542) --- imgui.cpp | 42 +++++++++++++++++++++--------------------- imgui_internal.h | 4 ++-- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 9d689e8acc02..4fae6cd44927 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2331,7 +2331,7 @@ static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFla } // Filter by viewport - if (window->Viewport != g.MouseRefViewport) + if (window->Viewport != g.MouseViewport) if (g.MovingWindow == NULL || window->RootWindow != g.MovingWindow->RootWindow) return false; @@ -3722,12 +3722,12 @@ void ImGui::UpdateMouseMovingWindow() else { // Try to merge the window back into the main viewport. - // This works because MouseRefViewport shouldn't be == MovingWindow->Viewport which should have the NoInputs flag during moving. - UpdateTryMergeWindowIntoHostViewport(moving_window, g.MouseRefViewport); + // This works because MouseViewport should be != MovingWindow->Viewport on release (as per code in UpdateViewports) + UpdateTryMergeWindowIntoHostViewport(moving_window, g.MouseViewport); // Restore the mouse viewport so that we don't hover the viewport _under_ the moved window during the frame we released the mouse button. if (!IsDragDropPayloadBeingAccepted()) - g.MouseRefViewport = moving_window->Viewport; + g.MouseViewport = moving_window->Viewport; // Clear the NoInput window flag set by the Viewport system moving_window->Viewport->Flags &= ~ImGuiViewportFlags_NoInputs; @@ -3817,7 +3817,7 @@ static void ImGui::UpdateViewports() AddUpdateViewport(NULL, IMGUI_VIEWPORT_DEFAULT_ID, main_viewport_platform_pos, g.IO.DisplaySize, ImGuiViewportFlags_CanHostOtherWindows); g.CurrentViewport = NULL; - g.MouseRefViewport = NULL; + g.MouseViewport = NULL; for (int n = 0; n < g.Viewports.Size; n++) { // Erase unused viewports @@ -3886,7 +3886,7 @@ static void ImGui::UpdateViewports() if (!(g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) { - g.MouseRefViewport = main_viewport; + g.MouseViewport = main_viewport; return; } @@ -3914,24 +3914,24 @@ static void ImGui::UpdateViewports() g.MouseLastHoveredViewport = viewport_hovered; // Update mouse reference viewport - g.MouseRefViewport = g.IO.MousePosViewport ? FindViewportByID(g.IO.MousePosViewport) : g.Viewports[0]; - - // When moving a window we aim at its viewport + // (when moving a window we aim at its viewport, but this will be overwritten below if we go in drag and drop mode) if (g.MovingWindow) - g.MouseRefViewport = g.MovingWindow->Viewport; + g.MouseViewport = g.MovingWindow->Viewport; + else + g.MouseViewport = g.IO.MousePosViewport ? FindViewportByID(g.IO.MousePosViewport) : g.Viewports[0]; // When dragging something, always refer to the last hovered viewport. // - when releasing a moving window we will revert to aiming behind (at viewport_hovered) // - when we are between viewports, our dragged preview will tend to show in the last viewport _even_ if we don't have tooltips in their viewports (when lacking monitor info) // - consider the case of holding on a menu item to browse child menus: even thou a mouse button is held, there's no active id because menu items only react on mouse release. - const bool is_mouse_dragging_with_an_expected_destination = g.DragDropActive;// || (g.MovingWindow != NULL); + const bool is_mouse_dragging_with_an_expected_destination = g.DragDropActive; if (is_mouse_dragging_with_an_expected_destination && viewport_hovered == NULL) viewport_hovered = g.MouseLastHoveredViewport; if (is_mouse_dragging_with_an_expected_destination || g.ActiveId == 0 || !ImGui::IsAnyMouseDown()) - if (viewport_hovered != NULL && viewport_hovered != g.MouseRefViewport && !(viewport_hovered->Flags & ImGuiViewportFlags_NoInputs)) - g.MouseRefViewport = viewport_hovered; + if (viewport_hovered != NULL && viewport_hovered != g.MouseViewport && !(viewport_hovered->Flags & ImGuiViewportFlags_NoInputs)) + g.MouseViewport = viewport_hovered; - IM_ASSERT(g.MouseRefViewport != NULL); + IM_ASSERT(g.MouseViewport != NULL); } static bool IsWindowActiveAndVisible(ImGuiWindow* window) @@ -4152,7 +4152,7 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags() // - When moving a window we can skip the search, which also conveniently bypasses the fact that window->WindowRectClipped is lagging as this point of the frame. // - We also support the moved window toggling the NoInputs flag after moving has started in order to be able to detect windows below it, which is useful for e.g. docking mechanisms. FindHoveredWindow(); - IM_ASSERT(g.HoveredWindow == NULL || g.HoveredWindow == g.MovingWindow || g.HoveredWindow->Viewport == g.MouseRefViewport); + IM_ASSERT(g.HoveredWindow == NULL || g.HoveredWindow == g.MovingWindow || g.HoveredWindow->Viewport == g.MouseViewport); // Modal windows prevents cursor from hovering behind them. ImGuiWindow* modal_window = GetFrontMostPopupModal(); @@ -4596,7 +4596,7 @@ void ImGui::Shutdown(ImGuiContext* context) g.FontStack.clear(); g.OpenPopupStack.clear(); g.CurrentPopupStack.clear(); - g.CurrentViewport = g.MouseRefViewport = g.MouseLastHoveredViewport = NULL; + g.CurrentViewport = g.MouseViewport = g.MouseLastHoveredViewport = NULL; for (int i = 0; i < g.Viewports.Size; i++) IM_DELETE(g.Viewports[i]); g.Viewports.clear(); @@ -5549,7 +5549,7 @@ static void FindHoveredWindow() // Special handling for the window being moved: Ignore the mouse viewport check (because it may reset/lose its viewport during the undocking frame) ImGuiViewportP* moving_window_viewport = g.MovingWindow ? g.MovingWindow->Viewport : NULL; if (g.MovingWindow) - g.MovingWindow->Viewport = g.MouseRefViewport; + g.MovingWindow->Viewport = g.MouseViewport; ImGuiWindow* hovered_window = NULL; ImGuiWindow* hovered_window_ignoring_moving_window = NULL; @@ -5564,7 +5564,7 @@ static void FindHoveredWindow() if (window->Flags & ImGuiWindowFlags_NoInputs) continue; IM_ASSERT(window->Viewport); - if (window->Viewport != g.MouseRefViewport) + if (window->Viewport != g.MouseViewport) continue; // Using the clipped AABB, a child window will typically be clipped by its parent (not always) @@ -5604,7 +5604,7 @@ bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool c const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding); if (!rect_for_touch.Contains(g.IO.MousePos)) return false; - if (!g.MouseRefViewport->GetRect().Overlaps(rect_clipped)) + if (!g.MouseViewport->GetRect().Overlaps(rect_clipped)) return false; return true; } @@ -6811,7 +6811,7 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) } else if (flags & ImGuiWindowFlags_Tooltip) { - window->Viewport = g.MouseRefViewport; + window->Viewport = g.MouseViewport; } else if (g.MovingWindow && g.MovingWindow->RootWindow == window && IsMousePosValid()) { @@ -15165,7 +15165,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover); ImGui::Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL"); ImGui::Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize); - ImGui::Text("MousePosViewport: 0x%08X, Hovered: 0x%08X -> Ref 0x%08X", g.IO.MousePosViewport, g.IO.MouseHoveredViewport, g.MouseRefViewport->ID); + ImGui::Text("MousePosViewport: 0x%08X, Hovered: 0x%08X -> Ref 0x%08X", g.IO.MousePosViewport, g.IO.MouseHoveredViewport, g.MouseViewport->ID); ImGui::TreePop(); } diff --git a/imgui_internal.h b/imgui_internal.h index e81ab7c58d43..73765345acbe 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -700,7 +700,7 @@ struct ImGuiContext // Viewports ImVector Viewports; // Active viewports (always 1+, and generally 1 unless multi-viewports are enabled). Each viewports hold their copy of ImDrawData. ImGuiViewportP* CurrentViewport; // We track changes of viewport (happening in Begin) so we can call Platform_OnChangedViewport() - ImGuiViewportP* MouseRefViewport; + ImGuiViewportP* MouseViewport; ImGuiViewportP* MouseLastHoveredViewport; // Last known viewport that was hovered by mouse (even if we are not hovering any viewport any more) ImGuiID PlatformLastFocusedViewport; // Record of last focused platform window/viewport, when this changes we stamp the viewport as front-most @@ -843,7 +843,7 @@ struct ImGuiContext NextTreeNodeOpenCond = 0; CurrentViewport = NULL; - MouseRefViewport = MouseLastHoveredViewport = NULL; + MouseViewport = MouseLastHoveredViewport = NULL; PlatformLastFocusedViewport = 0; NavWindow = NULL; From 7b4fbf43013989af3af9875284be5dd71fa3bd7a Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 17 Jul 2018 16:37:10 +0200 Subject: [PATCH 179/828] Viewport: Removed the need for the back-end to fill io.MousePosViewport, it seems unnecessary at this point. (#1542) --- examples/imgui_impl_glfw.cpp | 2 -- examples/imgui_impl_sdl.cpp | 4 ---- examples/imgui_impl_win32.cpp | 5 +---- imgui.cpp | 6 ++++-- imgui.h | 1 - imgui_demo.cpp | 1 - imgui_internal.h | 2 +- 7 files changed, 6 insertions(+), 15 deletions(-) diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index 1acccd0c3462..bb72dfb7b320 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -207,7 +207,6 @@ static void ImGui_ImplGlfw_UpdateMousePosAndButtons() ImGuiIO& io = ImGui::GetIO(); const ImVec2 mouse_pos_backup = io.MousePos; io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); - io.MousePosViewport = 0; io.MouseHoveredViewport = 0; // Update buttons @@ -236,7 +235,6 @@ static void ImGui_ImplGlfw_UpdateMousePosAndButtons() glfwGetCursorPos(window, &mouse_x, &mouse_y); io.MousePos = ImVec2((float)mouse_x + viewport->Pos.x, (float)mouse_y + viewport->Pos.y); } - io.MousePosViewport = viewport->ID; for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) io.MouseDown[i] |= glfwGetMouseButton(window, i) != 0; } diff --git a/examples/imgui_impl_sdl.cpp b/examples/imgui_impl_sdl.cpp index c9e81eadfa9b..1fd766abcf81 100644 --- a/examples/imgui_impl_sdl.cpp +++ b/examples/imgui_impl_sdl.cpp @@ -233,7 +233,6 @@ static void ImGui_ImplSDL2_UpdateMousePosAndButtons() ImGuiIO& io = ImGui::GetIO(); const ImVec2 mouse_pos_backup = io.MousePos; io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); - io.MousePosViewport = 0; io.MouseHoveredViewport = 0; // Set OS mouse position if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user) @@ -268,10 +267,7 @@ static void ImGui_ImplSDL2_UpdateMousePosAndButtons() my -= wy; } if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle((void*)focused_window)) - { io.MousePos = ImVec2(viewport->Pos.x + (float)mx, viewport->Pos.y + (float)my); - io.MousePosViewport = viewport->ID; - } // We already retrieve global mouse position, SDL_CaptureMouse() also let the OS know e.g. that our imgui drag outside the SDL window boundaries shouldn't trigger the OS window resize cursor // The function is only supported from SDL 2.0.4 (released Jan 2016) diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index cdcd751f5d01..60785639fb0d 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -137,8 +137,7 @@ static bool ImGui_ImplWin32_UpdateMouseCursor() // B) In Multi-viewport mode imgui needs: (when ImGuiConfigFlags_ViewportsEnable is set) // - io.MousePos ............... mouse position, in OS absolute coordinates (what you'd get from GetCursorPos(), or from WM_MOUSEMOVE+viewport->Pos). // io.MousePos is (0,0) when the mouse is on the upper-left of the primary monitor. -// - io.MousePosViewport ....... viewport which mouse position is based from (generally the focused/active/capturing viewport) -// - io.MouseHoveredViewport ... [optional] viewport which mouse is hovering, with _very_ specific/strict conditions (Read comments next to io.MouseHoveredViewport. This is _NOT_ easy to provide in many high-level engine because of how we handle the ImGuiViewportFlags_NoInputs flag) +// - io.MouseHoveredViewport ... [optional] viewport which mouse is hovering, with _VERY_ specific and strict conditions (Read comments next to io.MouseHoveredViewport. This is _NOT_ easy to provide in many high-level engine because of how we use the ImGuiViewportFlags_NoInputs flag) static void ImGui_ImplWin32_UpdateMousePos() { ImGuiIO& io = ImGui::GetIO(); @@ -154,7 +153,6 @@ static void ImGui_ImplWin32_UpdateMousePos() } io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); - io.MousePosViewport = 0; io.MouseHoveredViewport = 0; // Set mouse position and viewport @@ -168,7 +166,6 @@ static void ImGui_ImplWin32_UpdateMousePos() POINT client_pos = pos; ::ScreenToClient(focused_hwnd, &client_pos); io.MousePos = ImVec2(viewport->Pos.x + (float)client_pos.x, viewport->Pos.y + (float)client_pos.y); - io.MousePosViewport = viewport->ID; } // Our back-end can tell which window is under the mouse cursor (not every back-end can), so pass that info to imgui diff --git a/imgui.cpp b/imgui.cpp index 4fae6cd44927..d6ab51b22ac1 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3912,13 +3912,15 @@ static void ImGui::UpdateViewports() } if (viewport_hovered != NULL) g.MouseLastHoveredViewport = viewport_hovered; + else if (g.MouseLastHoveredViewport == NULL) + g.MouseLastHoveredViewport = g.Viewports[0]; // Update mouse reference viewport // (when moving a window we aim at its viewport, but this will be overwritten below if we go in drag and drop mode) if (g.MovingWindow) g.MouseViewport = g.MovingWindow->Viewport; else - g.MouseViewport = g.IO.MousePosViewport ? FindViewportByID(g.IO.MousePosViewport) : g.Viewports[0]; + g.MouseViewport = g.MouseLastHoveredViewport; // When dragging something, always refer to the last hovered viewport. // - when releasing a moving window we will revert to aiming behind (at viewport_hovered) @@ -15165,7 +15167,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover); ImGui::Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL"); ImGui::Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize); - ImGui::Text("MousePosViewport: 0x%08X, Hovered: 0x%08X -> Ref 0x%08X", g.IO.MousePosViewport, g.IO.MouseHoveredViewport, g.MouseViewport->ID); + ImGui::Text("MouseViewport: 0x%08X (UserHovered 0x%08X, LastHovered 0x%08X)", g.MouseViewport->ID, g.IO.MouseHoveredViewport, g.MouseLastHoveredViewport->ID); ImGui::TreePop(); } diff --git a/imgui.h b/imgui.h index 69f1b6310b85..458e6c87ef48 100644 --- a/imgui.h +++ b/imgui.h @@ -1144,7 +1144,6 @@ struct ImGuiIO bool MouseDown[5]; // Mouse buttons: 0=left, 1=right, 2=middle + extras. ImGui itself mostly only uses left button (BeginPopupContext** are using right button). Others buttons allows us to track if the mouse is being used by your application + available to user as a convenience via IsMouse** API. float MouseWheel; // Mouse wheel Vertical: 1 unit scrolls about 5 lines text. float MouseWheelH; // Mouse wheel Horizontal. Most users don't have a mouse with an horizontal wheel, may not be filled by all back-ends. - ImGuiID MousePosViewport; // (Optional) When using multiple viewports: viewport from which io.MousePos is based from (when dragging this is generally the captured/focused viewport, even though we can drag outside of it and then it's not hovered anymore). (0 == default viewport) ImGuiID MouseHoveredViewport; // (Optional) When using multiple viewports: viewport the OS mouse cursor is hovering _IGNORING_ viewports with the ImGuiViewportFlags_NoInputs flag, and _REGARDLESS_ of whether another viewport is focused. Set io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport if you can provide this info. If you don't imgui will use a decent heuristic instead. bool MouseDrawCursor; // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). bool KeyCtrl; // Keyboard modifier pressed: Control diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 6f1da63ea571..277841b01bb0 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2133,7 +2133,6 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::Text("Mouse dbl-clicked:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseDoubleClicked(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } ImGui::Text("Mouse released:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseReleased(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } ImGui::Text("Mouse wheel: %.1f", io.MouseWheel); - ImGui::Text("Mouse viewport: Ref 0x%08X Hovered 0x%08X", io.MousePosViewport, io.MouseHoveredViewport); ImGui::Text("Keys down:"); for (int i = 0; i < IM_ARRAYSIZE(io.KeysDown); i++) if (io.KeysDownDuration[i] >= 0.0f) { ImGui::SameLine(); ImGui::Text("%d (%.02f secs)", i, io.KeysDownDuration[i]); } ImGui::Text("Keys pressed:"); for (int i = 0; i < IM_ARRAYSIZE(io.KeysDown); i++) if (ImGui::IsKeyPressed(i)) { ImGui::SameLine(); ImGui::Text("%d", i); } diff --git a/imgui_internal.h b/imgui_internal.h index 73765345acbe..7c7bc76a2bff 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -701,7 +701,7 @@ struct ImGuiContext ImVector Viewports; // Active viewports (always 1+, and generally 1 unless multi-viewports are enabled). Each viewports hold their copy of ImDrawData. ImGuiViewportP* CurrentViewport; // We track changes of viewport (happening in Begin) so we can call Platform_OnChangedViewport() ImGuiViewportP* MouseViewport; - ImGuiViewportP* MouseLastHoveredViewport; // Last known viewport that was hovered by mouse (even if we are not hovering any viewport any more) + ImGuiViewportP* MouseLastHoveredViewport; // Last known viewport that was hovered by mouse (even if we are not hovering any viewport any more) + honoring the _NoInputs flag. ImGuiID PlatformLastFocusedViewport; // Record of last focused platform window/viewport, when this changes we stamp the viewport as front-most // Navigation data (for gamepad/keyboard) From 74077491cec810fc47a7d463a7e1ee15dba5be2f Mon Sep 17 00:00:00 2001 From: omar Date: Sun, 22 Jul 2018 16:23:35 +0200 Subject: [PATCH 180/828] Viewport: Fixed a situation when the implicit Debug window can hold on a zombie viewport which platform window is not properly destroyed. (#1542) --- imgui.cpp | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 77b12602c01e..17803f5cdaa1 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3955,29 +3955,29 @@ void ImGui::UpdatePlatformWindows() { ImGuiViewportP* viewport = g.Viewports[i]; viewport->LastPos = viewport->Pos; - if (viewport->LastFrameActive < g.FrameCount) - { - if (viewport->LastFrameActive < g.FrameCount - 1) - { - if (g.PlatformIO.Renderer_DestroyWindow) - g.PlatformIO.Renderer_DestroyWindow(viewport); - if (g.PlatformIO.Platform_DestroyWindow) - g.PlatformIO.Platform_DestroyWindow(viewport); - viewport->CreatedPlatformWindow = false; - IM_ASSERT(viewport->RendererUserData == NULL); - IM_ASSERT(viewport->PlatformUserData == NULL && viewport->PlatformHandle == NULL); - } + + // Destroy platform window if the viewport hasn't been submitted or if it is hosting a hidden window (the implicit Debug window will be registered its viewport then be disabled) + bool destroy_platform_window = false; + destroy_platform_window |= (viewport->LastFrameActive < g.FrameCount - 1); + destroy_platform_window |= (viewport->Window && !IsWindowActiveAndVisible(viewport->Window)); + if (destroy_platform_window) + { + if (viewport->CreatedPlatformWindow && g.PlatformIO.Renderer_DestroyWindow) + g.PlatformIO.Renderer_DestroyWindow(viewport); + if (viewport->CreatedPlatformWindow && g.PlatformIO.Platform_DestroyWindow) + g.PlatformIO.Platform_DestroyWindow(viewport); + viewport->CreatedPlatformWindow = false; + IM_ASSERT(viewport->RendererUserData == NULL); + IM_ASSERT(viewport->PlatformUserData == NULL && viewport->PlatformHandle == NULL); continue; } + if (viewport->LastFrameActive < g.FrameCount) + continue; // New windows that appears directly in a new viewport won't always have a size on their frame if (viewport->Size.x <= 0 || viewport->Size.y <= 0) continue; - // Ignore viewport that are hosting a hidden window (also check the Active flag, as the implicit Debug window will be registering its viewport then immediately disabled) - if (viewport->Window && !IsWindowActiveAndVisible(viewport->Window)) - continue; - // Update viewport flags if (viewport->Window != NULL) { @@ -15102,7 +15102,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) if (ImGui::TreeNode((void*)(intptr_t)viewport->ID, "Viewport #%d, ID: 0x%08X, Window: \"%s\"", viewport->Idx, viewport->ID, viewport->Window ? viewport->Window->Name : "N/A")) { ImGuiWindowFlags flags = viewport->Flags; - ImGui::BulletText("Pos: (%.0f,%.0f), Size: (%.0f, %.0f), Monitor: %d, DpiScale: %.0f%%", viewport->Pos.x, viewport->Pos.y, viewport->Size.x, viewport->Size.y, viewport->PlatformMonitor, viewport->DpiScale * 100.0f); + ImGui::BulletText("Pos: (%.0f,%.0f), Size: (%.0f,%.0f), Monitor: %d, DpiScale: %.0f%%", viewport->Pos.x, viewport->Pos.y, viewport->Size.x, viewport->Size.y, viewport->PlatformMonitor, viewport->DpiScale * 100.0f); if (viewport->Idx > 0) { ImGui::SameLine(); if (ImGui::SmallButton("Reset Pos")) { viewport->Pos = ImVec2(200,200); if (viewport->Window) viewport->Window->Pos = ImVec2(200,200); } } ImGui::BulletText("Flags: 0x%04X =%s%s%s%s%s", viewport->Flags, (flags & ImGuiViewportFlags_CanHostOtherWindows) ? " CanHostOtherWindows" : "", (flags & ImGuiViewportFlags_NoDecoration) ? " NoDecoration" : "", From 049c51584096278b055199c7bf1f613c8e0fca81 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 18 Jul 2018 13:03:21 +0200 Subject: [PATCH 181/828] Added non-const ImVec2 [] operator for consistency (and because some docking code needs it.) --- imgui.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/imgui.h b/imgui.h index b0563ce42f98..b37a26152883 100644 --- a/imgui.h +++ b/imgui.h @@ -135,7 +135,8 @@ struct ImVec2 float x, y; ImVec2() { x = y = 0.0f; } ImVec2(float _x, float _y) { x = _x; y = _y; } - float operator[] (size_t i) const { IM_ASSERT(i <= 1); return (&x)[i]; } // We very rarely use this [] operator, the assert overhead is fine. + float operator[] (size_t idx) const { IM_ASSERT(idx <= 1); return (&x)[idx]; } // We very rarely use this [] operator, the assert overhead is fine. + float& operator[] (size_t idx) { IM_ASSERT(idx <= 1); return (&x)[idx]; } // We very rarely use this [] operator, the assert overhead is fine. #ifdef IM_VEC2_CLASS_EXTRA IM_VEC2_CLASS_EXTRA // Define additional constructors and implicit cast operators in imconfig.h to convert back and forth between your math types and ImVec2. #endif From f22024cb19af47084fd2e3977fa6591a311a54eb Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 18 Jul 2018 16:16:44 +0200 Subject: [PATCH 182/828] Internals: Exposed NavScoreItemGetQuadrant() as ImGetDirQuadrantFromDelta() in imgui_internal.h --- imgui.cpp | 6 +++--- imgui_internal.h | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 0d21ed21d276..a32eb07e3cfe 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2378,7 +2378,7 @@ void ImGui::ItemSize(const ImRect& bb, float text_offset_y) ItemSize(bb.GetSize(), text_offset_y); } -static ImGuiDir inline NavScoreItemGetQuadrant(float dx, float dy) +ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy) { if (ImFabs(dx) > ImFabs(dy)) return (dx > 0.0f) ? ImGuiDir_Right : ImGuiDir_Left; @@ -2454,7 +2454,7 @@ static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand) dax = dbx; day = dby; dist_axial = dist_box; - quadrant = NavScoreItemGetQuadrant(dbx, dby); + quadrant = ImGetDirQuadrantFromDelta(dbx, dby); } else if (dcx != 0.0f || dcy != 0.0f) { @@ -2462,7 +2462,7 @@ static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand) dax = dcx; day = dcy; dist_axial = dist_center; - quadrant = NavScoreItemGetQuadrant(dcx, dcy); + quadrant = ImGetDirQuadrantFromDelta(dcx, dcy); } else { diff --git a/imgui_internal.h b/imgui_internal.h index b55246d3f51e..d5d4d4edf262 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -127,6 +127,7 @@ IMGUI_API ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, con IMGUI_API bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p); IMGUI_API ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p); IMGUI_API void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w); +IMGUI_API ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy); // Helpers: String IMGUI_API int ImStricmp(const char* str1, const char* str2); From 1b646a44404672a13cd2a178ab32955d7fc75a53 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 26 Jul 2018 19:00:17 +0200 Subject: [PATCH 183/828] Nav: Tweaked CTRL+TAB to hide visual noise on fast switch + fading out screen dimming and highlight to make the experience less harsh --- imgui.cpp | 42 +++++++++++++++++++++++++++++------------- imgui_internal.h | 7 ++++--- 2 files changed, 33 insertions(+), 16 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index a32eb07e3cfe..48ce89e97c19 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -862,6 +862,10 @@ static const ImS64 IM_S64_MAX = 9223372036854775807ll; static const ImU64 IM_U64_MIN = 0; static const ImU64 IM_U64_MAX = 0xFFFFFFFFFFFFFFFFull; +// When using CTRL+TAB (or Gamepad Square+L/R) we delay the visual a little in order to reduce visual noise doing a fast switch. +static const float NAV_WINDOWING_HIGHLIGHT_DELAY = 0.20f; // Time before the highlight and screen dimming starts fading in +static const float NAV_WINDOWING_LIST_APPEAR_DELAY = 0.15f; // Time before the window list starts to appear + //------------------------------------------------------------------------- // Forward Declarations //------------------------------------------------------------------------- @@ -3108,7 +3112,7 @@ static void NavUpdateWindowingHighlightWindow(int focus_change_dir) if (!window_target) window_target = FindWindowNavigable((focus_change_dir < 0) ? (g.Windows.Size - 1) : 0, i_current, focus_change_dir); if (window_target) // Don't reset windowing target if there's a single window in the list - g.NavWindowingTarget = window_target; + g.NavWindowingTarget = g.NavWindowingTargetAnim = window_target; g.NavWindowingToggleLayer = false; } @@ -3126,23 +3130,32 @@ static void ImGui::NavUpdateWindowing() return; } + // Fade out + if (g.NavWindowingTargetAnim && g.NavWindowingTarget == NULL) + { + g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha - g.IO.DeltaTime * 10.0f, 0.0f); + if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f) + g.NavWindowingTargetAnim = NULL; + } + + // Start CTRL-TAB or Square+L/R window selection bool start_windowing_with_gamepad = !g.NavWindowingTarget && IsNavInputPressed(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed); bool start_windowing_with_keyboard = !g.NavWindowingTarget && g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard); if (start_windowing_with_gamepad || start_windowing_with_keyboard) if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavigable(g.Windows.Size - 1, -INT_MAX, -1)) { - g.NavWindowingTarget = window; - g.NavWindowingHighlightTimer = g.NavWindowingHighlightAlpha = 0.0f; + g.NavWindowingTarget = g.NavWindowingTargetAnim = window; + g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f; g.NavWindowingToggleLayer = start_windowing_with_keyboard ? false : true; g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_NavKeyboard : ImGuiInputSource_NavGamepad; } // Gamepad update - g.NavWindowingHighlightTimer += g.IO.DeltaTime; + g.NavWindowingTimer += g.IO.DeltaTime; if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavGamepad) { // Highlight only appears after a brief time holding the button, so that a fast tap on PadMenu (to toggle NavLayer) doesn't add visual noise - g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingHighlightTimer - 0.20f) / 0.05f)); + g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); // Select window to focus const int focus_change_dir = (int)IsNavInputPressed(ImGuiNavInput_FocusPrev, ImGuiInputReadMode_RepeatSlow) - (int)IsNavInputPressed(ImGuiNavInput_FocusNext, ImGuiInputReadMode_RepeatSlow); @@ -3168,7 +3181,7 @@ static void ImGui::NavUpdateWindowing() if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavKeyboard) { // Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise - g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingHighlightTimer - 0.15f) / 0.04f)); // 1.0f + g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); // 1.0f if (IsKeyPressedMap(ImGuiKey_Tab, true)) NavUpdateWindowingHighlightWindow(g.IO.KeyShift ? +1 : -1); if (!g.IO.KeyCtrl) @@ -3253,6 +3266,9 @@ void ImGui::NavUpdateWindowingList() ImGuiContext& g = *GImGui; IM_ASSERT(g.NavWindowingTarget != NULL); + if (g.NavWindowingTimer < NAV_WINDOWING_LIST_APPEAR_DELAY) + return; + if (g.NavWindowingList == NULL) g.NavWindowingList = FindWindowByName("###NavWindowingList"); ImGuiViewportP* viewport = /*g.NavWindow ? g.NavWindow->Viewport :*/ (ImGuiViewportP*)GetMainViewport(); @@ -4414,10 +4430,10 @@ void ImGui::NewFrame() UpdateMouseMovingWindow(); // Background darkening/whitening - if (GetFrontMostPopupModal() != NULL || g.NavWindowingTarget != NULL) + if (GetFrontMostPopupModal() != NULL || (g.NavWindowingTarget != NULL && g.NavWindowingHighlightAlpha > 0.0f)) g.DimBgRatio = ImMin(g.DimBgRatio + g.IO.DeltaTime * 6.0f, 1.0f); else - g.DimBgRatio = 0.0f; + g.DimBgRatio = ImMax(g.DimBgRatio - g.IO.DeltaTime * 10.0f, 0.0f); g.MouseCursor = ImGuiMouseCursor_Arrow; g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1; @@ -4940,7 +4956,7 @@ void ImGui::EndFrame() // Draw modal whitening background on _other_ viewports than the one the modal is one ImGuiWindow* modal_window = GetFrontMostPopupModal(); const bool dim_bg_for_modal = (modal_window != NULL); - const bool dim_bg_for_window_list = (g.NavWindowingTarget != NULL); + const bool dim_bg_for_window_list = (g.NavWindowingTargetAnim != NULL); if (dim_bg_for_modal || dim_bg_for_window_list) for (int viewport_n = 0; viewport_n < g.Viewports.Size; viewport_n++) { @@ -4949,7 +4965,7 @@ void ImGui::EndFrame() continue; if (g.NavWindowingList && viewport == g.NavWindowingList->Viewport) continue; - if (g.NavWindowingTarget && viewport == g.NavWindowingTarget->Viewport) + if (g.NavWindowingTargetAnim && viewport == g.NavWindowingTargetAnim->Viewport) continue; ImDrawList* draw_list = GetOverlayDrawList(viewport); const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio); @@ -7444,7 +7460,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Draw modal or window list full viewport dimming background (for other viewports we'll render them in EndFrame) const bool dim_bg_for_modal = (flags & ImGuiWindowFlags_Modal) && window == GetFrontMostPopupModal() && window->HiddenFrames <= 0; - const bool dim_bg_for_window_list = g.NavWindowingTarget && ((window == g.NavWindowingTarget->RootWindow) || (g.NavWindowingList && (window == g.NavWindowingList) && g.NavWindowingList->Viewport != g.NavWindowingTarget->Viewport)); + const bool dim_bg_for_window_list = g.NavWindowingTargetAnim && ((window == g.NavWindowingTargetAnim->RootWindow) || (g.NavWindowingList && (window == g.NavWindowingList) && g.NavWindowingList->Viewport != g.NavWindowingTargetAnim->Viewport)); if (dim_bg_for_modal || dim_bg_for_window_list) { const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio); @@ -7452,7 +7468,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) } // Draw navigation selection/windowing rectangle background - if (dim_bg_for_window_list && window == g.NavWindowingTarget->RootWindow) + if (dim_bg_for_window_list && window == g.NavWindowingTargetAnim) { ImRect bb = window->Rect(); bb.Expand(g.FontSize); @@ -7536,7 +7552,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) } // Draw navigation selection/windowing rectangle border - if (g.NavWindowingTarget == window) + if (g.NavWindowingTargetAnim == window) { float rounding = ImMax(window->WindowRounding, g.Style.WindowRounding); ImRect bb = window->Rect(); diff --git a/imgui_internal.h b/imgui_internal.h index d5d4d4edf262..4a095541aa20 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -719,8 +719,9 @@ struct ImGuiContext ImRect NavScoringRectScreen; // Rectangle used for scoring, in screen space. Based of window->DC.NavRefRectRel[], modified for directional navigation scoring. int NavScoringCount; // Metrics for debugging ImGuiWindow* NavWindowingTarget; // When selecting a window (holding Menu+FocusPrev/Next, or equivalent of CTRL-TAB) this window is temporarily displayed front-most. + ImGuiWindow* NavWindowingTargetAnim; // Record of last valid NavWindowingTarget until DimBgRatio and NavWindowingHighlightAlpha becomes 0.0f ImGuiWindow* NavWindowingList; - float NavWindowingHighlightTimer; + float NavWindowingTimer; float NavWindowingHighlightAlpha; bool NavWindowingToggleLayer; int NavLayer; // Layer we are navigating on. For now the system is hard-coded for 0=main contents and 1=menu/title bar, may expose layers later. @@ -853,8 +854,8 @@ struct ImGuiContext NavInputSource = ImGuiInputSource_None; NavScoringRectScreen = ImRect(); NavScoringCount = 0; - NavWindowingTarget = NavWindowingList = NULL; - NavWindowingHighlightTimer = NavWindowingHighlightAlpha = 0.0f; + NavWindowingTarget = NavWindowingTargetAnim = NavWindowingList = NULL; + NavWindowingTimer = NavWindowingHighlightAlpha = 0.0f; NavWindowingToggleLayer = false; NavLayer = 0; NavIdTabCounter = INT_MAX; From c031ea088ae84286332e96981d5ab7d333dac6ad Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 31 Jul 2018 13:38:24 +0200 Subject: [PATCH 184/828] Viewport: Fixed an assert on loss of valid mouse position while dragging a tooltip (would happen often when using debugger breakpoint). Not totally sure about best solution, may not be important. --- imgui.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 9da49f553d99..b07c5819718a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6923,8 +6923,9 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) { ImVec2 mouse_ref = (flags & ImGuiWindowFlags_Tooltip) ? g.IO.MousePos : g.CurrentPopupStack.back().OpenMousePos; - if (window->Appearing || (flags & ImGuiWindowFlags_Tooltip)) - window->ViewportAllowPlatformMonitorExtend = FindPlatformMonitorForPos((use_mouse_ref && IsMousePosValid(&mouse_ref)) ? mouse_ref : NavCalcPreferredRefPos()); + bool mouse_valid = IsMousePosValid(&mouse_ref); + if ((window->Appearing || (flags & ImGuiWindowFlags_Tooltip)) && (!use_mouse_ref || mouse_valid)) + window->ViewportAllowPlatformMonitorExtend = FindPlatformMonitorForPos((use_mouse_ref && mouse_valid) ? mouse_ref : NavCalcPreferredRefPos()); else window->ViewportAllowPlatformMonitorExtend = window->Viewport->PlatformMonitor; } From 1722a31155ffddbfe254d3690c162a6675083f78 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 14 Aug 2018 11:32:24 -0700 Subject: [PATCH 185/828] Viewport: ImGuiWindowFlags_NoFocusOnAppearing affects viewport accordingly. (#1542) --- imgui.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 64b3540ed2e9..33cd67f326b1 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5233,7 +5233,10 @@ ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const viewport->Size = size; viewport->PlatformMonitor = FindPlatformMonitorForRect(viewport->GetRect()); g.Viewports.push_back(viewport); - + + if (window && (window->Flags & ImGuiWindowFlags_NoFocusOnAppearing)) + flags |= ImGuiViewportFlags_NoFocusOnAppearing; + // We normally setup for all viewports in NewFrame() but here need to handle the mid-frame creation of a new viewport. // We need to extend the fullscreen clip rect so the OverlayDrawList clip is correct for that the first frame g.DrawListSharedData.ClipRectFullscreen.z = ImMax(g.DrawListSharedData.ClipRectFullscreen.z, viewport->Pos.x + viewport->Size.x); From 9852649e972a9d741586e8050fa46cac949b3330 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 14 Aug 2018 19:17:33 -0700 Subject: [PATCH 186/828] Viewport: Initial viewport DpiScale only queried on viewport creation. Because AddUpdateViewport() is called repeatedly on MovingWindow or with ImGuiConfigFlags_ViewportsNoMerge, the DpiScale update would break ImGuiConfigFlags_DpiEnableScaleViewports. (#1542) --- imgui.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 33cd67f326b1..bcb90656c6c5 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5241,6 +5241,11 @@ ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const // We need to extend the fullscreen clip rect so the OverlayDrawList clip is correct for that the first frame g.DrawListSharedData.ClipRectFullscreen.z = ImMax(g.DrawListSharedData.ClipRectFullscreen.z, viewport->Pos.x + viewport->Size.x); g.DrawListSharedData.ClipRectFullscreen.w = ImMax(g.DrawListSharedData.ClipRectFullscreen.w, viewport->Pos.y + viewport->Size.y); + + // Request an initial DpiScale before the OS platform window creation + // This is so we can select an appropriate font size on the first frame of our window lifetime + if (g.PlatformIO.Platform_GetWindowDpiScale) + viewport->DpiScale = g.PlatformIO.Platform_GetWindowDpiScale(viewport); } viewport->Window = window; @@ -5251,10 +5256,6 @@ ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const if (window != NULL) window->ViewportOwned = true; - // Request an initial DpiScale before the OS platform window creation - // This is so we can select an appropriate font size on the first frame of our window lifetime - if (g.PlatformIO.Platform_GetWindowDpiScale) - viewport->DpiScale = g.PlatformIO.Platform_GetWindowDpiScale(viewport); return viewport; } From 5d630c930dc5899a0f33729543fee8b0ad3c7828 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 15 Aug 2018 16:07:07 -0700 Subject: [PATCH 187/828] Viewport: DestroyPlatformWindows() checks for the bool CreatedPlatformWindow flag correctly. Note that we set CreatedPlatformWindow=true for the main viewport to allow the back-end to store data in the public Viewport structure (for consistency). (#1542) --- examples/example_win32_directx11/main.cpp | 2 +- imgui.cpp | 23 ++++++++++++++--------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/examples/example_win32_directx11/main.cpp b/examples/example_win32_directx11/main.cpp index 3a8bb783f2ec..e9a0d790c6a4 100644 --- a/examples/example_win32_directx11/main.cpp +++ b/examples/example_win32_directx11/main.cpp @@ -137,7 +137,7 @@ int main(int, char**) io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoTaskBarIcons; //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoMerge; - io.ConfigFlags |= ImGuiConfigFlags_DpiEnableScaleFonts; + io.ConfigFlags |= ImGuiConfigFlags_DpiEnableScaleFonts; // FIXME-DPI: THIS CURRENTLY DOESN'T WORK AS EXPECTED. DON'T USE IN USER APP! io.ConfigFlags |= ImGuiConfigFlags_DpiEnableScaleViewports; io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls diff --git a/imgui.cpp b/imgui.cpp index bcb90656c6c5..61201758f296 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4599,6 +4599,7 @@ void ImGui::Initialize(ImGuiContext* context) ImGuiViewportP* viewport = IM_NEW(ImGuiViewportP)(); viewport->ID = IMGUI_VIEWPORT_DEFAULT_ID; viewport->Idx = 0; + viewport->CreatedPlatformWindow = true; // Set this flag so DestroyPlatformWindows() gives a chance for backend to receive DestroyWindow calls for the main viewport. g.Viewports.push_back(viewport); g.PlatformIO.MainViewport = g.Viewports[0]; // Make it accessible in public-facing GetPlatformIO() immediately (before the first call to EndFrame) g.PlatformIO.Viewports.push_back(g.Viewports[0]); @@ -4608,16 +4609,20 @@ void ImGui::Initialize(ImGuiContext* context) void ImGui::DestroyPlatformWindows() { - // We call the destroy window on the main viewport (index 0) to give a chance to the back-end to clear any data it may hold on it. + // We call the destroy window on the main viewport (index 0) to give a chance to the back-end to clear any data + // have stored in e.g. PlatformHandle. // It is expected that the back-end stored a flag to remember that it doesn't own the window created for the main viewport, - // and won't destroy the underlying platform/renderer data. - ImGuiContext& g = *GImGui; - if (g.PlatformIO.Renderer_DestroyWindow) - for (int i = 0; i < g.Viewports.Size; i++) - g.PlatformIO.Renderer_DestroyWindow(g.Viewports[i]); - if (g.PlatformIO.Platform_DestroyWindow) - for (int i = 0; i < g.Viewports.Size; i++) - g.PlatformIO.Platform_DestroyWindow(g.Viewports[i]); + // and won't destroy the underlying platform/renderer data (e.g. + ImGuiContext& g = *GImGui; + for (int i = 0; i < g.Viewports.Size; i++) + if (g.Viewports[i]->CreatedPlatformWindow) + { + if (g.PlatformIO.Renderer_DestroyWindow) + g.PlatformIO.Renderer_DestroyWindow(g.Viewports[i]); + if (g.PlatformIO.Platform_DestroyWindow) + g.PlatformIO.Platform_DestroyWindow(g.Viewports[i]); + g.Viewports[i]->CreatedPlatformWindow = false; + } } // This function is merely here to free heap allocations. From e9c849884a312581551a659d2b096a4b257168de Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 24 Aug 2018 15:42:31 +0200 Subject: [PATCH 188/828] Viewport: Fixed warnings due to bad merge. --- imgui.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 5f37f1121212..9ec4f6fa1463 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -905,9 +905,7 @@ static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* wind static void AddDrawListToDrawData(ImVector* out_list, ImDrawList* draw_list); static void AddWindowToSortedBuffer(ImVector* out_sorted_windows, ImGuiWindow* window); -static ImRect GetViewportRect(); - -static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data); +static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data); static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end); static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining = NULL, ImVec2* out_offset = NULL, bool stop_on_new_line = false); From 0ada71618429056db566c1ead7a8b4e5f29e23a7 Mon Sep 17 00:00:00 2001 From: omar Date: Sat, 25 Aug 2018 20:15:18 +0200 Subject: [PATCH 189/828] Examples: Viewport: Disabling swap intervals / vsync on secondary context to match what the DX11 bindings is doing. (#1542) --- examples/imgui_impl_glfw.cpp | 5 +++++ examples/imgui_impl_sdl.cpp | 3 +++ 2 files changed, 8 insertions(+) diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index e11381290f7c..bf178a5ba2d2 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -391,6 +391,11 @@ static void ImGui_ImplGlfw_CreateWindow(ImGuiViewport* viewport) glfwSetWindowCloseCallback(data->Window, ImGui_ImplGlfw_WindowCloseCallback); glfwSetWindowPosCallback(data->Window, ImGui_ImplGlfw_WindowPosCallback); glfwSetWindowSizeCallback(data->Window, ImGui_ImplGlfw_WindowSizeCallback); + if (g_ClientApi == GlfwClientApi_OpenGL) + { + glfwMakeContextCurrent(data->Window); + glfwSwapInterval(0); + } } static void ImGui_ImplGlfw_DestroyWindow(ImGuiViewport* viewport) diff --git a/examples/imgui_impl_sdl.cpp b/examples/imgui_impl_sdl.cpp index d95f74a527c3..ef174d9a14bf 100644 --- a/examples/imgui_impl_sdl.cpp +++ b/examples/imgui_impl_sdl.cpp @@ -369,7 +369,10 @@ static void ImGui_ImplSDL2_CreateWindow(ImGuiViewport* viewport) data->Window = SDL_CreateWindow("No Title Yet", (int)viewport->Pos.x, (int)viewport->Pos.y, (int)viewport->Size.x, (int)viewport->Size.y, sdl_flags); data->WindowOwned = true; if (use_opengl) + { data->GLContext = SDL_GL_CreateContext(data->Window); + SDL_GL_SetSwapInterval(0); + } if (use_opengl && backup_context) SDL_GL_MakeCurrent(data->Window, backup_context); viewport->PlatformHandle = (void*)data->Window; From 705ff494003edf825a188e7c670c7b33dfcc10d1 Mon Sep 17 00:00:00 2001 From: omar Date: Sat, 25 Aug 2018 21:21:14 +0200 Subject: [PATCH 190/828] Examples: Vulkan: Viewport: Tweak to improve framerate on multiple-viewport situations, tho it is still halving the unthrottled framerate on nvidia for some reason.. Followup to 4dea032 (#1542) --- examples/imgui_impl_vulkan.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/examples/imgui_impl_vulkan.cpp b/examples/imgui_impl_vulkan.cpp index 108c7ff11c24..fb27de2cc559 100644 --- a/examples/imgui_impl_vulkan.cpp +++ b/examples/imgui_impl_vulkan.cpp @@ -1113,14 +1113,16 @@ static void ImGui_ImplVulkan_CreateWindow(ImGuiViewport* viewport) exit(-1); } - // Get Surface Format + // Select Surface Format const VkFormat requestSurfaceImageFormat[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM }; const VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; wd->SurfaceFormat = ImGui_ImplVulkanH_SelectSurfaceFormat(g_PhysicalDevice, wd->Surface, requestSurfaceImageFormat, (size_t)IM_ARRAYSIZE(requestSurfaceImageFormat), requestSurfaceColorSpace); - // Get Present Mode - VkPresentModeKHR present_mode = VK_PRESENT_MODE_IMMEDIATE_KHR; - wd->PresentMode = ImGui_ImplVulkanH_SelectPresentMode(g_PhysicalDevice, wd->Surface, &present_mode, 1); + // Select Present Mode + // FIXME-VULKAN: Even thought mailbox seems to get us maximum framerate with a single window, it halves framerate with a second window etc. (w/ Nvidia and SDK 1.82.1) + VkPresentModeKHR present_modes[] = { VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_FIFO_KHR }; + wd->PresentMode = ImGui_ImplVulkanH_SelectPresentMode(g_PhysicalDevice, wd->Surface, &present_modes[0], IM_ARRAYSIZE(present_modes)); + //printf("[vulkan] Secondary window selected PresentMode = %d\n", wd->PresentMode); // Create SwapChain, RenderPass, Framebuffer, etc. wd->ClearEnable = (viewport->Flags & ImGuiViewportFlags_NoRendererClear) ? false : true; From 6c34bffbb51108ae272078dae8c493fdcb4ef0bd Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 30 Aug 2018 14:25:23 +0200 Subject: [PATCH 191/828] Version 1.64 WIP --- CHANGELOG.txt | 5 +++++ imgui.cpp | 2 +- imgui.h | 6 +++--- imgui_demo.cpp | 2 +- imgui_draw.cpp | 2 +- imgui_internal.h | 2 +- 6 files changed, 12 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 2b873373a8cd..783454d37731 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -29,6 +29,11 @@ HOW TO UPDATE? - Please report any issue! +----------------------------------------------------------------------- + VERSION 1.64 (in progress) +----------------------------------------------------------------------- + + ----------------------------------------------------------------------- VERSION 1.63 (Released 2018-08-29) ----------------------------------------------------------------------- diff --git a/imgui.cpp b/imgui.cpp index 15900c0422f1..9c3233984b3d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.63 +// dear imgui, v1.64 WIP // (main code and documentation) // Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp for demo code. diff --git a/imgui.h b/imgui.h index a4ef7c630a4b..581dd86b18bc 100644 --- a/imgui.h +++ b/imgui.h @@ -1,4 +1,4 @@ -// dear imgui, v1.63 +// dear imgui, v1.64 WIP // (headers) // See imgui.cpp file for documentation. @@ -23,8 +23,8 @@ // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY00 then bounced up to XYY01 when release tagging happens) -#define IMGUI_VERSION "1.63" -#define IMGUI_VERSION_NUM 16301 +#define IMGUI_VERSION "1.64 WIP" +#define IMGUI_VERSION_NUM 16400 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert)) #define IMGUI_HAS_VIEWPORT 1 // Viewport WIP branch diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 6c104b5ec9e4..272395685712 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.63 +// dear imgui, v1.64 WIP // (demo code) // Message to the person tempted to delete this file when integrating ImGui into their code base: diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 3692187435e3..c70530560a05 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.63 +// dear imgui, v1.64 WIP // (drawing and font code) // Contains implementation for diff --git a/imgui_internal.h b/imgui_internal.h index 696855c673c0..cf5163bad94e 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1,4 +1,4 @@ -// dear imgui, v1.63 +// dear imgui, v1.64 WIP // (internal structures/api) // You may use this file to debug, understand or extend ImGui features but we don't provide any guarantee of forward compatibility! From e312363007cefa6140ca884515d2e0c6442dcb47 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 28 Aug 2018 21:59:14 +0200 Subject: [PATCH 192/828] Refactor: Added empty imgui_widgets.cpp + updated project files / makefiles etc. accordingly (#2036) --- README.md | 1 + .../example_allegro5/example_allegro5.vcxproj | 1 + .../example_allegro5.vcxproj.filters | 3 +++ .../example_freeglut_opengl2.vcxproj | 1 + .../example_freeglut_opengl2.vcxproj.filters | 3 +++ examples/example_glfw_opengl2/Makefile | 2 +- .../example_glfw_opengl2.vcxproj | 1 + .../example_glfw_opengl2.vcxproj.filters | 3 +++ examples/example_glfw_opengl3/Makefile | 2 +- .../example_glfw_opengl3.vcxproj | 1 + .../example_glfw_opengl3.vcxproj.filters | 3 +++ .../example_marmalade/marmalade_example.mkb | 1 + examples/example_sdl_opengl2/Makefile | 2 +- .../example_sdl_opengl2.vcxproj | 3 ++- .../example_sdl_opengl2.vcxproj.filters | 3 +++ examples/example_sdl_opengl3/Makefile | 2 +- .../example_sdl_opengl3.vcxproj | 1 + .../example_sdl_opengl3.vcxproj.filters | 3 +++ .../example_sdl_vulkan.vcxproj | 1 + .../example_sdl_vulkan.vcxproj.filters | 3 +++ .../example_win32_directx10.vcxproj | 1 + .../example_win32_directx10.vcxproj.filters | 3 +++ .../example_win32_directx11.vcxproj | 1 + .../example_win32_directx11.vcxproj.filters | 3 +++ .../example_win32_directx12.vcxproj | 1 + .../example_win32_directx12.vcxproj.filters | 3 +++ .../example_win32_directx9.vcxproj | 1 + .../example_win32_directx9.vcxproj.filters | 3 +++ imgui_widgets.cpp | 25 +++++++++++++++++++ 29 files changed, 76 insertions(+), 5 deletions(-) create mode 100644 imgui_widgets.cpp diff --git a/README.md b/README.md index e2624d06d664..6d724a0bce01 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ Dear ImGui is self-contained within a few files that you can easily copy and com - imgui.h - imgui_demo.cpp - imgui_draw.cpp +- imgui_widgets.cpp - imgui_internal.h - imconfig.h (empty by default, user-editable) - stb_rect_pack.h diff --git a/examples/example_allegro5/example_allegro5.vcxproj b/examples/example_allegro5/example_allegro5.vcxproj index ec96f62110d8..b07926973c08 100644 --- a/examples/example_allegro5/example_allegro5.vcxproj +++ b/examples/example_allegro5/example_allegro5.vcxproj @@ -153,6 +153,7 @@ + diff --git a/examples/example_allegro5/example_allegro5.vcxproj.filters b/examples/example_allegro5/example_allegro5.vcxproj.filters index 09dbc5ebcad9..8019ebfdc30d 100644 --- a/examples/example_allegro5/example_allegro5.vcxproj.filters +++ b/examples/example_allegro5/example_allegro5.vcxproj.filters @@ -28,6 +28,9 @@ sources + + imgui + diff --git a/examples/example_freeglut_opengl2/example_freeglut_opengl2.vcxproj b/examples/example_freeglut_opengl2/example_freeglut_opengl2.vcxproj index 0c60ba368fb9..e3bd4176a643 100644 --- a/examples/example_freeglut_opengl2/example_freeglut_opengl2.vcxproj +++ b/examples/example_freeglut_opengl2/example_freeglut_opengl2.vcxproj @@ -153,6 +153,7 @@ + diff --git a/examples/example_freeglut_opengl2/example_freeglut_opengl2.vcxproj.filters b/examples/example_freeglut_opengl2/example_freeglut_opengl2.vcxproj.filters index b45919454a8b..eb6d8526a0c2 100644 --- a/examples/example_freeglut_opengl2/example_freeglut_opengl2.vcxproj.filters +++ b/examples/example_freeglut_opengl2/example_freeglut_opengl2.vcxproj.filters @@ -28,6 +28,9 @@ sources + + imgui + diff --git a/examples/example_glfw_opengl2/Makefile b/examples/example_glfw_opengl2/Makefile index 7ec7eeee059f..482b0e5d4c65 100644 --- a/examples/example_glfw_opengl2/Makefile +++ b/examples/example_glfw_opengl2/Makefile @@ -17,7 +17,7 @@ EXE = example_glfw_opengl2 SOURCES = main.cpp SOURCES += ../imgui_impl_glfw.cpp ../imgui_impl_opengl2.cpp -SOURCES += ../../imgui.cpp ../../imgui_demo.cpp ../../imgui_draw.cpp +SOURCES += ../../imgui.cpp ../../imgui_demo.cpp ../../imgui_draw.cpp ../../imgui_widgets.cpp OBJS = $(addsuffix .o, $(basename $(notdir $(SOURCES)))) UNAME_S := $(shell uname -s) diff --git a/examples/example_glfw_opengl2/example_glfw_opengl2.vcxproj b/examples/example_glfw_opengl2/example_glfw_opengl2.vcxproj index acec434b770d..73c7ba9db031 100644 --- a/examples/example_glfw_opengl2/example_glfw_opengl2.vcxproj +++ b/examples/example_glfw_opengl2/example_glfw_opengl2.vcxproj @@ -153,6 +153,7 @@ + diff --git a/examples/example_glfw_opengl2/example_glfw_opengl2.vcxproj.filters b/examples/example_glfw_opengl2/example_glfw_opengl2.vcxproj.filters index acf77fa4f06e..b7a37e68a589 100644 --- a/examples/example_glfw_opengl2/example_glfw_opengl2.vcxproj.filters +++ b/examples/example_glfw_opengl2/example_glfw_opengl2.vcxproj.filters @@ -28,6 +28,9 @@ sources + + imgui + diff --git a/examples/example_glfw_opengl3/Makefile b/examples/example_glfw_opengl3/Makefile index d36b78d80cf2..a9c7007f9163 100644 --- a/examples/example_glfw_opengl3/Makefile +++ b/examples/example_glfw_opengl3/Makefile @@ -17,7 +17,7 @@ EXE = example_glfw_opengl3 SOURCES = main.cpp SOURCES += ../imgui_impl_glfw.cpp ../imgui_impl_opengl3.cpp -SOURCES += ../../imgui.cpp ../../imgui_demo.cpp ../../imgui_draw.cpp +SOURCES += ../../imgui.cpp ../../imgui_demo.cpp ../../imgui_draw.cpp ../../imgui_widgets.cpp OBJS = $(addsuffix .o, $(basename $(notdir $(SOURCES)))) UNAME_S := $(shell uname -s) diff --git a/examples/example_glfw_opengl3/example_glfw_opengl3.vcxproj b/examples/example_glfw_opengl3/example_glfw_opengl3.vcxproj index 07f28e303221..172a34d896b5 100644 --- a/examples/example_glfw_opengl3/example_glfw_opengl3.vcxproj +++ b/examples/example_glfw_opengl3/example_glfw_opengl3.vcxproj @@ -153,6 +153,7 @@ + diff --git a/examples/example_glfw_opengl3/example_glfw_opengl3.vcxproj.filters b/examples/example_glfw_opengl3/example_glfw_opengl3.vcxproj.filters index bffa93731721..efb570cec98b 100644 --- a/examples/example_glfw_opengl3/example_glfw_opengl3.vcxproj.filters +++ b/examples/example_glfw_opengl3/example_glfw_opengl3.vcxproj.filters @@ -34,6 +34,9 @@ sources + + imgui + diff --git a/examples/example_marmalade/marmalade_example.mkb b/examples/example_marmalade/marmalade_example.mkb index f605078e6281..34315b9a7293 100644 --- a/examples/example_marmalade/marmalade_example.mkb +++ b/examples/example_marmalade/marmalade_example.mkb @@ -33,6 +33,7 @@ files ../../imgui.cpp ../../imgui_demo.cpp ../../imgui_draw.cpp + ../../imgui_widgets.cpp ../../imconfig.h ../../imgui.h ../../imgui_internal.h diff --git a/examples/example_sdl_opengl2/Makefile b/examples/example_sdl_opengl2/Makefile index 1872d7cb56a7..4a948aa49c07 100644 --- a/examples/example_sdl_opengl2/Makefile +++ b/examples/example_sdl_opengl2/Makefile @@ -16,7 +16,7 @@ EXE = example_sdl_opengl2 SOURCES = main.cpp ../imgui_impl_sdl.cpp ../imgui_impl_opengl2.cpp -SOURCES += ../../imgui.cpp ../../imgui_demo.cpp ../../imgui_draw.cpp +SOURCES += ../../imgui.cpp ../../imgui_demo.cpp ../../imgui_draw.cpp ../../imgui_widgets.cpp OBJS = $(addsuffix .o, $(basename $(notdir $(SOURCES)))) UNAME_S := $(shell uname -s) diff --git a/examples/example_sdl_opengl2/example_sdl_opengl2.vcxproj b/examples/example_sdl_opengl2/example_sdl_opengl2.vcxproj index bdec85b39888..fa6b8d3afec1 100644 --- a/examples/example_sdl_opengl2/example_sdl_opengl2.vcxproj +++ b/examples/example_sdl_opengl2/example_sdl_opengl2.vcxproj @@ -19,7 +19,7 @@ - {2AE17FDE-F7F3-4CAC-ADAB-0710EDA4F741} + {94E991D0-790A-4DAF-B442-AAADE3233C75} example_sdl_opengl2 @@ -153,6 +153,7 @@ + diff --git a/examples/example_sdl_opengl2/example_sdl_opengl2.vcxproj.filters b/examples/example_sdl_opengl2/example_sdl_opengl2.vcxproj.filters index 602fa0b0a0fa..e0c1bf2ebb73 100644 --- a/examples/example_sdl_opengl2/example_sdl_opengl2.vcxproj.filters +++ b/examples/example_sdl_opengl2/example_sdl_opengl2.vcxproj.filters @@ -28,6 +28,9 @@ sources + + imgui + diff --git a/examples/example_sdl_opengl3/Makefile b/examples/example_sdl_opengl3/Makefile index 765c4460637e..69874119f66b 100644 --- a/examples/example_sdl_opengl3/Makefile +++ b/examples/example_sdl_opengl3/Makefile @@ -17,7 +17,7 @@ EXE = example_sdl_opengl3 SOURCES = main.cpp SOURCES += ../imgui_impl_sdl.cpp ../imgui_impl_opengl3.cpp -SOURCES += ../../imgui.cpp ../../imgui_demo.cpp ../../imgui_draw.cpp +SOURCES += ../../imgui.cpp ../../imgui_demo.cpp ../../imgui_draw.cpp ../../imgui_widgets.cpp OBJS = $(addsuffix .o, $(basename $(notdir $(SOURCES)))) UNAME_S := $(shell uname -s) diff --git a/examples/example_sdl_opengl3/example_sdl_opengl3.vcxproj b/examples/example_sdl_opengl3/example_sdl_opengl3.vcxproj index b194e6207275..9fda1897132e 100644 --- a/examples/example_sdl_opengl3/example_sdl_opengl3.vcxproj +++ b/examples/example_sdl_opengl3/example_sdl_opengl3.vcxproj @@ -153,6 +153,7 @@ + diff --git a/examples/example_sdl_opengl3/example_sdl_opengl3.vcxproj.filters b/examples/example_sdl_opengl3/example_sdl_opengl3.vcxproj.filters index 87ae4313da28..fbef18ac915c 100644 --- a/examples/example_sdl_opengl3/example_sdl_opengl3.vcxproj.filters +++ b/examples/example_sdl_opengl3/example_sdl_opengl3.vcxproj.filters @@ -34,6 +34,9 @@ sources + + imgui + diff --git a/examples/example_sdl_vulkan/example_sdl_vulkan.vcxproj b/examples/example_sdl_vulkan/example_sdl_vulkan.vcxproj index 3d2a424a5cf0..622dc0932397 100644 --- a/examples/example_sdl_vulkan/example_sdl_vulkan.vcxproj +++ b/examples/example_sdl_vulkan/example_sdl_vulkan.vcxproj @@ -153,6 +153,7 @@ + diff --git a/examples/example_sdl_vulkan/example_sdl_vulkan.vcxproj.filters b/examples/example_sdl_vulkan/example_sdl_vulkan.vcxproj.filters index 4f7c79281522..6f08252419f3 100644 --- a/examples/example_sdl_vulkan/example_sdl_vulkan.vcxproj.filters +++ b/examples/example_sdl_vulkan/example_sdl_vulkan.vcxproj.filters @@ -28,6 +28,9 @@ sources + + imgui + diff --git a/examples/example_win32_directx10/example_win32_directx10.vcxproj b/examples/example_win32_directx10/example_win32_directx10.vcxproj index df5a026ab48f..5e2973b9a150 100644 --- a/examples/example_win32_directx10/example_win32_directx10.vcxproj +++ b/examples/example_win32_directx10/example_win32_directx10.vcxproj @@ -150,6 +150,7 @@ + diff --git a/examples/example_win32_directx10/example_win32_directx10.vcxproj.filters b/examples/example_win32_directx10/example_win32_directx10.vcxproj.filters index c3422a5838aa..976200285bff 100644 --- a/examples/example_win32_directx10/example_win32_directx10.vcxproj.filters +++ b/examples/example_win32_directx10/example_win32_directx10.vcxproj.filters @@ -44,6 +44,9 @@ sources + + imgui + diff --git a/examples/example_win32_directx11/example_win32_directx11.vcxproj b/examples/example_win32_directx11/example_win32_directx11.vcxproj index 9aa098658211..166602a37c23 100644 --- a/examples/example_win32_directx11/example_win32_directx11.vcxproj +++ b/examples/example_win32_directx11/example_win32_directx11.vcxproj @@ -150,6 +150,7 @@ + diff --git a/examples/example_win32_directx11/example_win32_directx11.vcxproj.filters b/examples/example_win32_directx11/example_win32_directx11.vcxproj.filters index ab1e3fba2ef5..1df6a0c415aa 100644 --- a/examples/example_win32_directx11/example_win32_directx11.vcxproj.filters +++ b/examples/example_win32_directx11/example_win32_directx11.vcxproj.filters @@ -44,6 +44,9 @@ sources + + imgui + diff --git a/examples/example_win32_directx12/example_win32_directx12.vcxproj b/examples/example_win32_directx12/example_win32_directx12.vcxproj index c82f00078955..38c8335154ac 100644 --- a/examples/example_win32_directx12/example_win32_directx12.vcxproj +++ b/examples/example_win32_directx12/example_win32_directx12.vcxproj @@ -153,6 +153,7 @@ + diff --git a/examples/example_win32_directx12/example_win32_directx12.vcxproj.filters b/examples/example_win32_directx12/example_win32_directx12.vcxproj.filters index 43fdebab4413..28a25572ebd5 100644 --- a/examples/example_win32_directx12/example_win32_directx12.vcxproj.filters +++ b/examples/example_win32_directx12/example_win32_directx12.vcxproj.filters @@ -44,6 +44,9 @@ sources + + imgui + diff --git a/examples/example_win32_directx9/example_win32_directx9.vcxproj b/examples/example_win32_directx9/example_win32_directx9.vcxproj index ebc8a921b46f..08f21c8751de 100644 --- a/examples/example_win32_directx9/example_win32_directx9.vcxproj +++ b/examples/example_win32_directx9/example_win32_directx9.vcxproj @@ -143,6 +143,7 @@ + diff --git a/examples/example_win32_directx9/example_win32_directx9.vcxproj.filters b/examples/example_win32_directx9/example_win32_directx9.vcxproj.filters index 2ac28d181b4d..914cd260cb34 100644 --- a/examples/example_win32_directx9/example_win32_directx9.vcxproj.filters +++ b/examples/example_win32_directx9/example_win32_directx9.vcxproj.filters @@ -28,6 +28,9 @@ sources + + imgui + diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp new file mode 100644 index 000000000000..09283437a2a5 --- /dev/null +++ b/imgui_widgets.cpp @@ -0,0 +1,25 @@ +// dear imgui, v1.64 WIP +// (widgets code) + +#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include "imgui.h" +#ifndef IMGUI_DEFINE_MATH_OPERATORS +#define IMGUI_DEFINE_MATH_OPERATORS +#endif +#include "imgui_internal.h" + +//------------------------------------------------------------------------- +// Forward Declarations +//------------------------------------------------------------------------- + +//------------------------------------------------------------------------- +// Shared Utilities +//------------------------------------------------------------------------- + +//------------------------------------------------------------------------- +// Widgets +//------------------------------------------------------------------------- + From 18972c5513d2aa0158bb58148658e71e180f5ebb Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 29 Aug 2018 15:15:36 +0200 Subject: [PATCH 193/828] Refactor: Added imgui_widgets.cpp headers to easily merge in the functions in all our branches. (#2036) --- imgui_widgets.cpp | 245 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 243 insertions(+), 2 deletions(-) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 09283437a2a5..538b90579391 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -11,15 +11,256 @@ #endif #include "imgui_internal.h" +#include // toupper, isprint + +// Visual Studio warnings +#ifdef _MSC_VER +#pragma warning (disable: 4127) // condition expression is constant +#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen +#endif + +// Clang/GCC warnings with -Weverything +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wformat-nonliteral" // warning : format string is not a string literal // passing non-literal to vsnformat(). yes, user passing incorrect format strings can crash the code. +#pragma clang diagnostic ignored "-Wsign-conversion" // warning : implicit conversion changes signedness // +#elif defined(__GNUC__) +#pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked +#if __GNUC__ >= 8 +#pragma GCC diagnostic ignored "-Wclass-memaccess" // warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead +#endif +#endif + //------------------------------------------------------------------------- // Forward Declarations //------------------------------------------------------------------------- //------------------------------------------------------------------------- -// Shared Utilities +// SHARED UTILITIES +//------------------------------------------------------------------------- + +//------------------------------------------------------------------------- +// WIDGETS: Text +// - TextUnformatted() +// - Text() +// - TextV() +// - TextColored() +// - TextColoredV() +// - TextDisabled() +// - TextDisabledV() +// - TextWrapped() +// - TextWrappedV() +// - LabelText() +// - LabelTextV() +// - BulletText() +// - BulletTextV() +//------------------------------------------------------------------------- + + +//------------------------------------------------------------------------- +// WIDGETS: Main +// - ButtonBehavior() [Internal] +// - Button() +// - SmallButton() +// - InvisibleButton() +// - ArrowButton() +// - CloseButton() [Internal] +// - CollapseButton() [Internal] +// - Image() +// - ImageButton() +// - Checkbox() +// - CheckboxFlags() +// - RadioButton() +// - ProgressBar() +// - Bullet() +//------------------------------------------------------------------------- + + +//------------------------------------------------------------------------- +// WIDGETS: Combo Box +// - BeginCombo() +// - EndCombo() +// - Combo() +//------------------------------------------------------------------------- + + +//------------------------------------------------------------------------- +// WIDGETS: Data Type and Data Formatting Helpers [Internal] +// - PatchFormatStringFloatToInt() +// - DataTypeFormatString() +// - DataTypeApplyOp() +// - DataTypeApplyOpFromText() +// - GetMinimumStepAtDecimalPrecision +// - RoundScalarWithFormat<>() +//------------------------------------------------------------------------- + + //------------------------------------------------------------------------- +// WIDGETS: Drags +// - DragBehaviorT<>() [Internal] +// - DragBehavior() [Internal] +// - DragScalar() +// - DragScalarN() +// - DragFloat() +// - DragFloat2() +// - DragFloat3() +// - DragFloat4() +// - DragFloatRange2() +// - DragInt() +// - DragInt2() +// - DragInt3() +// - DragInt4() +// - DragIntRange2() +//------------------------------------------------------------------------- + + +//------------------------------------------------------------------------- +// WIDGETS: Sliders +// - SliderBehaviorT<>() [Internal] +// - SliderBehavior() [Internal] +// - SliderScalar() +// - SliderScalarN() +// - SliderFloat() +// - SliderFloat2() +// - SliderFloat3() +// - SliderFloat4() +// - SliderAngle() +// - SliderInt() +// - SliderInt2() +// - SliderInt3() +// - SliderInt4() +// - VSliderScalar() +// - VSliderFloat() +// - VSliderInt() +//------------------------------------------------------------------------- + + + + + + + + + + + + + + + + + + +//------------------------------------------------------------------------- +// WIDGETS: Inputs (_excepted InputText_) +// - ImParseFormatFindStart() +// - ImParseFormatFindEnd() +// - ImParseFormatTrimDecorations() +// - ImParseFormatPrecision() +// - InputScalarAsWidgetReplacement() [Internal] +// - InputScalar() +// - InputScalarN() +// - InputFloat() +// - InputFloat2() +// - InputFloat3() +// - InputFloat4() +// - InputInt() +// - InputInt2() +// - InputInt3() +// - InputInt4() +// - InputDouble() +//------------------------------------------------------------------------- + + + + + + + + + + + + + + + + + +//------------------------------------------------------------------------- +// WIDGETS: InputText +// - InputText() +// - InputTextMultiline() +// - InputTextEx() [Internal] +//------------------------------------------------------------------------- + + +//------------------------------------------------------------------------- +// WIDGETS: Color Editor / Picker +// - ColorEdit3() +// - ColorEdit4() +// - ColorPicker3() +// - RenderColorRectWithAlphaCheckerboard() [Internal] +// - ColorPicker4() +// - ColorButton() +// - SetColorEditOptions() +// - ColorTooltip() [Internal] +// - ColorEditOptionsPopup() [Internal] +// - ColorPickerOptionsPopup() [Internal] +//------------------------------------------------------------------------- + + +//------------------------------------------------------------------------- +// WIDGETS: Trees +// - TreeNode() +// - TreeNodeV() +// - TreeNodeEx() +// - TreeNodeExV() +// - TreeNodeBehavior() [Internal] +// - TreePush() +// - TreePop() +// - TreeAdvanceToLabelPos() +// - GetTreeNodeToLabelSpacing() +// - SetNextTreeNodeOpen() +// - CollapsingHeader() +//------------------------------------------------------------------------- + + +//------------------------------------------------------------------------- +// WIDGETS: Selectables +// - Selectable() +//------------------------------------------------------------------------- + + +//------------------------------------------------------------------------- +// WIDGETS: List Box +// - ListBox() +// - ListBoxHeader() +// - ListBoxFooter() +//------------------------------------------------------------------------- + + +//------------------------------------------------------------------------- +// WIDGETS: Data Plotting +// - PlotEx() [Internal] +// - PlotLines() +// - PlotHistogram() +//------------------------------------------------------------------------- + + +//------------------------------------------------------------------------- +// WIDGETS: Value() helpers +// - Value() +//------------------------------------------------------------------------- + //------------------------------------------------------------------------- -// Widgets +// WIDGETS: Menus +// - BeginMainMenuBar() +// - EndMainMenuBar() +// - BeginMenuBar() +// - EndMenuBar() +// - BeginMenu() +// - EndMenu() +// - MenuItem() //------------------------------------------------------------------------- From 99b27488e7d149758d91cc90de8a97a6c69490a3 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 30 Aug 2018 14:38:44 +0200 Subject: [PATCH 194/828] Refactor: Moved Text functions from imgui.cpp to imgui_widgets.cpp (#2036) --- imgui.cpp | 238 ---------------------------------------------- imgui_widgets.cpp | 237 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 237 insertions(+), 238 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 9c3233984b3d..079914c4b0c9 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -8881,177 +8881,6 @@ ImGuiStorage* ImGui::GetStateStorage() return window->DC.StateStorage; } -void ImGui::TextV(const char* fmt, va_list args) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - - ImGuiContext& g = *GImGui; - const char* text_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); - TextUnformatted(g.TempBuffer, text_end); -} - -void ImGui::Text(const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - TextV(fmt, args); - va_end(args); -} - -void ImGui::TextColoredV(const ImVec4& col, const char* fmt, va_list args) -{ - PushStyleColor(ImGuiCol_Text, col); - TextV(fmt, args); - PopStyleColor(); -} - -void ImGui::TextColored(const ImVec4& col, const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - TextColoredV(col, fmt, args); - va_end(args); -} - -void ImGui::TextDisabledV(const char* fmt, va_list args) -{ - PushStyleColor(ImGuiCol_Text, GImGui->Style.Colors[ImGuiCol_TextDisabled]); - TextV(fmt, args); - PopStyleColor(); -} - -void ImGui::TextDisabled(const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - TextDisabledV(fmt, args); - va_end(args); -} - -void ImGui::TextWrappedV(const char* fmt, va_list args) -{ - bool need_wrap = (GImGui->CurrentWindow->DC.TextWrapPos < 0.0f); // Keep existing wrap position is one ia already set - if (need_wrap) PushTextWrapPos(0.0f); - TextV(fmt, args); - if (need_wrap) PopTextWrapPos(); -} - -void ImGui::TextWrapped(const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - TextWrappedV(fmt, args); - va_end(args); -} - -void ImGui::TextUnformatted(const char* text, const char* text_end) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - - ImGuiContext& g = *GImGui; - IM_ASSERT(text != NULL); - const char* text_begin = text; - if (text_end == NULL) - text_end = text + strlen(text); // FIXME-OPT - - const ImVec2 text_pos(window->DC.CursorPos.x, window->DC.CursorPos.y + window->DC.CurrentLineTextBaseOffset); - const float wrap_pos_x = window->DC.TextWrapPos; - const bool wrap_enabled = wrap_pos_x >= 0.0f; - if (text_end - text > 2000 && !wrap_enabled) - { - // Long text! - // Perform manual coarse clipping to optimize for long multi-line text - // From this point we will only compute the width of lines that are visible. Optimization only available when word-wrapping is disabled. - // We also don't vertically center the text within the line full height, which is unlikely to matter because we are likely the biggest and only item on the line. - const char* line = text; - const float line_height = GetTextLineHeight(); - const ImRect clip_rect = window->ClipRect; - ImVec2 text_size(0,0); - - if (text_pos.y <= clip_rect.Max.y) - { - ImVec2 pos = text_pos; - - // Lines to skip (can't skip when logging text) - if (!g.LogEnabled) - { - int lines_skippable = (int)((clip_rect.Min.y - text_pos.y) / line_height); - if (lines_skippable > 0) - { - int lines_skipped = 0; - while (line < text_end && lines_skipped < lines_skippable) - { - const char* line_end = strchr(line, '\n'); - if (!line_end) - line_end = text_end; - line = line_end + 1; - lines_skipped++; - } - pos.y += lines_skipped * line_height; - } - } - - // Lines to render - if (line < text_end) - { - ImRect line_rect(pos, pos + ImVec2(FLT_MAX, line_height)); - while (line < text_end) - { - const char* line_end = strchr(line, '\n'); - if (IsClippedEx(line_rect, 0, false)) - break; - - const ImVec2 line_size = CalcTextSize(line, line_end, false); - text_size.x = ImMax(text_size.x, line_size.x); - RenderText(pos, line, line_end, false); - if (!line_end) - line_end = text_end; - line = line_end + 1; - line_rect.Min.y += line_height; - line_rect.Max.y += line_height; - pos.y += line_height; - } - - // Count remaining lines - int lines_skipped = 0; - while (line < text_end) - { - const char* line_end = strchr(line, '\n'); - if (!line_end) - line_end = text_end; - line = line_end + 1; - lines_skipped++; - } - pos.y += lines_skipped * line_height; - } - - text_size.y += (pos - text_pos).y; - } - - ImRect bb(text_pos, text_pos + text_size); - ItemSize(bb); - ItemAdd(bb, 0); - } - else - { - const float wrap_width = wrap_enabled ? CalcWrapWidthForPos(window->DC.CursorPos, wrap_pos_x) : 0.0f; - const ImVec2 text_size = CalcTextSize(text_begin, text_end, false, wrap_width); - - // Account of baseline offset - ImRect bb(text_pos, text_pos + text_size); - ItemSize(text_size); - if (!ItemAdd(bb, 0)) - return; - - // Render (we don't hide text after ## in this end-user function) - RenderTextWrapped(bb.Min, text_begin, text_end, wrap_width); - } -} - void ImGui::AlignTextToFramePadding() { ImGuiWindow* window = GetCurrentWindow(); @@ -9063,40 +8892,6 @@ void ImGui::AlignTextToFramePadding() window->DC.CurrentLineTextBaseOffset = ImMax(window->DC.CurrentLineTextBaseOffset, g.Style.FramePadding.y); } -// Add a label+text combo aligned to other label+value widgets -void ImGui::LabelTextV(const char* label, const char* fmt, va_list args) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - const float w = CalcItemWidth(); - - const ImVec2 label_size = CalcTextSize(label, NULL, true); - const ImRect value_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2)); - const ImRect total_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w + (label_size.x > 0.0f ? style.ItemInnerSpacing.x : 0.0f), style.FramePadding.y*2) + label_size); - ItemSize(total_bb, style.FramePadding.y); - if (!ItemAdd(total_bb, 0)) - return; - - // Render - const char* value_text_begin = &g.TempBuffer[0]; - const char* value_text_end = value_text_begin + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); - RenderTextClipped(value_bb.Min, value_bb.Max, value_text_begin, value_text_end, NULL, ImVec2(0.0f,0.5f)); - if (label_size.x > 0.0f) - RenderText(ImVec2(value_bb.Max.x + style.ItemInnerSpacing.x, value_bb.Min.y + style.FramePadding.y), label); -} - -void ImGui::LabelText(const char* label, const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - LabelTextV(label, fmt, args); - va_end(args); -} - bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags) { ImGuiContext& g = *GImGui; @@ -9989,39 +9784,6 @@ void ImGui::Bullet() SameLine(0, style.FramePadding.x*2); } -// Text with a little bullet aligned to the typical tree node. -void ImGui::BulletTextV(const char* fmt, va_list args) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - - const char* text_begin = g.TempBuffer; - const char* text_end = text_begin + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); - const ImVec2 label_size = CalcTextSize(text_begin, text_end, false); - const float text_base_offset_y = ImMax(0.0f, window->DC.CurrentLineTextBaseOffset); // Latch before ItemSize changes it - const float line_height = ImMax(ImMin(window->DC.CurrentLineSize.y, g.FontSize + g.Style.FramePadding.y*2), g.FontSize); - const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(g.FontSize + (label_size.x > 0.0f ? (label_size.x + style.FramePadding.x*2) : 0.0f), ImMax(line_height, label_size.y))); // Empty text doesn't add padding - ItemSize(bb); - if (!ItemAdd(bb, 0)) - return; - - // Render - RenderBullet(bb.Min + ImVec2(style.FramePadding.x + g.FontSize*0.5f, line_height*0.5f)); - RenderText(bb.Min+ImVec2(g.FontSize + style.FramePadding.x*2, text_base_offset_y), text_begin, text_end, false); -} - -void ImGui::BulletText(const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - BulletTextV(fmt, args); - va_end(args); -} - static inline int DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type, const void* data_ptr, const char* format) { if (data_type == ImGuiDataType_S32 || data_type == ImGuiDataType_U32) // Signedness doesn't matter when pushing the argument diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 538b90579391..5ea5c3be7a11 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -55,6 +55,243 @@ // - BulletTextV() //------------------------------------------------------------------------- +void ImGui::TextUnformatted(const char* text, const char* text_end) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + ImGuiContext& g = *GImGui; + IM_ASSERT(text != NULL); + const char* text_begin = text; + if (text_end == NULL) + text_end = text + strlen(text); // FIXME-OPT + + const ImVec2 text_pos(window->DC.CursorPos.x, window->DC.CursorPos.y + window->DC.CurrentLineTextBaseOffset); + const float wrap_pos_x = window->DC.TextWrapPos; + const bool wrap_enabled = wrap_pos_x >= 0.0f; + if (text_end - text > 2000 && !wrap_enabled) + { + // Long text! + // Perform manual coarse clipping to optimize for long multi-line text + // From this point we will only compute the width of lines that are visible. Optimization only available when word-wrapping is disabled. + // We also don't vertically center the text within the line full height, which is unlikely to matter because we are likely the biggest and only item on the line. + const char* line = text; + const float line_height = GetTextLineHeight(); + const ImRect clip_rect = window->ClipRect; + ImVec2 text_size(0,0); + + if (text_pos.y <= clip_rect.Max.y) + { + ImVec2 pos = text_pos; + + // Lines to skip (can't skip when logging text) + if (!g.LogEnabled) + { + int lines_skippable = (int)((clip_rect.Min.y - text_pos.y) / line_height); + if (lines_skippable > 0) + { + int lines_skipped = 0; + while (line < text_end && lines_skipped < lines_skippable) + { + const char* line_end = strchr(line, '\n'); + if (!line_end) + line_end = text_end; + line = line_end + 1; + lines_skipped++; + } + pos.y += lines_skipped * line_height; + } + } + + // Lines to render + if (line < text_end) + { + ImRect line_rect(pos, pos + ImVec2(FLT_MAX, line_height)); + while (line < text_end) + { + const char* line_end = strchr(line, '\n'); + if (IsClippedEx(line_rect, 0, false)) + break; + + const ImVec2 line_size = CalcTextSize(line, line_end, false); + text_size.x = ImMax(text_size.x, line_size.x); + RenderText(pos, line, line_end, false); + if (!line_end) + line_end = text_end; + line = line_end + 1; + line_rect.Min.y += line_height; + line_rect.Max.y += line_height; + pos.y += line_height; + } + + // Count remaining lines + int lines_skipped = 0; + while (line < text_end) + { + const char* line_end = strchr(line, '\n'); + if (!line_end) + line_end = text_end; + line = line_end + 1; + lines_skipped++; + } + pos.y += lines_skipped * line_height; + } + + text_size.y += (pos - text_pos).y; + } + + ImRect bb(text_pos, text_pos + text_size); + ItemSize(bb); + ItemAdd(bb, 0); + } + else + { + const float wrap_width = wrap_enabled ? CalcWrapWidthForPos(window->DC.CursorPos, wrap_pos_x) : 0.0f; + const ImVec2 text_size = CalcTextSize(text_begin, text_end, false, wrap_width); + + // Account of baseline offset + ImRect bb(text_pos, text_pos + text_size); + ItemSize(text_size); + if (!ItemAdd(bb, 0)) + return; + + // Render (we don't hide text after ## in this end-user function) + RenderTextWrapped(bb.Min, text_begin, text_end, wrap_width); + } +} + +void ImGui::Text(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + TextV(fmt, args); + va_end(args); +} + +void ImGui::TextV(const char* fmt, va_list args) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + ImGuiContext& g = *GImGui; + const char* text_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); + TextUnformatted(g.TempBuffer, text_end); +} + +void ImGui::TextColored(const ImVec4& col, const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + TextColoredV(col, fmt, args); + va_end(args); +} + +void ImGui::TextColoredV(const ImVec4& col, const char* fmt, va_list args) +{ + PushStyleColor(ImGuiCol_Text, col); + TextV(fmt, args); + PopStyleColor(); +} + +void ImGui::TextDisabled(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + TextDisabledV(fmt, args); + va_end(args); +} + +void ImGui::TextDisabledV(const char* fmt, va_list args) +{ + PushStyleColor(ImGuiCol_Text, GImGui->Style.Colors[ImGuiCol_TextDisabled]); + TextV(fmt, args); + PopStyleColor(); +} + +void ImGui::TextWrapped(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + TextWrappedV(fmt, args); + va_end(args); +} + +void ImGui::TextWrappedV(const char* fmt, va_list args) +{ + bool need_wrap = (GImGui->CurrentWindow->DC.TextWrapPos < 0.0f); // Keep existing wrap position is one ia already set + if (need_wrap) PushTextWrapPos(0.0f); + TextV(fmt, args); + if (need_wrap) PopTextWrapPos(); +} + +void ImGui::LabelText(const char* label, const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + LabelTextV(label, fmt, args); + va_end(args); +} + +// Add a label+text combo aligned to other label+value widgets +void ImGui::LabelTextV(const char* label, const char* fmt, va_list args) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const float w = CalcItemWidth(); + + const ImVec2 label_size = CalcTextSize(label, NULL, true); + const ImRect value_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2)); + const ImRect total_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w + (label_size.x > 0.0f ? style.ItemInnerSpacing.x : 0.0f), style.FramePadding.y*2) + label_size); + ItemSize(total_bb, style.FramePadding.y); + if (!ItemAdd(total_bb, 0)) + return; + + // Render + const char* value_text_begin = &g.TempBuffer[0]; + const char* value_text_end = value_text_begin + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); + RenderTextClipped(value_bb.Min, value_bb.Max, value_text_begin, value_text_end, NULL, ImVec2(0.0f,0.5f)); + if (label_size.x > 0.0f) + RenderText(ImVec2(value_bb.Max.x + style.ItemInnerSpacing.x, value_bb.Min.y + style.FramePadding.y), label); +} + +void ImGui::BulletText(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + BulletTextV(fmt, args); + va_end(args); +} + +// Text with a little bullet aligned to the typical tree node. +void ImGui::BulletTextV(const char* fmt, va_list args) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + + const char* text_begin = g.TempBuffer; + const char* text_end = text_begin + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); + const ImVec2 label_size = CalcTextSize(text_begin, text_end, false); + const float text_base_offset_y = ImMax(0.0f, window->DC.CurrentLineTextBaseOffset); // Latch before ItemSize changes it + const float line_height = ImMax(ImMin(window->DC.CurrentLineSize.y, g.FontSize + g.Style.FramePadding.y*2), g.FontSize); + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(g.FontSize + (label_size.x > 0.0f ? (label_size.x + style.FramePadding.x*2) : 0.0f), ImMax(line_height, label_size.y))); // Empty text doesn't add padding + ItemSize(bb); + if (!ItemAdd(bb, 0)) + return; + + // Render + RenderBullet(bb.Min + ImVec2(style.FramePadding.x + g.FontSize*0.5f, line_height*0.5f)); + RenderText(bb.Min+ImVec2(g.FontSize + style.FramePadding.x*2, text_base_offset_y), text_begin, text_end, false); +} //------------------------------------------------------------------------- // WIDGETS: Main From 43219d36a6217ebd2d1409012ab1883223d8b3ef Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 30 Aug 2018 14:41:55 +0200 Subject: [PATCH 195/828] Refactor: Moved Button/Image/Checkbox/RadioButton/Bullet/ProgressBar functions from imgui.cpp to imgui_widgets.cpp (#2036) --- imgui.cpp | 574 ---------------------------------------------- imgui_widgets.cpp | 573 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 573 insertions(+), 574 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 079914c4b0c9..96af6461ef07 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -8892,387 +8892,6 @@ void ImGui::AlignTextToFramePadding() window->DC.CurrentLineTextBaseOffset = ImMax(window->DC.CurrentLineTextBaseOffset, g.Style.FramePadding.y); } -bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = GetCurrentWindow(); - - if (flags & ImGuiButtonFlags_Disabled) - { - if (out_hovered) *out_hovered = false; - if (out_held) *out_held = false; - if (g.ActiveId == id) ClearActiveID(); - return false; - } - - // Default behavior requires click+release on same spot - if ((flags & (ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnRelease | ImGuiButtonFlags_PressedOnDoubleClick)) == 0) - flags |= ImGuiButtonFlags_PressedOnClickRelease; - - ImGuiWindow* backup_hovered_window = g.HoveredWindow; - if ((flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredRootWindow == window) - g.HoveredWindow = window; - - bool pressed = false; - bool hovered = ItemHoverable(bb, id); - - // Drag source doesn't report as hovered - if (hovered && g.DragDropActive && g.DragDropPayload.SourceId == id && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoDisableHover)) - hovered = false; - - // Special mode for Drag and Drop where holding button pressed for a long time while dragging another item triggers the button - if (g.DragDropActive && (flags & ImGuiButtonFlags_PressedOnDragDropHold) && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoHoldToOpenOthers)) - if (IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) - { - hovered = true; - SetHoveredID(id); - if (CalcTypematicPressedRepeatAmount(g.HoveredIdTimer + 0.0001f, g.HoveredIdTimer + 0.0001f - g.IO.DeltaTime, 0.01f, 0.70f)) // FIXME: Our formula for CalcTypematicPressedRepeatAmount() is fishy - { - pressed = true; - FocusWindow(window); - } - } - - if ((flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredRootWindow == window) - g.HoveredWindow = backup_hovered_window; - - // AllowOverlap mode (rarely used) requires previous frame HoveredId to be null or to match. This allows using patterns where a later submitted widget overlaps a previous one. - if (hovered && (flags & ImGuiButtonFlags_AllowItemOverlap) && (g.HoveredIdPreviousFrame != id && g.HoveredIdPreviousFrame != 0)) - hovered = false; - - // Mouse - if (hovered) - { - if (!(flags & ImGuiButtonFlags_NoKeyModifiers) || (!g.IO.KeyCtrl && !g.IO.KeyShift && !g.IO.KeyAlt)) - { - // | CLICKING | HOLDING with ImGuiButtonFlags_Repeat - // PressedOnClickRelease | * | .. (NOT on release) <-- MOST COMMON! (*) only if both click/release were over bounds - // PressedOnClick | | .. - // PressedOnRelease | | .. (NOT on release) - // PressedOnDoubleClick | | .. - // FIXME-NAV: We don't honor those different behaviors. - if ((flags & ImGuiButtonFlags_PressedOnClickRelease) && g.IO.MouseClicked[0]) - { - SetActiveID(id, window); - if (!(flags & ImGuiButtonFlags_NoNavFocus)) - SetFocusID(id, window); - FocusWindow(window); - } - if (((flags & ImGuiButtonFlags_PressedOnClick) && g.IO.MouseClicked[0]) || ((flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseDoubleClicked[0])) - { - pressed = true; - if (flags & ImGuiButtonFlags_NoHoldingActiveID) - ClearActiveID(); - else - SetActiveID(id, window); // Hold on ID - FocusWindow(window); - } - if ((flags & ImGuiButtonFlags_PressedOnRelease) && g.IO.MouseReleased[0]) - { - if (!((flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[0] >= g.IO.KeyRepeatDelay)) // Repeat mode trumps - pressed = true; - ClearActiveID(); - } - - // 'Repeat' mode acts when held regardless of _PressedOn flags (see table above). - // Relies on repeat logic of IsMouseClicked() but we may as well do it ourselves if we end up exposing finer RepeatDelay/RepeatRate settings. - if ((flags & ImGuiButtonFlags_Repeat) && g.ActiveId == id && g.IO.MouseDownDuration[0] > 0.0f && IsMouseClicked(0, true)) - pressed = true; - } - - if (pressed) - g.NavDisableHighlight = true; - } - - // Gamepad/Keyboard navigation - // We report navigated item as hovered but we don't set g.HoveredId to not interfere with mouse. - if (g.NavId == id && !g.NavDisableHighlight && g.NavDisableMouseHover && (g.ActiveId == 0 || g.ActiveId == id || g.ActiveId == window->MoveId)) - hovered = true; - - if (g.NavActivateDownId == id) - { - bool nav_activated_by_code = (g.NavActivateId == id); - bool nav_activated_by_inputs = IsNavInputPressed(ImGuiNavInput_Activate, (flags & ImGuiButtonFlags_Repeat) ? ImGuiInputReadMode_Repeat : ImGuiInputReadMode_Pressed); - if (nav_activated_by_code || nav_activated_by_inputs) - pressed = true; - if (nav_activated_by_code || nav_activated_by_inputs || g.ActiveId == id) - { - // Set active id so it can be queried by user via IsItemActive(), equivalent of holding the mouse button. - g.NavActivateId = id; // This is so SetActiveId assign a Nav source - SetActiveID(id, window); - if (!(flags & ImGuiButtonFlags_NoNavFocus)) - SetFocusID(id, window); - g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right) | (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); - } - } - - bool held = false; - if (g.ActiveId == id) - { - if (g.ActiveIdSource == ImGuiInputSource_Mouse) - { - if (g.ActiveIdIsJustActivated) - g.ActiveIdClickOffset = g.IO.MousePos - bb.Min; - if (g.IO.MouseDown[0]) - { - held = true; - } - else - { - if (hovered && (flags & ImGuiButtonFlags_PressedOnClickRelease)) - if (!((flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[0] >= g.IO.KeyRepeatDelay)) // Repeat mode trumps - if (!g.DragDropActive) - pressed = true; - ClearActiveID(); - } - if (!(flags & ImGuiButtonFlags_NoNavFocus)) - g.NavDisableHighlight = true; - } - else if (g.ActiveIdSource == ImGuiInputSource_Nav) - { - if (g.NavActivateDownId != id) - ClearActiveID(); - } - } - - if (out_hovered) *out_hovered = hovered; - if (out_held) *out_held = held; - - return pressed; -} - -bool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags flags) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - const ImGuiID id = window->GetID(label); - const ImVec2 label_size = CalcTextSize(label, NULL, true); - - ImVec2 pos = window->DC.CursorPos; - if ((flags & ImGuiButtonFlags_AlignTextBaseLine) && style.FramePadding.y < window->DC.CurrentLineTextBaseOffset) // Try to vertically align buttons that are smaller/have no padding so that text baseline matches (bit hacky, since it shouldn't be a flag) - pos.y += window->DC.CurrentLineTextBaseOffset - style.FramePadding.y; - ImVec2 size = CalcItemSize(size_arg, label_size.x + style.FramePadding.x * 2.0f, label_size.y + style.FramePadding.y * 2.0f); - - const ImRect bb(pos, pos + size); - ItemSize(bb, style.FramePadding.y); - if (!ItemAdd(bb, id)) - return false; - - if (window->DC.ItemFlags & ImGuiItemFlags_ButtonRepeat) - flags |= ImGuiButtonFlags_Repeat; - bool hovered, held; - bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags); - if (pressed) - MarkItemEdited(id); - - // Render - const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); - RenderNavHighlight(bb, id); - RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding); - RenderTextClipped(bb.Min + style.FramePadding, bb.Max - style.FramePadding, label, NULL, &label_size, style.ButtonTextAlign, &bb); - - // Automatically close popups - //if (pressed && !(flags & ImGuiButtonFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup)) - // CloseCurrentPopup(); - - return pressed; -} - -bool ImGui::Button(const char* label, const ImVec2& size_arg) -{ - return ButtonEx(label, size_arg, 0); -} - -// Small buttons fits within text without additional vertical spacing. -bool ImGui::SmallButton(const char* label) -{ - ImGuiContext& g = *GImGui; - float backup_padding_y = g.Style.FramePadding.y; - g.Style.FramePadding.y = 0.0f; - bool pressed = ButtonEx(label, ImVec2(0,0), ImGuiButtonFlags_AlignTextBaseLine); - g.Style.FramePadding.y = backup_padding_y; - return pressed; -} - -bool ImGui::ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size, ImGuiButtonFlags flags) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const ImGuiID id = window->GetID(str_id); - const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); - const float default_size = GetFrameHeight(); - ItemSize(bb, (size.y >= default_size) ? g.Style.FramePadding.y : 0.0f); - if (!ItemAdd(bb, id)) - return false; - - if (window->DC.ItemFlags & ImGuiItemFlags_ButtonRepeat) - flags |= ImGuiButtonFlags_Repeat; - - bool hovered, held; - bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags); - - // Render - const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); - RenderNavHighlight(bb, id); - RenderFrame(bb.Min, bb.Max, col, true, g.Style.FrameRounding); - RenderArrow(bb.Min + ImVec2(ImMax(0.0f, size.x - g.FontSize - g.Style.FramePadding.x), ImMax(0.0f, size.y - g.FontSize - g.Style.FramePadding.y)), dir); - - return pressed; -} - -bool ImGui::ArrowButton(const char* str_id, ImGuiDir dir) -{ - float sz = GetFrameHeight(); - return ArrowButtonEx(str_id, dir, ImVec2(sz, sz), 0); -} - -// Tip: use ImGui::PushID()/PopID() to push indices or pointers in the ID stack. -// Then you can keep 'str_id' empty or the same for all your buttons (instead of creating a string based on a non-string id) -bool ImGui::InvisibleButton(const char* str_id, const ImVec2& size_arg) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - // Cannot use zero-size for InvisibleButton(). Unlike Button() there is not way to fallback using the label size. - IM_ASSERT(size_arg.x != 0.0f && size_arg.y != 0.0f); - - const ImGuiID id = window->GetID(str_id); - ImVec2 size = CalcItemSize(size_arg, 0.0f, 0.0f); - const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); - ItemSize(bb); - if (!ItemAdd(bb, id)) - return false; - - bool hovered, held; - bool pressed = ButtonBehavior(bb, id, &hovered, &held); - - return pressed; -} - -// Button to close a window -bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos, float radius) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - // We intentionally allow interaction when clipped so that a mechanical Alt,Right,Validate sequence close a window. - // (this isn't the regular behavior of buttons, but it doesn't affect the user much because navigation tends to keep items visible). - const ImRect bb(pos - ImVec2(radius,radius), pos + ImVec2(radius,radius)); - bool is_clipped = !ItemAdd(bb, id); - - bool hovered, held; - bool pressed = ButtonBehavior(bb, id, &hovered, &held); - if (is_clipped) - return pressed; - - // Render - ImVec2 center = bb.GetCenter(); - if (hovered) - window->DrawList->AddCircleFilled(center, ImMax(2.0f, radius), GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : ImGuiCol_ButtonHovered), 9); - - float cross_extent = (radius * 0.7071f) - 1.0f; - ImU32 cross_col = GetColorU32(ImGuiCol_Text); - center -= ImVec2(0.5f, 0.5f); - window->DrawList->AddLine(center + ImVec2(+cross_extent,+cross_extent), center + ImVec2(-cross_extent,-cross_extent), cross_col, 1.0f); - window->DrawList->AddLine(center + ImVec2(+cross_extent,-cross_extent), center + ImVec2(-cross_extent,+cross_extent), cross_col, 1.0f); - - return pressed; -} - -bool ImGui::CollapseButton(ImGuiID id, const ImVec2& pos) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - ImRect bb(pos, pos + ImVec2(g.FontSize, g.FontSize) + g.Style.FramePadding * 2.0f); - ItemAdd(bb, id); - bool hovered, held; - bool pressed = ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_None); - - ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); - if (hovered || held) - window->DrawList->AddCircleFilled(bb.GetCenter() + ImVec2(0.0f, -0.5f), g.FontSize * 0.5f + 1.0f, col, 9); - RenderArrow(bb.Min + g.Style.FramePadding, window->Collapsed ? ImGuiDir_Right : ImGuiDir_Down, 1.0f); - - // Switch to moving the window after mouse is moved beyond the initial drag threshold - if (IsItemActive() && IsMouseDragging()) - StartMouseMovingWindow(window); - - return pressed; -} - -void ImGui::Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - - ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); - if (border_col.w > 0.0f) - bb.Max += ImVec2(2,2); - ItemSize(bb); - if (!ItemAdd(bb, 0)) - return; - - if (border_col.w > 0.0f) - { - window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(border_col), 0.0f); - window->DrawList->AddImage(user_texture_id, bb.Min+ImVec2(1,1), bb.Max-ImVec2(1,1), uv0, uv1, GetColorU32(tint_col)); - } - else - { - window->DrawList->AddImage(user_texture_id, bb.Min, bb.Max, uv0, uv1, GetColorU32(tint_col)); - } -} - -// frame_padding < 0: uses FramePadding from style (default) -// frame_padding = 0: no framing -// frame_padding > 0: set framing size -// The color used are the button colors. -bool ImGui::ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, int frame_padding, const ImVec4& bg_col, const ImVec4& tint_col) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - - // Default to using texture ID as ID. User can still push string/integer prefixes. - // We could hash the size/uv to create a unique ID but that would prevent the user from animating UV. - PushID((void*)user_texture_id); - const ImGuiID id = window->GetID("#image"); - PopID(); - - const ImVec2 padding = (frame_padding >= 0) ? ImVec2((float)frame_padding, (float)frame_padding) : style.FramePadding; - const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size + padding*2); - const ImRect image_bb(window->DC.CursorPos + padding, window->DC.CursorPos + padding + size); - ItemSize(bb); - if (!ItemAdd(bb, id)) - return false; - - bool hovered, held; - bool pressed = ButtonBehavior(bb, id, &hovered, &held); - - // Render - const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); - RenderNavHighlight(bb, id); - RenderFrame(bb.Min, bb.Max, col, true, ImClamp((float)ImMin(padding.x, padding.y), 0.0f, style.FrameRounding)); - if (bg_col.w > 0.0f) - window->DrawList->AddRectFilled(image_bb.Min, image_bb.Max, GetColorU32(bg_col)); - window->DrawList->AddImage(user_texture_id, image_bb.Min, image_bb.Max, uv0, uv1, GetColorU32(tint_col)); - - return pressed; -} - // Start logging ImGui output to TTY void ImGui::LogToTTY(int max_depth) { @@ -9762,28 +9381,6 @@ ImGuiID ImGui::GetID(const void* ptr_id) return GImGui->CurrentWindow->GetID(ptr_id); } -void ImGui::Bullet() -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - const float line_height = ImMax(ImMin(window->DC.CurrentLineSize.y, g.FontSize + g.Style.FramePadding.y*2), g.FontSize); - const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(g.FontSize, line_height)); - ItemSize(bb); - if (!ItemAdd(bb, 0)) - { - SameLine(0, style.FramePadding.x*2); - return; - } - - // Render and stay on same line - RenderBullet(bb.Min + ImVec2(style.FramePadding.x + g.FontSize*0.5f, line_height*0.5f)); - SameLine(0, style.FramePadding.x*2); -} - static inline int DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type, const void* data_ptr, const char* format) { if (data_type == ImGuiDataType_S32 || data_type == ImGuiDataType_U32) // Signedness doesn't matter when pushing the argument @@ -11005,177 +10602,6 @@ void ImGui::PlotHistogram(const char* label, float (*values_getter)(void* data, PlotEx(ImGuiPlotType_Histogram, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size); } -// size_arg (for each axis) < 0.0f: align to end, 0.0f: auto, > 0.0f: specified size -void ImGui::ProgressBar(float fraction, const ImVec2& size_arg, const char* overlay) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - - ImVec2 pos = window->DC.CursorPos; - ImRect bb(pos, pos + CalcItemSize(size_arg, CalcItemWidth(), g.FontSize + style.FramePadding.y*2.0f)); - ItemSize(bb, style.FramePadding.y); - if (!ItemAdd(bb, 0)) - return; - - // Render - fraction = ImSaturate(fraction); - RenderFrame(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); - bb.Expand(ImVec2(-style.FrameBorderSize, -style.FrameBorderSize)); - const ImVec2 fill_br = ImVec2(ImLerp(bb.Min.x, bb.Max.x, fraction), bb.Max.y); - RenderRectFilledRangeH(window->DrawList, bb, GetColorU32(ImGuiCol_PlotHistogram), 0.0f, fraction, style.FrameRounding); - - // Default displaying the fraction as percentage string, but user can override it - char overlay_buf[32]; - if (!overlay) - { - ImFormatString(overlay_buf, IM_ARRAYSIZE(overlay_buf), "%.0f%%", fraction*100+0.01f); - overlay = overlay_buf; - } - - ImVec2 overlay_size = CalcTextSize(overlay, NULL); - if (overlay_size.x > 0.0f) - RenderTextClipped(ImVec2(ImClamp(fill_br.x + style.ItemSpacing.x, bb.Min.x, bb.Max.x - overlay_size.x - style.ItemInnerSpacing.x), bb.Min.y), bb.Max, overlay, NULL, &overlay_size, ImVec2(0.0f,0.5f), &bb); -} - -bool ImGui::Checkbox(const char* label, bool* v) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - const ImGuiID id = window->GetID(label); - const ImVec2 label_size = CalcTextSize(label, NULL, true); - - const ImRect check_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(label_size.y + style.FramePadding.y*2, label_size.y + style.FramePadding.y*2)); // We want a square shape to we use Y twice - ItemSize(check_bb, style.FramePadding.y); - - ImRect total_bb = check_bb; - if (label_size.x > 0) - SameLine(0, style.ItemInnerSpacing.x); - const ImRect text_bb(window->DC.CursorPos + ImVec2(0,style.FramePadding.y), window->DC.CursorPos + ImVec2(0,style.FramePadding.y) + label_size); - if (label_size.x > 0) - { - ItemSize(ImVec2(text_bb.GetWidth(), check_bb.GetHeight()), style.FramePadding.y); - total_bb = ImRect(ImMin(check_bb.Min, text_bb.Min), ImMax(check_bb.Max, text_bb.Max)); - } - - if (!ItemAdd(total_bb, id)) - return false; - - bool hovered, held; - bool pressed = ButtonBehavior(total_bb, id, &hovered, &held); - if (pressed) - { - *v = !(*v); - MarkItemEdited(id); - } - - RenderNavHighlight(total_bb, id); - RenderFrame(check_bb.Min, check_bb.Max, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding); - if (*v) - { - const float check_sz = ImMin(check_bb.GetWidth(), check_bb.GetHeight()); - const float pad = ImMax(1.0f, (float)(int)(check_sz / 6.0f)); - RenderCheckMark(check_bb.Min + ImVec2(pad,pad), GetColorU32(ImGuiCol_CheckMark), check_bb.GetWidth() - pad*2.0f); - } - - if (g.LogEnabled) - LogRenderedText(&text_bb.Min, *v ? "[x]" : "[ ]"); - if (label_size.x > 0.0f) - RenderText(text_bb.Min, label); - - return pressed; -} - -bool ImGui::CheckboxFlags(const char* label, unsigned int* flags, unsigned int flags_value) -{ - bool v = ((*flags & flags_value) == flags_value); - bool pressed = Checkbox(label, &v); - if (pressed) - { - if (v) - *flags |= flags_value; - else - *flags &= ~flags_value; - } - - return pressed; -} - -bool ImGui::RadioButton(const char* label, bool active) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - const ImGuiID id = window->GetID(label); - const ImVec2 label_size = CalcTextSize(label, NULL, true); - - const ImRect check_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(label_size.y + style.FramePadding.y*2-1, label_size.y + style.FramePadding.y*2-1)); - ItemSize(check_bb, style.FramePadding.y); - - ImRect total_bb = check_bb; - if (label_size.x > 0) - SameLine(0, style.ItemInnerSpacing.x); - const ImRect text_bb(window->DC.CursorPos + ImVec2(0, style.FramePadding.y), window->DC.CursorPos + ImVec2(0, style.FramePadding.y) + label_size); - if (label_size.x > 0) - { - ItemSize(ImVec2(text_bb.GetWidth(), check_bb.GetHeight()), style.FramePadding.y); - total_bb.Add(text_bb); - } - - if (!ItemAdd(total_bb, id)) - return false; - - ImVec2 center = check_bb.GetCenter(); - center.x = (float)(int)center.x + 0.5f; - center.y = (float)(int)center.y + 0.5f; - const float radius = check_bb.GetHeight() * 0.5f; - - bool hovered, held; - bool pressed = ButtonBehavior(total_bb, id, &hovered, &held); - if (pressed) - MarkItemEdited(id); - - RenderNavHighlight(total_bb, id); - window->DrawList->AddCircleFilled(center, radius, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), 16); - if (active) - { - const float check_sz = ImMin(check_bb.GetWidth(), check_bb.GetHeight()); - const float pad = ImMax(1.0f, (float)(int)(check_sz / 6.0f)); - window->DrawList->AddCircleFilled(center, radius-pad, GetColorU32(ImGuiCol_CheckMark), 16); - } - - if (style.FrameBorderSize > 0.0f) - { - window->DrawList->AddCircle(center+ImVec2(1,1), radius, GetColorU32(ImGuiCol_BorderShadow), 16, style.FrameBorderSize); - window->DrawList->AddCircle(center, radius, GetColorU32(ImGuiCol_Border), 16, style.FrameBorderSize); - } - - if (g.LogEnabled) - LogRenderedText(&text_bb.Min, active ? "(x)" : "( )"); - if (label_size.x > 0.0f) - RenderText(text_bb.Min, label); - - return pressed; -} - -bool ImGui::RadioButton(const char* label, int* v, int v_button) -{ - const bool pressed = RadioButton(label, *v == v_button); - if (pressed) - *v = v_button; - return pressed; -} - static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end) { int line_count = 0; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 5ea5c3be7a11..07fcc970f0e7 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -311,6 +311,579 @@ void ImGui::BulletTextV(const char* fmt, va_list args) // - Bullet() //------------------------------------------------------------------------- +bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + + if (flags & ImGuiButtonFlags_Disabled) + { + if (out_hovered) *out_hovered = false; + if (out_held) *out_held = false; + if (g.ActiveId == id) ClearActiveID(); + return false; + } + + // Default behavior requires click+release on same spot + if ((flags & (ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnRelease | ImGuiButtonFlags_PressedOnDoubleClick)) == 0) + flags |= ImGuiButtonFlags_PressedOnClickRelease; + + ImGuiWindow* backup_hovered_window = g.HoveredWindow; + if ((flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredRootWindow == window) + g.HoveredWindow = window; + + bool pressed = false; + bool hovered = ItemHoverable(bb, id); + + // Drag source doesn't report as hovered + if (hovered && g.DragDropActive && g.DragDropPayload.SourceId == id && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoDisableHover)) + hovered = false; + + // Special mode for Drag and Drop where holding button pressed for a long time while dragging another item triggers the button + if (g.DragDropActive && (flags & ImGuiButtonFlags_PressedOnDragDropHold) && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoHoldToOpenOthers)) + if (IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) + { + hovered = true; + SetHoveredID(id); + if (CalcTypematicPressedRepeatAmount(g.HoveredIdTimer + 0.0001f, g.HoveredIdTimer + 0.0001f - g.IO.DeltaTime, 0.01f, 0.70f)) // FIXME: Our formula for CalcTypematicPressedRepeatAmount() is fishy + { + pressed = true; + FocusWindow(window); + } + } + + if ((flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredRootWindow == window) + g.HoveredWindow = backup_hovered_window; + + // AllowOverlap mode (rarely used) requires previous frame HoveredId to be null or to match. This allows using patterns where a later submitted widget overlaps a previous one. + if (hovered && (flags & ImGuiButtonFlags_AllowItemOverlap) && (g.HoveredIdPreviousFrame != id && g.HoveredIdPreviousFrame != 0)) + hovered = false; + + // Mouse + if (hovered) + { + if (!(flags & ImGuiButtonFlags_NoKeyModifiers) || (!g.IO.KeyCtrl && !g.IO.KeyShift && !g.IO.KeyAlt)) + { + // | CLICKING | HOLDING with ImGuiButtonFlags_Repeat + // PressedOnClickRelease | * | .. (NOT on release) <-- MOST COMMON! (*) only if both click/release were over bounds + // PressedOnClick | | .. + // PressedOnRelease | | .. (NOT on release) + // PressedOnDoubleClick | | .. + // FIXME-NAV: We don't honor those different behaviors. + if ((flags & ImGuiButtonFlags_PressedOnClickRelease) && g.IO.MouseClicked[0]) + { + SetActiveID(id, window); + if (!(flags & ImGuiButtonFlags_NoNavFocus)) + SetFocusID(id, window); + FocusWindow(window); + } + if (((flags & ImGuiButtonFlags_PressedOnClick) && g.IO.MouseClicked[0]) || ((flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseDoubleClicked[0])) + { + pressed = true; + if (flags & ImGuiButtonFlags_NoHoldingActiveID) + ClearActiveID(); + else + SetActiveID(id, window); // Hold on ID + FocusWindow(window); + } + if ((flags & ImGuiButtonFlags_PressedOnRelease) && g.IO.MouseReleased[0]) + { + if (!((flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[0] >= g.IO.KeyRepeatDelay)) // Repeat mode trumps + pressed = true; + ClearActiveID(); + } + + // 'Repeat' mode acts when held regardless of _PressedOn flags (see table above). + // Relies on repeat logic of IsMouseClicked() but we may as well do it ourselves if we end up exposing finer RepeatDelay/RepeatRate settings. + if ((flags & ImGuiButtonFlags_Repeat) && g.ActiveId == id && g.IO.MouseDownDuration[0] > 0.0f && IsMouseClicked(0, true)) + pressed = true; + } + + if (pressed) + g.NavDisableHighlight = true; + } + + // Gamepad/Keyboard navigation + // We report navigated item as hovered but we don't set g.HoveredId to not interfere with mouse. + if (g.NavId == id && !g.NavDisableHighlight && g.NavDisableMouseHover && (g.ActiveId == 0 || g.ActiveId == id || g.ActiveId == window->MoveId)) + hovered = true; + + if (g.NavActivateDownId == id) + { + bool nav_activated_by_code = (g.NavActivateId == id); + bool nav_activated_by_inputs = IsNavInputPressed(ImGuiNavInput_Activate, (flags & ImGuiButtonFlags_Repeat) ? ImGuiInputReadMode_Repeat : ImGuiInputReadMode_Pressed); + if (nav_activated_by_code || nav_activated_by_inputs) + pressed = true; + if (nav_activated_by_code || nav_activated_by_inputs || g.ActiveId == id) + { + // Set active id so it can be queried by user via IsItemActive(), equivalent of holding the mouse button. + g.NavActivateId = id; // This is so SetActiveId assign a Nav source + SetActiveID(id, window); + if (!(flags & ImGuiButtonFlags_NoNavFocus)) + SetFocusID(id, window); + g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right) | (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); + } + } + + bool held = false; + if (g.ActiveId == id) + { + if (g.ActiveIdSource == ImGuiInputSource_Mouse) + { + if (g.ActiveIdIsJustActivated) + g.ActiveIdClickOffset = g.IO.MousePos - bb.Min; + if (g.IO.MouseDown[0]) + { + held = true; + } + else + { + if (hovered && (flags & ImGuiButtonFlags_PressedOnClickRelease)) + if (!((flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[0] >= g.IO.KeyRepeatDelay)) // Repeat mode trumps + if (!g.DragDropActive) + pressed = true; + ClearActiveID(); + } + if (!(flags & ImGuiButtonFlags_NoNavFocus)) + g.NavDisableHighlight = true; + } + else if (g.ActiveIdSource == ImGuiInputSource_Nav) + { + if (g.NavActivateDownId != id) + ClearActiveID(); + } + } + + if (out_hovered) *out_hovered = hovered; + if (out_held) *out_held = held; + + return pressed; +} + +bool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); + const ImVec2 label_size = CalcTextSize(label, NULL, true); + + ImVec2 pos = window->DC.CursorPos; + if ((flags & ImGuiButtonFlags_AlignTextBaseLine) && style.FramePadding.y < window->DC.CurrentLineTextBaseOffset) // Try to vertically align buttons that are smaller/have no padding so that text baseline matches (bit hacky, since it shouldn't be a flag) + pos.y += window->DC.CurrentLineTextBaseOffset - style.FramePadding.y; + ImVec2 size = CalcItemSize(size_arg, label_size.x + style.FramePadding.x * 2.0f, label_size.y + style.FramePadding.y * 2.0f); + + const ImRect bb(pos, pos + size); + ItemSize(bb, style.FramePadding.y); + if (!ItemAdd(bb, id)) + return false; + + if (window->DC.ItemFlags & ImGuiItemFlags_ButtonRepeat) + flags |= ImGuiButtonFlags_Repeat; + bool hovered, held; + bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags); + if (pressed) + MarkItemEdited(id); + + // Render + const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); + RenderNavHighlight(bb, id); + RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding); + RenderTextClipped(bb.Min + style.FramePadding, bb.Max - style.FramePadding, label, NULL, &label_size, style.ButtonTextAlign, &bb); + + // Automatically close popups + //if (pressed && !(flags & ImGuiButtonFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup)) + // CloseCurrentPopup(); + + return pressed; +} + +bool ImGui::Button(const char* label, const ImVec2& size_arg) +{ + return ButtonEx(label, size_arg, 0); +} + +// Small buttons fits within text without additional vertical spacing. +bool ImGui::SmallButton(const char* label) +{ + ImGuiContext& g = *GImGui; + float backup_padding_y = g.Style.FramePadding.y; + g.Style.FramePadding.y = 0.0f; + bool pressed = ButtonEx(label, ImVec2(0, 0), ImGuiButtonFlags_AlignTextBaseLine); + g.Style.FramePadding.y = backup_padding_y; + return pressed; +} + +// Tip: use ImGui::PushID()/PopID() to push indices or pointers in the ID stack. +// Then you can keep 'str_id' empty or the same for all your buttons (instead of creating a string based on a non-string id) +bool ImGui::InvisibleButton(const char* str_id, const ImVec2& size_arg) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + // Cannot use zero-size for InvisibleButton(). Unlike Button() there is not way to fallback using the label size. + IM_ASSERT(size_arg.x != 0.0f && size_arg.y != 0.0f); + + const ImGuiID id = window->GetID(str_id); + ImVec2 size = CalcItemSize(size_arg, 0.0f, 0.0f); + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); + ItemSize(bb); + if (!ItemAdd(bb, id)) + return false; + + bool hovered, held; + bool pressed = ButtonBehavior(bb, id, &hovered, &held); + + return pressed; +} + +bool ImGui::ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size, ImGuiButtonFlags flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiID id = window->GetID(str_id); + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); + const float default_size = GetFrameHeight(); + ItemSize(bb, (size.y >= default_size) ? g.Style.FramePadding.y : 0.0f); + if (!ItemAdd(bb, id)) + return false; + + if (window->DC.ItemFlags & ImGuiItemFlags_ButtonRepeat) + flags |= ImGuiButtonFlags_Repeat; + + bool hovered, held; + bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags); + + // Render + const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); + RenderNavHighlight(bb, id); + RenderFrame(bb.Min, bb.Max, col, true, g.Style.FrameRounding); + RenderArrow(bb.Min + ImVec2(ImMax(0.0f, size.x - g.FontSize - g.Style.FramePadding.x), ImMax(0.0f, size.y - g.FontSize - g.Style.FramePadding.y)), dir); + + return pressed; +} + +bool ImGui::ArrowButton(const char* str_id, ImGuiDir dir) +{ + float sz = GetFrameHeight(); + return ArrowButtonEx(str_id, dir, ImVec2(sz, sz), 0); +} + +// Button to close a window +bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos, float radius) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + + // We intentionally allow interaction when clipped so that a mechanical Alt,Right,Validate sequence close a window. + // (this isn't the regular behavior of buttons, but it doesn't affect the user much because navigation tends to keep items visible). + const ImRect bb(pos - ImVec2(radius,radius), pos + ImVec2(radius,radius)); + bool is_clipped = !ItemAdd(bb, id); + + bool hovered, held; + bool pressed = ButtonBehavior(bb, id, &hovered, &held); + if (is_clipped) + return pressed; + + // Render + ImVec2 center = bb.GetCenter(); + if (hovered) + window->DrawList->AddCircleFilled(center, ImMax(2.0f, radius), GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : ImGuiCol_ButtonHovered), 9); + + float cross_extent = (radius * 0.7071f) - 1.0f; + ImU32 cross_col = GetColorU32(ImGuiCol_Text); + center -= ImVec2(0.5f, 0.5f); + window->DrawList->AddLine(center + ImVec2(+cross_extent,+cross_extent), center + ImVec2(-cross_extent,-cross_extent), cross_col, 1.0f); + window->DrawList->AddLine(center + ImVec2(+cross_extent,-cross_extent), center + ImVec2(-cross_extent,+cross_extent), cross_col, 1.0f); + + return pressed; +} + +bool ImGui::CollapseButton(ImGuiID id, const ImVec2& pos) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + + ImRect bb(pos, pos + ImVec2(g.FontSize, g.FontSize) + g.Style.FramePadding * 2.0f); + ItemAdd(bb, id); + bool hovered, held; + bool pressed = ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_None); + + ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); + if (hovered || held) + window->DrawList->AddCircleFilled(bb.GetCenter() + ImVec2(0.0f, -0.5f), g.FontSize * 0.5f + 1.0f, col, 9); + RenderArrow(bb.Min + g.Style.FramePadding, window->Collapsed ? ImGuiDir_Right : ImGuiDir_Down, 1.0f); + + // Switch to moving the window after mouse is moved beyond the initial drag threshold + if (IsItemActive() && IsMouseDragging()) + StartMouseMovingWindow(window); + + return pressed; +} + +void ImGui::Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); + if (border_col.w > 0.0f) + bb.Max += ImVec2(2, 2); + ItemSize(bb); + if (!ItemAdd(bb, 0)) + return; + + if (border_col.w > 0.0f) + { + window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(border_col), 0.0f); + window->DrawList->AddImage(user_texture_id, bb.Min + ImVec2(1, 1), bb.Max - ImVec2(1, 1), uv0, uv1, GetColorU32(tint_col)); + } + else + { + window->DrawList->AddImage(user_texture_id, bb.Min, bb.Max, uv0, uv1, GetColorU32(tint_col)); + } +} + +// frame_padding < 0: uses FramePadding from style (default) +// frame_padding = 0: no framing +// frame_padding > 0: set framing size +// The color used are the button colors. +bool ImGui::ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, int frame_padding, const ImVec4& bg_col, const ImVec4& tint_col) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + + // Default to using texture ID as ID. User can still push string/integer prefixes. + // We could hash the size/uv to create a unique ID but that would prevent the user from animating UV. + PushID((void*)user_texture_id); + const ImGuiID id = window->GetID("#image"); + PopID(); + + const ImVec2 padding = (frame_padding >= 0) ? ImVec2((float)frame_padding, (float)frame_padding) : style.FramePadding; + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size + padding * 2); + const ImRect image_bb(window->DC.CursorPos + padding, window->DC.CursorPos + padding + size); + ItemSize(bb); + if (!ItemAdd(bb, id)) + return false; + + bool hovered, held; + bool pressed = ButtonBehavior(bb, id, &hovered, &held); + + // Render + const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); + RenderNavHighlight(bb, id); + RenderFrame(bb.Min, bb.Max, col, true, ImClamp((float)ImMin(padding.x, padding.y), 0.0f, style.FrameRounding)); + if (bg_col.w > 0.0f) + window->DrawList->AddRectFilled(image_bb.Min, image_bb.Max, GetColorU32(bg_col)); + window->DrawList->AddImage(user_texture_id, image_bb.Min, image_bb.Max, uv0, uv1, GetColorU32(tint_col)); + + return pressed; +} + +bool ImGui::Checkbox(const char* label, bool* v) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); + const ImVec2 label_size = CalcTextSize(label, NULL, true); + + const ImRect check_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(label_size.y + style.FramePadding.y*2, label_size.y + style.FramePadding.y*2)); // We want a square shape to we use Y twice + ItemSize(check_bb, style.FramePadding.y); + + ImRect total_bb = check_bb; + if (label_size.x > 0) + SameLine(0, style.ItemInnerSpacing.x); + const ImRect text_bb(window->DC.CursorPos + ImVec2(0,style.FramePadding.y), window->DC.CursorPos + ImVec2(0,style.FramePadding.y) + label_size); + if (label_size.x > 0) + { + ItemSize(ImVec2(text_bb.GetWidth(), check_bb.GetHeight()), style.FramePadding.y); + total_bb = ImRect(ImMin(check_bb.Min, text_bb.Min), ImMax(check_bb.Max, text_bb.Max)); + } + + if (!ItemAdd(total_bb, id)) + return false; + + bool hovered, held; + bool pressed = ButtonBehavior(total_bb, id, &hovered, &held); + if (pressed) + { + *v = !(*v); + MarkItemEdited(id); + } + + RenderNavHighlight(total_bb, id); + RenderFrame(check_bb.Min, check_bb.Max, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding); + if (*v) + { + const float check_sz = ImMin(check_bb.GetWidth(), check_bb.GetHeight()); + const float pad = ImMax(1.0f, (float)(int)(check_sz / 6.0f)); + RenderCheckMark(check_bb.Min + ImVec2(pad,pad), GetColorU32(ImGuiCol_CheckMark), check_bb.GetWidth() - pad*2.0f); + } + + if (g.LogEnabled) + LogRenderedText(&text_bb.Min, *v ? "[x]" : "[ ]"); + if (label_size.x > 0.0f) + RenderText(text_bb.Min, label); + + return pressed; +} + +bool ImGui::CheckboxFlags(const char* label, unsigned int* flags, unsigned int flags_value) +{ + bool v = ((*flags & flags_value) == flags_value); + bool pressed = Checkbox(label, &v); + if (pressed) + { + if (v) + *flags |= flags_value; + else + *flags &= ~flags_value; + } + + return pressed; +} + +bool ImGui::RadioButton(const char* label, bool active) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); + const ImVec2 label_size = CalcTextSize(label, NULL, true); + + const ImRect check_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(label_size.y + style.FramePadding.y*2-1, label_size.y + style.FramePadding.y*2-1)); + ItemSize(check_bb, style.FramePadding.y); + + ImRect total_bb = check_bb; + if (label_size.x > 0) + SameLine(0, style.ItemInnerSpacing.x); + const ImRect text_bb(window->DC.CursorPos + ImVec2(0, style.FramePadding.y), window->DC.CursorPos + ImVec2(0, style.FramePadding.y) + label_size); + if (label_size.x > 0) + { + ItemSize(ImVec2(text_bb.GetWidth(), check_bb.GetHeight()), style.FramePadding.y); + total_bb.Add(text_bb); + } + + if (!ItemAdd(total_bb, id)) + return false; + + ImVec2 center = check_bb.GetCenter(); + center.x = (float)(int)center.x + 0.5f; + center.y = (float)(int)center.y + 0.5f; + const float radius = check_bb.GetHeight() * 0.5f; + + bool hovered, held; + bool pressed = ButtonBehavior(total_bb, id, &hovered, &held); + if (pressed) + MarkItemEdited(id); + + RenderNavHighlight(total_bb, id); + window->DrawList->AddCircleFilled(center, radius, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), 16); + if (active) + { + const float check_sz = ImMin(check_bb.GetWidth(), check_bb.GetHeight()); + const float pad = ImMax(1.0f, (float)(int)(check_sz / 6.0f)); + window->DrawList->AddCircleFilled(center, radius-pad, GetColorU32(ImGuiCol_CheckMark), 16); + } + + if (style.FrameBorderSize > 0.0f) + { + window->DrawList->AddCircle(center+ImVec2(1,1), radius, GetColorU32(ImGuiCol_BorderShadow), 16, style.FrameBorderSize); + window->DrawList->AddCircle(center, radius, GetColorU32(ImGuiCol_Border), 16, style.FrameBorderSize); + } + + if (g.LogEnabled) + LogRenderedText(&text_bb.Min, active ? "(x)" : "( )"); + if (label_size.x > 0.0f) + RenderText(text_bb.Min, label); + + return pressed; +} + +bool ImGui::RadioButton(const char* label, int* v, int v_button) +{ + const bool pressed = RadioButton(label, *v == v_button); + if (pressed) + *v = v_button; + return pressed; +} + +// size_arg (for each axis) < 0.0f: align to end, 0.0f: auto, > 0.0f: specified size +void ImGui::ProgressBar(float fraction, const ImVec2& size_arg, const char* overlay) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + + ImVec2 pos = window->DC.CursorPos; + ImRect bb(pos, pos + CalcItemSize(size_arg, CalcItemWidth(), g.FontSize + style.FramePadding.y*2.0f)); + ItemSize(bb, style.FramePadding.y); + if (!ItemAdd(bb, 0)) + return; + + // Render + fraction = ImSaturate(fraction); + RenderFrame(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); + bb.Expand(ImVec2(-style.FrameBorderSize, -style.FrameBorderSize)); + const ImVec2 fill_br = ImVec2(ImLerp(bb.Min.x, bb.Max.x, fraction), bb.Max.y); + RenderRectFilledRangeH(window->DrawList, bb, GetColorU32(ImGuiCol_PlotHistogram), 0.0f, fraction, style.FrameRounding); + + // Default displaying the fraction as percentage string, but user can override it + char overlay_buf[32]; + if (!overlay) + { + ImFormatString(overlay_buf, IM_ARRAYSIZE(overlay_buf), "%.0f%%", fraction*100+0.01f); + overlay = overlay_buf; + } + + ImVec2 overlay_size = CalcTextSize(overlay, NULL); + if (overlay_size.x > 0.0f) + RenderTextClipped(ImVec2(ImClamp(fill_br.x + style.ItemSpacing.x, bb.Min.x, bb.Max.x - overlay_size.x - style.ItemInnerSpacing.x), bb.Min.y), bb.Max, overlay, NULL, &overlay_size, ImVec2(0.0f,0.5f), &bb); +} + +void ImGui::Bullet() +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const float line_height = ImMax(ImMin(window->DC.CurrentLineSize.y, g.FontSize + g.Style.FramePadding.y*2), g.FontSize); + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(g.FontSize, line_height)); + ItemSize(bb); + if (!ItemAdd(bb, 0)) + { + SameLine(0, style.FramePadding.x*2); + return; + } + + // Render and stay on same line + RenderBullet(bb.Min + ImVec2(style.FramePadding.x + g.FontSize*0.5f, line_height*0.5f)); + SameLine(0, style.FramePadding.x*2); +} //------------------------------------------------------------------------- // WIDGETS: Combo Box From 2d952504ed547f0fa54aa502965b91abb7ff27aa Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 30 Aug 2018 14:49:28 +0200 Subject: [PATCH 196/828] Refactor: Moved Combo/ListBox functions from imgui.cpp to imgui_widgets.cpp (#2036) --- imgui.cpp | 309 ---------------------------------------------- imgui_widgets.cpp | 307 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 307 insertions(+), 309 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 96af6461ef07..f1679bcf153d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11745,213 +11745,6 @@ bool ImGui::InputInt4(const char* label, int v[4], ImGuiInputTextFlags extra_fla return InputScalarN(label, ImGuiDataType_S32, v, 4, NULL, NULL, "%d", extra_flags); } -static float CalcMaxPopupHeightFromItemCount(int items_count) -{ - ImGuiContext& g = *GImGui; - if (items_count <= 0) - return FLT_MAX; - return (g.FontSize + g.Style.ItemSpacing.y) * items_count - g.Style.ItemSpacing.y + (g.Style.WindowPadding.y * 2); -} - -bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboFlags flags) -{ - // Always consume the SetNextWindowSizeConstraint() call in our early return paths - ImGuiContext& g = *GImGui; - ImGuiCond backup_next_window_size_constraint = g.NextWindowData.SizeConstraintCond; - g.NextWindowData.SizeConstraintCond = 0; - - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - IM_ASSERT((flags & (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)) != (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)); // Can't use both flags together - - const ImGuiStyle& style = g.Style; - const ImGuiID id = window->GetID(label); - - const float arrow_size = (flags & ImGuiComboFlags_NoArrowButton) ? 0.0f : GetFrameHeight(); - const ImVec2 label_size = CalcTextSize(label, NULL, true); - const float w = (flags & ImGuiComboFlags_NoPreview) ? arrow_size : CalcItemWidth(); - const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f)); - const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); - ItemSize(total_bb, style.FramePadding.y); - if (!ItemAdd(total_bb, id, &frame_bb)) - return false; - - bool hovered, held; - bool pressed = ButtonBehavior(frame_bb, id, &hovered, &held); - bool popup_open = IsPopupOpen(id); - - const ImRect value_bb(frame_bb.Min, frame_bb.Max - ImVec2(arrow_size, 0.0f)); - const ImU32 frame_col = GetColorU32(hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); - RenderNavHighlight(frame_bb, id); - if (!(flags & ImGuiComboFlags_NoPreview)) - window->DrawList->AddRectFilled(frame_bb.Min, ImVec2(frame_bb.Max.x - arrow_size, frame_bb.Max.y), frame_col, style.FrameRounding, ImDrawCornerFlags_Left); - if (!(flags & ImGuiComboFlags_NoArrowButton)) - { - window->DrawList->AddRectFilled(ImVec2(frame_bb.Max.x - arrow_size, frame_bb.Min.y), frame_bb.Max, GetColorU32((popup_open || hovered) ? ImGuiCol_ButtonHovered : ImGuiCol_Button), style.FrameRounding, (w <= arrow_size) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Right); - RenderArrow(ImVec2(frame_bb.Max.x - arrow_size + style.FramePadding.y, frame_bb.Min.y + style.FramePadding.y), ImGuiDir_Down); - } - RenderFrameBorder(frame_bb.Min, frame_bb.Max, style.FrameRounding); - if (preview_value != NULL && !(flags & ImGuiComboFlags_NoPreview)) - RenderTextClipped(frame_bb.Min + style.FramePadding, value_bb.Max, preview_value, NULL, NULL, ImVec2(0.0f,0.0f)); - if (label_size.x > 0) - RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); - - if ((pressed || g.NavActivateId == id) && !popup_open) - { - if (window->DC.NavLayerCurrent == 0) - window->NavLastIds[0] = id; - OpenPopupEx(id); - popup_open = true; - } - - if (!popup_open) - return false; - - if (backup_next_window_size_constraint) - { - g.NextWindowData.SizeConstraintCond = backup_next_window_size_constraint; - g.NextWindowData.SizeConstraintRect.Min.x = ImMax(g.NextWindowData.SizeConstraintRect.Min.x, w); - } - else - { - if ((flags & ImGuiComboFlags_HeightMask_) == 0) - flags |= ImGuiComboFlags_HeightRegular; - IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiComboFlags_HeightMask_)); // Only one - int popup_max_height_in_items = -1; - if (flags & ImGuiComboFlags_HeightRegular) popup_max_height_in_items = 8; - else if (flags & ImGuiComboFlags_HeightSmall) popup_max_height_in_items = 4; - else if (flags & ImGuiComboFlags_HeightLarge) popup_max_height_in_items = 20; - SetNextWindowSizeConstraints(ImVec2(w, 0.0f), ImVec2(FLT_MAX, CalcMaxPopupHeightFromItemCount(popup_max_height_in_items))); - } - - char name[16]; - ImFormatString(name, IM_ARRAYSIZE(name), "##Combo_%02d", g.CurrentPopupStack.Size); // Recycle windows based on depth - - // Peak into expected window size so we can position it - if (ImGuiWindow* popup_window = FindWindowByName(name)) - if (popup_window->WasActive) - { - ImVec2 size_expected = CalcWindowExpectedSize(popup_window); - if (flags & ImGuiComboFlags_PopupAlignLeft) - popup_window->AutoPosLastDirection = ImGuiDir_Left; - ImRect r_outer = GetWindowAllowedExtentRect(popup_window); - ImVec2 pos = FindBestWindowPosForPopupEx(frame_bb.GetBL(), size_expected, &popup_window->AutoPosLastDirection, r_outer, frame_bb, ImGuiPopupPositionPolicy_ComboBox); - SetNextWindowPos(pos); - } - - // Horizontally align ourselves with the framed text - ImGuiWindowFlags window_flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_Popup | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings; - PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(style.FramePadding.x, style.WindowPadding.y)); - bool ret = Begin(name, NULL, window_flags); - PopStyleVar(); - if (!ret) - { - EndPopup(); - IM_ASSERT(0); // This should never happen as we tested for IsPopupOpen() above - return false; - } - return true; -} - -void ImGui::EndCombo() -{ - EndPopup(); -} - -// Getter for the old Combo() API: const char*[] -static bool Items_ArrayGetter(void* data, int idx, const char** out_text) -{ - const char* const* items = (const char* const*)data; - if (out_text) - *out_text = items[idx]; - return true; -} - -// Getter for the old Combo() API: "item1\0item2\0item3\0" -static bool Items_SingleStringGetter(void* data, int idx, const char** out_text) -{ - // FIXME-OPT: we could pre-compute the indices to fasten this. But only 1 active combo means the waste is limited. - const char* items_separated_by_zeros = (const char*)data; - int items_count = 0; - const char* p = items_separated_by_zeros; - while (*p) - { - if (idx == items_count) - break; - p += strlen(p) + 1; - items_count++; - } - if (!*p) - return false; - if (out_text) - *out_text = p; - return true; -} - -// Old API, prefer using BeginCombo() nowadays if you can. -bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(void*, int, const char**), void* data, int items_count, int popup_max_height_in_items) -{ - ImGuiContext& g = *GImGui; - - // Call the getter to obtain the preview string which is a parameter to BeginCombo() - const char* preview_value = NULL; - if (*current_item >= 0 && *current_item < items_count) - items_getter(data, *current_item, &preview_value); - - // The old Combo() API exposed "popup_max_height_in_items". The new more general BeginCombo() API doesn't have/need it, but we emulate it here. - if (popup_max_height_in_items != -1 && !g.NextWindowData.SizeConstraintCond) - SetNextWindowSizeConstraints(ImVec2(0,0), ImVec2(FLT_MAX, CalcMaxPopupHeightFromItemCount(popup_max_height_in_items))); - - if (!BeginCombo(label, preview_value, ImGuiComboFlags_None)) - return false; - - // Display items - // FIXME-OPT: Use clipper (but we need to disable it on the appearing frame to make sure our call to SetItemDefaultFocus() is processed) - bool value_changed = false; - for (int i = 0; i < items_count; i++) - { - PushID((void*)(intptr_t)i); - const bool item_selected = (i == *current_item); - const char* item_text; - if (!items_getter(data, i, &item_text)) - item_text = "*Unknown item*"; - if (Selectable(item_text, item_selected)) - { - value_changed = true; - *current_item = i; - } - if (item_selected) - SetItemDefaultFocus(); - PopID(); - } - - EndCombo(); - return value_changed; -} - -// Combo box helper allowing to pass an array of strings. -bool ImGui::Combo(const char* label, int* current_item, const char* const items[], int items_count, int height_in_items) -{ - const bool value_changed = Combo(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_in_items); - return value_changed; -} - -// Combo box helper allowing to pass all items in a single string literal holding multiple zero-terminated items "item1\0item2\0" -bool ImGui::Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int height_in_items) -{ - int items_count = 0; - const char* p = items_separated_by_zeros; // FIXME-OPT: Avoid computing this, or at least only when combo is open - while (*p) - { - p += strlen(p) + 1; - items_count++; - } - bool value_changed = Combo(label, current_item, Items_SingleStringGetter, (void*)items_separated_by_zeros, items_count, height_in_items); - return value_changed; -} - // Tip: pass an empty label (e.g. "##dummy") then you can use the space to draw other text or image. // But you need to make sure the ID is unique, e.g. enclose calls in PushID/PopID or use ##unique_id. bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags flags, const ImVec2& size_arg) @@ -12055,108 +11848,6 @@ bool ImGui::Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags return false; } -// FIXME: Rename to BeginListBox() -// Helper to calculate the size of a listbox and display a label on the right. -// Tip: To have a list filling the entire window width, PushItemWidth(-1) and pass an empty label "##empty" -bool ImGui::ListBoxHeader(const char* label, const ImVec2& size_arg) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - const ImGuiStyle& style = GetStyle(); - const ImGuiID id = GetID(label); - const ImVec2 label_size = CalcTextSize(label, NULL, true); - - // Size default to hold ~7 items. Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar. - ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), GetTextLineHeightWithSpacing() * 7.4f + style.ItemSpacing.y); - ImVec2 frame_size = ImVec2(size.x, ImMax(size.y, label_size.y)); - ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size); - ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); - window->DC.LastItemRect = bb; // Forward storage for ListBoxFooter.. dodgy. - - BeginGroup(); - if (label_size.x > 0) - RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); - - BeginChildFrame(id, frame_bb.GetSize()); - return true; -} - -// FIXME: Rename to BeginListBox() -bool ImGui::ListBoxHeader(const char* label, int items_count, int height_in_items) -{ - // Size default to hold ~7 items. Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar. - // We don't add +0.40f if items_count <= height_in_items. It is slightly dodgy, because it means a dynamic list of items will make the widget resize occasionally when it crosses that size. - // I am expecting that someone will come and complain about this behavior in a remote future, then we can advise on a better solution. - if (height_in_items < 0) - height_in_items = ImMin(items_count, 7); - float height_in_items_f = height_in_items < items_count ? (height_in_items + 0.40f) : (height_in_items + 0.00f); - - // We include ItemSpacing.y so that a list sized for the exact number of items doesn't make a scrollbar appears. We could also enforce that by passing a flag to BeginChild(). - ImVec2 size; - size.x = 0.0f; - size.y = GetTextLineHeightWithSpacing() * height_in_items_f + GetStyle().ItemSpacing.y; - return ListBoxHeader(label, size); -} - -// FIXME: Rename to EndListBox() -void ImGui::ListBoxFooter() -{ - ImGuiWindow* parent_window = GetCurrentWindow()->ParentWindow; - const ImRect bb = parent_window->DC.LastItemRect; - const ImGuiStyle& style = GetStyle(); - - EndChildFrame(); - - // Redeclare item size so that it includes the label (we have stored the full size in LastItemRect) - // We call SameLine() to restore DC.CurrentLine* data - SameLine(); - parent_window->DC.CursorPos = bb.Min; - ItemSize(bb, style.FramePadding.y); - EndGroup(); -} - -bool ImGui::ListBox(const char* label, int* current_item, const char* const items[], int items_count, int height_items) -{ - const bool value_changed = ListBox(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_items); - return value_changed; -} - -bool ImGui::ListBox(const char* label, int* current_item, bool (*items_getter)(void*, int, const char**), void* data, int items_count, int height_in_items) -{ - if (!ListBoxHeader(label, items_count, height_in_items)) - return false; - - // Assume all items have even height (= 1 line of text). If you need items of different or variable sizes you can create a custom version of ListBox() in your code without using the clipper. - ImGuiContext& g = *GImGui; - bool value_changed = false; - ImGuiListClipper clipper(items_count, GetTextLineHeightWithSpacing()); // We know exactly our line height here so we pass it as a minor optimization, but generally you don't need to. - while (clipper.Step()) - for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) - { - const bool item_selected = (i == *current_item); - const char* item_text; - if (!items_getter(data, i, &item_text)) - item_text = "*Unknown item*"; - - PushID(i); - if (Selectable(item_text, item_selected)) - { - *current_item = i; - value_changed = true; - } - if (item_selected) - SetItemDefaultFocus(); - PopID(); - } - ListBoxFooter(); - if (value_changed) - MarkItemEdited(g.CurrentWindow->DC.LastItemId); - - return value_changed; -} - bool ImGui::MenuItem(const char* label, const char* shortcut, bool selected, bool enabled) { ImGuiWindow* window = GetCurrentWindow(); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 07fcc970f0e7..76ef649746db 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -892,6 +892,212 @@ void ImGui::Bullet() // - Combo() //------------------------------------------------------------------------- +static float CalcMaxPopupHeightFromItemCount(int items_count) +{ + ImGuiContext& g = *GImGui; + if (items_count <= 0) + return FLT_MAX; + return (g.FontSize + g.Style.ItemSpacing.y) * items_count - g.Style.ItemSpacing.y + (g.Style.WindowPadding.y * 2); +} + +bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboFlags flags) +{ + // Always consume the SetNextWindowSizeConstraint() call in our early return paths + ImGuiContext& g = *GImGui; + ImGuiCond backup_next_window_size_constraint = g.NextWindowData.SizeConstraintCond; + g.NextWindowData.SizeConstraintCond = 0; + + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + IM_ASSERT((flags & (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)) != (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)); // Can't use both flags together + + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); + + const float arrow_size = (flags & ImGuiComboFlags_NoArrowButton) ? 0.0f : GetFrameHeight(); + const ImVec2 label_size = CalcTextSize(label, NULL, true); + const float w = (flags & ImGuiComboFlags_NoPreview) ? arrow_size : CalcItemWidth(); + const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f)); + const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); + ItemSize(total_bb, style.FramePadding.y); + if (!ItemAdd(total_bb, id, &frame_bb)) + return false; + + bool hovered, held; + bool pressed = ButtonBehavior(frame_bb, id, &hovered, &held); + bool popup_open = IsPopupOpen(id); + + const ImRect value_bb(frame_bb.Min, frame_bb.Max - ImVec2(arrow_size, 0.0f)); + const ImU32 frame_col = GetColorU32(hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); + RenderNavHighlight(frame_bb, id); + if (!(flags & ImGuiComboFlags_NoPreview)) + window->DrawList->AddRectFilled(frame_bb.Min, ImVec2(frame_bb.Max.x - arrow_size, frame_bb.Max.y), frame_col, style.FrameRounding, ImDrawCornerFlags_Left); + if (!(flags & ImGuiComboFlags_NoArrowButton)) + { + window->DrawList->AddRectFilled(ImVec2(frame_bb.Max.x - arrow_size, frame_bb.Min.y), frame_bb.Max, GetColorU32((popup_open || hovered) ? ImGuiCol_ButtonHovered : ImGuiCol_Button), style.FrameRounding, (w <= arrow_size) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Right); + RenderArrow(ImVec2(frame_bb.Max.x - arrow_size + style.FramePadding.y, frame_bb.Min.y + style.FramePadding.y), ImGuiDir_Down); + } + RenderFrameBorder(frame_bb.Min, frame_bb.Max, style.FrameRounding); + if (preview_value != NULL && !(flags & ImGuiComboFlags_NoPreview)) + RenderTextClipped(frame_bb.Min + style.FramePadding, value_bb.Max, preview_value, NULL, NULL, ImVec2(0.0f,0.0f)); + if (label_size.x > 0) + RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); + + if ((pressed || g.NavActivateId == id) && !popup_open) + { + if (window->DC.NavLayerCurrent == 0) + window->NavLastIds[0] = id; + OpenPopupEx(id); + popup_open = true; + } + + if (!popup_open) + return false; + + if (backup_next_window_size_constraint) + { + g.NextWindowData.SizeConstraintCond = backup_next_window_size_constraint; + g.NextWindowData.SizeConstraintRect.Min.x = ImMax(g.NextWindowData.SizeConstraintRect.Min.x, w); + } + else + { + if ((flags & ImGuiComboFlags_HeightMask_) == 0) + flags |= ImGuiComboFlags_HeightRegular; + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiComboFlags_HeightMask_)); // Only one + int popup_max_height_in_items = -1; + if (flags & ImGuiComboFlags_HeightRegular) popup_max_height_in_items = 8; + else if (flags & ImGuiComboFlags_HeightSmall) popup_max_height_in_items = 4; + else if (flags & ImGuiComboFlags_HeightLarge) popup_max_height_in_items = 20; + SetNextWindowSizeConstraints(ImVec2(w, 0.0f), ImVec2(FLT_MAX, CalcMaxPopupHeightFromItemCount(popup_max_height_in_items))); + } + + char name[16]; + ImFormatString(name, IM_ARRAYSIZE(name), "##Combo_%02d", g.CurrentPopupStack.Size); // Recycle windows based on depth + + // Peak into expected window size so we can position it + if (ImGuiWindow* popup_window = FindWindowByName(name)) + if (popup_window->WasActive) + { + ImVec2 size_expected = CalcWindowExpectedSize(popup_window); + if (flags & ImGuiComboFlags_PopupAlignLeft) + popup_window->AutoPosLastDirection = ImGuiDir_Left; + ImRect r_outer = GetWindowAllowedExtentRect(popup_window); + ImVec2 pos = FindBestWindowPosForPopupEx(frame_bb.GetBL(), size_expected, &popup_window->AutoPosLastDirection, r_outer, frame_bb, ImGuiPopupPositionPolicy_ComboBox); + SetNextWindowPos(pos); + } + + // Horizontally align ourselves with the framed text + ImGuiWindowFlags window_flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_Popup | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings; + PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(style.FramePadding.x, style.WindowPadding.y)); + bool ret = Begin(name, NULL, window_flags); + PopStyleVar(); + if (!ret) + { + EndPopup(); + IM_ASSERT(0); // This should never happen as we tested for IsPopupOpen() above + return false; + } + return true; +} + +void ImGui::EndCombo() +{ + EndPopup(); +} + +// Getter for the old Combo() API: const char*[] +static bool Items_ArrayGetter(void* data, int idx, const char** out_text) +{ + const char* const* items = (const char* const*)data; + if (out_text) + *out_text = items[idx]; + return true; +} + +// Getter for the old Combo() API: "item1\0item2\0item3\0" +static bool Items_SingleStringGetter(void* data, int idx, const char** out_text) +{ + // FIXME-OPT: we could pre-compute the indices to fasten this. But only 1 active combo means the waste is limited. + const char* items_separated_by_zeros = (const char*)data; + int items_count = 0; + const char* p = items_separated_by_zeros; + while (*p) + { + if (idx == items_count) + break; + p += strlen(p) + 1; + items_count++; + } + if (!*p) + return false; + if (out_text) + *out_text = p; + return true; +} + +// Old API, prefer using BeginCombo() nowadays if you can. +bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(void*, int, const char**), void* data, int items_count, int popup_max_height_in_items) +{ + ImGuiContext& g = *GImGui; + + // Call the getter to obtain the preview string which is a parameter to BeginCombo() + const char* preview_value = NULL; + if (*current_item >= 0 && *current_item < items_count) + items_getter(data, *current_item, &preview_value); + + // The old Combo() API exposed "popup_max_height_in_items". The new more general BeginCombo() API doesn't have/need it, but we emulate it here. + if (popup_max_height_in_items != -1 && !g.NextWindowData.SizeConstraintCond) + SetNextWindowSizeConstraints(ImVec2(0,0), ImVec2(FLT_MAX, CalcMaxPopupHeightFromItemCount(popup_max_height_in_items))); + + if (!BeginCombo(label, preview_value, ImGuiComboFlags_None)) + return false; + + // Display items + // FIXME-OPT: Use clipper (but we need to disable it on the appearing frame to make sure our call to SetItemDefaultFocus() is processed) + bool value_changed = false; + for (int i = 0; i < items_count; i++) + { + PushID((void*)(intptr_t)i); + const bool item_selected = (i == *current_item); + const char* item_text; + if (!items_getter(data, i, &item_text)) + item_text = "*Unknown item*"; + if (Selectable(item_text, item_selected)) + { + value_changed = true; + *current_item = i; + } + if (item_selected) + SetItemDefaultFocus(); + PopID(); + } + + EndCombo(); + return value_changed; +} + +// Combo box helper allowing to pass an array of strings. +bool ImGui::Combo(const char* label, int* current_item, const char* const items[], int items_count, int height_in_items) +{ + const bool value_changed = Combo(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_in_items); + return value_changed; +} + +// Combo box helper allowing to pass all items in a single string literal holding multiple zero-terminated items "item1\0item2\0" +bool ImGui::Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int height_in_items) +{ + int items_count = 0; + const char* p = items_separated_by_zeros; // FIXME-OPT: Avoid computing this, or at least only when combo is open + while (*p) + { + p += strlen(p) + 1; + items_count++; + } + bool value_changed = Combo(label, current_item, Items_SingleStringGetter, (void*)items_separated_by_zeros, items_count, height_in_items); + return value_changed; +} //------------------------------------------------------------------------- // WIDGETS: Data Type and Data Formatting Helpers [Internal] @@ -1048,6 +1254,107 @@ void ImGui::Bullet() // - ListBoxFooter() //------------------------------------------------------------------------- +// FIXME: Rename to BeginListBox() +// Helper to calculate the size of a listbox and display a label on the right. +// Tip: To have a list filling the entire window width, PushItemWidth(-1) and pass an empty label "##empty" +bool ImGui::ListBoxHeader(const char* label, const ImVec2& size_arg) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + const ImGuiStyle& style = GetStyle(); + const ImGuiID id = GetID(label); + const ImVec2 label_size = CalcTextSize(label, NULL, true); + + // Size default to hold ~7 items. Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar. + ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), GetTextLineHeightWithSpacing() * 7.4f + style.ItemSpacing.y); + ImVec2 frame_size = ImVec2(size.x, ImMax(size.y, label_size.y)); + ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size); + ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); + window->DC.LastItemRect = bb; // Forward storage for ListBoxFooter.. dodgy. + + BeginGroup(); + if (label_size.x > 0) + RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); + + BeginChildFrame(id, frame_bb.GetSize()); + return true; +} + +// FIXME: Rename to BeginListBox() +bool ImGui::ListBoxHeader(const char* label, int items_count, int height_in_items) +{ + // Size default to hold ~7 items. Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar. + // We don't add +0.40f if items_count <= height_in_items. It is slightly dodgy, because it means a dynamic list of items will make the widget resize occasionally when it crosses that size. + // I am expecting that someone will come and complain about this behavior in a remote future, then we can advise on a better solution. + if (height_in_items < 0) + height_in_items = ImMin(items_count, 7); + float height_in_items_f = height_in_items < items_count ? (height_in_items + 0.40f) : (height_in_items + 0.00f); + + // We include ItemSpacing.y so that a list sized for the exact number of items doesn't make a scrollbar appears. We could also enforce that by passing a flag to BeginChild(). + ImVec2 size; + size.x = 0.0f; + size.y = GetTextLineHeightWithSpacing() * height_in_items_f + GetStyle().ItemSpacing.y; + return ListBoxHeader(label, size); +} + +// FIXME: Rename to EndListBox() +void ImGui::ListBoxFooter() +{ + ImGuiWindow* parent_window = GetCurrentWindow()->ParentWindow; + const ImRect bb = parent_window->DC.LastItemRect; + const ImGuiStyle& style = GetStyle(); + + EndChildFrame(); + + // Redeclare item size so that it includes the label (we have stored the full size in LastItemRect) + // We call SameLine() to restore DC.CurrentLine* data + SameLine(); + parent_window->DC.CursorPos = bb.Min; + ItemSize(bb, style.FramePadding.y); + EndGroup(); +} + +bool ImGui::ListBox(const char* label, int* current_item, const char* const items[], int items_count, int height_items) +{ + const bool value_changed = ListBox(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_items); + return value_changed; +} + +bool ImGui::ListBox(const char* label, int* current_item, bool (*items_getter)(void*, int, const char**), void* data, int items_count, int height_in_items) +{ + if (!ListBoxHeader(label, items_count, height_in_items)) + return false; + + // Assume all items have even height (= 1 line of text). If you need items of different or variable sizes you can create a custom version of ListBox() in your code without using the clipper. + ImGuiContext& g = *GImGui; + bool value_changed = false; + ImGuiListClipper clipper(items_count, GetTextLineHeightWithSpacing()); // We know exactly our line height here so we pass it as a minor optimization, but generally you don't need to. + while (clipper.Step()) + for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) + { + const bool item_selected = (i == *current_item); + const char* item_text; + if (!items_getter(data, i, &item_text)) + item_text = "*Unknown item*"; + + PushID(i); + if (Selectable(item_text, item_selected)) + { + *current_item = i; + value_changed = true; + } + if (item_selected) + SetItemDefaultFocus(); + PopID(); + } + ListBoxFooter(); + if (value_changed) + MarkItemEdited(g.CurrentWindow->DC.LastItemId); + + return value_changed; +} //------------------------------------------------------------------------- // WIDGETS: Data Plotting From 4be79a89551e158b4f5a0d83ed535e45f1907b9b Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 30 Aug 2018 14:58:55 +0200 Subject: [PATCH 197/828] Refactor: Moved Menu functions from imgui.cpp to imgui_widgets.cpp (#2036) --- imgui.cpp | 349 ---------------------------------------------- imgui_widgets.cpp | 346 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 346 insertions(+), 349 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index f1679bcf153d..d6385bdc2126 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1996,51 +1996,6 @@ void ImGuiTextBuffer::appendf(const char* fmt, ...) va_end(args); } -//----------------------------------------------------------------------------- -// ImGuiSimpleColumns (internal use only) -//----------------------------------------------------------------------------- - -ImGuiMenuColumns::ImGuiMenuColumns() -{ - Count = 0; - Spacing = Width = NextWidth = 0.0f; - memset(Pos, 0, sizeof(Pos)); - memset(NextWidths, 0, sizeof(NextWidths)); -} - -void ImGuiMenuColumns::Update(int count, float spacing, bool clear) -{ - IM_ASSERT(Count <= IM_ARRAYSIZE(Pos)); - Count = count; - Width = NextWidth = 0.0f; - Spacing = spacing; - if (clear) memset(NextWidths, 0, sizeof(NextWidths)); - for (int i = 0; i < Count; i++) - { - if (i > 0 && NextWidths[i] > 0.0f) - Width += Spacing; - Pos[i] = (float)(int)Width; - Width += NextWidths[i]; - NextWidths[i] = 0.0f; - } -} - -float ImGuiMenuColumns::DeclColumns(float w0, float w1, float w2) // not using va_arg because they promote float to double -{ - NextWidth = 0.0f; - NextWidths[0] = ImMax(NextWidths[0], w0); - NextWidths[1] = ImMax(NextWidths[1], w1); - NextWidths[2] = ImMax(NextWidths[2], w2); - for (int i = 0; i < 3; i++) - NextWidth += NextWidths[i] + ((i > 0 && NextWidths[i] > 0.0f) ? Spacing : 0.0f); - return ImMax(Width, NextWidth); -} - -float ImGuiMenuColumns::CalcExtraSpace(float avail_w) -{ - return ImMax(0.0f, avail_w - Width); -} - //----------------------------------------------------------------------------- // ImGuiListClipper //----------------------------------------------------------------------------- @@ -11848,310 +11803,6 @@ bool ImGui::Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags return false; } -bool ImGui::MenuItem(const char* label, const char* shortcut, bool selected, bool enabled) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - ImGuiStyle& style = g.Style; - ImVec2 pos = window->DC.CursorPos; - ImVec2 label_size = CalcTextSize(label, NULL, true); - - ImGuiSelectableFlags flags = ImGuiSelectableFlags_PressedOnRelease | (enabled ? 0 : ImGuiSelectableFlags_Disabled); - bool pressed; - if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) - { - // Mimic the exact layout spacing of BeginMenu() to allow MenuItem() inside a menu bar, which is a little misleading but may be useful - // Note that in this situation we render neither the shortcut neither the selected tick mark - float w = label_size.x; - window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * 0.5f); - PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing * 2.0f); - pressed = Selectable(label, false, flags, ImVec2(w, 0.0f)); - PopStyleVar(); - window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar(). - } - else - { - ImVec2 shortcut_size = shortcut ? CalcTextSize(shortcut, NULL) : ImVec2(0.0f, 0.0f); - float w = window->MenuColumns.DeclColumns(label_size.x, shortcut_size.x, (float)(int)(g.FontSize * 1.20f)); // Feedback for next frame - float extra_w = ImMax(0.0f, GetContentRegionAvail().x - w); - pressed = Selectable(label, false, flags | ImGuiSelectableFlags_DrawFillAvailWidth, ImVec2(w, 0.0f)); - if (shortcut_size.x > 0.0f) - { - PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]); - RenderText(pos + ImVec2(window->MenuColumns.Pos[1] + extra_w, 0.0f), shortcut, NULL, false); - PopStyleColor(); - } - if (selected) - RenderCheckMark(pos + ImVec2(window->MenuColumns.Pos[2] + extra_w + g.FontSize * 0.40f, g.FontSize * 0.134f * 0.5f), GetColorU32(enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled), g.FontSize * 0.866f); - } - return pressed; -} - -bool ImGui::MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled) -{ - if (MenuItem(label, shortcut, p_selected ? *p_selected : false, enabled)) - { - if (p_selected) - *p_selected = !*p_selected; - return true; - } - return false; -} - -// For the main menu bar, which cannot be moved, we honor g.Style.DisplaySafeAreaPadding to ensure text can be visible on a TV set. -bool ImGui::BeginMainMenuBar() -{ - ImGuiContext& g = *GImGui; - g.NextWindowData.MenuBarOffsetMinVal = ImVec2(g.Style.DisplaySafeAreaPadding.x, ImMax(g.Style.DisplaySafeAreaPadding.y - g.Style.FramePadding.y, 0.0f)); - SetNextWindowPos(g.Viewports[0]->Pos); - SetNextWindowSize(ImVec2(g.IO.DisplaySize.x, g.NextWindowData.MenuBarOffsetMinVal.y + g.FontBaseSize + g.Style.FramePadding.y)); - PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); - PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0,0)); - ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_MenuBar; - bool is_open = Begin("##MainMenuBar", NULL, window_flags) && BeginMenuBar(); - PopStyleVar(2); - g.NextWindowData.MenuBarOffsetMinVal = ImVec2(0.0f, 0.0f); - if (!is_open) - { - End(); - return false; - } - return true; -} - -void ImGui::EndMainMenuBar() -{ - EndMenuBar(); - - // When the user has left the menu layer (typically: closed menus through activation of an item), we restore focus to the previous window - ImGuiContext& g = *GImGui; - if (g.CurrentWindow == g.NavWindow && g.NavLayer == 0) - FocusFrontMostActiveWindowIgnoringOne(g.NavWindow); - - End(); -} - -bool ImGui::BeginMenuBar() -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - if (!(window->Flags & ImGuiWindowFlags_MenuBar)) - return false; - - IM_ASSERT(!window->DC.MenuBarAppending); - BeginGroup(); // Backup position on layer 0 - PushID("##menubar"); - - // We don't clip with current window clipping rectangle as it is already set to the area below. However we clip with window full rect. - // We remove 1 worth of rounding to Max.x to that text in long menus and small windows don't tend to display over the lower-right rounded area, which looks particularly glitchy. - ImRect bar_rect = window->MenuBarRect(); - ImRect clip_rect(ImFloor(bar_rect.Min.x + 0.5f), ImFloor(bar_rect.Min.y + window->WindowBorderSize + 0.5f), ImFloor(ImMax(bar_rect.Min.x, bar_rect.Max.x - window->WindowRounding) + 0.5f), ImFloor(bar_rect.Max.y + 0.5f)); - clip_rect.ClipWith(window->OuterRectClipped); - PushClipRect(clip_rect.Min, clip_rect.Max, false); - - window->DC.CursorPos = ImVec2(bar_rect.Min.x + window->DC.MenuBarOffset.x, bar_rect.Min.y + window->DC.MenuBarOffset.y); - window->DC.LayoutType = ImGuiLayoutType_Horizontal; - window->DC.NavLayerCurrent++; - window->DC.NavLayerCurrentMask <<= 1; - window->DC.MenuBarAppending = true; - AlignTextToFramePadding(); - return true; -} - -void ImGui::EndMenuBar() -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - ImGuiContext& g = *GImGui; - - // Nav: When a move request within one of our child menu failed, capture the request to navigate among our siblings. - if (NavMoveRequestButNoResultYet() && (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) && (g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu)) - { - ImGuiWindow* nav_earliest_child = g.NavWindow; - while (nav_earliest_child->ParentWindow && (nav_earliest_child->ParentWindow->Flags & ImGuiWindowFlags_ChildMenu)) - nav_earliest_child = nav_earliest_child->ParentWindow; - if (nav_earliest_child->ParentWindow == window && nav_earliest_child->DC.ParentLayoutType == ImGuiLayoutType_Horizontal && g.NavMoveRequestForward == ImGuiNavForward_None) - { - // To do so we claim focus back, restore NavId and then process the movement request for yet another frame. - // This involve a one-frame delay which isn't very problematic in this situation. We could remove it by scoring in advance for multiple window (probably not worth the hassle/cost) - IM_ASSERT(window->DC.NavLayerActiveMaskNext & 0x02); // Sanity check - FocusWindow(window); - SetNavIDWithRectRel(window->NavLastIds[1], 1, window->NavRectRel[1]); - g.NavLayer = 1; - g.NavDisableHighlight = true; // Hide highlight for the current frame so we don't see the intermediary selection. - g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued; - NavMoveRequestCancel(); - } - } - - IM_ASSERT(window->Flags & ImGuiWindowFlags_MenuBar); - IM_ASSERT(window->DC.MenuBarAppending); - PopClipRect(); - PopID(); - window->DC.MenuBarOffset.x = window->DC.CursorPos.x - window->MenuBarRect().Min.x; // Save horizontal position so next append can reuse it. This is kinda equivalent to a per-layer CursorPos. - window->DC.GroupStack.back().AdvanceCursor = false; - EndGroup(); // Restore position on layer 0 - window->DC.LayoutType = ImGuiLayoutType_Vertical; - window->DC.NavLayerCurrent--; - window->DC.NavLayerCurrentMask >>= 1; - window->DC.MenuBarAppending = false; -} - -bool ImGui::BeginMenu(const char* label, bool enabled) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - const ImGuiID id = window->GetID(label); - - ImVec2 label_size = CalcTextSize(label, NULL, true); - - bool pressed; - bool menu_is_open = IsPopupOpen(id); - bool menuset_is_open = !(window->Flags & ImGuiWindowFlags_Popup) && (g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].OpenParentId == window->IDStack.back()); - ImGuiWindow* backed_nav_window = g.NavWindow; - if (menuset_is_open) - g.NavWindow = window; // Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent) - - // The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu (using FindBestWindowPosForPopup). - ImVec2 popup_pos, pos = window->DC.CursorPos; - if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) - { - // Menu inside an horizontal menu bar - // Selectable extend their highlight by half ItemSpacing in each direction. - // For ChildMenu, the popup position will be overwritten by the call to FindBestWindowPosForPopup() in Begin() - popup_pos = ImVec2(pos.x - window->WindowPadding.x, pos.y - style.FramePadding.y + window->MenuBarHeight()); - window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * 0.5f); - PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing * 2.0f); - float w = label_size.x; - pressed = Selectable(label, menu_is_open, ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_PressedOnClick | ImGuiSelectableFlags_DontClosePopups | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(w, 0.0f)); - PopStyleVar(); - window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar(). - } - else - { - // Menu inside a menu - popup_pos = ImVec2(pos.x, pos.y - style.WindowPadding.y); - float w = window->MenuColumns.DeclColumns(label_size.x, 0.0f, (float)(int)(g.FontSize * 1.20f)); // Feedback to next frame - float extra_w = ImMax(0.0f, GetContentRegionAvail().x - w); - pressed = Selectable(label, menu_is_open, ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_PressedOnClick | ImGuiSelectableFlags_DontClosePopups | ImGuiSelectableFlags_DrawFillAvailWidth | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(w, 0.0f)); - if (!enabled) PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]); - RenderArrow(pos + ImVec2(window->MenuColumns.Pos[2] + extra_w + g.FontSize * 0.30f, 0.0f), ImGuiDir_Right); - if (!enabled) PopStyleColor(); - } - - const bool hovered = enabled && ItemHoverable(window->DC.LastItemRect, id); - if (menuset_is_open) - g.NavWindow = backed_nav_window; - - bool want_open = false, want_close = false; - if (window->DC.LayoutType == ImGuiLayoutType_Vertical) // (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) - { - // Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive. - bool moving_within_opened_triangle = false; - if (g.HoveredWindow == window && g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].ParentWindow == window && !(window->Flags & ImGuiWindowFlags_MenuBar)) - { - if (ImGuiWindow* next_window = g.OpenPopupStack[g.CurrentPopupStack.Size].Window) - { - ImRect next_window_rect = next_window->Rect(); - ImVec2 ta = g.IO.MousePos - g.IO.MouseDelta; - ImVec2 tb = (window->Pos.x < next_window->Pos.x) ? next_window_rect.GetTL() : next_window_rect.GetTR(); - ImVec2 tc = (window->Pos.x < next_window->Pos.x) ? next_window_rect.GetBL() : next_window_rect.GetBR(); - float extra = ImClamp(ImFabs(ta.x - tb.x) * 0.30f, 5.0f, 30.0f); // add a bit of extra slack. - ta.x += (window->Pos.x < next_window->Pos.x) ? -0.5f : +0.5f; // to avoid numerical issues - tb.y = ta.y + ImMax((tb.y - extra) - ta.y, -100.0f); // triangle is maximum 200 high to limit the slope and the bias toward large sub-menus // FIXME: Multiply by fb_scale? - tc.y = ta.y + ImMin((tc.y + extra) - ta.y, +100.0f); - moving_within_opened_triangle = ImTriangleContainsPoint(ta, tb, tc, g.IO.MousePos); - //window->DrawList->PushClipRectFullScreen(); window->DrawList->AddTriangleFilled(ta, tb, tc, moving_within_opened_triangle ? IM_COL32(0,128,0,128) : IM_COL32(128,0,0,128)); window->DrawList->PopClipRect(); // Debug - } - } - - want_close = (menu_is_open && !hovered && g.HoveredWindow == window && g.HoveredIdPreviousFrame != 0 && g.HoveredIdPreviousFrame != id && !moving_within_opened_triangle); - want_open = (!menu_is_open && hovered && !moving_within_opened_triangle) || (!menu_is_open && hovered && pressed); - - if (g.NavActivateId == id) - { - want_close = menu_is_open; - want_open = !menu_is_open; - } - if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Right) // Nav-Right to open - { - want_open = true; - NavMoveRequestCancel(); - } - } - else - { - // Menu bar - if (menu_is_open && pressed && menuset_is_open) // Click an open menu again to close it - { - want_close = true; - want_open = menu_is_open = false; - } - else if (pressed || (hovered && menuset_is_open && !menu_is_open)) // First click to open, then hover to open others - { - want_open = true; - } - else if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Down) // Nav-Down to open - { - want_open = true; - NavMoveRequestCancel(); - } - } - - if (!enabled) // explicitly close if an open menu becomes disabled, facilitate users code a lot in pattern such as 'if (BeginMenu("options", has_object)) { ..use object.. }' - want_close = true; - if (want_close && IsPopupOpen(id)) - ClosePopupToLevel(g.CurrentPopupStack.Size); - - if (!menu_is_open && want_open && g.OpenPopupStack.Size > g.CurrentPopupStack.Size) - { - // Don't recycle same menu level in the same frame, first close the other menu and yield for a frame. - OpenPopup(label); - return false; - } - - menu_is_open |= want_open; - if (want_open) - OpenPopup(label); - - if (menu_is_open) - { - // Sub-menus are ChildWindow so that mouse can be hovering across them (otherwise top-most popup menu would steal focus and not allow hovering on parent menu) - SetNextWindowPos(popup_pos, ImGuiCond_Always); - ImGuiWindowFlags flags = ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus; - if (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) - flags |= ImGuiWindowFlags_ChildWindow; - menu_is_open = BeginPopupEx(id, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display) - } - - return menu_is_open; -} - -void ImGui::EndMenu() -{ - // Nav: When a left move request _within our child menu_ failed, close the menu. - // A menu doesn't close itself because EndMenuBar() wants the catch the last Left<>Right inputs. - // However, it means that with the current code, a BeginMenu() from outside another menu or a menu-bar won't be closable with the Left direction. - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - if (g.NavWindow && g.NavWindow->ParentWindow == window && g.NavMoveDir == ImGuiDir_Left && NavMoveRequestButNoResultYet() && window->DC.LayoutType == ImGuiLayoutType_Vertical) - { - ClosePopupToLevel(g.OpenPopupStack.Size - 1); - NavMoveRequestCancel(); - } - - EndPopup(); -} - // Note: only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set. void ImGui::ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags flags) { diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 76ef649746db..210670032e06 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1372,6 +1372,7 @@ bool ImGui::ListBox(const char* label, int* current_item, bool (*items_getter)(v //------------------------------------------------------------------------- // WIDGETS: Menus +// - ImGuiMenuColumns // - BeginMainMenuBar() // - EndMainMenuBar() // - BeginMenuBar() @@ -1381,3 +1382,348 @@ bool ImGui::ListBox(const char* label, int* current_item, bool (*items_getter)(v // - MenuItem() //------------------------------------------------------------------------- +// Helpers for internal use +ImGuiMenuColumns::ImGuiMenuColumns() +{ + Count = 0; + Spacing = Width = NextWidth = 0.0f; + memset(Pos, 0, sizeof(Pos)); + memset(NextWidths, 0, sizeof(NextWidths)); +} + +void ImGuiMenuColumns::Update(int count, float spacing, bool clear) +{ + IM_ASSERT(Count <= IM_ARRAYSIZE(Pos)); + Count = count; + Width = NextWidth = 0.0f; + Spacing = spacing; + if (clear) memset(NextWidths, 0, sizeof(NextWidths)); + for (int i = 0; i < Count; i++) + { + if (i > 0 && NextWidths[i] > 0.0f) + Width += Spacing; + Pos[i] = (float)(int)Width; + Width += NextWidths[i]; + NextWidths[i] = 0.0f; + } +} + +float ImGuiMenuColumns::DeclColumns(float w0, float w1, float w2) // not using va_arg because they promote float to double +{ + NextWidth = 0.0f; + NextWidths[0] = ImMax(NextWidths[0], w0); + NextWidths[1] = ImMax(NextWidths[1], w1); + NextWidths[2] = ImMax(NextWidths[2], w2); + for (int i = 0; i < 3; i++) + NextWidth += NextWidths[i] + ((i > 0 && NextWidths[i] > 0.0f) ? Spacing : 0.0f); + return ImMax(Width, NextWidth); +} + +float ImGuiMenuColumns::CalcExtraSpace(float avail_w) +{ + return ImMax(0.0f, avail_w - Width); +} + +// For the main menu bar, which cannot be moved, we honor g.Style.DisplaySafeAreaPadding to ensure text can be visible on a TV set. +bool ImGui::BeginMainMenuBar() +{ + ImGuiContext& g = *GImGui; + g.NextWindowData.MenuBarOffsetMinVal = ImVec2(g.Style.DisplaySafeAreaPadding.x, ImMax(g.Style.DisplaySafeAreaPadding.y - g.Style.FramePadding.y, 0.0f)); + SetNextWindowPos(g.Viewports[0]->Pos); + SetNextWindowSize(ImVec2(g.Viewports[0]->Size.x, g.NextWindowData.MenuBarOffsetMinVal.y + g.FontBaseSize + g.Style.FramePadding.y)); + PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0,0)); + ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_MenuBar; + bool is_open = Begin("##MainMenuBar", NULL, window_flags) && BeginMenuBar(); + PopStyleVar(2); + g.NextWindowData.MenuBarOffsetMinVal = ImVec2(0.0f, 0.0f); + if (!is_open) + { + End(); + return false; + } + return true; +} + +void ImGui::EndMainMenuBar() +{ + EndMenuBar(); + + // When the user has left the menu layer (typically: closed menus through activation of an item), we restore focus to the previous window + ImGuiContext& g = *GImGui; + if (g.CurrentWindow == g.NavWindow && g.NavLayer == 0) + FocusFrontMostActiveWindowIgnoringOne(g.NavWindow); + + End(); +} + +bool ImGui::BeginMenuBar() +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + if (!(window->Flags & ImGuiWindowFlags_MenuBar)) + return false; + + IM_ASSERT(!window->DC.MenuBarAppending); + BeginGroup(); // Backup position on layer 0 + PushID("##menubar"); + + // We don't clip with current window clipping rectangle as it is already set to the area below. However we clip with window full rect. + // We remove 1 worth of rounding to Max.x to that text in long menus and small windows don't tend to display over the lower-right rounded area, which looks particularly glitchy. + ImRect bar_rect = window->MenuBarRect(); + ImRect clip_rect(ImFloor(bar_rect.Min.x + 0.5f), ImFloor(bar_rect.Min.y + window->WindowBorderSize + 0.5f), ImFloor(ImMax(bar_rect.Min.x, bar_rect.Max.x - window->WindowRounding) + 0.5f), ImFloor(bar_rect.Max.y + 0.5f)); + clip_rect.ClipWith(window->OuterRectClipped); + PushClipRect(clip_rect.Min, clip_rect.Max, false); + + window->DC.CursorPos = ImVec2(bar_rect.Min.x + window->DC.MenuBarOffset.x, bar_rect.Min.y + window->DC.MenuBarOffset.y); + window->DC.LayoutType = ImGuiLayoutType_Horizontal; + window->DC.NavLayerCurrent++; + window->DC.NavLayerCurrentMask <<= 1; + window->DC.MenuBarAppending = true; + AlignTextToFramePadding(); + return true; +} + +void ImGui::EndMenuBar() +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + ImGuiContext& g = *GImGui; + + // Nav: When a move request within one of our child menu failed, capture the request to navigate among our siblings. + if (NavMoveRequestButNoResultYet() && (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) && (g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu)) + { + ImGuiWindow* nav_earliest_child = g.NavWindow; + while (nav_earliest_child->ParentWindow && (nav_earliest_child->ParentWindow->Flags & ImGuiWindowFlags_ChildMenu)) + nav_earliest_child = nav_earliest_child->ParentWindow; + if (nav_earliest_child->ParentWindow == window && nav_earliest_child->DC.ParentLayoutType == ImGuiLayoutType_Horizontal && g.NavMoveRequestForward == ImGuiNavForward_None) + { + // To do so we claim focus back, restore NavId and then process the movement request for yet another frame. + // This involve a one-frame delay which isn't very problematic in this situation. We could remove it by scoring in advance for multiple window (probably not worth the hassle/cost) + IM_ASSERT(window->DC.NavLayerActiveMaskNext & 0x02); // Sanity check + FocusWindow(window); + SetNavIDWithRectRel(window->NavLastIds[1], 1, window->NavRectRel[1]); + g.NavLayer = 1; + g.NavDisableHighlight = true; // Hide highlight for the current frame so we don't see the intermediary selection. + g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued; + NavMoveRequestCancel(); + } + } + + IM_ASSERT(window->Flags & ImGuiWindowFlags_MenuBar); + IM_ASSERT(window->DC.MenuBarAppending); + PopClipRect(); + PopID(); + window->DC.MenuBarOffset.x = window->DC.CursorPos.x - window->MenuBarRect().Min.x; // Save horizontal position so next append can reuse it. This is kinda equivalent to a per-layer CursorPos. + window->DC.GroupStack.back().AdvanceCursor = false; + EndGroup(); // Restore position on layer 0 + window->DC.LayoutType = ImGuiLayoutType_Vertical; + window->DC.NavLayerCurrent--; + window->DC.NavLayerCurrentMask >>= 1; + window->DC.MenuBarAppending = false; +} + +bool ImGui::BeginMenu(const char* label, bool enabled) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); + + ImVec2 label_size = CalcTextSize(label, NULL, true); + + bool pressed; + bool menu_is_open = IsPopupOpen(id); + bool menuset_is_open = !(window->Flags & ImGuiWindowFlags_Popup) && (g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].OpenParentId == window->IDStack.back()); + ImGuiWindow* backed_nav_window = g.NavWindow; + if (menuset_is_open) + g.NavWindow = window; // Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent) + + // The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu (using FindBestWindowPosForPopup). + ImVec2 popup_pos, pos = window->DC.CursorPos; + if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) + { + // Menu inside an horizontal menu bar + // Selectable extend their highlight by half ItemSpacing in each direction. + // For ChildMenu, the popup position will be overwritten by the call to FindBestWindowPosForPopup() in Begin() + popup_pos = ImVec2(pos.x - window->WindowPadding.x, pos.y - style.FramePadding.y + window->MenuBarHeight()); + window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * 0.5f); + PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing * 2.0f); + float w = label_size.x; + pressed = Selectable(label, menu_is_open, ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_PressedOnClick | ImGuiSelectableFlags_DontClosePopups | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(w, 0.0f)); + PopStyleVar(); + window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar(). + } + else + { + // Menu inside a menu + popup_pos = ImVec2(pos.x, pos.y - style.WindowPadding.y); + float w = window->MenuColumns.DeclColumns(label_size.x, 0.0f, (float)(int)(g.FontSize * 1.20f)); // Feedback to next frame + float extra_w = ImMax(0.0f, GetContentRegionAvail().x - w); + pressed = Selectable(label, menu_is_open, ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_PressedOnClick | ImGuiSelectableFlags_DontClosePopups | ImGuiSelectableFlags_DrawFillAvailWidth | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(w, 0.0f)); + if (!enabled) PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]); + RenderArrow(pos + ImVec2(window->MenuColumns.Pos[2] + extra_w + g.FontSize * 0.30f, 0.0f), ImGuiDir_Right); + if (!enabled) PopStyleColor(); + } + + const bool hovered = enabled && ItemHoverable(window->DC.LastItemRect, id); + if (menuset_is_open) + g.NavWindow = backed_nav_window; + + bool want_open = false, want_close = false; + if (window->DC.LayoutType == ImGuiLayoutType_Vertical) // (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) + { + // Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive. + bool moving_within_opened_triangle = false; + if (g.HoveredWindow == window && g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].ParentWindow == window && !(window->Flags & ImGuiWindowFlags_MenuBar)) + { + if (ImGuiWindow* next_window = g.OpenPopupStack[g.CurrentPopupStack.Size].Window) + { + ImRect next_window_rect = next_window->Rect(); + ImVec2 ta = g.IO.MousePos - g.IO.MouseDelta; + ImVec2 tb = (window->Pos.x < next_window->Pos.x) ? next_window_rect.GetTL() : next_window_rect.GetTR(); + ImVec2 tc = (window->Pos.x < next_window->Pos.x) ? next_window_rect.GetBL() : next_window_rect.GetBR(); + float extra = ImClamp(ImFabs(ta.x - tb.x) * 0.30f, 5.0f, 30.0f); // add a bit of extra slack. + ta.x += (window->Pos.x < next_window->Pos.x) ? -0.5f : +0.5f; // to avoid numerical issues + tb.y = ta.y + ImMax((tb.y - extra) - ta.y, -100.0f); // triangle is maximum 200 high to limit the slope and the bias toward large sub-menus // FIXME: Multiply by fb_scale? + tc.y = ta.y + ImMin((tc.y + extra) - ta.y, +100.0f); + moving_within_opened_triangle = ImTriangleContainsPoint(ta, tb, tc, g.IO.MousePos); + //window->DrawList->PushClipRectFullScreen(); window->DrawList->AddTriangleFilled(ta, tb, tc, moving_within_opened_triangle ? IM_COL32(0,128,0,128) : IM_COL32(128,0,0,128)); window->DrawList->PopClipRect(); // Debug + } + } + + want_close = (menu_is_open && !hovered && g.HoveredWindow == window && g.HoveredIdPreviousFrame != 0 && g.HoveredIdPreviousFrame != id && !moving_within_opened_triangle); + want_open = (!menu_is_open && hovered && !moving_within_opened_triangle) || (!menu_is_open && hovered && pressed); + + if (g.NavActivateId == id) + { + want_close = menu_is_open; + want_open = !menu_is_open; + } + if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Right) // Nav-Right to open + { + want_open = true; + NavMoveRequestCancel(); + } + } + else + { + // Menu bar + if (menu_is_open && pressed && menuset_is_open) // Click an open menu again to close it + { + want_close = true; + want_open = menu_is_open = false; + } + else if (pressed || (hovered && menuset_is_open && !menu_is_open)) // First click to open, then hover to open others + { + want_open = true; + } + else if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Down) // Nav-Down to open + { + want_open = true; + NavMoveRequestCancel(); + } + } + + if (!enabled) // explicitly close if an open menu becomes disabled, facilitate users code a lot in pattern such as 'if (BeginMenu("options", has_object)) { ..use object.. }' + want_close = true; + if (want_close && IsPopupOpen(id)) + ClosePopupToLevel(g.CurrentPopupStack.Size); + + if (!menu_is_open && want_open && g.OpenPopupStack.Size > g.CurrentPopupStack.Size) + { + // Don't recycle same menu level in the same frame, first close the other menu and yield for a frame. + OpenPopup(label); + return false; + } + + menu_is_open |= want_open; + if (want_open) + OpenPopup(label); + + if (menu_is_open) + { + // Sub-menus are ChildWindow so that mouse can be hovering across them (otherwise top-most popup menu would steal focus and not allow hovering on parent menu) + SetNextWindowPos(popup_pos, ImGuiCond_Always); + ImGuiWindowFlags flags = ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus; + if (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) + flags |= ImGuiWindowFlags_ChildWindow; + menu_is_open = BeginPopupEx(id, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display) + } + + return menu_is_open; +} + +void ImGui::EndMenu() +{ + // Nav: When a left move request _within our child menu_ failed, close the menu. + // A menu doesn't close itself because EndMenuBar() wants the catch the last Left<>Right inputs. + // However, it means that with the current code, a BeginMenu() from outside another menu or a menu-bar won't be closable with the Left direction. + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + if (g.NavWindow && g.NavWindow->ParentWindow == window && g.NavMoveDir == ImGuiDir_Left && NavMoveRequestButNoResultYet() && window->DC.LayoutType == ImGuiLayoutType_Vertical) + { + ClosePopupToLevel(g.OpenPopupStack.Size - 1); + NavMoveRequestCancel(); + } + + EndPopup(); +} + +bool ImGui::MenuItem(const char* label, const char* shortcut, bool selected, bool enabled) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + ImGuiStyle& style = g.Style; + ImVec2 pos = window->DC.CursorPos; + ImVec2 label_size = CalcTextSize(label, NULL, true); + + ImGuiSelectableFlags flags = ImGuiSelectableFlags_PressedOnRelease | (enabled ? 0 : ImGuiSelectableFlags_Disabled); + bool pressed; + if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) + { + // Mimic the exact layout spacing of BeginMenu() to allow MenuItem() inside a menu bar, which is a little misleading but may be useful + // Note that in this situation we render neither the shortcut neither the selected tick mark + float w = label_size.x; + window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * 0.5f); + PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing * 2.0f); + pressed = Selectable(label, false, flags, ImVec2(w, 0.0f)); + PopStyleVar(); + window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar(). + } + else + { + ImVec2 shortcut_size = shortcut ? CalcTextSize(shortcut, NULL) : ImVec2(0.0f, 0.0f); + float w = window->MenuColumns.DeclColumns(label_size.x, shortcut_size.x, (float)(int)(g.FontSize * 1.20f)); // Feedback for next frame + float extra_w = ImMax(0.0f, GetContentRegionAvail().x - w); + pressed = Selectable(label, false, flags | ImGuiSelectableFlags_DrawFillAvailWidth, ImVec2(w, 0.0f)); + if (shortcut_size.x > 0.0f) + { + PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]); + RenderText(pos + ImVec2(window->MenuColumns.Pos[1] + extra_w, 0.0f), shortcut, NULL, false); + PopStyleColor(); + } + if (selected) + RenderCheckMark(pos + ImVec2(window->MenuColumns.Pos[2] + extra_w + g.FontSize * 0.40f, g.FontSize * 0.134f * 0.5f), GetColorU32(enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled), g.FontSize * 0.866f); + } + return pressed; +} + +bool ImGui::MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled) +{ + if (MenuItem(label, shortcut, p_selected ? *p_selected : false, enabled)) + { + if (p_selected) + *p_selected = !*p_selected; + return true; + } + return false; +} From 905e14f384d8c702d209a999a18fc65cfc8be850 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 30 Aug 2018 15:00:24 +0200 Subject: [PATCH 198/828] Refactor: Moved Plot/Value functions from imgui.cpp to imgui_widgets.cpp (#2036) --- imgui.cpp | 176 ---------------------------------------------- imgui_widgets.cpp | 174 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 174 insertions(+), 176 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index d6385bdc2126..465305195add 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10410,153 +10410,6 @@ bool ImGui::DragIntRange2(const char* label, int* v_current_min, int* v_current_ return value_changed; } -void ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - - const ImVec2 label_size = CalcTextSize(label, NULL, true); - if (graph_size.x == 0.0f) - graph_size.x = CalcItemWidth(); - if (graph_size.y == 0.0f) - graph_size.y = label_size.y + (style.FramePadding.y * 2); - - const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(graph_size.x, graph_size.y)); - const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding); - const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0)); - ItemSize(total_bb, style.FramePadding.y); - if (!ItemAdd(total_bb, 0, &frame_bb)) - return; - const bool hovered = ItemHoverable(inner_bb, 0); - - // Determine scale from values if not specified - if (scale_min == FLT_MAX || scale_max == FLT_MAX) - { - float v_min = FLT_MAX; - float v_max = -FLT_MAX; - for (int i = 0; i < values_count; i++) - { - const float v = values_getter(data, i); - v_min = ImMin(v_min, v); - v_max = ImMax(v_max, v); - } - if (scale_min == FLT_MAX) - scale_min = v_min; - if (scale_max == FLT_MAX) - scale_max = v_max; - } - - RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); - - if (values_count > 0) - { - int res_w = ImMin((int)graph_size.x, values_count) + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0); - int item_count = values_count + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0); - - // Tooltip on hover - int v_hovered = -1; - if (hovered) - { - const float t = ImClamp((g.IO.MousePos.x - inner_bb.Min.x) / (inner_bb.Max.x - inner_bb.Min.x), 0.0f, 0.9999f); - const int v_idx = (int)(t * item_count); - IM_ASSERT(v_idx >= 0 && v_idx < values_count); - - const float v0 = values_getter(data, (v_idx + values_offset) % values_count); - const float v1 = values_getter(data, (v_idx + 1 + values_offset) % values_count); - if (plot_type == ImGuiPlotType_Lines) - SetTooltip("%d: %8.4g\n%d: %8.4g", v_idx, v0, v_idx+1, v1); - else if (plot_type == ImGuiPlotType_Histogram) - SetTooltip("%d: %8.4g", v_idx, v0); - v_hovered = v_idx; - } - - const float t_step = 1.0f / (float)res_w; - const float inv_scale = (scale_min == scale_max) ? 0.0f : (1.0f / (scale_max - scale_min)); - - float v0 = values_getter(data, (0 + values_offset) % values_count); - float t0 = 0.0f; - ImVec2 tp0 = ImVec2( t0, 1.0f - ImSaturate((v0 - scale_min) * inv_scale) ); // Point in the normalized space of our target rectangle - float histogram_zero_line_t = (scale_min * scale_max < 0.0f) ? (-scale_min * inv_scale) : (scale_min < 0.0f ? 0.0f : 1.0f); // Where does the zero line stands - - const ImU32 col_base = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLines : ImGuiCol_PlotHistogram); - const ImU32 col_hovered = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLinesHovered : ImGuiCol_PlotHistogramHovered); - - for (int n = 0; n < res_w; n++) - { - const float t1 = t0 + t_step; - const int v1_idx = (int)(t0 * item_count + 0.5f); - IM_ASSERT(v1_idx >= 0 && v1_idx < values_count); - const float v1 = values_getter(data, (v1_idx + values_offset + 1) % values_count); - const ImVec2 tp1 = ImVec2( t1, 1.0f - ImSaturate((v1 - scale_min) * inv_scale) ); - - // NB: Draw calls are merged together by the DrawList system. Still, we should render our batch are lower level to save a bit of CPU. - ImVec2 pos0 = ImLerp(inner_bb.Min, inner_bb.Max, tp0); - ImVec2 pos1 = ImLerp(inner_bb.Min, inner_bb.Max, (plot_type == ImGuiPlotType_Lines) ? tp1 : ImVec2(tp1.x, histogram_zero_line_t)); - if (plot_type == ImGuiPlotType_Lines) - { - window->DrawList->AddLine(pos0, pos1, v_hovered == v1_idx ? col_hovered : col_base); - } - else if (plot_type == ImGuiPlotType_Histogram) - { - if (pos1.x >= pos0.x + 2.0f) - pos1.x -= 1.0f; - window->DrawList->AddRectFilled(pos0, pos1, v_hovered == v1_idx ? col_hovered : col_base); - } - - t0 = t1; - tp0 = tp1; - } - } - - // Text overlay - if (overlay_text) - RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, overlay_text, NULL, NULL, ImVec2(0.5f,0.0f)); - - if (label_size.x > 0.0f) - RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label); -} - -struct ImGuiPlotArrayGetterData -{ - const float* Values; - int Stride; - - ImGuiPlotArrayGetterData(const float* values, int stride) { Values = values; Stride = stride; } -}; - -static float Plot_ArrayGetter(void* data, int idx) -{ - ImGuiPlotArrayGetterData* plot_data = (ImGuiPlotArrayGetterData*)data; - const float v = *(const float*)(const void*)((const unsigned char*)plot_data->Values + (size_t)idx * plot_data->Stride); - return v; -} - -void ImGui::PlotLines(const char* label, const float* values, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size, int stride) -{ - ImGuiPlotArrayGetterData data(values, stride); - PlotEx(ImGuiPlotType_Lines, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size); -} - -void ImGui::PlotLines(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size) -{ - PlotEx(ImGuiPlotType_Lines, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size); -} - -void ImGui::PlotHistogram(const char* label, const float* values, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size, int stride) -{ - ImGuiPlotArrayGetterData data(values, stride); - PlotEx(ImGuiPlotType_Histogram, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size); -} - -void ImGui::PlotHistogram(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size) -{ - PlotEx(ImGuiPlotType_Histogram, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size); -} - static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end) { int line_count = 0; @@ -13229,35 +13082,6 @@ void ImGui::TreePop() PopID(); } -void ImGui::Value(const char* prefix, bool b) -{ - Text("%s: %s", prefix, (b ? "true" : "false")); -} - -void ImGui::Value(const char* prefix, int v) -{ - Text("%s: %d", prefix, v); -} - -void ImGui::Value(const char* prefix, unsigned int v) -{ - Text("%s: %d", prefix, v); -} - -void ImGui::Value(const char* prefix, float v, const char* float_format) -{ - if (float_format) - { - char fmt[64]; - ImFormatString(fmt, IM_ARRAYSIZE(fmt), "%%s: %s", float_format); - Text(fmt, prefix, v); - } - else - { - Text("%s: %.3f", prefix, v); - } -} - //----------------------------------------------------------------------------- // DRAG AND DROP //----------------------------------------------------------------------------- diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 210670032e06..48378b7ff1b9 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1363,12 +1363,186 @@ bool ImGui::ListBox(const char* label, int* current_item, bool (*items_getter)(v // - PlotHistogram() //------------------------------------------------------------------------- +void ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + + const ImVec2 label_size = CalcTextSize(label, NULL, true); + if (graph_size.x == 0.0f) + graph_size.x = CalcItemWidth(); + if (graph_size.y == 0.0f) + graph_size.y = label_size.y + (style.FramePadding.y * 2); + + const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(graph_size.x, graph_size.y)); + const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding); + const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0)); + ItemSize(total_bb, style.FramePadding.y); + if (!ItemAdd(total_bb, 0, &frame_bb)) + return; + const bool hovered = ItemHoverable(inner_bb, 0); + + // Determine scale from values if not specified + if (scale_min == FLT_MAX || scale_max == FLT_MAX) + { + float v_min = FLT_MAX; + float v_max = -FLT_MAX; + for (int i = 0; i < values_count; i++) + { + const float v = values_getter(data, i); + v_min = ImMin(v_min, v); + v_max = ImMax(v_max, v); + } + if (scale_min == FLT_MAX) + scale_min = v_min; + if (scale_max == FLT_MAX) + scale_max = v_max; + } + + RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); + + if (values_count > 0) + { + int res_w = ImMin((int)graph_size.x, values_count) + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0); + int item_count = values_count + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0); + + // Tooltip on hover + int v_hovered = -1; + if (hovered) + { + const float t = ImClamp((g.IO.MousePos.x - inner_bb.Min.x) / (inner_bb.Max.x - inner_bb.Min.x), 0.0f, 0.9999f); + const int v_idx = (int)(t * item_count); + IM_ASSERT(v_idx >= 0 && v_idx < values_count); + + const float v0 = values_getter(data, (v_idx + values_offset) % values_count); + const float v1 = values_getter(data, (v_idx + 1 + values_offset) % values_count); + if (plot_type == ImGuiPlotType_Lines) + SetTooltip("%d: %8.4g\n%d: %8.4g", v_idx, v0, v_idx+1, v1); + else if (plot_type == ImGuiPlotType_Histogram) + SetTooltip("%d: %8.4g", v_idx, v0); + v_hovered = v_idx; + } + + const float t_step = 1.0f / (float)res_w; + const float inv_scale = (scale_min == scale_max) ? 0.0f : (1.0f / (scale_max - scale_min)); + + float v0 = values_getter(data, (0 + values_offset) % values_count); + float t0 = 0.0f; + ImVec2 tp0 = ImVec2( t0, 1.0f - ImSaturate((v0 - scale_min) * inv_scale) ); // Point in the normalized space of our target rectangle + float histogram_zero_line_t = (scale_min * scale_max < 0.0f) ? (-scale_min * inv_scale) : (scale_min < 0.0f ? 0.0f : 1.0f); // Where does the zero line stands + + const ImU32 col_base = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLines : ImGuiCol_PlotHistogram); + const ImU32 col_hovered = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLinesHovered : ImGuiCol_PlotHistogramHovered); + + for (int n = 0; n < res_w; n++) + { + const float t1 = t0 + t_step; + const int v1_idx = (int)(t0 * item_count + 0.5f); + IM_ASSERT(v1_idx >= 0 && v1_idx < values_count); + const float v1 = values_getter(data, (v1_idx + values_offset + 1) % values_count); + const ImVec2 tp1 = ImVec2( t1, 1.0f - ImSaturate((v1 - scale_min) * inv_scale) ); + + // NB: Draw calls are merged together by the DrawList system. Still, we should render our batch are lower level to save a bit of CPU. + ImVec2 pos0 = ImLerp(inner_bb.Min, inner_bb.Max, tp0); + ImVec2 pos1 = ImLerp(inner_bb.Min, inner_bb.Max, (plot_type == ImGuiPlotType_Lines) ? tp1 : ImVec2(tp1.x, histogram_zero_line_t)); + if (plot_type == ImGuiPlotType_Lines) + { + window->DrawList->AddLine(pos0, pos1, v_hovered == v1_idx ? col_hovered : col_base); + } + else if (plot_type == ImGuiPlotType_Histogram) + { + if (pos1.x >= pos0.x + 2.0f) + pos1.x -= 1.0f; + window->DrawList->AddRectFilled(pos0, pos1, v_hovered == v1_idx ? col_hovered : col_base); + } + + t0 = t1; + tp0 = tp1; + } + } + + // Text overlay + if (overlay_text) + RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, overlay_text, NULL, NULL, ImVec2(0.5f,0.0f)); + + if (label_size.x > 0.0f) + RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label); +} + +struct ImGuiPlotArrayGetterData +{ + const float* Values; + int Stride; + + ImGuiPlotArrayGetterData(const float* values, int stride) { Values = values; Stride = stride; } +}; + +static float Plot_ArrayGetter(void* data, int idx) +{ + ImGuiPlotArrayGetterData* plot_data = (ImGuiPlotArrayGetterData*)data; + const float v = *(const float*)(const void*)((const unsigned char*)plot_data->Values + (size_t)idx * plot_data->Stride); + return v; +} + +void ImGui::PlotLines(const char* label, const float* values, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size, int stride) +{ + ImGuiPlotArrayGetterData data(values, stride); + PlotEx(ImGuiPlotType_Lines, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size); +} + +void ImGui::PlotLines(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size) +{ + PlotEx(ImGuiPlotType_Lines, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size); +} + +void ImGui::PlotHistogram(const char* label, const float* values, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size, int stride) +{ + ImGuiPlotArrayGetterData data(values, stride); + PlotEx(ImGuiPlotType_Histogram, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size); +} + +void ImGui::PlotHistogram(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size) +{ + PlotEx(ImGuiPlotType_Histogram, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size); +} //------------------------------------------------------------------------- // WIDGETS: Value() helpers // - Value() //------------------------------------------------------------------------- +void ImGui::Value(const char* prefix, bool b) +{ + Text("%s: %s", prefix, (b ? "true" : "false")); +} + +void ImGui::Value(const char* prefix, int v) +{ + Text("%s: %d", prefix, v); +} + +void ImGui::Value(const char* prefix, unsigned int v) +{ + Text("%s: %d", prefix, v); +} + +void ImGui::Value(const char* prefix, float v, const char* float_format) +{ + if (float_format) + { + char fmt[64]; + ImFormatString(fmt, IM_ARRAYSIZE(fmt), "%%s: %s", float_format); + Text(fmt, prefix, v); + } + else + { + Text("%s: %.3f", prefix, v); + } +} //------------------------------------------------------------------------- // WIDGETS: Menus From 6caf074bd552579c2af84655a1453ba2d138d139 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 30 Aug 2018 15:03:21 +0200 Subject: [PATCH 199/828] Refactor: Moved Tree/Selectable functions from imgui.cpp to imgui_widgets.cpp (#2036) --- imgui.cpp | 481 ---------------------------------------------- imgui_widgets.cpp | 479 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 479 insertions(+), 481 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 465305195add..62e756cb4487 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -8955,341 +8955,6 @@ void ImGui::LogButtons() LogToClipboard(g.LogAutoExpandMaxDepth); } -bool ImGui::TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags) -{ - if (flags & ImGuiTreeNodeFlags_Leaf) - return true; - - // We only write to the tree storage if the user clicks (or explicitly use SetNextTreeNode*** functions) - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - ImGuiStorage* storage = window->DC.StateStorage; - - bool is_open; - if (g.NextTreeNodeOpenCond != 0) - { - if (g.NextTreeNodeOpenCond & ImGuiCond_Always) - { - is_open = g.NextTreeNodeOpenVal; - storage->SetInt(id, is_open); - } - else - { - // We treat ImGuiCond_Once and ImGuiCond_FirstUseEver the same because tree node state are not saved persistently. - const int stored_value = storage->GetInt(id, -1); - if (stored_value == -1) - { - is_open = g.NextTreeNodeOpenVal; - storage->SetInt(id, is_open); - } - else - { - is_open = stored_value != 0; - } - } - g.NextTreeNodeOpenCond = 0; - } - else - { - is_open = storage->GetInt(id, (flags & ImGuiTreeNodeFlags_DefaultOpen) ? 1 : 0) != 0; - } - - // When logging is enabled, we automatically expand tree nodes (but *NOT* collapsing headers.. seems like sensible behavior). - // NB- If we are above max depth we still allow manually opened nodes to be logged. - if (g.LogEnabled && !(flags & ImGuiTreeNodeFlags_NoAutoOpenOnLog) && window->DC.TreeDepth < g.LogAutoExpandMaxDepth) - is_open = true; - - return is_open; -} - -bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - const bool display_frame = (flags & ImGuiTreeNodeFlags_Framed) != 0; - const ImVec2 padding = (display_frame || (flags & ImGuiTreeNodeFlags_FramePadding)) ? style.FramePadding : ImVec2(style.FramePadding.x, 0.0f); - - if (!label_end) - label_end = FindRenderedTextEnd(label); - const ImVec2 label_size = CalcTextSize(label, label_end, false); - - // We vertically grow up to current line height up the typical widget height. - const float text_base_offset_y = ImMax(padding.y, window->DC.CurrentLineTextBaseOffset); // Latch before ItemSize changes it - const float frame_height = ImMax(ImMin(window->DC.CurrentLineSize.y, g.FontSize + style.FramePadding.y*2), label_size.y + padding.y*2); - ImRect frame_bb = ImRect(window->DC.CursorPos, ImVec2(window->Pos.x + GetContentRegionMax().x, window->DC.CursorPos.y + frame_height)); - if (display_frame) - { - // Framed header expand a little outside the default padding - frame_bb.Min.x -= (float)(int)(window->WindowPadding.x*0.5f) - 1; - frame_bb.Max.x += (float)(int)(window->WindowPadding.x*0.5f) - 1; - } - - const float text_offset_x = (g.FontSize + (display_frame ? padding.x*3 : padding.x*2)); // Collapser arrow width + Spacing - const float text_width = g.FontSize + (label_size.x > 0.0f ? label_size.x + padding.x*2 : 0.0f); // Include collapser - ItemSize(ImVec2(text_width, frame_height), text_base_offset_y); - - // For regular tree nodes, we arbitrary allow to click past 2 worth of ItemSpacing - // (Ideally we'd want to add a flag for the user to specify if we want the hit test to be done up to the right side of the content or not) - const ImRect interact_bb = display_frame ? frame_bb : ImRect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + text_width + style.ItemSpacing.x*2, frame_bb.Max.y); - bool is_open = TreeNodeBehaviorIsOpen(id, flags); - - // Store a flag for the current depth to tell if we will allow closing this node when navigating one of its child. - // For this purpose we essentially compare if g.NavIdIsAlive went from 0 to 1 between TreeNode() and TreePop(). - // This is currently only support 32 level deep and we are fine with (1 << Depth) overflowing into a zero. - if (is_open && !g.NavIdIsAlive && (flags & ImGuiTreeNodeFlags_NavLeftJumpsBackHere) && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) - window->DC.TreeDepthMayJumpToParentOnPop |= (1 << window->DC.TreeDepth); - - bool item_add = ItemAdd(interact_bb, id); - window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HasDisplayRect; - window->DC.LastItemDisplayRect = frame_bb; - - if (!item_add) - { - if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) - TreePushRawID(id); - return is_open; - } - - // Flags that affects opening behavior: - // - 0(default) ..................... single-click anywhere to open - // - OpenOnDoubleClick .............. double-click anywhere to open - // - OpenOnArrow .................... single-click on arrow to open - // - OpenOnDoubleClick|OpenOnArrow .. single-click on arrow or double-click anywhere to open - ImGuiButtonFlags button_flags = ImGuiButtonFlags_NoKeyModifiers | ((flags & ImGuiTreeNodeFlags_AllowItemOverlap) ? ImGuiButtonFlags_AllowItemOverlap : 0); - if (!(flags & ImGuiTreeNodeFlags_Leaf)) - button_flags |= ImGuiButtonFlags_PressedOnDragDropHold; - if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick) - button_flags |= ImGuiButtonFlags_PressedOnDoubleClick | ((flags & ImGuiTreeNodeFlags_OpenOnArrow) ? ImGuiButtonFlags_PressedOnClickRelease : 0); - - bool hovered, held, pressed = ButtonBehavior(interact_bb, id, &hovered, &held, button_flags); - if (!(flags & ImGuiTreeNodeFlags_Leaf)) - { - bool toggled = false; - if (pressed) - { - toggled = !(flags & (ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick)) || (g.NavActivateId == id); - if (flags & ImGuiTreeNodeFlags_OpenOnArrow) - toggled |= IsMouseHoveringRect(interact_bb.Min, ImVec2(interact_bb.Min.x + text_offset_x, interact_bb.Max.y)) && (!g.NavDisableMouseHover); - if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick) - toggled |= g.IO.MouseDoubleClicked[0]; - if (g.DragDropActive && is_open) // When using Drag and Drop "hold to open" we keep the node highlighted after opening, but never close it again. - toggled = false; - } - - if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Left && is_open) - { - toggled = true; - NavMoveRequestCancel(); - } - if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Right && !is_open) // If there's something upcoming on the line we may want to give it the priority? - { - toggled = true; - NavMoveRequestCancel(); - } - - if (toggled) - { - is_open = !is_open; - window->DC.StateStorage->SetInt(id, is_open); - } - } - if (flags & ImGuiTreeNodeFlags_AllowItemOverlap) - SetItemAllowOverlap(); - - // Render - const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); - const ImVec2 text_pos = frame_bb.Min + ImVec2(text_offset_x, text_base_offset_y); - if (display_frame) - { - // Framed type - RenderFrame(frame_bb.Min, frame_bb.Max, col, true, style.FrameRounding); - RenderNavHighlight(frame_bb, id, ImGuiNavHighlightFlags_TypeThin); - RenderArrow(frame_bb.Min + ImVec2(padding.x, text_base_offset_y), is_open ? ImGuiDir_Down : ImGuiDir_Right, 1.0f); - if (g.LogEnabled) - { - // NB: '##' is normally used to hide text (as a library-wide feature), so we need to specify the text range to make sure the ## aren't stripped out here. - const char log_prefix[] = "\n##"; - const char log_suffix[] = "##"; - LogRenderedText(&text_pos, log_prefix, log_prefix+3); - RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size); - LogRenderedText(&text_pos, log_suffix+1, log_suffix+3); - } - else - { - RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size); - } - } - else - { - // Unframed typed for tree nodes - if (hovered || (flags & ImGuiTreeNodeFlags_Selected)) - { - RenderFrame(frame_bb.Min, frame_bb.Max, col, false); - RenderNavHighlight(frame_bb, id, ImGuiNavHighlightFlags_TypeThin); - } - - if (flags & ImGuiTreeNodeFlags_Bullet) - RenderBullet(frame_bb.Min + ImVec2(text_offset_x * 0.5f, g.FontSize*0.50f + text_base_offset_y)); - else if (!(flags & ImGuiTreeNodeFlags_Leaf)) - RenderArrow(frame_bb.Min + ImVec2(padding.x, g.FontSize*0.15f + text_base_offset_y), is_open ? ImGuiDir_Down : ImGuiDir_Right, 0.70f); - if (g.LogEnabled) - LogRenderedText(&text_pos, ">"); - RenderText(text_pos, label, label_end, false); - } - - if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) - TreePushRawID(id); - return is_open; -} - -// CollapsingHeader returns true when opened but do not indent nor push into the ID stack (because of the ImGuiTreeNodeFlags_NoTreePushOnOpen flag). -// This is basically the same as calling TreeNodeEx(label, ImGuiTreeNodeFlags_CollapsingHeader). You can remove the _NoTreePushOnOpen flag if you want behavior closer to normal TreeNode(). -bool ImGui::CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - return TreeNodeBehavior(window->GetID(label), flags | ImGuiTreeNodeFlags_CollapsingHeader, label); -} - -bool ImGui::CollapsingHeader(const char* label, bool* p_open, ImGuiTreeNodeFlags flags) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - if (p_open && !*p_open) - return false; - - ImGuiID id = window->GetID(label); - bool is_open = TreeNodeBehavior(id, flags | ImGuiTreeNodeFlags_CollapsingHeader | (p_open ? ImGuiTreeNodeFlags_AllowItemOverlap : 0), label); - if (p_open) - { - // Create a small overlapping close button // FIXME: We can evolve this into user accessible helpers to add extra buttons on title bars, headers, etc. - ImGuiContext& g = *GImGui; - ImGuiItemHoveredDataBackup last_item_backup; - float button_radius = g.FontSize * 0.5f; - ImVec2 button_center = ImVec2(ImMin(window->DC.LastItemRect.Max.x, window->ClipRect.Max.x) - g.Style.FramePadding.x - button_radius, window->DC.LastItemRect.GetCenter().y); - if (CloseButton(window->GetID((void*)(intptr_t)(id+1)), button_center, button_radius)) - *p_open = false; - last_item_backup.Restore(); - } - - return is_open; -} - -bool ImGui::TreeNodeEx(const char* label, ImGuiTreeNodeFlags flags) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - return TreeNodeBehavior(window->GetID(label), flags, label, NULL); -} - -bool ImGui::TreeNodeExV(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const char* label_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); - return TreeNodeBehavior(window->GetID(str_id), flags, g.TempBuffer, label_end); -} - -bool ImGui::TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const char* label_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); - return TreeNodeBehavior(window->GetID(ptr_id), flags, g.TempBuffer, label_end); -} - -bool ImGui::TreeNodeV(const char* str_id, const char* fmt, va_list args) -{ - return TreeNodeExV(str_id, 0, fmt, args); -} - -bool ImGui::TreeNodeV(const void* ptr_id, const char* fmt, va_list args) -{ - return TreeNodeExV(ptr_id, 0, fmt, args); -} - -bool ImGui::TreeNodeEx(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - bool is_open = TreeNodeExV(str_id, flags, fmt, args); - va_end(args); - return is_open; -} - -bool ImGui::TreeNodeEx(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - bool is_open = TreeNodeExV(ptr_id, flags, fmt, args); - va_end(args); - return is_open; -} - -bool ImGui::TreeNode(const char* str_id, const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - bool is_open = TreeNodeExV(str_id, 0, fmt, args); - va_end(args); - return is_open; -} - -bool ImGui::TreeNode(const void* ptr_id, const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - bool is_open = TreeNodeExV(ptr_id, 0, fmt, args); - va_end(args); - return is_open; -} - -bool ImGui::TreeNode(const char* label) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - return TreeNodeBehavior(window->GetID(label), 0, label, NULL); -} - -void ImGui::TreeAdvanceToLabelPos() -{ - ImGuiContext& g = *GImGui; - g.CurrentWindow->DC.CursorPos.x += GetTreeNodeToLabelSpacing(); -} - -// Horizontal distance preceding label when using TreeNode() or Bullet() -float ImGui::GetTreeNodeToLabelSpacing() -{ - ImGuiContext& g = *GImGui; - return g.FontSize + (g.Style.FramePadding.x * 2.0f); -} - -void ImGui::SetNextTreeNodeOpen(bool is_open, ImGuiCond cond) -{ - ImGuiContext& g = *GImGui; - if (g.CurrentWindow->SkipItems) - return; - g.NextTreeNodeOpenVal = is_open; - g.NextTreeNodeOpenCond = cond ? cond : ImGuiCond_Always; -} - void ImGui::PushID(const char* str_id) { ImGuiWindow* window = GetCurrentWindowRead(); @@ -11553,109 +11218,6 @@ bool ImGui::InputInt4(const char* label, int v[4], ImGuiInputTextFlags extra_fla return InputScalarN(label, ImGuiDataType_S32, v, 4, NULL, NULL, "%d", extra_flags); } -// Tip: pass an empty label (e.g. "##dummy") then you can use the space to draw other text or image. -// But you need to make sure the ID is unique, e.g. enclose calls in PushID/PopID or use ##unique_id. -bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags flags, const ImVec2& size_arg) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - - if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsSet) // FIXME-OPT: Avoid if vertically clipped. - PopClipRect(); - - ImGuiID id = window->GetID(label); - ImVec2 label_size = CalcTextSize(label, NULL, true); - ImVec2 size(size_arg.x != 0.0f ? size_arg.x : label_size.x, size_arg.y != 0.0f ? size_arg.y : label_size.y); - ImVec2 pos = window->DC.CursorPos; - pos.y += window->DC.CurrentLineTextBaseOffset; - ImRect bb_inner(pos, pos + size); - ItemSize(bb_inner); - - // Fill horizontal space. - ImVec2 window_padding = window->WindowPadding; - float max_x = (flags & ImGuiSelectableFlags_SpanAllColumns) ? GetWindowContentRegionMax().x : GetContentRegionMax().x; - float w_draw = ImMax(label_size.x, window->Pos.x + max_x - window_padding.x - window->DC.CursorPos.x); - ImVec2 size_draw((size_arg.x != 0 && !(flags & ImGuiSelectableFlags_DrawFillAvailWidth)) ? size_arg.x : w_draw, size_arg.y != 0.0f ? size_arg.y : size.y); - ImRect bb(pos, pos + size_draw); - if (size_arg.x == 0.0f || (flags & ImGuiSelectableFlags_DrawFillAvailWidth)) - bb.Max.x += window_padding.x; - - // Selectables are tightly packed together, we extend the box to cover spacing between selectable. - float spacing_L = (float)(int)(style.ItemSpacing.x * 0.5f); - float spacing_U = (float)(int)(style.ItemSpacing.y * 0.5f); - float spacing_R = style.ItemSpacing.x - spacing_L; - float spacing_D = style.ItemSpacing.y - spacing_U; - bb.Min.x -= spacing_L; - bb.Min.y -= spacing_U; - bb.Max.x += spacing_R; - bb.Max.y += spacing_D; - if (!ItemAdd(bb, (flags & ImGuiSelectableFlags_Disabled) ? 0 : id)) - { - if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsSet) - PushColumnClipRect(); - return false; - } - - // We use NoHoldingActiveID on menus so user can click and _hold_ on a menu then drag to browse child entries - ImGuiButtonFlags button_flags = 0; - if (flags & ImGuiSelectableFlags_NoHoldingActiveID) button_flags |= ImGuiButtonFlags_NoHoldingActiveID; - if (flags & ImGuiSelectableFlags_PressedOnClick) button_flags |= ImGuiButtonFlags_PressedOnClick; - if (flags & ImGuiSelectableFlags_PressedOnRelease) button_flags |= ImGuiButtonFlags_PressedOnRelease; - if (flags & ImGuiSelectableFlags_Disabled) button_flags |= ImGuiButtonFlags_Disabled; - if (flags & ImGuiSelectableFlags_AllowDoubleClick) button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick; - bool hovered, held; - bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags); - if (flags & ImGuiSelectableFlags_Disabled) - selected = false; - - // Hovering selectable with mouse updates NavId accordingly so navigation can be resumed with gamepad/keyboard (this doesn't happen on most widgets) - if (pressed || hovered) - if (!g.NavDisableMouseHover && g.NavWindow == window && g.NavLayer == window->DC.NavLayerCurrent) - { - g.NavDisableHighlight = true; - SetNavID(id, window->DC.NavLayerCurrent); - } - if (pressed) - MarkItemEdited(id); - - // Render - if (hovered || selected) - { - const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); - RenderFrame(bb.Min, bb.Max, col, false, 0.0f); - RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding); - } - - if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsSet) - { - PushColumnClipRect(); - bb.Max.x -= (GetContentRegionMax().x - max_x); - } - - if (flags & ImGuiSelectableFlags_Disabled) PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]); - RenderTextClipped(bb_inner.Min, bb.Max, label, NULL, &label_size, ImVec2(0.0f,0.0f)); - if (flags & ImGuiSelectableFlags_Disabled) PopStyleColor(); - - // Automatically close popups - if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_DontClosePopups) && !(window->DC.ItemFlags & ImGuiItemFlags_SelectableDontClosePopup)) - CloseCurrentPopup(); - return pressed; -} - -bool ImGui::Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags, const ImVec2& size_arg) -{ - if (Selectable(label, *p_selected, flags, size_arg)) - { - *p_selected = !*p_selected; - return true; - } - return false; -} - // Note: only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set. void ImGui::ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags flags) { @@ -13039,49 +12601,6 @@ void ImGui::Unindent(float indent_w) window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x; } -void ImGui::TreePush(const char* str_id) -{ - ImGuiWindow* window = GetCurrentWindow(); - Indent(); - window->DC.TreeDepth++; - PushID(str_id ? str_id : "#TreePush"); -} - -void ImGui::TreePush(const void* ptr_id) -{ - ImGuiWindow* window = GetCurrentWindow(); - Indent(); - window->DC.TreeDepth++; - PushID(ptr_id ? ptr_id : (const void*)"#TreePush"); -} - -void ImGui::TreePushRawID(ImGuiID id) -{ - ImGuiWindow* window = GetCurrentWindow(); - Indent(); - window->DC.TreeDepth++; - window->IDStack.push_back(id); -} - -void ImGui::TreePop() -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - Unindent(); - - window->DC.TreeDepth--; - if (g.NavMoveDir == ImGuiDir_Left && g.NavWindow == window && NavMoveRequestButNoResultYet()) - if (g.NavIdIsAlive && (window->DC.TreeDepthMayJumpToParentOnPop & (1 << window->DC.TreeDepth))) - { - SetNavID(window->IDStack.back(), g.NavLayer); - NavMoveRequestCancel(); - } - window->DC.TreeDepthMayJumpToParentOnPop &= (1 << window->DC.TreeDepth) - 1; - - IM_ASSERT(window->IDStack.Size > 1); // There should always be 1 element in the IDStack (pushed during window creation). If this triggers you called TreePop/PopID too much. - PopID(); -} - //----------------------------------------------------------------------------- // DRAG AND DROP //----------------------------------------------------------------------------- diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 48378b7ff1b9..2eee942559cb 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1240,12 +1240,491 @@ bool ImGui::Combo(const char* label, int* current_item, const char* items_separa // - CollapsingHeader() //------------------------------------------------------------------------- +bool ImGui::TreeNode(const char* str_id, const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + bool is_open = TreeNodeExV(str_id, 0, fmt, args); + va_end(args); + return is_open; +} + +bool ImGui::TreeNode(const void* ptr_id, const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + bool is_open = TreeNodeExV(ptr_id, 0, fmt, args); + va_end(args); + return is_open; +} + +bool ImGui::TreeNode(const char* label) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + return TreeNodeBehavior(window->GetID(label), 0, label, NULL); +} + +bool ImGui::TreeNodeV(const char* str_id, const char* fmt, va_list args) +{ + return TreeNodeExV(str_id, 0, fmt, args); +} + +bool ImGui::TreeNodeV(const void* ptr_id, const char* fmt, va_list args) +{ + return TreeNodeExV(ptr_id, 0, fmt, args); +} + +bool ImGui::TreeNodeEx(const char* label, ImGuiTreeNodeFlags flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + return TreeNodeBehavior(window->GetID(label), flags, label, NULL); +} + +bool ImGui::TreeNodeEx(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + bool is_open = TreeNodeExV(str_id, flags, fmt, args); + va_end(args); + return is_open; +} + +bool ImGui::TreeNodeEx(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + bool is_open = TreeNodeExV(ptr_id, flags, fmt, args); + va_end(args); + return is_open; +} + +bool ImGui::TreeNodeExV(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const char* label_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); + return TreeNodeBehavior(window->GetID(str_id), flags, g.TempBuffer, label_end); +} + +bool ImGui::TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const char* label_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); + return TreeNodeBehavior(window->GetID(ptr_id), flags, g.TempBuffer, label_end); +} + +bool ImGui::TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags) +{ + if (flags & ImGuiTreeNodeFlags_Leaf) + return true; + + // We only write to the tree storage if the user clicks (or explicitly use SetNextTreeNode*** functions) + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + ImGuiStorage* storage = window->DC.StateStorage; + + bool is_open; + if (g.NextTreeNodeOpenCond != 0) + { + if (g.NextTreeNodeOpenCond & ImGuiCond_Always) + { + is_open = g.NextTreeNodeOpenVal; + storage->SetInt(id, is_open); + } + else + { + // We treat ImGuiCond_Once and ImGuiCond_FirstUseEver the same because tree node state are not saved persistently. + const int stored_value = storage->GetInt(id, -1); + if (stored_value == -1) + { + is_open = g.NextTreeNodeOpenVal; + storage->SetInt(id, is_open); + } + else + { + is_open = stored_value != 0; + } + } + g.NextTreeNodeOpenCond = 0; + } + else + { + is_open = storage->GetInt(id, (flags & ImGuiTreeNodeFlags_DefaultOpen) ? 1 : 0) != 0; + } + + // When logging is enabled, we automatically expand tree nodes (but *NOT* collapsing headers.. seems like sensible behavior). + // NB- If we are above max depth we still allow manually opened nodes to be logged. + if (g.LogEnabled && !(flags & ImGuiTreeNodeFlags_NoAutoOpenOnLog) && window->DC.TreeDepth < g.LogAutoExpandMaxDepth) + is_open = true; + + return is_open; +} + +bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const bool display_frame = (flags & ImGuiTreeNodeFlags_Framed) != 0; + const ImVec2 padding = (display_frame || (flags & ImGuiTreeNodeFlags_FramePadding)) ? style.FramePadding : ImVec2(style.FramePadding.x, 0.0f); + + if (!label_end) + label_end = FindRenderedTextEnd(label); + const ImVec2 label_size = CalcTextSize(label, label_end, false); + + // We vertically grow up to current line height up the typical widget height. + const float text_base_offset_y = ImMax(padding.y, window->DC.CurrentLineTextBaseOffset); // Latch before ItemSize changes it + const float frame_height = ImMax(ImMin(window->DC.CurrentLineSize.y, g.FontSize + style.FramePadding.y*2), label_size.y + padding.y*2); + ImRect frame_bb = ImRect(window->DC.CursorPos, ImVec2(window->Pos.x + GetContentRegionMax().x, window->DC.CursorPos.y + frame_height)); + if (display_frame) + { + // Framed header expand a little outside the default padding + frame_bb.Min.x -= (float)(int)(window->WindowPadding.x*0.5f) - 1; + frame_bb.Max.x += (float)(int)(window->WindowPadding.x*0.5f) - 1; + } + + const float text_offset_x = (g.FontSize + (display_frame ? padding.x*3 : padding.x*2)); // Collapser arrow width + Spacing + const float text_width = g.FontSize + (label_size.x > 0.0f ? label_size.x + padding.x*2 : 0.0f); // Include collapser + ItemSize(ImVec2(text_width, frame_height), text_base_offset_y); + + // For regular tree nodes, we arbitrary allow to click past 2 worth of ItemSpacing + // (Ideally we'd want to add a flag for the user to specify if we want the hit test to be done up to the right side of the content or not) + const ImRect interact_bb = display_frame ? frame_bb : ImRect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + text_width + style.ItemSpacing.x*2, frame_bb.Max.y); + bool is_open = TreeNodeBehaviorIsOpen(id, flags); + + // Store a flag for the current depth to tell if we will allow closing this node when navigating one of its child. + // For this purpose we essentially compare if g.NavIdIsAlive went from 0 to 1 between TreeNode() and TreePop(). + // This is currently only support 32 level deep and we are fine with (1 << Depth) overflowing into a zero. + if (is_open && !g.NavIdIsAlive && (flags & ImGuiTreeNodeFlags_NavLeftJumpsBackHere) && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) + window->DC.TreeDepthMayJumpToParentOnPop |= (1 << window->DC.TreeDepth); + + bool item_add = ItemAdd(interact_bb, id); + window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HasDisplayRect; + window->DC.LastItemDisplayRect = frame_bb; + + if (!item_add) + { + if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) + TreePushRawID(id); + return is_open; + } + + // Flags that affects opening behavior: + // - 0(default) ..................... single-click anywhere to open + // - OpenOnDoubleClick .............. double-click anywhere to open + // - OpenOnArrow .................... single-click on arrow to open + // - OpenOnDoubleClick|OpenOnArrow .. single-click on arrow or double-click anywhere to open + ImGuiButtonFlags button_flags = ImGuiButtonFlags_NoKeyModifiers | ((flags & ImGuiTreeNodeFlags_AllowItemOverlap) ? ImGuiButtonFlags_AllowItemOverlap : 0); + if (!(flags & ImGuiTreeNodeFlags_Leaf)) + button_flags |= ImGuiButtonFlags_PressedOnDragDropHold; + if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick) + button_flags |= ImGuiButtonFlags_PressedOnDoubleClick | ((flags & ImGuiTreeNodeFlags_OpenOnArrow) ? ImGuiButtonFlags_PressedOnClickRelease : 0); + + bool hovered, held, pressed = ButtonBehavior(interact_bb, id, &hovered, &held, button_flags); + if (!(flags & ImGuiTreeNodeFlags_Leaf)) + { + bool toggled = false; + if (pressed) + { + toggled = !(flags & (ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick)) || (g.NavActivateId == id); + if (flags & ImGuiTreeNodeFlags_OpenOnArrow) + toggled |= IsMouseHoveringRect(interact_bb.Min, ImVec2(interact_bb.Min.x + text_offset_x, interact_bb.Max.y)) && (!g.NavDisableMouseHover); + if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick) + toggled |= g.IO.MouseDoubleClicked[0]; + if (g.DragDropActive && is_open) // When using Drag and Drop "hold to open" we keep the node highlighted after opening, but never close it again. + toggled = false; + } + + if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Left && is_open) + { + toggled = true; + NavMoveRequestCancel(); + } + if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Right && !is_open) // If there's something upcoming on the line we may want to give it the priority? + { + toggled = true; + NavMoveRequestCancel(); + } + + if (toggled) + { + is_open = !is_open; + window->DC.StateStorage->SetInt(id, is_open); + } + } + if (flags & ImGuiTreeNodeFlags_AllowItemOverlap) + SetItemAllowOverlap(); + + // Render + const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); + const ImVec2 text_pos = frame_bb.Min + ImVec2(text_offset_x, text_base_offset_y); + if (display_frame) + { + // Framed type + RenderFrame(frame_bb.Min, frame_bb.Max, col, true, style.FrameRounding); + RenderNavHighlight(frame_bb, id, ImGuiNavHighlightFlags_TypeThin); + RenderArrow(frame_bb.Min + ImVec2(padding.x, text_base_offset_y), is_open ? ImGuiDir_Down : ImGuiDir_Right, 1.0f); + if (g.LogEnabled) + { + // NB: '##' is normally used to hide text (as a library-wide feature), so we need to specify the text range to make sure the ## aren't stripped out here. + const char log_prefix[] = "\n##"; + const char log_suffix[] = "##"; + LogRenderedText(&text_pos, log_prefix, log_prefix+3); + RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size); + LogRenderedText(&text_pos, log_suffix+1, log_suffix+3); + } + else + { + RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size); + } + } + else + { + // Unframed typed for tree nodes + if (hovered || (flags & ImGuiTreeNodeFlags_Selected)) + { + RenderFrame(frame_bb.Min, frame_bb.Max, col, false); + RenderNavHighlight(frame_bb, id, ImGuiNavHighlightFlags_TypeThin); + } + + if (flags & ImGuiTreeNodeFlags_Bullet) + RenderBullet(frame_bb.Min + ImVec2(text_offset_x * 0.5f, g.FontSize*0.50f + text_base_offset_y)); + else if (!(flags & ImGuiTreeNodeFlags_Leaf)) + RenderArrow(frame_bb.Min + ImVec2(padding.x, g.FontSize*0.15f + text_base_offset_y), is_open ? ImGuiDir_Down : ImGuiDir_Right, 0.70f); + if (g.LogEnabled) + LogRenderedText(&text_pos, ">"); + RenderText(text_pos, label, label_end, false); + } + + if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) + TreePushRawID(id); + return is_open; +} + +void ImGui::TreePush(const char* str_id) +{ + ImGuiWindow* window = GetCurrentWindow(); + Indent(); + window->DC.TreeDepth++; + PushID(str_id ? str_id : "#TreePush"); +} + +void ImGui::TreePush(const void* ptr_id) +{ + ImGuiWindow* window = GetCurrentWindow(); + Indent(); + window->DC.TreeDepth++; + PushID(ptr_id ? ptr_id : (const void*)"#TreePush"); +} + +void ImGui::TreePushRawID(ImGuiID id) +{ + ImGuiWindow* window = GetCurrentWindow(); + Indent(); + window->DC.TreeDepth++; + window->IDStack.push_back(id); +} + +void ImGui::TreePop() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + Unindent(); + + window->DC.TreeDepth--; + if (g.NavMoveDir == ImGuiDir_Left && g.NavWindow == window && NavMoveRequestButNoResultYet()) + if (g.NavIdIsAlive && (window->DC.TreeDepthMayJumpToParentOnPop & (1 << window->DC.TreeDepth))) + { + SetNavID(window->IDStack.back(), g.NavLayer); + NavMoveRequestCancel(); + } + window->DC.TreeDepthMayJumpToParentOnPop &= (1 << window->DC.TreeDepth) - 1; + + IM_ASSERT(window->IDStack.Size > 1); // There should always be 1 element in the IDStack (pushed during window creation). If this triggers you called TreePop/PopID too much. + PopID(); +} + +void ImGui::TreeAdvanceToLabelPos() +{ + ImGuiContext& g = *GImGui; + g.CurrentWindow->DC.CursorPos.x += GetTreeNodeToLabelSpacing(); +} + +// Horizontal distance preceding label when using TreeNode() or Bullet() +float ImGui::GetTreeNodeToLabelSpacing() +{ + ImGuiContext& g = *GImGui; + return g.FontSize + (g.Style.FramePadding.x * 2.0f); +} + +void ImGui::SetNextTreeNodeOpen(bool is_open, ImGuiCond cond) +{ + ImGuiContext& g = *GImGui; + if (g.CurrentWindow->SkipItems) + return; + g.NextTreeNodeOpenVal = is_open; + g.NextTreeNodeOpenCond = cond ? cond : ImGuiCond_Always; +} + +// CollapsingHeader returns true when opened but do not indent nor push into the ID stack (because of the ImGuiTreeNodeFlags_NoTreePushOnOpen flag). +// This is basically the same as calling TreeNodeEx(label, ImGuiTreeNodeFlags_CollapsingHeader). You can remove the _NoTreePushOnOpen flag if you want behavior closer to normal TreeNode(). +bool ImGui::CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + return TreeNodeBehavior(window->GetID(label), flags | ImGuiTreeNodeFlags_CollapsingHeader, label); +} + +bool ImGui::CollapsingHeader(const char* label, bool* p_open, ImGuiTreeNodeFlags flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + if (p_open && !*p_open) + return false; + + ImGuiID id = window->GetID(label); + bool is_open = TreeNodeBehavior(id, flags | ImGuiTreeNodeFlags_CollapsingHeader | (p_open ? ImGuiTreeNodeFlags_AllowItemOverlap : 0), label); + if (p_open) + { + // Create a small overlapping close button // FIXME: We can evolve this into user accessible helpers to add extra buttons on title bars, headers, etc. + ImGuiContext& g = *GImGui; + ImGuiItemHoveredDataBackup last_item_backup; + float button_radius = g.FontSize * 0.5f; + ImVec2 button_center = ImVec2(ImMin(window->DC.LastItemRect.Max.x, window->ClipRect.Max.x) - g.Style.FramePadding.x - button_radius, window->DC.LastItemRect.GetCenter().y); + if (CloseButton(window->GetID((void*)(intptr_t)(id+1)), button_center, button_radius)) + *p_open = false; + last_item_backup.Restore(); + } + + return is_open; +} //------------------------------------------------------------------------- // WIDGETS: Selectables // - Selectable() //------------------------------------------------------------------------- +// Tip: pass an empty label (e.g. "##dummy") then you can use the space to draw other text or image. +// But you need to make sure the ID is unique, e.g. enclose calls in PushID/PopID or use ##unique_id. +bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags flags, const ImVec2& size_arg) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + + if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsSet) // FIXME-OPT: Avoid if vertically clipped. + PopClipRect(); + + ImGuiID id = window->GetID(label); + ImVec2 label_size = CalcTextSize(label, NULL, true); + ImVec2 size(size_arg.x != 0.0f ? size_arg.x : label_size.x, size_arg.y != 0.0f ? size_arg.y : label_size.y); + ImVec2 pos = window->DC.CursorPos; + pos.y += window->DC.CurrentLineTextBaseOffset; + ImRect bb_inner(pos, pos + size); + ItemSize(bb_inner); + + // Fill horizontal space. + ImVec2 window_padding = window->WindowPadding; + float max_x = (flags & ImGuiSelectableFlags_SpanAllColumns) ? GetWindowContentRegionMax().x : GetContentRegionMax().x; + float w_draw = ImMax(label_size.x, window->Pos.x + max_x - window_padding.x - window->DC.CursorPos.x); + ImVec2 size_draw((size_arg.x != 0 && !(flags & ImGuiSelectableFlags_DrawFillAvailWidth)) ? size_arg.x : w_draw, size_arg.y != 0.0f ? size_arg.y : size.y); + ImRect bb(pos, pos + size_draw); + if (size_arg.x == 0.0f || (flags & ImGuiSelectableFlags_DrawFillAvailWidth)) + bb.Max.x += window_padding.x; + + // Selectables are tightly packed together, we extend the box to cover spacing between selectable. + float spacing_L = (float)(int)(style.ItemSpacing.x * 0.5f); + float spacing_U = (float)(int)(style.ItemSpacing.y * 0.5f); + float spacing_R = style.ItemSpacing.x - spacing_L; + float spacing_D = style.ItemSpacing.y - spacing_U; + bb.Min.x -= spacing_L; + bb.Min.y -= spacing_U; + bb.Max.x += spacing_R; + bb.Max.y += spacing_D; + if (!ItemAdd(bb, (flags & ImGuiSelectableFlags_Disabled) ? 0 : id)) + { + if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsSet) + PushColumnClipRect(); + return false; + } + + // We use NoHoldingActiveID on menus so user can click and _hold_ on a menu then drag to browse child entries + ImGuiButtonFlags button_flags = 0; + if (flags & ImGuiSelectableFlags_NoHoldingActiveID) button_flags |= ImGuiButtonFlags_NoHoldingActiveID; + if (flags & ImGuiSelectableFlags_PressedOnClick) button_flags |= ImGuiButtonFlags_PressedOnClick; + if (flags & ImGuiSelectableFlags_PressedOnRelease) button_flags |= ImGuiButtonFlags_PressedOnRelease; + if (flags & ImGuiSelectableFlags_Disabled) button_flags |= ImGuiButtonFlags_Disabled; + if (flags & ImGuiSelectableFlags_AllowDoubleClick) button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick; + bool hovered, held; + bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags); + if (flags & ImGuiSelectableFlags_Disabled) + selected = false; + + // Hovering selectable with mouse updates NavId accordingly so navigation can be resumed with gamepad/keyboard (this doesn't happen on most widgets) + if (pressed || hovered) + if (!g.NavDisableMouseHover && g.NavWindow == window && g.NavLayer == window->DC.NavLayerCurrent) + { + g.NavDisableHighlight = true; + SetNavID(id, window->DC.NavLayerCurrent); + } + if (pressed) + MarkItemEdited(id); + + // Render + if (hovered || selected) + { + const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); + RenderFrame(bb.Min, bb.Max, col, false, 0.0f); + RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding); + } + + if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsSet) + { + PushColumnClipRect(); + bb.Max.x -= (GetContentRegionMax().x - max_x); + } + + if (flags & ImGuiSelectableFlags_Disabled) PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]); + RenderTextClipped(bb_inner.Min, bb.Max, label, NULL, &label_size, ImVec2(0.0f,0.0f)); + if (flags & ImGuiSelectableFlags_Disabled) PopStyleColor(); + + // Automatically close popups + if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_DontClosePopups) && !(window->DC.ItemFlags & ImGuiItemFlags_SelectableDontClosePopup)) + CloseCurrentPopup(); + return pressed; +} + +bool ImGui::Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags, const ImVec2& size_arg) +{ + if (Selectable(label, *p_selected, flags, size_arg)) + { + *p_selected = !*p_selected; + return true; + } + return false; +} //------------------------------------------------------------------------- // WIDGETS: List Box From 158a65c98f4005c05fe43f099415ed26389c7982 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 30 Aug 2018 15:06:05 +0200 Subject: [PATCH 200/828] Refactor: Moved ColorEdit/ColorPicker/ColorButton/etc. functions from imgui.cpp to imgui_widgets.cpp (#2036) --- imgui.cpp | 784 --------------------------------------------- imgui_widgets.cpp | 785 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 785 insertions(+), 784 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 62e756cb4487..3b5e4555be7a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11218,790 +11218,6 @@ bool ImGui::InputInt4(const char* label, int v[4], ImGuiInputTextFlags extra_fla return InputScalarN(label, ImGuiDataType_S32, v, 4, NULL, NULL, "%d", extra_flags); } -// Note: only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set. -void ImGui::ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags flags) -{ - ImGuiContext& g = *GImGui; - - int cr = IM_F32_TO_INT8_SAT(col[0]), cg = IM_F32_TO_INT8_SAT(col[1]), cb = IM_F32_TO_INT8_SAT(col[2]), ca = (flags & ImGuiColorEditFlags_NoAlpha) ? 255 : IM_F32_TO_INT8_SAT(col[3]); - BeginTooltipEx(0, true); - - const char* text_end = text ? FindRenderedTextEnd(text, NULL) : text; - if (text_end > text) - { - TextUnformatted(text, text_end); - Separator(); - } - - ImVec2 sz(g.FontSize * 3 + g.Style.FramePadding.y * 2, g.FontSize * 3 + g.Style.FramePadding.y * 2); - ColorButton("##preview", ImVec4(col[0], col[1], col[2], col[3]), (flags & (ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)) | ImGuiColorEditFlags_NoTooltip, sz); - SameLine(); - if (flags & ImGuiColorEditFlags_NoAlpha) - Text("#%02X%02X%02X\nR: %d, G: %d, B: %d\n(%.3f, %.3f, %.3f)", cr, cg, cb, cr, cg, cb, col[0], col[1], col[2]); - else - Text("#%02X%02X%02X%02X\nR:%d, G:%d, B:%d, A:%d\n(%.3f, %.3f, %.3f, %.3f)", cr, cg, cb, ca, cr, cg, cb, ca, col[0], col[1], col[2], col[3]); - EndTooltip(); -} - -static inline ImU32 ImAlphaBlendColor(ImU32 col_a, ImU32 col_b) -{ - float t = ((col_b >> IM_COL32_A_SHIFT) & 0xFF) / 255.f; - int r = ImLerp((int)(col_a >> IM_COL32_R_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_R_SHIFT) & 0xFF, t); - int g = ImLerp((int)(col_a >> IM_COL32_G_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_G_SHIFT) & 0xFF, t); - int b = ImLerp((int)(col_a >> IM_COL32_B_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_B_SHIFT) & 0xFF, t); - return IM_COL32(r, g, b, 0xFF); -} - -// NB: This is rather brittle and will show artifact when rounding this enabled if rounded corners overlap multiple cells. Caller currently responsible for avoiding that. -// I spent a non reasonable amount of time trying to getting this right for ColorButton with rounding+anti-aliasing+ImGuiColorEditFlags_HalfAlphaPreview flag + various grid sizes and offsets, and eventually gave up... probably more reasonable to disable rounding alltogether. -void ImGui::RenderColorRectWithAlphaCheckerboard(ImVec2 p_min, ImVec2 p_max, ImU32 col, float grid_step, ImVec2 grid_off, float rounding, int rounding_corners_flags) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (((col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT) < 0xFF) - { - ImU32 col_bg1 = GetColorU32(ImAlphaBlendColor(IM_COL32(204,204,204,255), col)); - ImU32 col_bg2 = GetColorU32(ImAlphaBlendColor(IM_COL32(128,128,128,255), col)); - window->DrawList->AddRectFilled(p_min, p_max, col_bg1, rounding, rounding_corners_flags); - - int yi = 0; - for (float y = p_min.y + grid_off.y; y < p_max.y; y += grid_step, yi++) - { - float y1 = ImClamp(y, p_min.y, p_max.y), y2 = ImMin(y + grid_step, p_max.y); - if (y2 <= y1) - continue; - for (float x = p_min.x + grid_off.x + (yi & 1) * grid_step; x < p_max.x; x += grid_step * 2.0f) - { - float x1 = ImClamp(x, p_min.x, p_max.x), x2 = ImMin(x + grid_step, p_max.x); - if (x2 <= x1) - continue; - int rounding_corners_flags_cell = 0; - if (y1 <= p_min.y) { if (x1 <= p_min.x) rounding_corners_flags_cell |= ImDrawCornerFlags_TopLeft; if (x2 >= p_max.x) rounding_corners_flags_cell |= ImDrawCornerFlags_TopRight; } - if (y2 >= p_max.y) { if (x1 <= p_min.x) rounding_corners_flags_cell |= ImDrawCornerFlags_BotLeft; if (x2 >= p_max.x) rounding_corners_flags_cell |= ImDrawCornerFlags_BotRight; } - rounding_corners_flags_cell &= rounding_corners_flags; - window->DrawList->AddRectFilled(ImVec2(x1,y1), ImVec2(x2,y2), col_bg2, rounding_corners_flags_cell ? rounding : 0.0f, rounding_corners_flags_cell); - } - } - } - else - { - window->DrawList->AddRectFilled(p_min, p_max, col, rounding, rounding_corners_flags); - } -} - -void ImGui::SetColorEditOptions(ImGuiColorEditFlags flags) -{ - ImGuiContext& g = *GImGui; - if ((flags & ImGuiColorEditFlags__InputsMask) == 0) - flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__InputsMask; - if ((flags & ImGuiColorEditFlags__DataTypeMask) == 0) - flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__DataTypeMask; - if ((flags & ImGuiColorEditFlags__PickerMask) == 0) - flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__PickerMask; - IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__InputsMask))); // Check only 1 option is selected - IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__DataTypeMask))); // Check only 1 option is selected - IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__PickerMask))); // Check only 1 option is selected - g.ColorEditOptions = flags; -} - -// A little colored square. Return true when clicked. -// FIXME: May want to display/ignore the alpha component in the color display? Yet show it in the tooltip. -// 'desc_id' is not called 'label' because we don't display it next to the button, but only in the tooltip. -bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFlags flags, ImVec2 size) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const ImGuiID id = window->GetID(desc_id); - float default_size = GetFrameHeight(); - if (size.x == 0.0f) - size.x = default_size; - if (size.y == 0.0f) - size.y = default_size; - const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); - ItemSize(bb, (size.y >= default_size) ? g.Style.FramePadding.y : 0.0f); - if (!ItemAdd(bb, id)) - return false; - - bool hovered, held; - bool pressed = ButtonBehavior(bb, id, &hovered, &held); - - if (flags & ImGuiColorEditFlags_NoAlpha) - flags &= ~(ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf); - - ImVec4 col_without_alpha(col.x, col.y, col.z, 1.0f); - float grid_step = ImMin(size.x, size.y) / 2.99f; - float rounding = ImMin(g.Style.FrameRounding, grid_step * 0.5f); - ImRect bb_inner = bb; - float off = -0.75f; // The border (using Col_FrameBg) tends to look off when color is near-opaque and rounding is enabled. This offset seemed like a good middle ground to reduce those artifacts. - bb_inner.Expand(off); - if ((flags & ImGuiColorEditFlags_AlphaPreviewHalf) && col.w < 1.0f) - { - float mid_x = (float)(int)((bb_inner.Min.x + bb_inner.Max.x) * 0.5f + 0.5f); - RenderColorRectWithAlphaCheckerboard(ImVec2(bb_inner.Min.x + grid_step, bb_inner.Min.y), bb_inner.Max, GetColorU32(col), grid_step, ImVec2(-grid_step + off, off), rounding, ImDrawCornerFlags_TopRight| ImDrawCornerFlags_BotRight); - window->DrawList->AddRectFilled(bb_inner.Min, ImVec2(mid_x, bb_inner.Max.y), GetColorU32(col_without_alpha), rounding, ImDrawCornerFlags_TopLeft|ImDrawCornerFlags_BotLeft); - } - else - { - // Because GetColorU32() multiplies by the global style Alpha and we don't want to display a checkerboard if the source code had no alpha - ImVec4 col_source = (flags & ImGuiColorEditFlags_AlphaPreview) ? col : col_without_alpha; - if (col_source.w < 1.0f) - RenderColorRectWithAlphaCheckerboard(bb_inner.Min, bb_inner.Max, GetColorU32(col_source), grid_step, ImVec2(off, off), rounding); - else - window->DrawList->AddRectFilled(bb_inner.Min, bb_inner.Max, GetColorU32(col_source), rounding, ImDrawCornerFlags_All); - } - RenderNavHighlight(bb, id); - if (g.Style.FrameBorderSize > 0.0f) - RenderFrameBorder(bb.Min, bb.Max, rounding); - else - window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), rounding); // Color button are often in need of some sort of border - - // Drag and Drop Source - // NB: The ActiveId test is merely an optional micro-optimization, BeginDragDropSource() does the same test. - if (g.ActiveId == id && !(flags & ImGuiColorEditFlags_NoDragDrop) && BeginDragDropSource()) - { - if (flags & ImGuiColorEditFlags_NoAlpha) - SetDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F, &col, sizeof(float) * 3, ImGuiCond_Once); - else - SetDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F, &col, sizeof(float) * 4, ImGuiCond_Once); - ColorButton(desc_id, col, flags); - SameLine(); - TextUnformatted("Color"); - EndDragDropSource(); - } - - // Tooltip - if (!(flags & ImGuiColorEditFlags_NoTooltip) && hovered) - ColorTooltip(desc_id, &col.x, flags & (ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)); - - if (pressed) - MarkItemEdited(id); - - return pressed; -} - -bool ImGui::ColorEdit3(const char* label, float col[3], ImGuiColorEditFlags flags) -{ - return ColorEdit4(label, col, flags | ImGuiColorEditFlags_NoAlpha); -} - -void ImGui::ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags) -{ - bool allow_opt_inputs = !(flags & ImGuiColorEditFlags__InputsMask); - bool allow_opt_datatype = !(flags & ImGuiColorEditFlags__DataTypeMask); - if ((!allow_opt_inputs && !allow_opt_datatype) || !BeginPopup("context")) - return; - ImGuiContext& g = *GImGui; - ImGuiColorEditFlags opts = g.ColorEditOptions; - if (allow_opt_inputs) - { - if (RadioButton("RGB", (opts & ImGuiColorEditFlags_RGB) != 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_RGB; - if (RadioButton("HSV", (opts & ImGuiColorEditFlags_HSV) != 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_HSV; - if (RadioButton("HEX", (opts & ImGuiColorEditFlags_HEX) != 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_HEX; - } - if (allow_opt_datatype) - { - if (allow_opt_inputs) Separator(); - if (RadioButton("0..255", (opts & ImGuiColorEditFlags_Uint8) != 0)) opts = (opts & ~ImGuiColorEditFlags__DataTypeMask) | ImGuiColorEditFlags_Uint8; - if (RadioButton("0.00..1.00", (opts & ImGuiColorEditFlags_Float) != 0)) opts = (opts & ~ImGuiColorEditFlags__DataTypeMask) | ImGuiColorEditFlags_Float; - } - - if (allow_opt_inputs || allow_opt_datatype) - Separator(); - if (Button("Copy as..", ImVec2(-1,0))) - OpenPopup("Copy"); - if (BeginPopup("Copy")) - { - int cr = IM_F32_TO_INT8_SAT(col[0]), cg = IM_F32_TO_INT8_SAT(col[1]), cb = IM_F32_TO_INT8_SAT(col[2]), ca = (flags & ImGuiColorEditFlags_NoAlpha) ? 255 : IM_F32_TO_INT8_SAT(col[3]); - char buf[64]; - ImFormatString(buf, IM_ARRAYSIZE(buf), "(%.3ff, %.3ff, %.3ff, %.3ff)", col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]); - if (Selectable(buf)) - SetClipboardText(buf); - ImFormatString(buf, IM_ARRAYSIZE(buf), "(%d,%d,%d,%d)", cr, cg, cb, ca); - if (Selectable(buf)) - SetClipboardText(buf); - if (flags & ImGuiColorEditFlags_NoAlpha) - ImFormatString(buf, IM_ARRAYSIZE(buf), "0x%02X%02X%02X", cr, cg, cb); - else - ImFormatString(buf, IM_ARRAYSIZE(buf), "0x%02X%02X%02X%02X", cr, cg, cb, ca); - if (Selectable(buf)) - SetClipboardText(buf); - EndPopup(); - } - - g.ColorEditOptions = opts; - EndPopup(); -} - -void ImGui::ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags flags) -{ - bool allow_opt_picker = !(flags & ImGuiColorEditFlags__PickerMask); - bool allow_opt_alpha_bar = !(flags & ImGuiColorEditFlags_NoAlpha) && !(flags & ImGuiColorEditFlags_AlphaBar); - if ((!allow_opt_picker && !allow_opt_alpha_bar) || !ImGui::BeginPopup("context")) - return; - ImGuiContext& g = *GImGui; - if (allow_opt_picker) - { - ImVec2 picker_size(g.FontSize * 8, ImMax(g.FontSize * 8 - (ImGui::GetFrameHeight() + g.Style.ItemInnerSpacing.x), 1.0f)); // FIXME: Picker size copied from main picker function - ImGui::PushItemWidth(picker_size.x); - for (int picker_type = 0; picker_type < 2; picker_type++) - { - // Draw small/thumbnail version of each picker type (over an invisible button for selection) - if (picker_type > 0) ImGui::Separator(); - ImGui::PushID(picker_type); - ImGuiColorEditFlags picker_flags = ImGuiColorEditFlags_NoInputs|ImGuiColorEditFlags_NoOptions|ImGuiColorEditFlags_NoLabel|ImGuiColorEditFlags_NoSidePreview|(flags & ImGuiColorEditFlags_NoAlpha); - if (picker_type == 0) picker_flags |= ImGuiColorEditFlags_PickerHueBar; - if (picker_type == 1) picker_flags |= ImGuiColorEditFlags_PickerHueWheel; - ImVec2 backup_pos = ImGui::GetCursorScreenPos(); - if (ImGui::Selectable("##selectable", false, 0, picker_size)) // By default, Selectable() is closing popup - g.ColorEditOptions = (g.ColorEditOptions & ~ImGuiColorEditFlags__PickerMask) | (picker_flags & ImGuiColorEditFlags__PickerMask); - ImGui::SetCursorScreenPos(backup_pos); - ImVec4 dummy_ref_col; - memcpy(&dummy_ref_col.x, ref_col, sizeof(float) * (picker_flags & ImGuiColorEditFlags_NoAlpha ? 3 : 4)); - ImGui::ColorPicker4("##dummypicker", &dummy_ref_col.x, picker_flags); - ImGui::PopID(); - } - ImGui::PopItemWidth(); - } - if (allow_opt_alpha_bar) - { - if (allow_opt_picker) ImGui::Separator(); - ImGui::CheckboxFlags("Alpha Bar", (unsigned int*)&g.ColorEditOptions, ImGuiColorEditFlags_AlphaBar); - } - ImGui::EndPopup(); -} - -// Edit colors components (each component in 0.0f..1.0f range). -// See enum ImGuiColorEditFlags_ for available options. e.g. Only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set. -// With typical options: Left-click on colored square to open color picker. Right-click to open option menu. CTRL-Click over input fields to edit them and TAB to go to next item. -bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - const float square_sz = GetFrameHeight(); - const float w_extra = (flags & ImGuiColorEditFlags_NoSmallPreview) ? 0.0f : (square_sz + style.ItemInnerSpacing.x); - const float w_items_all = CalcItemWidth() - w_extra; - const char* label_display_end = FindRenderedTextEnd(label); - - BeginGroup(); - PushID(label); - - // If we're not showing any slider there's no point in doing any HSV conversions - const ImGuiColorEditFlags flags_untouched = flags; - if (flags & ImGuiColorEditFlags_NoInputs) - flags = (flags & (~ImGuiColorEditFlags__InputsMask)) | ImGuiColorEditFlags_RGB | ImGuiColorEditFlags_NoOptions; - - // Context menu: display and modify options (before defaults are applied) - if (!(flags & ImGuiColorEditFlags_NoOptions)) - ColorEditOptionsPopup(col, flags); - - // Read stored options - if (!(flags & ImGuiColorEditFlags__InputsMask)) - flags |= (g.ColorEditOptions & ImGuiColorEditFlags__InputsMask); - if (!(flags & ImGuiColorEditFlags__DataTypeMask)) - flags |= (g.ColorEditOptions & ImGuiColorEditFlags__DataTypeMask); - if (!(flags & ImGuiColorEditFlags__PickerMask)) - flags |= (g.ColorEditOptions & ImGuiColorEditFlags__PickerMask); - flags |= (g.ColorEditOptions & ~(ImGuiColorEditFlags__InputsMask | ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags__PickerMask)); - - const bool alpha = (flags & ImGuiColorEditFlags_NoAlpha) == 0; - const bool hdr = (flags & ImGuiColorEditFlags_HDR) != 0; - const int components = alpha ? 4 : 3; - - // Convert to the formats we need - float f[4] = { col[0], col[1], col[2], alpha ? col[3] : 1.0f }; - if (flags & ImGuiColorEditFlags_HSV) - ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]); - int i[4] = { IM_F32_TO_INT8_UNBOUND(f[0]), IM_F32_TO_INT8_UNBOUND(f[1]), IM_F32_TO_INT8_UNBOUND(f[2]), IM_F32_TO_INT8_UNBOUND(f[3]) }; - - bool value_changed = false; - bool value_changed_as_float = false; - - if ((flags & (ImGuiColorEditFlags_RGB | ImGuiColorEditFlags_HSV)) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0) - { - // RGB/HSV 0..255 Sliders - const float w_item_one = ImMax(1.0f, (float)(int)((w_items_all - (style.ItemInnerSpacing.x) * (components-1)) / (float)components)); - const float w_item_last = ImMax(1.0f, (float)(int)(w_items_all - (w_item_one + style.ItemInnerSpacing.x) * (components-1))); - - const bool hide_prefix = (w_item_one <= CalcTextSize((flags & ImGuiColorEditFlags_Float) ? "M:0.000" : "M:000").x); - const char* ids[4] = { "##X", "##Y", "##Z", "##W" }; - const char* fmt_table_int[3][4] = - { - { "%3d", "%3d", "%3d", "%3d" }, // Short display - { "R:%3d", "G:%3d", "B:%3d", "A:%3d" }, // Long display for RGBA - { "H:%3d", "S:%3d", "V:%3d", "A:%3d" } // Long display for HSVA - }; - const char* fmt_table_float[3][4] = - { - { "%0.3f", "%0.3f", "%0.3f", "%0.3f" }, // Short display - { "R:%0.3f", "G:%0.3f", "B:%0.3f", "A:%0.3f" }, // Long display for RGBA - { "H:%0.3f", "S:%0.3f", "V:%0.3f", "A:%0.3f" } // Long display for HSVA - }; - const int fmt_idx = hide_prefix ? 0 : (flags & ImGuiColorEditFlags_HSV) ? 2 : 1; - - PushItemWidth(w_item_one); - for (int n = 0; n < components; n++) - { - if (n > 0) - SameLine(0, style.ItemInnerSpacing.x); - if (n + 1 == components) - PushItemWidth(w_item_last); - if (flags & ImGuiColorEditFlags_Float) - value_changed = value_changed_as_float = value_changed | DragFloat(ids[n], &f[n], 1.0f/255.0f, 0.0f, hdr ? 0.0f : 1.0f, fmt_table_float[fmt_idx][n]); - else - value_changed |= DragInt(ids[n], &i[n], 1.0f, 0, hdr ? 0 : 255, fmt_table_int[fmt_idx][n]); - if (!(flags & ImGuiColorEditFlags_NoOptions)) - OpenPopupOnItemClick("context"); - } - PopItemWidth(); - PopItemWidth(); - } - else if ((flags & ImGuiColorEditFlags_HEX) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0) - { - // RGB Hexadecimal Input - char buf[64]; - if (alpha) - ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X%02X", ImClamp(i[0],0,255), ImClamp(i[1],0,255), ImClamp(i[2],0,255), ImClamp(i[3],0,255)); - else - ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X", ImClamp(i[0],0,255), ImClamp(i[1],0,255), ImClamp(i[2],0,255)); - PushItemWidth(w_items_all); - if (InputText("##Text", buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase)) - { - value_changed = true; - char* p = buf; - while (*p == '#' || ImCharIsBlankA(*p)) - p++; - i[0] = i[1] = i[2] = i[3] = 0; - if (alpha) - sscanf(p, "%02X%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2], (unsigned int*)&i[3]); // Treat at unsigned (%X is unsigned) - else - sscanf(p, "%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2]); - } - if (!(flags & ImGuiColorEditFlags_NoOptions)) - OpenPopupOnItemClick("context"); - PopItemWidth(); - } - - ImGuiWindow* picker_active_window = NULL; - if (!(flags & ImGuiColorEditFlags_NoSmallPreview)) - { - if (!(flags & ImGuiColorEditFlags_NoInputs)) - SameLine(0, style.ItemInnerSpacing.x); - - const ImVec4 col_v4(col[0], col[1], col[2], alpha ? col[3] : 1.0f); - if (ColorButton("##ColorButton", col_v4, flags)) - { - if (!(flags & ImGuiColorEditFlags_NoPicker)) - { - // Store current color and open a picker - g.ColorPickerRef = col_v4; - OpenPopup("picker"); - SetNextWindowPos(window->DC.LastItemRect.GetBL() + ImVec2(-1,style.ItemSpacing.y)); - } - } - if (!(flags & ImGuiColorEditFlags_NoOptions)) - OpenPopupOnItemClick("context"); - - if (BeginPopup("picker")) - { - picker_active_window = g.CurrentWindow; - if (label != label_display_end) - { - TextUnformatted(label, label_display_end); - Separator(); - } - ImGuiColorEditFlags picker_flags_to_forward = ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags__PickerMask | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaBar; - ImGuiColorEditFlags picker_flags = (flags_untouched & picker_flags_to_forward) | ImGuiColorEditFlags__InputsMask | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_AlphaPreviewHalf; - PushItemWidth(square_sz * 12.0f); // Use 256 + bar sizes? - value_changed |= ColorPicker4("##picker", col, picker_flags, &g.ColorPickerRef.x); - PopItemWidth(); - EndPopup(); - } - } - - if (label != label_display_end && !(flags & ImGuiColorEditFlags_NoLabel)) - { - SameLine(0, style.ItemInnerSpacing.x); - TextUnformatted(label, label_display_end); - } - - // Convert back - if (picker_active_window == NULL) - { - if (!value_changed_as_float) - for (int n = 0; n < 4; n++) - f[n] = i[n] / 255.0f; - if (flags & ImGuiColorEditFlags_HSV) - ColorConvertHSVtoRGB(f[0], f[1], f[2], f[0], f[1], f[2]); - if (value_changed) - { - col[0] = f[0]; - col[1] = f[1]; - col[2] = f[2]; - if (alpha) - col[3] = f[3]; - } - } - - PopID(); - EndGroup(); - - // Drag and Drop Target - // NB: The flag test is merely an optional micro-optimization, BeginDragDropTarget() does the same test. - if ((window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect) && !(flags & ImGuiColorEditFlags_NoDragDrop) && BeginDragDropTarget()) - { - if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F)) - { - memcpy((float*)col, payload->Data, sizeof(float) * 3); - value_changed = true; - } - if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F)) - { - memcpy((float*)col, payload->Data, sizeof(float) * components); - value_changed = true; - } - EndDragDropTarget(); - } - - // When picker is being actively used, use its active id so IsItemActive() will function on ColorEdit4(). - if (picker_active_window && g.ActiveId != 0 && g.ActiveIdWindow == picker_active_window) - window->DC.LastItemId = g.ActiveId; - - if (value_changed) - MarkItemEdited(window->DC.LastItemId); - - return value_changed; -} - -bool ImGui::ColorPicker3(const char* label, float col[3], ImGuiColorEditFlags flags) -{ - float col4[4] = { col[0], col[1], col[2], 1.0f }; - if (!ColorPicker4(label, col4, flags | ImGuiColorEditFlags_NoAlpha)) - return false; - col[0] = col4[0]; col[1] = col4[1]; col[2] = col4[2]; - return true; -} - -static void RenderArrowsForVerticalBar(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, float bar_w) -{ - ImGui::RenderArrowPointingAt(draw_list, ImVec2(pos.x + half_sz.x + 1, pos.y), ImVec2(half_sz.x + 2, half_sz.y + 1), ImGuiDir_Right, IM_COL32_BLACK); - ImGui::RenderArrowPointingAt(draw_list, ImVec2(pos.x + half_sz.x, pos.y), half_sz, ImGuiDir_Right, IM_COL32_WHITE); - ImGui::RenderArrowPointingAt(draw_list, ImVec2(pos.x + bar_w - half_sz.x - 1, pos.y), ImVec2(half_sz.x + 2, half_sz.y + 1), ImGuiDir_Left, IM_COL32_BLACK); - ImGui::RenderArrowPointingAt(draw_list, ImVec2(pos.x + bar_w - half_sz.x, pos.y), half_sz, ImGuiDir_Left, IM_COL32_WHITE); -} - -// ColorPicker -// Note: only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set. -// FIXME: we adjust the big color square height based on item width, which may cause a flickering feedback loop (if automatic height makes a vertical scrollbar appears, affecting automatic width..) -bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags flags, const float* ref_col) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = GetCurrentWindow(); - ImDrawList* draw_list = window->DrawList; - - ImGuiStyle& style = g.Style; - ImGuiIO& io = g.IO; - - PushID(label); - BeginGroup(); - - if (!(flags & ImGuiColorEditFlags_NoSidePreview)) - flags |= ImGuiColorEditFlags_NoSmallPreview; - - // Context menu: display and store options. - if (!(flags & ImGuiColorEditFlags_NoOptions)) - - // Read stored options - if (!(flags & ImGuiColorEditFlags__PickerMask)) - flags |= ((g.ColorEditOptions & ImGuiColorEditFlags__PickerMask) ? g.ColorEditOptions : ImGuiColorEditFlags__OptionsDefault) & ImGuiColorEditFlags__PickerMask; - IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__PickerMask))); // Check that only 1 is selected - if (!(flags & ImGuiColorEditFlags_NoOptions)) - flags |= (g.ColorEditOptions & ImGuiColorEditFlags_AlphaBar); - - // Setup - int components = (flags & ImGuiColorEditFlags_NoAlpha) ? 3 : 4; - bool alpha_bar = (flags & ImGuiColorEditFlags_AlphaBar) && !(flags & ImGuiColorEditFlags_NoAlpha); - ImVec2 picker_pos = window->DC.CursorPos; - float square_sz = GetFrameHeight(); - float bars_width = square_sz; // Arbitrary smallish width of Hue/Alpha picking bars - float sv_picker_size = ImMax(bars_width * 1, CalcItemWidth() - (alpha_bar ? 2 : 1) * (bars_width + style.ItemInnerSpacing.x)); // Saturation/Value picking box - float bar0_pos_x = picker_pos.x + sv_picker_size + style.ItemInnerSpacing.x; - float bar1_pos_x = bar0_pos_x + bars_width + style.ItemInnerSpacing.x; - float bars_triangles_half_sz = (float)(int)(bars_width * 0.20f); - - float backup_initial_col[4]; - memcpy(backup_initial_col, col, components * sizeof(float)); - - float wheel_thickness = sv_picker_size * 0.08f; - float wheel_r_outer = sv_picker_size * 0.50f; - float wheel_r_inner = wheel_r_outer - wheel_thickness; - ImVec2 wheel_center(picker_pos.x + (sv_picker_size + bars_width)*0.5f, picker_pos.y + sv_picker_size*0.5f); - - // Note: the triangle is displayed rotated with triangle_pa pointing to Hue, but most coordinates stays unrotated for logic. - float triangle_r = wheel_r_inner - (int)(sv_picker_size * 0.027f); - ImVec2 triangle_pa = ImVec2(triangle_r, 0.0f); // Hue point. - ImVec2 triangle_pb = ImVec2(triangle_r * -0.5f, triangle_r * -0.866025f); // Black point. - ImVec2 triangle_pc = ImVec2(triangle_r * -0.5f, triangle_r * +0.866025f); // White point. - - float H,S,V; - ColorConvertRGBtoHSV(col[0], col[1], col[2], H, S, V); - - bool value_changed = false, value_changed_h = false, value_changed_sv = false; - - PushItemFlag(ImGuiItemFlags_NoNav, true); - if (flags & ImGuiColorEditFlags_PickerHueWheel) - { - // Hue wheel + SV triangle logic - InvisibleButton("hsv", ImVec2(sv_picker_size + style.ItemInnerSpacing.x + bars_width, sv_picker_size)); - if (IsItemActive()) - { - ImVec2 initial_off = g.IO.MouseClickedPos[0] - wheel_center; - ImVec2 current_off = g.IO.MousePos - wheel_center; - float initial_dist2 = ImLengthSqr(initial_off); - if (initial_dist2 >= (wheel_r_inner-1)*(wheel_r_inner-1) && initial_dist2 <= (wheel_r_outer+1)*(wheel_r_outer+1)) - { - // Interactive with Hue wheel - H = ImAtan2(current_off.y, current_off.x) / IM_PI*0.5f; - if (H < 0.0f) - H += 1.0f; - value_changed = value_changed_h = true; - } - float cos_hue_angle = ImCos(-H * 2.0f * IM_PI); - float sin_hue_angle = ImSin(-H * 2.0f * IM_PI); - if (ImTriangleContainsPoint(triangle_pa, triangle_pb, triangle_pc, ImRotate(initial_off, cos_hue_angle, sin_hue_angle))) - { - // Interacting with SV triangle - ImVec2 current_off_unrotated = ImRotate(current_off, cos_hue_angle, sin_hue_angle); - if (!ImTriangleContainsPoint(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated)) - current_off_unrotated = ImTriangleClosestPoint(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated); - float uu, vv, ww; - ImTriangleBarycentricCoords(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated, uu, vv, ww); - V = ImClamp(1.0f - vv, 0.0001f, 1.0f); - S = ImClamp(uu / V, 0.0001f, 1.0f); - value_changed = value_changed_sv = true; - } - } - if (!(flags & ImGuiColorEditFlags_NoOptions)) - OpenPopupOnItemClick("context"); - } - else if (flags & ImGuiColorEditFlags_PickerHueBar) - { - // SV rectangle logic - InvisibleButton("sv", ImVec2(sv_picker_size, sv_picker_size)); - if (IsItemActive()) - { - S = ImSaturate((io.MousePos.x - picker_pos.x) / (sv_picker_size-1)); - V = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size-1)); - value_changed = value_changed_sv = true; - } - if (!(flags & ImGuiColorEditFlags_NoOptions)) - OpenPopupOnItemClick("context"); - - // Hue bar logic - SetCursorScreenPos(ImVec2(bar0_pos_x, picker_pos.y)); - InvisibleButton("hue", ImVec2(bars_width, sv_picker_size)); - if (IsItemActive()) - { - H = ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size-1)); - value_changed = value_changed_h = true; - } - } - - // Alpha bar logic - if (alpha_bar) - { - SetCursorScreenPos(ImVec2(bar1_pos_x, picker_pos.y)); - InvisibleButton("alpha", ImVec2(bars_width, sv_picker_size)); - if (IsItemActive()) - { - col[3] = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size-1)); - value_changed = true; - } - } - PopItemFlag(); // ImGuiItemFlags_NoNav - - if (!(flags & ImGuiColorEditFlags_NoSidePreview)) - { - SameLine(0, style.ItemInnerSpacing.x); - BeginGroup(); - } - - if (!(flags & ImGuiColorEditFlags_NoLabel)) - { - const char* label_display_end = FindRenderedTextEnd(label); - if (label != label_display_end) - { - if ((flags & ImGuiColorEditFlags_NoSidePreview)) - SameLine(0, style.ItemInnerSpacing.x); - TextUnformatted(label, label_display_end); - } - } - - if (!(flags & ImGuiColorEditFlags_NoSidePreview)) - { - PushItemFlag(ImGuiItemFlags_NoNavDefaultFocus, true); - ImVec4 col_v4(col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]); - if ((flags & ImGuiColorEditFlags_NoLabel)) - Text("Current"); - ColorButton("##current", col_v4, (flags & (ImGuiColorEditFlags_HDR|ImGuiColorEditFlags_AlphaPreview|ImGuiColorEditFlags_AlphaPreviewHalf|ImGuiColorEditFlags_NoTooltip)), ImVec2(square_sz * 3, square_sz * 2)); - if (ref_col != NULL) - { - Text("Original"); - ImVec4 ref_col_v4(ref_col[0], ref_col[1], ref_col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : ref_col[3]); - if (ColorButton("##original", ref_col_v4, (flags & (ImGuiColorEditFlags_HDR|ImGuiColorEditFlags_AlphaPreview|ImGuiColorEditFlags_AlphaPreviewHalf|ImGuiColorEditFlags_NoTooltip)), ImVec2(square_sz * 3, square_sz * 2))) - { - memcpy(col, ref_col, components * sizeof(float)); - value_changed = true; - } - } - PopItemFlag(); - EndGroup(); - } - - // Convert back color to RGB - if (value_changed_h || value_changed_sv) - ColorConvertHSVtoRGB(H >= 1.0f ? H - 10 * 1e-6f : H, S > 0.0f ? S : 10*1e-6f, V > 0.0f ? V : 1e-6f, col[0], col[1], col[2]); - - // R,G,B and H,S,V slider color editor - bool value_changed_fix_hue_wrap = false; - if ((flags & ImGuiColorEditFlags_NoInputs) == 0) - { - PushItemWidth((alpha_bar ? bar1_pos_x : bar0_pos_x) + bars_width - picker_pos.x); - ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoOptions | ImGuiColorEditFlags_NoSmallPreview | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf; - ImGuiColorEditFlags sub_flags = (flags & sub_flags_to_forward) | ImGuiColorEditFlags_NoPicker; - if (flags & ImGuiColorEditFlags_RGB || (flags & ImGuiColorEditFlags__InputsMask) == 0) - if (ColorEdit4("##rgb", col, sub_flags | ImGuiColorEditFlags_RGB)) - { - // FIXME: Hackily differenciating using the DragInt (ActiveId != 0 && !ActiveIdAllowOverlap) vs. using the InputText or DropTarget. - // For the later we don't want to run the hue-wrap canceling code. If you are well versed in HSV picker please provide your input! (See #2050) - value_changed_fix_hue_wrap = (g.ActiveId != 0 && !g.ActiveIdAllowOverlap); - value_changed = true; - } - if (flags & ImGuiColorEditFlags_HSV || (flags & ImGuiColorEditFlags__InputsMask) == 0) - value_changed |= ColorEdit4("##hsv", col, sub_flags | ImGuiColorEditFlags_HSV); - if (flags & ImGuiColorEditFlags_HEX || (flags & ImGuiColorEditFlags__InputsMask) == 0) - value_changed |= ColorEdit4("##hex", col, sub_flags | ImGuiColorEditFlags_HEX); - PopItemWidth(); - } - - // Try to cancel hue wrap (after ColorEdit4 call), if any - if (value_changed_fix_hue_wrap) - { - float new_H, new_S, new_V; - ColorConvertRGBtoHSV(col[0], col[1], col[2], new_H, new_S, new_V); - if (new_H <= 0 && H > 0) - { - if (new_V <= 0 && V != new_V) - ColorConvertHSVtoRGB(H, S, new_V <= 0 ? V * 0.5f : new_V, col[0], col[1], col[2]); - else if (new_S <= 0) - ColorConvertHSVtoRGB(H, new_S <= 0 ? S * 0.5f : new_S, new_V, col[0], col[1], col[2]); - } - } - - ImVec4 hue_color_f(1, 1, 1, 1); ColorConvertHSVtoRGB(H, 1, 1, hue_color_f.x, hue_color_f.y, hue_color_f.z); - ImU32 hue_color32 = ColorConvertFloat4ToU32(hue_color_f); - ImU32 col32_no_alpha = ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 1.0f)); - - const ImU32 hue_colors[6+1] = { IM_COL32(255,0,0,255), IM_COL32(255,255,0,255), IM_COL32(0,255,0,255), IM_COL32(0,255,255,255), IM_COL32(0,0,255,255), IM_COL32(255,0,255,255), IM_COL32(255,0,0,255) }; - ImVec2 sv_cursor_pos; - - if (flags & ImGuiColorEditFlags_PickerHueWheel) - { - // Render Hue Wheel - const float aeps = 1.5f / wheel_r_outer; // Half a pixel arc length in radians (2pi cancels out). - const int segment_per_arc = ImMax(4, (int)wheel_r_outer / 12); - for (int n = 0; n < 6; n++) - { - const float a0 = (n) /6.0f * 2.0f * IM_PI - aeps; - const float a1 = (n+1.0f)/6.0f * 2.0f * IM_PI + aeps; - const int vert_start_idx = draw_list->VtxBuffer.Size; - draw_list->PathArcTo(wheel_center, (wheel_r_inner + wheel_r_outer)*0.5f, a0, a1, segment_per_arc); - draw_list->PathStroke(IM_COL32_WHITE, false, wheel_thickness); - const int vert_end_idx = draw_list->VtxBuffer.Size; - - // Paint colors over existing vertices - ImVec2 gradient_p0(wheel_center.x + ImCos(a0) * wheel_r_inner, wheel_center.y + ImSin(a0) * wheel_r_inner); - ImVec2 gradient_p1(wheel_center.x + ImCos(a1) * wheel_r_inner, wheel_center.y + ImSin(a1) * wheel_r_inner); - ShadeVertsLinearColorGradientKeepAlpha(draw_list, vert_start_idx, vert_end_idx, gradient_p0, gradient_p1, hue_colors[n], hue_colors[n+1]); - } - - // Render Cursor + preview on Hue Wheel - float cos_hue_angle = ImCos(H * 2.0f * IM_PI); - float sin_hue_angle = ImSin(H * 2.0f * IM_PI); - ImVec2 hue_cursor_pos(wheel_center.x + cos_hue_angle * (wheel_r_inner+wheel_r_outer)*0.5f, wheel_center.y + sin_hue_angle * (wheel_r_inner+wheel_r_outer)*0.5f); - float hue_cursor_rad = value_changed_h ? wheel_thickness * 0.65f : wheel_thickness * 0.55f; - int hue_cursor_segments = ImClamp((int)(hue_cursor_rad / 1.4f), 9, 32); - draw_list->AddCircleFilled(hue_cursor_pos, hue_cursor_rad, hue_color32, hue_cursor_segments); - draw_list->AddCircle(hue_cursor_pos, hue_cursor_rad+1, IM_COL32(128,128,128,255), hue_cursor_segments); - draw_list->AddCircle(hue_cursor_pos, hue_cursor_rad, IM_COL32_WHITE, hue_cursor_segments); - - // Render SV triangle (rotated according to hue) - ImVec2 tra = wheel_center + ImRotate(triangle_pa, cos_hue_angle, sin_hue_angle); - ImVec2 trb = wheel_center + ImRotate(triangle_pb, cos_hue_angle, sin_hue_angle); - ImVec2 trc = wheel_center + ImRotate(triangle_pc, cos_hue_angle, sin_hue_angle); - ImVec2 uv_white = GetFontTexUvWhitePixel(); - draw_list->PrimReserve(6, 6); - draw_list->PrimVtx(tra, uv_white, hue_color32); - draw_list->PrimVtx(trb, uv_white, hue_color32); - draw_list->PrimVtx(trc, uv_white, IM_COL32_WHITE); - draw_list->PrimVtx(tra, uv_white, IM_COL32_BLACK_TRANS); - draw_list->PrimVtx(trb, uv_white, IM_COL32_BLACK); - draw_list->PrimVtx(trc, uv_white, IM_COL32_BLACK_TRANS); - draw_list->AddTriangle(tra, trb, trc, IM_COL32(128,128,128,255), 1.5f); - sv_cursor_pos = ImLerp(ImLerp(trc, tra, ImSaturate(S)), trb, ImSaturate(1 - V)); - } - else if (flags & ImGuiColorEditFlags_PickerHueBar) - { - // Render SV Square - draw_list->AddRectFilledMultiColor(picker_pos, picker_pos + ImVec2(sv_picker_size,sv_picker_size), IM_COL32_WHITE, hue_color32, hue_color32, IM_COL32_WHITE); - draw_list->AddRectFilledMultiColor(picker_pos, picker_pos + ImVec2(sv_picker_size,sv_picker_size), IM_COL32_BLACK_TRANS, IM_COL32_BLACK_TRANS, IM_COL32_BLACK, IM_COL32_BLACK); - RenderFrameBorder(picker_pos, picker_pos + ImVec2(sv_picker_size,sv_picker_size), 0.0f); - sv_cursor_pos.x = ImClamp((float)(int)(picker_pos.x + ImSaturate(S) * sv_picker_size + 0.5f), picker_pos.x + 2, picker_pos.x + sv_picker_size - 2); // Sneakily prevent the circle to stick out too much - sv_cursor_pos.y = ImClamp((float)(int)(picker_pos.y + ImSaturate(1 - V) * sv_picker_size + 0.5f), picker_pos.y + 2, picker_pos.y + sv_picker_size - 2); - - // Render Hue Bar - for (int i = 0; i < 6; ++i) - draw_list->AddRectFilledMultiColor(ImVec2(bar0_pos_x, picker_pos.y + i * (sv_picker_size / 6)), ImVec2(bar0_pos_x + bars_width, picker_pos.y + (i + 1) * (sv_picker_size / 6)), hue_colors[i], hue_colors[i], hue_colors[i + 1], hue_colors[i + 1]); - float bar0_line_y = (float)(int)(picker_pos.y + H * sv_picker_size + 0.5f); - RenderFrameBorder(ImVec2(bar0_pos_x, picker_pos.y), ImVec2(bar0_pos_x + bars_width, picker_pos.y + sv_picker_size), 0.0f); - RenderArrowsForVerticalBar(draw_list, ImVec2(bar0_pos_x - 1, bar0_line_y), ImVec2(bars_triangles_half_sz + 1, bars_triangles_half_sz), bars_width + 2.0f); - } - - // Render cursor/preview circle (clamp S/V within 0..1 range because floating points colors may lead HSV values to be out of range) - float sv_cursor_rad = value_changed_sv ? 10.0f : 6.0f; - draw_list->AddCircleFilled(sv_cursor_pos, sv_cursor_rad, col32_no_alpha, 12); - draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad+1, IM_COL32(128,128,128,255), 12); - draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad, IM_COL32_WHITE, 12); - - // Render alpha bar - if (alpha_bar) - { - float alpha = ImSaturate(col[3]); - ImRect bar1_bb(bar1_pos_x, picker_pos.y, bar1_pos_x + bars_width, picker_pos.y + sv_picker_size); - RenderColorRectWithAlphaCheckerboard(bar1_bb.Min, bar1_bb.Max, IM_COL32(0,0,0,0), bar1_bb.GetWidth() / 2.0f, ImVec2(0.0f, 0.0f)); - draw_list->AddRectFilledMultiColor(bar1_bb.Min, bar1_bb.Max, col32_no_alpha, col32_no_alpha, col32_no_alpha & ~IM_COL32_A_MASK, col32_no_alpha & ~IM_COL32_A_MASK); - float bar1_line_y = (float)(int)(picker_pos.y + (1.0f - alpha) * sv_picker_size + 0.5f); - RenderFrameBorder(bar1_bb.Min, bar1_bb.Max, 0.0f); - RenderArrowsForVerticalBar(draw_list, ImVec2(bar1_pos_x - 1, bar1_line_y), ImVec2(bars_triangles_half_sz + 1, bars_triangles_half_sz), bars_width + 2.0f); - } - - EndGroup(); - - if (value_changed && memcmp(backup_initial_col, col, components * sizeof(float)) == 0) - value_changed = false; - if (value_changed) - MarkItemEdited(window->DC.LastItemId); - - PopID(); - - return value_changed; -} - // Horizontal/vertical separating line void ImGui::Separator() { diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 2eee942559cb..a944ce6ca2bb 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1224,6 +1224,791 @@ bool ImGui::Combo(const char* label, int* current_item, const char* items_separa // - ColorPickerOptionsPopup() [Internal] //------------------------------------------------------------------------- +bool ImGui::ColorEdit3(const char* label, float col[3], ImGuiColorEditFlags flags) +{ + return ColorEdit4(label, col, flags | ImGuiColorEditFlags_NoAlpha); +} + +// Edit colors components (each component in 0.0f..1.0f range). +// See enum ImGuiColorEditFlags_ for available options. e.g. Only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set. +// With typical options: Left-click on colored square to open color picker. Right-click to open option menu. CTRL-Click over input fields to edit them and TAB to go to next item. +bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const float square_sz = GetFrameHeight(); + const float w_extra = (flags & ImGuiColorEditFlags_NoSmallPreview) ? 0.0f : (square_sz + style.ItemInnerSpacing.x); + const float w_items_all = CalcItemWidth() - w_extra; + const char* label_display_end = FindRenderedTextEnd(label); + + BeginGroup(); + PushID(label); + + // If we're not showing any slider there's no point in doing any HSV conversions + const ImGuiColorEditFlags flags_untouched = flags; + if (flags & ImGuiColorEditFlags_NoInputs) + flags = (flags & (~ImGuiColorEditFlags__InputsMask)) | ImGuiColorEditFlags_RGB | ImGuiColorEditFlags_NoOptions; + + // Context menu: display and modify options (before defaults are applied) + if (!(flags & ImGuiColorEditFlags_NoOptions)) + ColorEditOptionsPopup(col, flags); + + // Read stored options + if (!(flags & ImGuiColorEditFlags__InputsMask)) + flags |= (g.ColorEditOptions & ImGuiColorEditFlags__InputsMask); + if (!(flags & ImGuiColorEditFlags__DataTypeMask)) + flags |= (g.ColorEditOptions & ImGuiColorEditFlags__DataTypeMask); + if (!(flags & ImGuiColorEditFlags__PickerMask)) + flags |= (g.ColorEditOptions & ImGuiColorEditFlags__PickerMask); + flags |= (g.ColorEditOptions & ~(ImGuiColorEditFlags__InputsMask | ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags__PickerMask)); + + const bool alpha = (flags & ImGuiColorEditFlags_NoAlpha) == 0; + const bool hdr = (flags & ImGuiColorEditFlags_HDR) != 0; + const int components = alpha ? 4 : 3; + + // Convert to the formats we need + float f[4] = { col[0], col[1], col[2], alpha ? col[3] : 1.0f }; + if (flags & ImGuiColorEditFlags_HSV) + ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]); + int i[4] = { IM_F32_TO_INT8_UNBOUND(f[0]), IM_F32_TO_INT8_UNBOUND(f[1]), IM_F32_TO_INT8_UNBOUND(f[2]), IM_F32_TO_INT8_UNBOUND(f[3]) }; + + bool value_changed = false; + bool value_changed_as_float = false; + + if ((flags & (ImGuiColorEditFlags_RGB | ImGuiColorEditFlags_HSV)) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0) + { + // RGB/HSV 0..255 Sliders + const float w_item_one = ImMax(1.0f, (float)(int)((w_items_all - (style.ItemInnerSpacing.x) * (components-1)) / (float)components)); + const float w_item_last = ImMax(1.0f, (float)(int)(w_items_all - (w_item_one + style.ItemInnerSpacing.x) * (components-1))); + + const bool hide_prefix = (w_item_one <= CalcTextSize((flags & ImGuiColorEditFlags_Float) ? "M:0.000" : "M:000").x); + const char* ids[4] = { "##X", "##Y", "##Z", "##W" }; + const char* fmt_table_int[3][4] = + { + { "%3d", "%3d", "%3d", "%3d" }, // Short display + { "R:%3d", "G:%3d", "B:%3d", "A:%3d" }, // Long display for RGBA + { "H:%3d", "S:%3d", "V:%3d", "A:%3d" } // Long display for HSVA + }; + const char* fmt_table_float[3][4] = + { + { "%0.3f", "%0.3f", "%0.3f", "%0.3f" }, // Short display + { "R:%0.3f", "G:%0.3f", "B:%0.3f", "A:%0.3f" }, // Long display for RGBA + { "H:%0.3f", "S:%0.3f", "V:%0.3f", "A:%0.3f" } // Long display for HSVA + }; + const int fmt_idx = hide_prefix ? 0 : (flags & ImGuiColorEditFlags_HSV) ? 2 : 1; + + PushItemWidth(w_item_one); + for (int n = 0; n < components; n++) + { + if (n > 0) + SameLine(0, style.ItemInnerSpacing.x); + if (n + 1 == components) + PushItemWidth(w_item_last); + if (flags & ImGuiColorEditFlags_Float) + value_changed = value_changed_as_float = value_changed | DragFloat(ids[n], &f[n], 1.0f/255.0f, 0.0f, hdr ? 0.0f : 1.0f, fmt_table_float[fmt_idx][n]); + else + value_changed |= DragInt(ids[n], &i[n], 1.0f, 0, hdr ? 0 : 255, fmt_table_int[fmt_idx][n]); + if (!(flags & ImGuiColorEditFlags_NoOptions)) + OpenPopupOnItemClick("context"); + } + PopItemWidth(); + PopItemWidth(); + } + else if ((flags & ImGuiColorEditFlags_HEX) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0) + { + // RGB Hexadecimal Input + char buf[64]; + if (alpha) + ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X%02X", ImClamp(i[0],0,255), ImClamp(i[1],0,255), ImClamp(i[2],0,255), ImClamp(i[3],0,255)); + else + ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X", ImClamp(i[0],0,255), ImClamp(i[1],0,255), ImClamp(i[2],0,255)); + PushItemWidth(w_items_all); + if (InputText("##Text", buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase)) + { + value_changed = true; + char* p = buf; + while (*p == '#' || ImCharIsBlankA(*p)) + p++; + i[0] = i[1] = i[2] = i[3] = 0; + if (alpha) + sscanf(p, "%02X%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2], (unsigned int*)&i[3]); // Treat at unsigned (%X is unsigned) + else + sscanf(p, "%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2]); + } + if (!(flags & ImGuiColorEditFlags_NoOptions)) + OpenPopupOnItemClick("context"); + PopItemWidth(); + } + + ImGuiWindow* picker_active_window = NULL; + if (!(flags & ImGuiColorEditFlags_NoSmallPreview)) + { + if (!(flags & ImGuiColorEditFlags_NoInputs)) + SameLine(0, style.ItemInnerSpacing.x); + + const ImVec4 col_v4(col[0], col[1], col[2], alpha ? col[3] : 1.0f); + if (ColorButton("##ColorButton", col_v4, flags)) + { + if (!(flags & ImGuiColorEditFlags_NoPicker)) + { + // Store current color and open a picker + g.ColorPickerRef = col_v4; + OpenPopup("picker"); + SetNextWindowPos(window->DC.LastItemRect.GetBL() + ImVec2(-1,style.ItemSpacing.y)); + } + } + if (!(flags & ImGuiColorEditFlags_NoOptions)) + OpenPopupOnItemClick("context"); + + if (BeginPopup("picker")) + { + picker_active_window = g.CurrentWindow; + if (label != label_display_end) + { + TextUnformatted(label, label_display_end); + Separator(); + } + ImGuiColorEditFlags picker_flags_to_forward = ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags__PickerMask | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaBar; + ImGuiColorEditFlags picker_flags = (flags_untouched & picker_flags_to_forward) | ImGuiColorEditFlags__InputsMask | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_AlphaPreviewHalf; + PushItemWidth(square_sz * 12.0f); // Use 256 + bar sizes? + value_changed |= ColorPicker4("##picker", col, picker_flags, &g.ColorPickerRef.x); + PopItemWidth(); + EndPopup(); + } + } + + if (label != label_display_end && !(flags & ImGuiColorEditFlags_NoLabel)) + { + SameLine(0, style.ItemInnerSpacing.x); + TextUnformatted(label, label_display_end); + } + + // Convert back + if (picker_active_window == NULL) + { + if (!value_changed_as_float) + for (int n = 0; n < 4; n++) + f[n] = i[n] / 255.0f; + if (flags & ImGuiColorEditFlags_HSV) + ColorConvertHSVtoRGB(f[0], f[1], f[2], f[0], f[1], f[2]); + if (value_changed) + { + col[0] = f[0]; + col[1] = f[1]; + col[2] = f[2]; + if (alpha) + col[3] = f[3]; + } + } + + PopID(); + EndGroup(); + + // Drag and Drop Target + // NB: The flag test is merely an optional micro-optimization, BeginDragDropTarget() does the same test. + if ((window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect) && !(flags & ImGuiColorEditFlags_NoDragDrop) && BeginDragDropTarget()) + { + if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F)) + { + memcpy((float*)col, payload->Data, sizeof(float) * 3); + value_changed = true; + } + if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F)) + { + memcpy((float*)col, payload->Data, sizeof(float) * components); + value_changed = true; + } + EndDragDropTarget(); + } + + // When picker is being actively used, use its active id so IsItemActive() will function on ColorEdit4(). + if (picker_active_window && g.ActiveId != 0 && g.ActiveIdWindow == picker_active_window) + window->DC.LastItemId = g.ActiveId; + + if (value_changed) + MarkItemEdited(window->DC.LastItemId); + + return value_changed; +} + +bool ImGui::ColorPicker3(const char* label, float col[3], ImGuiColorEditFlags flags) +{ + float col4[4] = { col[0], col[1], col[2], 1.0f }; + if (!ColorPicker4(label, col4, flags | ImGuiColorEditFlags_NoAlpha)) + return false; + col[0] = col4[0]; col[1] = col4[1]; col[2] = col4[2]; + return true; +} + +static inline ImU32 ImAlphaBlendColor(ImU32 col_a, ImU32 col_b) +{ + float t = ((col_b >> IM_COL32_A_SHIFT) & 0xFF) / 255.f; + int r = ImLerp((int)(col_a >> IM_COL32_R_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_R_SHIFT) & 0xFF, t); + int g = ImLerp((int)(col_a >> IM_COL32_G_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_G_SHIFT) & 0xFF, t); + int b = ImLerp((int)(col_a >> IM_COL32_B_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_B_SHIFT) & 0xFF, t); + return IM_COL32(r, g, b, 0xFF); +} + +// Helper for ColorPicker4() +// NB: This is rather brittle and will show artifact when rounding this enabled if rounded corners overlap multiple cells. Caller currently responsible for avoiding that. +// I spent a non reasonable amount of time trying to getting this right for ColorButton with rounding+anti-aliasing+ImGuiColorEditFlags_HalfAlphaPreview flag + various grid sizes and offsets, and eventually gave up... probably more reasonable to disable rounding alltogether. +void ImGui::RenderColorRectWithAlphaCheckerboard(ImVec2 p_min, ImVec2 p_max, ImU32 col, float grid_step, ImVec2 grid_off, float rounding, int rounding_corners_flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (((col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT) < 0xFF) + { + ImU32 col_bg1 = GetColorU32(ImAlphaBlendColor(IM_COL32(204,204,204,255), col)); + ImU32 col_bg2 = GetColorU32(ImAlphaBlendColor(IM_COL32(128,128,128,255), col)); + window->DrawList->AddRectFilled(p_min, p_max, col_bg1, rounding, rounding_corners_flags); + + int yi = 0; + for (float y = p_min.y + grid_off.y; y < p_max.y; y += grid_step, yi++) + { + float y1 = ImClamp(y, p_min.y, p_max.y), y2 = ImMin(y + grid_step, p_max.y); + if (y2 <= y1) + continue; + for (float x = p_min.x + grid_off.x + (yi & 1) * grid_step; x < p_max.x; x += grid_step * 2.0f) + { + float x1 = ImClamp(x, p_min.x, p_max.x), x2 = ImMin(x + grid_step, p_max.x); + if (x2 <= x1) + continue; + int rounding_corners_flags_cell = 0; + if (y1 <= p_min.y) { if (x1 <= p_min.x) rounding_corners_flags_cell |= ImDrawCornerFlags_TopLeft; if (x2 >= p_max.x) rounding_corners_flags_cell |= ImDrawCornerFlags_TopRight; } + if (y2 >= p_max.y) { if (x1 <= p_min.x) rounding_corners_flags_cell |= ImDrawCornerFlags_BotLeft; if (x2 >= p_max.x) rounding_corners_flags_cell |= ImDrawCornerFlags_BotRight; } + rounding_corners_flags_cell &= rounding_corners_flags; + window->DrawList->AddRectFilled(ImVec2(x1,y1), ImVec2(x2,y2), col_bg2, rounding_corners_flags_cell ? rounding : 0.0f, rounding_corners_flags_cell); + } + } + } + else + { + window->DrawList->AddRectFilled(p_min, p_max, col, rounding, rounding_corners_flags); + } +} + +// Helper for ColorPicker4() +static void RenderArrowsForVerticalBar(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, float bar_w) +{ + ImGui::RenderArrowPointingAt(draw_list, ImVec2(pos.x + half_sz.x + 1, pos.y), ImVec2(half_sz.x + 2, half_sz.y + 1), ImGuiDir_Right, IM_COL32_BLACK); + ImGui::RenderArrowPointingAt(draw_list, ImVec2(pos.x + half_sz.x, pos.y), half_sz, ImGuiDir_Right, IM_COL32_WHITE); + ImGui::RenderArrowPointingAt(draw_list, ImVec2(pos.x + bar_w - half_sz.x - 1, pos.y), ImVec2(half_sz.x + 2, half_sz.y + 1), ImGuiDir_Left, IM_COL32_BLACK); + ImGui::RenderArrowPointingAt(draw_list, ImVec2(pos.x + bar_w - half_sz.x, pos.y), half_sz, ImGuiDir_Left, IM_COL32_WHITE); +} + +// Note: ColorPicker4() only accesses 3 floats if ImGuiColorEditFlags_NoAlpha flag is set. +// FIXME: we adjust the big color square height based on item width, which may cause a flickering feedback loop (if automatic height makes a vertical scrollbar appears, affecting automatic width..) +bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags flags, const float* ref_col) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + ImDrawList* draw_list = window->DrawList; + + ImGuiStyle& style = g.Style; + ImGuiIO& io = g.IO; + + PushID(label); + BeginGroup(); + + if (!(flags & ImGuiColorEditFlags_NoSidePreview)) + flags |= ImGuiColorEditFlags_NoSmallPreview; + + // Context menu: display and store options. + if (!(flags & ImGuiColorEditFlags_NoOptions)) + ColorPickerOptionsPopup(col, flags); + + // Read stored options + if (!(flags & ImGuiColorEditFlags__PickerMask)) + flags |= ((g.ColorEditOptions & ImGuiColorEditFlags__PickerMask) ? g.ColorEditOptions : ImGuiColorEditFlags__OptionsDefault) & ImGuiColorEditFlags__PickerMask; + IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__PickerMask))); // Check that only 1 is selected + if (!(flags & ImGuiColorEditFlags_NoOptions)) + flags |= (g.ColorEditOptions & ImGuiColorEditFlags_AlphaBar); + + // Setup + int components = (flags & ImGuiColorEditFlags_NoAlpha) ? 3 : 4; + bool alpha_bar = (flags & ImGuiColorEditFlags_AlphaBar) && !(flags & ImGuiColorEditFlags_NoAlpha); + ImVec2 picker_pos = window->DC.CursorPos; + float square_sz = GetFrameHeight(); + float bars_width = square_sz; // Arbitrary smallish width of Hue/Alpha picking bars + float sv_picker_size = ImMax(bars_width * 1, CalcItemWidth() - (alpha_bar ? 2 : 1) * (bars_width + style.ItemInnerSpacing.x)); // Saturation/Value picking box + float bar0_pos_x = picker_pos.x + sv_picker_size + style.ItemInnerSpacing.x; + float bar1_pos_x = bar0_pos_x + bars_width + style.ItemInnerSpacing.x; + float bars_triangles_half_sz = (float)(int)(bars_width * 0.20f); + + float backup_initial_col[4]; + memcpy(backup_initial_col, col, components * sizeof(float)); + + float wheel_thickness = sv_picker_size * 0.08f; + float wheel_r_outer = sv_picker_size * 0.50f; + float wheel_r_inner = wheel_r_outer - wheel_thickness; + ImVec2 wheel_center(picker_pos.x + (sv_picker_size + bars_width)*0.5f, picker_pos.y + sv_picker_size*0.5f); + + // Note: the triangle is displayed rotated with triangle_pa pointing to Hue, but most coordinates stays unrotated for logic. + float triangle_r = wheel_r_inner - (int)(sv_picker_size * 0.027f); + ImVec2 triangle_pa = ImVec2(triangle_r, 0.0f); // Hue point. + ImVec2 triangle_pb = ImVec2(triangle_r * -0.5f, triangle_r * -0.866025f); // Black point. + ImVec2 triangle_pc = ImVec2(triangle_r * -0.5f, triangle_r * +0.866025f); // White point. + + float H,S,V; + ColorConvertRGBtoHSV(col[0], col[1], col[2], H, S, V); + + bool value_changed = false, value_changed_h = false, value_changed_sv = false; + + PushItemFlag(ImGuiItemFlags_NoNav, true); + if (flags & ImGuiColorEditFlags_PickerHueWheel) + { + // Hue wheel + SV triangle logic + InvisibleButton("hsv", ImVec2(sv_picker_size + style.ItemInnerSpacing.x + bars_width, sv_picker_size)); + if (IsItemActive()) + { + ImVec2 initial_off = g.IO.MouseClickedPos[0] - wheel_center; + ImVec2 current_off = g.IO.MousePos - wheel_center; + float initial_dist2 = ImLengthSqr(initial_off); + if (initial_dist2 >= (wheel_r_inner-1)*(wheel_r_inner-1) && initial_dist2 <= (wheel_r_outer+1)*(wheel_r_outer+1)) + { + // Interactive with Hue wheel + H = ImAtan2(current_off.y, current_off.x) / IM_PI*0.5f; + if (H < 0.0f) + H += 1.0f; + value_changed = value_changed_h = true; + } + float cos_hue_angle = ImCos(-H * 2.0f * IM_PI); + float sin_hue_angle = ImSin(-H * 2.0f * IM_PI); + if (ImTriangleContainsPoint(triangle_pa, triangle_pb, triangle_pc, ImRotate(initial_off, cos_hue_angle, sin_hue_angle))) + { + // Interacting with SV triangle + ImVec2 current_off_unrotated = ImRotate(current_off, cos_hue_angle, sin_hue_angle); + if (!ImTriangleContainsPoint(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated)) + current_off_unrotated = ImTriangleClosestPoint(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated); + float uu, vv, ww; + ImTriangleBarycentricCoords(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated, uu, vv, ww); + V = ImClamp(1.0f - vv, 0.0001f, 1.0f); + S = ImClamp(uu / V, 0.0001f, 1.0f); + value_changed = value_changed_sv = true; + } + } + if (!(flags & ImGuiColorEditFlags_NoOptions)) + OpenPopupOnItemClick("context"); + } + else if (flags & ImGuiColorEditFlags_PickerHueBar) + { + // SV rectangle logic + InvisibleButton("sv", ImVec2(sv_picker_size, sv_picker_size)); + if (IsItemActive()) + { + S = ImSaturate((io.MousePos.x - picker_pos.x) / (sv_picker_size-1)); + V = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size-1)); + value_changed = value_changed_sv = true; + } + if (!(flags & ImGuiColorEditFlags_NoOptions)) + OpenPopupOnItemClick("context"); + + // Hue bar logic + SetCursorScreenPos(ImVec2(bar0_pos_x, picker_pos.y)); + InvisibleButton("hue", ImVec2(bars_width, sv_picker_size)); + if (IsItemActive()) + { + H = ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size-1)); + value_changed = value_changed_h = true; + } + } + + // Alpha bar logic + if (alpha_bar) + { + SetCursorScreenPos(ImVec2(bar1_pos_x, picker_pos.y)); + InvisibleButton("alpha", ImVec2(bars_width, sv_picker_size)); + if (IsItemActive()) + { + col[3] = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size-1)); + value_changed = true; + } + } + PopItemFlag(); // ImGuiItemFlags_NoNav + + if (!(flags & ImGuiColorEditFlags_NoSidePreview)) + { + SameLine(0, style.ItemInnerSpacing.x); + BeginGroup(); + } + + if (!(flags & ImGuiColorEditFlags_NoLabel)) + { + const char* label_display_end = FindRenderedTextEnd(label); + if (label != label_display_end) + { + if ((flags & ImGuiColorEditFlags_NoSidePreview)) + SameLine(0, style.ItemInnerSpacing.x); + TextUnformatted(label, label_display_end); + } + } + + if (!(flags & ImGuiColorEditFlags_NoSidePreview)) + { + PushItemFlag(ImGuiItemFlags_NoNavDefaultFocus, true); + ImVec4 col_v4(col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]); + if ((flags & ImGuiColorEditFlags_NoLabel)) + Text("Current"); + ColorButton("##current", col_v4, (flags & (ImGuiColorEditFlags_HDR|ImGuiColorEditFlags_AlphaPreview|ImGuiColorEditFlags_AlphaPreviewHalf|ImGuiColorEditFlags_NoTooltip)), ImVec2(square_sz * 3, square_sz * 2)); + if (ref_col != NULL) + { + Text("Original"); + ImVec4 ref_col_v4(ref_col[0], ref_col[1], ref_col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : ref_col[3]); + if (ColorButton("##original", ref_col_v4, (flags & (ImGuiColorEditFlags_HDR|ImGuiColorEditFlags_AlphaPreview|ImGuiColorEditFlags_AlphaPreviewHalf|ImGuiColorEditFlags_NoTooltip)), ImVec2(square_sz * 3, square_sz * 2))) + { + memcpy(col, ref_col, components * sizeof(float)); + value_changed = true; + } + } + PopItemFlag(); + EndGroup(); + } + + // Convert back color to RGB + if (value_changed_h || value_changed_sv) + ColorConvertHSVtoRGB(H >= 1.0f ? H - 10 * 1e-6f : H, S > 0.0f ? S : 10*1e-6f, V > 0.0f ? V : 1e-6f, col[0], col[1], col[2]); + + // R,G,B and H,S,V slider color editor + bool value_changed_fix_hue_wrap = false; + if ((flags & ImGuiColorEditFlags_NoInputs) == 0) + { + PushItemWidth((alpha_bar ? bar1_pos_x : bar0_pos_x) + bars_width - picker_pos.x); + ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoOptions | ImGuiColorEditFlags_NoSmallPreview | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf; + ImGuiColorEditFlags sub_flags = (flags & sub_flags_to_forward) | ImGuiColorEditFlags_NoPicker; + if (flags & ImGuiColorEditFlags_RGB || (flags & ImGuiColorEditFlags__InputsMask) == 0) + if (ColorEdit4("##rgb", col, sub_flags | ImGuiColorEditFlags_RGB)) + { + // FIXME: Hackily differenciating using the DragInt (ActiveId != 0 && !ActiveIdAllowOverlap) vs. using the InputText or DropTarget. + // For the later we don't want to run the hue-wrap canceling code. If you are well versed in HSV picker please provide your input! (See #2050) + value_changed_fix_hue_wrap = (g.ActiveId != 0 && !g.ActiveIdAllowOverlap); + value_changed = true; + } + if (flags & ImGuiColorEditFlags_HSV || (flags & ImGuiColorEditFlags__InputsMask) == 0) + value_changed |= ColorEdit4("##hsv", col, sub_flags | ImGuiColorEditFlags_HSV); + if (flags & ImGuiColorEditFlags_HEX || (flags & ImGuiColorEditFlags__InputsMask) == 0) + value_changed |= ColorEdit4("##hex", col, sub_flags | ImGuiColorEditFlags_HEX); + PopItemWidth(); + } + + // Try to cancel hue wrap (after ColorEdit4 call), if any + if (value_changed_fix_hue_wrap) + { + float new_H, new_S, new_V; + ColorConvertRGBtoHSV(col[0], col[1], col[2], new_H, new_S, new_V); + if (new_H <= 0 && H > 0) + { + if (new_V <= 0 && V != new_V) + ColorConvertHSVtoRGB(H, S, new_V <= 0 ? V * 0.5f : new_V, col[0], col[1], col[2]); + else if (new_S <= 0) + ColorConvertHSVtoRGB(H, new_S <= 0 ? S * 0.5f : new_S, new_V, col[0], col[1], col[2]); + } + } + + ImVec4 hue_color_f(1, 1, 1, 1); ColorConvertHSVtoRGB(H, 1, 1, hue_color_f.x, hue_color_f.y, hue_color_f.z); + ImU32 hue_color32 = ColorConvertFloat4ToU32(hue_color_f); + ImU32 col32_no_alpha = ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 1.0f)); + + const ImU32 hue_colors[6+1] = { IM_COL32(255,0,0,255), IM_COL32(255,255,0,255), IM_COL32(0,255,0,255), IM_COL32(0,255,255,255), IM_COL32(0,0,255,255), IM_COL32(255,0,255,255), IM_COL32(255,0,0,255) }; + ImVec2 sv_cursor_pos; + + if (flags & ImGuiColorEditFlags_PickerHueWheel) + { + // Render Hue Wheel + const float aeps = 1.5f / wheel_r_outer; // Half a pixel arc length in radians (2pi cancels out). + const int segment_per_arc = ImMax(4, (int)wheel_r_outer / 12); + for (int n = 0; n < 6; n++) + { + const float a0 = (n) /6.0f * 2.0f * IM_PI - aeps; + const float a1 = (n+1.0f)/6.0f * 2.0f * IM_PI + aeps; + const int vert_start_idx = draw_list->VtxBuffer.Size; + draw_list->PathArcTo(wheel_center, (wheel_r_inner + wheel_r_outer)*0.5f, a0, a1, segment_per_arc); + draw_list->PathStroke(IM_COL32_WHITE, false, wheel_thickness); + const int vert_end_idx = draw_list->VtxBuffer.Size; + + // Paint colors over existing vertices + ImVec2 gradient_p0(wheel_center.x + ImCos(a0) * wheel_r_inner, wheel_center.y + ImSin(a0) * wheel_r_inner); + ImVec2 gradient_p1(wheel_center.x + ImCos(a1) * wheel_r_inner, wheel_center.y + ImSin(a1) * wheel_r_inner); + ShadeVertsLinearColorGradientKeepAlpha(draw_list, vert_start_idx, vert_end_idx, gradient_p0, gradient_p1, hue_colors[n], hue_colors[n+1]); + } + + // Render Cursor + preview on Hue Wheel + float cos_hue_angle = ImCos(H * 2.0f * IM_PI); + float sin_hue_angle = ImSin(H * 2.0f * IM_PI); + ImVec2 hue_cursor_pos(wheel_center.x + cos_hue_angle * (wheel_r_inner+wheel_r_outer)*0.5f, wheel_center.y + sin_hue_angle * (wheel_r_inner+wheel_r_outer)*0.5f); + float hue_cursor_rad = value_changed_h ? wheel_thickness * 0.65f : wheel_thickness * 0.55f; + int hue_cursor_segments = ImClamp((int)(hue_cursor_rad / 1.4f), 9, 32); + draw_list->AddCircleFilled(hue_cursor_pos, hue_cursor_rad, hue_color32, hue_cursor_segments); + draw_list->AddCircle(hue_cursor_pos, hue_cursor_rad+1, IM_COL32(128,128,128,255), hue_cursor_segments); + draw_list->AddCircle(hue_cursor_pos, hue_cursor_rad, IM_COL32_WHITE, hue_cursor_segments); + + // Render SV triangle (rotated according to hue) + ImVec2 tra = wheel_center + ImRotate(triangle_pa, cos_hue_angle, sin_hue_angle); + ImVec2 trb = wheel_center + ImRotate(triangle_pb, cos_hue_angle, sin_hue_angle); + ImVec2 trc = wheel_center + ImRotate(triangle_pc, cos_hue_angle, sin_hue_angle); + ImVec2 uv_white = GetFontTexUvWhitePixel(); + draw_list->PrimReserve(6, 6); + draw_list->PrimVtx(tra, uv_white, hue_color32); + draw_list->PrimVtx(trb, uv_white, hue_color32); + draw_list->PrimVtx(trc, uv_white, IM_COL32_WHITE); + draw_list->PrimVtx(tra, uv_white, IM_COL32_BLACK_TRANS); + draw_list->PrimVtx(trb, uv_white, IM_COL32_BLACK); + draw_list->PrimVtx(trc, uv_white, IM_COL32_BLACK_TRANS); + draw_list->AddTriangle(tra, trb, trc, IM_COL32(128,128,128,255), 1.5f); + sv_cursor_pos = ImLerp(ImLerp(trc, tra, ImSaturate(S)), trb, ImSaturate(1 - V)); + } + else if (flags & ImGuiColorEditFlags_PickerHueBar) + { + // Render SV Square + draw_list->AddRectFilledMultiColor(picker_pos, picker_pos + ImVec2(sv_picker_size,sv_picker_size), IM_COL32_WHITE, hue_color32, hue_color32, IM_COL32_WHITE); + draw_list->AddRectFilledMultiColor(picker_pos, picker_pos + ImVec2(sv_picker_size,sv_picker_size), IM_COL32_BLACK_TRANS, IM_COL32_BLACK_TRANS, IM_COL32_BLACK, IM_COL32_BLACK); + RenderFrameBorder(picker_pos, picker_pos + ImVec2(sv_picker_size,sv_picker_size), 0.0f); + sv_cursor_pos.x = ImClamp((float)(int)(picker_pos.x + ImSaturate(S) * sv_picker_size + 0.5f), picker_pos.x + 2, picker_pos.x + sv_picker_size - 2); // Sneakily prevent the circle to stick out too much + sv_cursor_pos.y = ImClamp((float)(int)(picker_pos.y + ImSaturate(1 - V) * sv_picker_size + 0.5f), picker_pos.y + 2, picker_pos.y + sv_picker_size - 2); + + // Render Hue Bar + for (int i = 0; i < 6; ++i) + draw_list->AddRectFilledMultiColor(ImVec2(bar0_pos_x, picker_pos.y + i * (sv_picker_size / 6)), ImVec2(bar0_pos_x + bars_width, picker_pos.y + (i + 1) * (sv_picker_size / 6)), hue_colors[i], hue_colors[i], hue_colors[i + 1], hue_colors[i + 1]); + float bar0_line_y = (float)(int)(picker_pos.y + H * sv_picker_size + 0.5f); + RenderFrameBorder(ImVec2(bar0_pos_x, picker_pos.y), ImVec2(bar0_pos_x + bars_width, picker_pos.y + sv_picker_size), 0.0f); + RenderArrowsForVerticalBar(draw_list, ImVec2(bar0_pos_x - 1, bar0_line_y), ImVec2(bars_triangles_half_sz + 1, bars_triangles_half_sz), bars_width + 2.0f); + } + + // Render cursor/preview circle (clamp S/V within 0..1 range because floating points colors may lead HSV values to be out of range) + float sv_cursor_rad = value_changed_sv ? 10.0f : 6.0f; + draw_list->AddCircleFilled(sv_cursor_pos, sv_cursor_rad, col32_no_alpha, 12); + draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad+1, IM_COL32(128,128,128,255), 12); + draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad, IM_COL32_WHITE, 12); + + // Render alpha bar + if (alpha_bar) + { + float alpha = ImSaturate(col[3]); + ImRect bar1_bb(bar1_pos_x, picker_pos.y, bar1_pos_x + bars_width, picker_pos.y + sv_picker_size); + RenderColorRectWithAlphaCheckerboard(bar1_bb.Min, bar1_bb.Max, IM_COL32(0,0,0,0), bar1_bb.GetWidth() / 2.0f, ImVec2(0.0f, 0.0f)); + draw_list->AddRectFilledMultiColor(bar1_bb.Min, bar1_bb.Max, col32_no_alpha, col32_no_alpha, col32_no_alpha & ~IM_COL32_A_MASK, col32_no_alpha & ~IM_COL32_A_MASK); + float bar1_line_y = (float)(int)(picker_pos.y + (1.0f - alpha) * sv_picker_size + 0.5f); + RenderFrameBorder(bar1_bb.Min, bar1_bb.Max, 0.0f); + RenderArrowsForVerticalBar(draw_list, ImVec2(bar1_pos_x - 1, bar1_line_y), ImVec2(bars_triangles_half_sz + 1, bars_triangles_half_sz), bars_width + 2.0f); + } + + EndGroup(); + + if (value_changed && memcmp(backup_initial_col, col, components * sizeof(float)) == 0) + value_changed = false; + if (value_changed) + MarkItemEdited(window->DC.LastItemId); + + PopID(); + + return value_changed; +} + +// A little colored square. Return true when clicked. +// FIXME: May want to display/ignore the alpha component in the color display? Yet show it in the tooltip. +// 'desc_id' is not called 'label' because we don't display it next to the button, but only in the tooltip. +bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFlags flags, ImVec2 size) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiID id = window->GetID(desc_id); + float default_size = GetFrameHeight(); + if (size.x == 0.0f) + size.x = default_size; + if (size.y == 0.0f) + size.y = default_size; + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); + ItemSize(bb, (size.y >= default_size) ? g.Style.FramePadding.y : 0.0f); + if (!ItemAdd(bb, id)) + return false; + + bool hovered, held; + bool pressed = ButtonBehavior(bb, id, &hovered, &held); + + if (flags & ImGuiColorEditFlags_NoAlpha) + flags &= ~(ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf); + + ImVec4 col_without_alpha(col.x, col.y, col.z, 1.0f); + float grid_step = ImMin(size.x, size.y) / 2.99f; + float rounding = ImMin(g.Style.FrameRounding, grid_step * 0.5f); + ImRect bb_inner = bb; + float off = -0.75f; // The border (using Col_FrameBg) tends to look off when color is near-opaque and rounding is enabled. This offset seemed like a good middle ground to reduce those artifacts. + bb_inner.Expand(off); + if ((flags & ImGuiColorEditFlags_AlphaPreviewHalf) && col.w < 1.0f) + { + float mid_x = (float)(int)((bb_inner.Min.x + bb_inner.Max.x) * 0.5f + 0.5f); + RenderColorRectWithAlphaCheckerboard(ImVec2(bb_inner.Min.x + grid_step, bb_inner.Min.y), bb_inner.Max, GetColorU32(col), grid_step, ImVec2(-grid_step + off, off), rounding, ImDrawCornerFlags_TopRight| ImDrawCornerFlags_BotRight); + window->DrawList->AddRectFilled(bb_inner.Min, ImVec2(mid_x, bb_inner.Max.y), GetColorU32(col_without_alpha), rounding, ImDrawCornerFlags_TopLeft|ImDrawCornerFlags_BotLeft); + } + else + { + // Because GetColorU32() multiplies by the global style Alpha and we don't want to display a checkerboard if the source code had no alpha + ImVec4 col_source = (flags & ImGuiColorEditFlags_AlphaPreview) ? col : col_without_alpha; + if (col_source.w < 1.0f) + RenderColorRectWithAlphaCheckerboard(bb_inner.Min, bb_inner.Max, GetColorU32(col_source), grid_step, ImVec2(off, off), rounding); + else + window->DrawList->AddRectFilled(bb_inner.Min, bb_inner.Max, GetColorU32(col_source), rounding, ImDrawCornerFlags_All); + } + RenderNavHighlight(bb, id); + if (g.Style.FrameBorderSize > 0.0f) + RenderFrameBorder(bb.Min, bb.Max, rounding); + else + window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), rounding); // Color button are often in need of some sort of border + + // Drag and Drop Source + // NB: The ActiveId test is merely an optional micro-optimization, BeginDragDropSource() does the same test. + if (g.ActiveId == id && !(flags & ImGuiColorEditFlags_NoDragDrop) && BeginDragDropSource()) + { + if (flags & ImGuiColorEditFlags_NoAlpha) + SetDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F, &col, sizeof(float) * 3, ImGuiCond_Once); + else + SetDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F, &col, sizeof(float) * 4, ImGuiCond_Once); + ColorButton(desc_id, col, flags); + SameLine(); + TextUnformatted("Color"); + EndDragDropSource(); + } + + // Tooltip + if (!(flags & ImGuiColorEditFlags_NoTooltip) && hovered) + ColorTooltip(desc_id, &col.x, flags & (ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)); + + if (pressed) + MarkItemEdited(id); + + return pressed; +} + +void ImGui::SetColorEditOptions(ImGuiColorEditFlags flags) +{ + ImGuiContext& g = *GImGui; + if ((flags & ImGuiColorEditFlags__InputsMask) == 0) + flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__InputsMask; + if ((flags & ImGuiColorEditFlags__DataTypeMask) == 0) + flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__DataTypeMask; + if ((flags & ImGuiColorEditFlags__PickerMask) == 0) + flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__PickerMask; + IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__InputsMask))); // Check only 1 option is selected + IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__DataTypeMask))); // Check only 1 option is selected + IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__PickerMask))); // Check only 1 option is selected + g.ColorEditOptions = flags; +} + +// Note: only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set. +void ImGui::ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags flags) +{ + ImGuiContext& g = *GImGui; + + int cr = IM_F32_TO_INT8_SAT(col[0]), cg = IM_F32_TO_INT8_SAT(col[1]), cb = IM_F32_TO_INT8_SAT(col[2]), ca = (flags & ImGuiColorEditFlags_NoAlpha) ? 255 : IM_F32_TO_INT8_SAT(col[3]); + BeginTooltipEx(0, true); + + const char* text_end = text ? FindRenderedTextEnd(text, NULL) : text; + if (text_end > text) + { + TextUnformatted(text, text_end); + Separator(); + } + + ImVec2 sz(g.FontSize * 3 + g.Style.FramePadding.y * 2, g.FontSize * 3 + g.Style.FramePadding.y * 2); + ColorButton("##preview", ImVec4(col[0], col[1], col[2], col[3]), (flags & (ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)) | ImGuiColorEditFlags_NoTooltip, sz); + SameLine(); + if (flags & ImGuiColorEditFlags_NoAlpha) + Text("#%02X%02X%02X\nR: %d, G: %d, B: %d\n(%.3f, %.3f, %.3f)", cr, cg, cb, cr, cg, cb, col[0], col[1], col[2]); + else + Text("#%02X%02X%02X%02X\nR:%d, G:%d, B:%d, A:%d\n(%.3f, %.3f, %.3f, %.3f)", cr, cg, cb, ca, cr, cg, cb, ca, col[0], col[1], col[2], col[3]); + EndTooltip(); +} + +void ImGui::ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags) +{ + bool allow_opt_inputs = !(flags & ImGuiColorEditFlags__InputsMask); + bool allow_opt_datatype = !(flags & ImGuiColorEditFlags__DataTypeMask); + if ((!allow_opt_inputs && !allow_opt_datatype) || !BeginPopup("context")) + return; + ImGuiContext& g = *GImGui; + ImGuiColorEditFlags opts = g.ColorEditOptions; + if (allow_opt_inputs) + { + if (RadioButton("RGB", (opts & ImGuiColorEditFlags_RGB) != 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_RGB; + if (RadioButton("HSV", (opts & ImGuiColorEditFlags_HSV) != 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_HSV; + if (RadioButton("HEX", (opts & ImGuiColorEditFlags_HEX) != 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_HEX; + } + if (allow_opt_datatype) + { + if (allow_opt_inputs) Separator(); + if (RadioButton("0..255", (opts & ImGuiColorEditFlags_Uint8) != 0)) opts = (opts & ~ImGuiColorEditFlags__DataTypeMask) | ImGuiColorEditFlags_Uint8; + if (RadioButton("0.00..1.00", (opts & ImGuiColorEditFlags_Float) != 0)) opts = (opts & ~ImGuiColorEditFlags__DataTypeMask) | ImGuiColorEditFlags_Float; + } + + if (allow_opt_inputs || allow_opt_datatype) + Separator(); + if (Button("Copy as..", ImVec2(-1,0))) + OpenPopup("Copy"); + if (BeginPopup("Copy")) + { + int cr = IM_F32_TO_INT8_SAT(col[0]), cg = IM_F32_TO_INT8_SAT(col[1]), cb = IM_F32_TO_INT8_SAT(col[2]), ca = (flags & ImGuiColorEditFlags_NoAlpha) ? 255 : IM_F32_TO_INT8_SAT(col[3]); + char buf[64]; + ImFormatString(buf, IM_ARRAYSIZE(buf), "(%.3ff, %.3ff, %.3ff, %.3ff)", col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]); + if (Selectable(buf)) + SetClipboardText(buf); + ImFormatString(buf, IM_ARRAYSIZE(buf), "(%d,%d,%d,%d)", cr, cg, cb, ca); + if (Selectable(buf)) + SetClipboardText(buf); + if (flags & ImGuiColorEditFlags_NoAlpha) + ImFormatString(buf, IM_ARRAYSIZE(buf), "0x%02X%02X%02X", cr, cg, cb); + else + ImFormatString(buf, IM_ARRAYSIZE(buf), "0x%02X%02X%02X%02X", cr, cg, cb, ca); + if (Selectable(buf)) + SetClipboardText(buf); + EndPopup(); + } + + g.ColorEditOptions = opts; + EndPopup(); +} + +void ImGui::ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags flags) +{ + bool allow_opt_picker = !(flags & ImGuiColorEditFlags__PickerMask); + bool allow_opt_alpha_bar = !(flags & ImGuiColorEditFlags_NoAlpha) && !(flags & ImGuiColorEditFlags_AlphaBar); + if ((!allow_opt_picker && !allow_opt_alpha_bar) || !ImGui::BeginPopup("context")) + return; + ImGuiContext& g = *GImGui; + if (allow_opt_picker) + { + ImVec2 picker_size(g.FontSize * 8, ImMax(g.FontSize * 8 - (ImGui::GetFrameHeight() + g.Style.ItemInnerSpacing.x), 1.0f)); // FIXME: Picker size copied from main picker function + ImGui::PushItemWidth(picker_size.x); + for (int picker_type = 0; picker_type < 2; picker_type++) + { + // Draw small/thumbnail version of each picker type (over an invisible button for selection) + if (picker_type > 0) ImGui::Separator(); + ImGui::PushID(picker_type); + ImGuiColorEditFlags picker_flags = ImGuiColorEditFlags_NoInputs|ImGuiColorEditFlags_NoOptions|ImGuiColorEditFlags_NoLabel|ImGuiColorEditFlags_NoSidePreview|(flags & ImGuiColorEditFlags_NoAlpha); + if (picker_type == 0) picker_flags |= ImGuiColorEditFlags_PickerHueBar; + if (picker_type == 1) picker_flags |= ImGuiColorEditFlags_PickerHueWheel; + ImVec2 backup_pos = ImGui::GetCursorScreenPos(); + if (ImGui::Selectable("##selectable", false, 0, picker_size)) // By default, Selectable() is closing popup + g.ColorEditOptions = (g.ColorEditOptions & ~ImGuiColorEditFlags__PickerMask) | (picker_flags & ImGuiColorEditFlags__PickerMask); + ImGui::SetCursorScreenPos(backup_pos); + ImVec4 dummy_ref_col; + memcpy(&dummy_ref_col.x, ref_col, sizeof(float) * (picker_flags & ImGuiColorEditFlags_NoAlpha ? 3 : 4)); + ImGui::ColorPicker4("##dummypicker", &dummy_ref_col.x, picker_flags); + ImGui::PopID(); + } + ImGui::PopItemWidth(); + } + if (allow_opt_alpha_bar) + { + if (allow_opt_picker) ImGui::Separator(); + ImGui::CheckboxFlags("Alpha Bar", (unsigned int*)&g.ColorEditOptions, ImGuiColorEditFlags_AlphaBar); + } + ImGui::EndPopup(); +} //------------------------------------------------------------------------- // WIDGETS: Trees From 24dfa0c9574ab3d7581cbb0f7ac742e8825d7f1c Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 30 Aug 2018 15:09:33 +0200 Subject: [PATCH 201/828] Refactor: Moved InputText functions from imgui.cpp to imgui_widgets.cpp (#2036) --- imgui.cpp | 975 ---------------------------------------------- imgui_widgets.cpp | 974 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 974 insertions(+), 975 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 3b5e4555be7a..5effa3f505a0 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -902,10 +902,6 @@ static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* wind static void AddDrawListToDrawData(ImVector* out_list, ImDrawList* draw_list); static void AddWindowToSortedBuffer(ImVector* out_sorted_windows, ImGuiWindow* window); -static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data); -static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end); -static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining = NULL, ImVec2* out_offset = NULL, bool stop_on_new_line = false); - static inline int DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type, const void* data_ptr, const char* format); static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, void* arg_1, const void* arg_2); static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* format); @@ -10075,977 +10071,6 @@ bool ImGui::DragIntRange2(const char* label, int* v_current_min, int* v_current_ return value_changed; } -static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end) -{ - int line_count = 0; - const char* s = text_begin; - while (char c = *s++) // We are only matching for \n so we can ignore UTF-8 decoding - if (c == '\n') - line_count++; - s--; - if (s[0] != '\n' && s[0] != '\r') - line_count++; - *out_text_end = s; - return line_count; -} - -static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining, ImVec2* out_offset, bool stop_on_new_line) -{ - ImFont* font = GImGui->Font; - const float line_height = GImGui->FontSize; - const float scale = line_height / font->FontSize; - - ImVec2 text_size = ImVec2(0,0); - float line_width = 0.0f; - - const ImWchar* s = text_begin; - while (s < text_end) - { - unsigned int c = (unsigned int)(*s++); - if (c == '\n') - { - text_size.x = ImMax(text_size.x, line_width); - text_size.y += line_height; - line_width = 0.0f; - if (stop_on_new_line) - break; - continue; - } - if (c == '\r') - continue; - - const float char_width = font->GetCharAdvance((unsigned short)c) * scale; - line_width += char_width; - } - - if (text_size.x < line_width) - text_size.x = line_width; - - if (out_offset) - *out_offset = ImVec2(line_width, text_size.y + line_height); // offset allow for the possibility of sitting after a trailing \n - - if (line_width > 0 || text_size.y == 0.0f) // whereas size.y will ignore the trailing \n - text_size.y += line_height; - - if (remaining) - *remaining = s; - - return text_size; -} - -// Wrapper for stb_textedit.h to edit text (our wrapper is for: statically sized buffer, single-line, wchar characters. InputText converts between UTF-8 and wchar) -namespace ImGuiStb -{ - -static int STB_TEXTEDIT_STRINGLEN(const STB_TEXTEDIT_STRING* obj) { return obj->CurLenW; } -static ImWchar STB_TEXTEDIT_GETCHAR(const STB_TEXTEDIT_STRING* obj, int idx) { return obj->TextW[idx]; } -static float STB_TEXTEDIT_GETWIDTH(STB_TEXTEDIT_STRING* obj, int line_start_idx, int char_idx) { ImWchar c = obj->TextW[line_start_idx+char_idx]; if (c == '\n') return STB_TEXTEDIT_GETWIDTH_NEWLINE; return GImGui->Font->GetCharAdvance(c) * (GImGui->FontSize / GImGui->Font->FontSize); } -static int STB_TEXTEDIT_KEYTOTEXT(int key) { return key >= 0x10000 ? 0 : key; } -static ImWchar STB_TEXTEDIT_NEWLINE = '\n'; -static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, STB_TEXTEDIT_STRING* obj, int line_start_idx) -{ - const ImWchar* text = obj->TextW.Data; - const ImWchar* text_remaining = NULL; - const ImVec2 size = InputTextCalcTextSizeW(text + line_start_idx, text + obj->CurLenW, &text_remaining, NULL, true); - r->x0 = 0.0f; - r->x1 = size.x; - r->baseline_y_delta = size.y; - r->ymin = 0.0f; - r->ymax = size.y; - r->num_chars = (int)(text_remaining - (text + line_start_idx)); -} - -static bool is_separator(unsigned int c) { return ImCharIsBlankW(c) || c==',' || c==';' || c=='(' || c==')' || c=='{' || c=='}' || c=='[' || c==']' || c=='|'; } -static int is_word_boundary_from_right(STB_TEXTEDIT_STRING* obj, int idx) { return idx > 0 ? (is_separator( obj->TextW[idx-1] ) && !is_separator( obj->TextW[idx] ) ) : 1; } -static int STB_TEXTEDIT_MOVEWORDLEFT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx--; while (idx >= 0 && !is_word_boundary_from_right(obj, idx)) idx--; return idx < 0 ? 0 : idx; } -#ifdef __APPLE__ // FIXME: Move setting to IO structure -static int is_word_boundary_from_left(STB_TEXTEDIT_STRING* obj, int idx) { return idx > 0 ? (!is_separator( obj->TextW[idx-1] ) && is_separator( obj->TextW[idx] ) ) : 1; } -static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_left(obj, idx)) idx++; return idx > len ? len : idx; } -#else -static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_right(obj, idx)) idx++; return idx > len ? len : idx; } -#endif -#define STB_TEXTEDIT_MOVEWORDLEFT STB_TEXTEDIT_MOVEWORDLEFT_IMPL // They need to be #define for stb_textedit.h -#define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_IMPL - -static void STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING* obj, int pos, int n) -{ - ImWchar* dst = obj->TextW.Data + pos; - - // We maintain our buffer length in both UTF-8 and wchar formats - obj->CurLenA -= ImTextCountUtf8BytesFromStr(dst, dst + n); - obj->CurLenW -= n; - - // Offset remaining text - const ImWchar* src = obj->TextW.Data + pos + n; - while (ImWchar c = *src++) - *dst++ = c; - *dst = '\0'; -} - -static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const ImWchar* new_text, int new_text_len) -{ - const bool is_resizable = (obj->UserFlags & ImGuiInputTextFlags_CallbackResize) != 0; - const int text_len = obj->CurLenW; - IM_ASSERT(pos <= text_len); - - const int new_text_len_utf8 = ImTextCountUtf8BytesFromStr(new_text, new_text + new_text_len); - if (!is_resizable && (new_text_len_utf8 + obj->CurLenA + 1 > obj->BufCapacityA)) - return false; - - // Grow internal buffer if needed - if (new_text_len + text_len + 1 > obj->TextW.Size) - { - if (!is_resizable) - return false; - IM_ASSERT(text_len < obj->TextW.Size); - obj->TextW.resize(text_len + ImClamp(new_text_len * 4, 32, ImMax(256, new_text_len)) + 1); - } - - ImWchar* text = obj->TextW.Data; - if (pos != text_len) - memmove(text + pos + new_text_len, text + pos, (size_t)(text_len - pos) * sizeof(ImWchar)); - memcpy(text + pos, new_text, (size_t)new_text_len * sizeof(ImWchar)); - - obj->CurLenW += new_text_len; - obj->CurLenA += new_text_len_utf8; - obj->TextW[obj->CurLenW] = '\0'; - - return true; -} - -// We don't use an enum so we can build even with conflicting symbols (if another user of stb_textedit.h leak their STB_TEXTEDIT_K_* symbols) -#define STB_TEXTEDIT_K_LEFT 0x10000 // keyboard input to move cursor left -#define STB_TEXTEDIT_K_RIGHT 0x10001 // keyboard input to move cursor right -#define STB_TEXTEDIT_K_UP 0x10002 // keyboard input to move cursor up -#define STB_TEXTEDIT_K_DOWN 0x10003 // keyboard input to move cursor down -#define STB_TEXTEDIT_K_LINESTART 0x10004 // keyboard input to move cursor to start of line -#define STB_TEXTEDIT_K_LINEEND 0x10005 // keyboard input to move cursor to end of line -#define STB_TEXTEDIT_K_TEXTSTART 0x10006 // keyboard input to move cursor to start of text -#define STB_TEXTEDIT_K_TEXTEND 0x10007 // keyboard input to move cursor to end of text -#define STB_TEXTEDIT_K_DELETE 0x10008 // keyboard input to delete selection or character under cursor -#define STB_TEXTEDIT_K_BACKSPACE 0x10009 // keyboard input to delete selection or character left of cursor -#define STB_TEXTEDIT_K_UNDO 0x1000A // keyboard input to perform undo -#define STB_TEXTEDIT_K_REDO 0x1000B // keyboard input to perform redo -#define STB_TEXTEDIT_K_WORDLEFT 0x1000C // keyboard input to move cursor left one word -#define STB_TEXTEDIT_K_WORDRIGHT 0x1000D // keyboard input to move cursor right one word -#define STB_TEXTEDIT_K_SHIFT 0x20000 - -#define STB_TEXTEDIT_IMPLEMENTATION -#include "stb_textedit.h" - -} - -void ImGuiInputTextState::OnKeyPressed(int key) -{ - stb_textedit_key(this, &StbState, key); - CursorFollow = true; - CursorAnimReset(); -} - -ImGuiInputTextCallbackData::ImGuiInputTextCallbackData() -{ - memset(this, 0, sizeof(*this)); -} - -// Public API to manipulate UTF-8 text -// We expose UTF-8 to the user (unlike the STB_TEXTEDIT_* functions which are manipulating wchar) -// FIXME: The existence of this rarely exercised code path is a bit of a nuisance. -void ImGuiInputTextCallbackData::DeleteChars(int pos, int bytes_count) -{ - IM_ASSERT(pos + bytes_count <= BufTextLen); - char* dst = Buf + pos; - const char* src = Buf + pos + bytes_count; - while (char c = *src++) - *dst++ = c; - *dst = '\0'; - - if (CursorPos + bytes_count >= pos) - CursorPos -= bytes_count; - else if (CursorPos >= pos) - CursorPos = pos; - SelectionStart = SelectionEnd = CursorPos; - BufDirty = true; - BufTextLen -= bytes_count; -} - -void ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, const char* new_text_end) -{ - const bool is_resizable = (Flags & ImGuiInputTextFlags_CallbackResize) != 0; - const int new_text_len = new_text_end ? (int)(new_text_end - new_text) : (int)strlen(new_text); - if (new_text_len + BufTextLen >= BufSize) - { - if (!is_resizable) - return; - - // Contrary to STB_TEXTEDIT_INSERTCHARS() this is working in the UTF8 buffer, hence the midly similar code (until we remove the U16 buffer alltogether!) - ImGuiContext& g = *GImGui; - ImGuiInputTextState* edit_state = &g.InputTextState; - IM_ASSERT(edit_state->ID != 0 && g.ActiveId == edit_state->ID); - IM_ASSERT(Buf == edit_state->TempBuffer.Data); - int new_buf_size = BufTextLen + ImClamp(new_text_len * 4, 32, ImMax(256, new_text_len)) + 1; - edit_state->TempBuffer.reserve(new_buf_size + 1); - Buf = edit_state->TempBuffer.Data; - BufSize = edit_state->BufCapacityA = new_buf_size; - } - - if (BufTextLen != pos) - memmove(Buf + pos + new_text_len, Buf + pos, (size_t)(BufTextLen - pos)); - memcpy(Buf + pos, new_text, (size_t)new_text_len * sizeof(char)); - Buf[BufTextLen + new_text_len] = '\0'; - - if (CursorPos >= pos) - CursorPos += new_text_len; - SelectionStart = SelectionEnd = CursorPos; - BufDirty = true; - BufTextLen += new_text_len; -} - -// Return false to discard a character. -static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data) -{ - unsigned int c = *p_char; - - if (c < 128 && c != ' ' && !isprint((int)(c & 0xFF))) - { - bool pass = false; - pass |= (c == '\n' && (flags & ImGuiInputTextFlags_Multiline)); - pass |= (c == '\t' && (flags & ImGuiInputTextFlags_AllowTabInput)); - if (!pass) - return false; - } - - if (c >= 0xE000 && c <= 0xF8FF) // Filter private Unicode range. I don't imagine anybody would want to input them. GLFW on OSX seems to send private characters for special keys like arrow keys. - return false; - - if (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_CharsNoBlank | ImGuiInputTextFlags_CharsScientific)) - { - if (flags & ImGuiInputTextFlags_CharsDecimal) - if (!(c >= '0' && c <= '9') && (c != '.') && (c != '-') && (c != '+') && (c != '*') && (c != '/')) - return false; - - if (flags & ImGuiInputTextFlags_CharsScientific) - if (!(c >= '0' && c <= '9') && (c != '.') && (c != '-') && (c != '+') && (c != '*') && (c != '/') && (c != 'e') && (c != 'E')) - return false; - - if (flags & ImGuiInputTextFlags_CharsHexadecimal) - if (!(c >= '0' && c <= '9') && !(c >= 'a' && c <= 'f') && !(c >= 'A' && c <= 'F')) - return false; - - if (flags & ImGuiInputTextFlags_CharsUppercase) - if (c >= 'a' && c <= 'z') - *p_char = (c += (unsigned int)('A'-'a')); - - if (flags & ImGuiInputTextFlags_CharsNoBlank) - if (ImCharIsBlankW(c)) - return false; - } - - if (flags & ImGuiInputTextFlags_CallbackCharFilter) - { - ImGuiInputTextCallbackData callback_data; - memset(&callback_data, 0, sizeof(ImGuiInputTextCallbackData)); - callback_data.EventFlag = ImGuiInputTextFlags_CallbackCharFilter; - callback_data.EventChar = (ImWchar)c; - callback_data.Flags = flags; - callback_data.UserData = user_data; - if (callback(&callback_data) != 0) - return false; - *p_char = callback_data.EventChar; - if (!callback_data.EventChar) - return false; - } - - return true; -} - -// Edit a string of text -// - buf_size account for the zero-terminator, so a buf_size of 6 can hold "Hello" but not "Hello!". -// This is so we can easily call InputText() on static arrays using ARRAYSIZE() and to match -// Note that in std::string world, capacity() would omit 1 byte used by the zero-terminator. -// - When active, hold on a privately held copy of the text (and apply back to 'buf'). So changing 'buf' while the InputText is active has no effect. -// - If you want to use ImGui::InputText() with std::string, see misc/stl/imgui_stl.h -// (FIXME: Rather messy function partly because we are doing UTF8 > u16 > UTF8 conversions on the go to more easily handle stb_textedit calls. Ideally we should stay in UTF-8 all the time. See https://github.com/nothings/stb/issues/188) -bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* callback_user_data) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackHistory) && (flags & ImGuiInputTextFlags_Multiline))); // Can't use both together (they both use up/down keys) - IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackCompletion) && (flags & ImGuiInputTextFlags_AllowTabInput))); // Can't use both together (they both use tab key) - - ImGuiContext& g = *GImGui; - const ImGuiIO& io = g.IO; - const ImGuiStyle& style = g.Style; - - const bool is_multiline = (flags & ImGuiInputTextFlags_Multiline) != 0; - const bool is_editable = (flags & ImGuiInputTextFlags_ReadOnly) == 0; - const bool is_password = (flags & ImGuiInputTextFlags_Password) != 0; - const bool is_undoable = (flags & ImGuiInputTextFlags_NoUndoRedo) == 0; - const bool is_resizable = (flags & ImGuiInputTextFlags_CallbackResize) != 0; - if (is_resizable) - IM_ASSERT(callback != NULL); // Must provide a callback if you set the ImGuiInputTextFlags_CallbackResize flag! - - if (is_multiline) // Open group before calling GetID() because groups tracks id created within their scope, - BeginGroup(); - const ImGuiID id = window->GetID(label); - const ImVec2 label_size = CalcTextSize(label, NULL, true); - ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), (is_multiline ? GetTextLineHeight() * 8.0f : label_size.y) + style.FramePadding.y*2.0f); // Arbitrary default of 8 lines high for multi-line - const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size); - const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? (style.ItemInnerSpacing.x + label_size.x) : 0.0f, 0.0f)); - - ImGuiWindow* draw_window = window; - if (is_multiline) - { - ItemAdd(total_bb, id, &frame_bb); - if (!BeginChildFrame(id, frame_bb.GetSize())) - { - EndChildFrame(); - EndGroup(); - return false; - } - draw_window = GetCurrentWindow(); - draw_window->DC.NavLayerActiveMaskNext |= draw_window->DC.NavLayerCurrentMask; // This is to ensure that EndChild() will display a navigation highlight - size.x -= draw_window->ScrollbarSizes.x; - } - else - { - ItemSize(total_bb, style.FramePadding.y); - if (!ItemAdd(total_bb, id, &frame_bb)) - return false; - } - const bool hovered = ItemHoverable(frame_bb, id); - if (hovered) - g.MouseCursor = ImGuiMouseCursor_TextInput; - - // Password pushes a temporary font with only a fallback glyph - if (is_password) - { - const ImFontGlyph* glyph = g.Font->FindGlyph('*'); - ImFont* password_font = &g.InputTextPasswordFont; - password_font->FontSize = g.Font->FontSize; - password_font->Scale = g.Font->Scale; - password_font->DisplayOffset = g.Font->DisplayOffset; - password_font->Ascent = g.Font->Ascent; - password_font->Descent = g.Font->Descent; - password_font->ContainerAtlas = g.Font->ContainerAtlas; - password_font->FallbackGlyph = glyph; - password_font->FallbackAdvanceX = glyph->AdvanceX; - IM_ASSERT(password_font->Glyphs.empty() && password_font->IndexAdvanceX.empty() && password_font->IndexLookup.empty()); - PushFont(password_font); - } - - // NB: we are only allowed to access 'edit_state' if we are the active widget. - ImGuiInputTextState& edit_state = g.InputTextState; - - const bool focus_requested = FocusableItemRegister(window, id, (flags & (ImGuiInputTextFlags_CallbackCompletion|ImGuiInputTextFlags_AllowTabInput)) == 0); // Using completion callback disable keyboard tabbing - const bool focus_requested_by_code = focus_requested && (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent); - const bool focus_requested_by_tab = focus_requested && !focus_requested_by_code; - - const bool user_clicked = hovered && io.MouseClicked[0]; - const bool user_scrolled = is_multiline && g.ActiveId == 0 && edit_state.ID == id && g.ActiveIdPreviousFrame == draw_window->GetIDNoKeepAlive("#SCROLLY"); - const bool user_nav_input_start = (g.ActiveId != id) && ((g.NavInputId == id) || (g.NavActivateId == id && g.NavInputSource == ImGuiInputSource_NavKeyboard)); - - bool clear_active_id = false; - - bool select_all = (g.ActiveId != id) && ((flags & ImGuiInputTextFlags_AutoSelectAll) != 0 || user_nav_input_start) && (!is_multiline); - if (focus_requested || user_clicked || user_scrolled || user_nav_input_start) - { - if (g.ActiveId != id) - { - // Start edition - // Take a copy of the initial buffer value (both in original UTF-8 format and converted to wchar) - // From the moment we focused we are ignoring the content of 'buf' (unless we are in read-only mode) - const int prev_len_w = edit_state.CurLenW; - const int init_buf_len = (int)strlen(buf); - edit_state.TextW.resize(buf_size+1); // wchar count <= UTF-8 count. we use +1 to make sure that .Data isn't NULL so it doesn't crash. - edit_state.InitialText.resize(init_buf_len + 1); // UTF-8. we use +1 to make sure that .Data isn't NULL so it doesn't crash. - memcpy(edit_state.InitialText.Data, buf, init_buf_len + 1); - const char* buf_end = NULL; - edit_state.CurLenW = ImTextStrFromUtf8(edit_state.TextW.Data, buf_size, buf, NULL, &buf_end); - edit_state.CurLenA = (int)(buf_end - buf); // We can't get the result from ImStrncpy() above because it is not UTF-8 aware. Here we'll cut off malformed UTF-8. - edit_state.CursorAnimReset(); - - // Preserve cursor position and undo/redo stack if we come back to same widget - // FIXME: We should probably compare the whole buffer to be on the safety side. Comparing buf (utf8) and edit_state.Text (wchar). - const bool recycle_state = (edit_state.ID == id) && (prev_len_w == edit_state.CurLenW); - if (recycle_state) - { - // Recycle existing cursor/selection/undo stack but clamp position - // Note a single mouse click will override the cursor/position immediately by calling stb_textedit_click handler. - edit_state.CursorClamp(); - } - else - { - edit_state.ID = id; - edit_state.ScrollX = 0.0f; - stb_textedit_initialize_state(&edit_state.StbState, !is_multiline); - if (!is_multiline && focus_requested_by_code) - select_all = true; - } - if (flags & ImGuiInputTextFlags_AlwaysInsertMode) - edit_state.StbState.insert_mode = true; - if (!is_multiline && (focus_requested_by_tab || (user_clicked && io.KeyCtrl))) - select_all = true; - } - SetActiveID(id, window); - SetFocusID(id, window); - FocusWindow(window); - if (!is_multiline && !(flags & ImGuiInputTextFlags_CallbackHistory)) - g.ActiveIdAllowNavDirFlags |= ((1 << ImGuiDir_Up) | (1 << ImGuiDir_Down)); - } - else if (io.MouseClicked[0]) - { - // Release focus when we click outside - clear_active_id = true; - } - - bool value_changed = false; - bool enter_pressed = false; - int backup_current_text_length = 0; - - if (g.ActiveId == id) - { - if (!is_editable && !g.ActiveIdIsJustActivated) - { - // When read-only we always use the live data passed to the function - edit_state.TextW.resize(buf_size+1); - const char* buf_end = NULL; - edit_state.CurLenW = ImTextStrFromUtf8(edit_state.TextW.Data, edit_state.TextW.Size, buf, NULL, &buf_end); - edit_state.CurLenA = (int)(buf_end - buf); - edit_state.CursorClamp(); - } - - backup_current_text_length = edit_state.CurLenA; - edit_state.BufCapacityA = buf_size; - edit_state.UserFlags = flags; - edit_state.UserCallback = callback; - edit_state.UserCallbackData = callback_user_data; - - // Although we are active we don't prevent mouse from hovering other elements unless we are interacting right now with the widget. - // Down the line we should have a cleaner library-wide concept of Selected vs Active. - g.ActiveIdAllowOverlap = !io.MouseDown[0]; - g.WantTextInputNextFrame = 1; - - // Edit in progress - const float mouse_x = (io.MousePos.x - frame_bb.Min.x - style.FramePadding.x) + edit_state.ScrollX; - const float mouse_y = (is_multiline ? (io.MousePos.y - draw_window->DC.CursorPos.y - style.FramePadding.y) : (g.FontSize*0.5f)); - - const bool is_osx = io.ConfigMacOSXBehaviors; - if (select_all || (hovered && !is_osx && io.MouseDoubleClicked[0])) - { - edit_state.SelectAll(); - edit_state.SelectedAllMouseLock = true; - } - else if (hovered && is_osx && io.MouseDoubleClicked[0]) - { - // Double-click select a word only, OS X style (by simulating keystrokes) - edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT); - edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT); - } - else if (io.MouseClicked[0] && !edit_state.SelectedAllMouseLock) - { - if (hovered) - { - stb_textedit_click(&edit_state, &edit_state.StbState, mouse_x, mouse_y); - edit_state.CursorAnimReset(); - } - } - else if (io.MouseDown[0] && !edit_state.SelectedAllMouseLock && (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f)) - { - stb_textedit_drag(&edit_state, &edit_state.StbState, mouse_x, mouse_y); - edit_state.CursorAnimReset(); - edit_state.CursorFollow = true; - } - if (edit_state.SelectedAllMouseLock && !io.MouseDown[0]) - edit_state.SelectedAllMouseLock = false; - - if (io.InputCharacters[0]) - { - // Process text input (before we check for Return because using some IME will effectively send a Return?) - // We ignore CTRL inputs, but need to allow ALT+CTRL as some keyboards (e.g. German) use AltGR (which _is_ Alt+Ctrl) to input certain characters. - bool ignore_inputs = (io.KeyCtrl && !io.KeyAlt) || (is_osx && io.KeySuper); - if (!ignore_inputs && is_editable && !user_nav_input_start) - for (int n = 0; n < IM_ARRAYSIZE(io.InputCharacters) && io.InputCharacters[n]; n++) - { - // Insert character if they pass filtering - unsigned int c = (unsigned int)io.InputCharacters[n]; - if (InputTextFilterCharacter(&c, flags, callback, callback_user_data)) - edit_state.OnKeyPressed((int)c); - } - - // Consume characters - memset(g.IO.InputCharacters, 0, sizeof(g.IO.InputCharacters)); - } - } - - bool cancel_edit = false; - if (g.ActiveId == id && !g.ActiveIdIsJustActivated && !clear_active_id) - { - // Handle key-presses - const int k_mask = (io.KeyShift ? STB_TEXTEDIT_K_SHIFT : 0); - const bool is_osx = io.ConfigMacOSXBehaviors; - const bool is_shortcut_key = (is_osx ? (io.KeySuper && !io.KeyCtrl) : (io.KeyCtrl && !io.KeySuper)) && !io.KeyAlt && !io.KeyShift; // OS X style: Shortcuts using Cmd/Super instead of Ctrl - const bool is_osx_shift_shortcut = is_osx && io.KeySuper && io.KeyShift && !io.KeyCtrl && !io.KeyAlt; - const bool is_wordmove_key_down = is_osx ? io.KeyAlt : io.KeyCtrl; // OS X style: Text editing cursor movement using Alt instead of Ctrl - const bool is_startend_key_down = is_osx && io.KeySuper && !io.KeyCtrl && !io.KeyAlt; // OS X style: Line/Text Start and End using Cmd+Arrows instead of Home/End - const bool is_ctrl_key_only = io.KeyCtrl && !io.KeyShift && !io.KeyAlt && !io.KeySuper; - const bool is_shift_key_only = io.KeyShift && !io.KeyCtrl && !io.KeyAlt && !io.KeySuper; - - const bool is_cut = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_X)) || (is_shift_key_only && IsKeyPressedMap(ImGuiKey_Delete))) && is_editable && !is_password && (!is_multiline || edit_state.HasSelection()); - const bool is_copy = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_C)) || (is_ctrl_key_only && IsKeyPressedMap(ImGuiKey_Insert))) && !is_password && (!is_multiline || edit_state.HasSelection()); - const bool is_paste = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_V)) || (is_shift_key_only && IsKeyPressedMap(ImGuiKey_Insert))) && is_editable; - const bool is_undo = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_Z)) && is_editable && is_undoable); - const bool is_redo = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_Y)) || (is_osx_shift_shortcut && IsKeyPressedMap(ImGuiKey_Z))) && is_editable && is_undoable; - - if (IsKeyPressedMap(ImGuiKey_LeftArrow)) { edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINESTART : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDLEFT : STB_TEXTEDIT_K_LEFT) | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_RightArrow)) { edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINEEND : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDRIGHT : STB_TEXTEDIT_K_RIGHT) | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_UpArrow) && is_multiline) { if (io.KeyCtrl) SetWindowScrollY(draw_window, ImMax(draw_window->Scroll.y - g.FontSize, 0.0f)); else edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTSTART : STB_TEXTEDIT_K_UP) | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_DownArrow) && is_multiline) { if (io.KeyCtrl) SetWindowScrollY(draw_window, ImMin(draw_window->Scroll.y + g.FontSize, GetScrollMaxY())); else edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTEND : STB_TEXTEDIT_K_DOWN) | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_Home)) { edit_state.OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTSTART | k_mask : STB_TEXTEDIT_K_LINESTART | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_End)) { edit_state.OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTEND | k_mask : STB_TEXTEDIT_K_LINEEND | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_Delete) && is_editable) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_Backspace) && is_editable) - { - if (!edit_state.HasSelection()) - { - if (is_wordmove_key_down) edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT|STB_TEXTEDIT_K_SHIFT); - else if (is_osx && io.KeySuper && !io.KeyAlt && !io.KeyCtrl) edit_state.OnKeyPressed(STB_TEXTEDIT_K_LINESTART|STB_TEXTEDIT_K_SHIFT); - } - edit_state.OnKeyPressed(STB_TEXTEDIT_K_BACKSPACE | k_mask); - } - else if (IsKeyPressedMap(ImGuiKey_Enter)) - { - bool ctrl_enter_for_new_line = (flags & ImGuiInputTextFlags_CtrlEnterForNewLine) != 0; - if (!is_multiline || (ctrl_enter_for_new_line && !io.KeyCtrl) || (!ctrl_enter_for_new_line && io.KeyCtrl)) - { - enter_pressed = clear_active_id = true; - } - else if (is_editable) - { - unsigned int c = '\n'; // Insert new line - if (InputTextFilterCharacter(&c, flags, callback, callback_user_data)) - edit_state.OnKeyPressed((int)c); - } - } - else if ((flags & ImGuiInputTextFlags_AllowTabInput) && IsKeyPressedMap(ImGuiKey_Tab) && !io.KeyCtrl && !io.KeyShift && !io.KeyAlt && is_editable) - { - unsigned int c = '\t'; // Insert TAB - if (InputTextFilterCharacter(&c, flags, callback, callback_user_data)) - edit_state.OnKeyPressed((int)c); - } - else if (IsKeyPressedMap(ImGuiKey_Escape)) - { - clear_active_id = cancel_edit = true; - } - else if (is_undo || is_redo) - { - edit_state.OnKeyPressed(is_undo ? STB_TEXTEDIT_K_UNDO : STB_TEXTEDIT_K_REDO); - edit_state.ClearSelection(); - } - else if (is_shortcut_key && IsKeyPressedMap(ImGuiKey_A)) - { - edit_state.SelectAll(); - edit_state.CursorFollow = true; - } - else if (is_cut || is_copy) - { - // Cut, Copy - if (io.SetClipboardTextFn) - { - const int ib = edit_state.HasSelection() ? ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end) : 0; - const int ie = edit_state.HasSelection() ? ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end) : edit_state.CurLenW; - edit_state.TempBuffer.resize((ie-ib) * 4 + 1); - ImTextStrToUtf8(edit_state.TempBuffer.Data, edit_state.TempBuffer.Size, edit_state.TextW.Data+ib, edit_state.TextW.Data+ie); - SetClipboardText(edit_state.TempBuffer.Data); - } - if (is_cut) - { - if (!edit_state.HasSelection()) - edit_state.SelectAll(); - edit_state.CursorFollow = true; - stb_textedit_cut(&edit_state, &edit_state.StbState); - } - } - else if (is_paste) - { - if (const char* clipboard = GetClipboardText()) - { - // Filter pasted buffer - const int clipboard_len = (int)strlen(clipboard); - ImWchar* clipboard_filtered = (ImWchar*)ImGui::MemAlloc((clipboard_len+1) * sizeof(ImWchar)); - int clipboard_filtered_len = 0; - for (const char* s = clipboard; *s; ) - { - unsigned int c; - s += ImTextCharFromUtf8(&c, s, NULL); - if (c == 0) - break; - if (c >= 0x10000 || !InputTextFilterCharacter(&c, flags, callback, callback_user_data)) - continue; - clipboard_filtered[clipboard_filtered_len++] = (ImWchar)c; - } - clipboard_filtered[clipboard_filtered_len] = 0; - if (clipboard_filtered_len > 0) // If everything was filtered, ignore the pasting operation - { - stb_textedit_paste(&edit_state, &edit_state.StbState, clipboard_filtered, clipboard_filtered_len); - edit_state.CursorFollow = true; - } - ImGui::MemFree(clipboard_filtered); - } - } - } - - if (g.ActiveId == id) - { - const char* apply_new_text = NULL; - int apply_new_text_length = 0; - if (cancel_edit) - { - // Restore initial value. Only return true if restoring to the initial value changes the current buffer contents. - if (is_editable && strcmp(buf, edit_state.InitialText.Data) != 0) - { - apply_new_text = edit_state.InitialText.Data; - apply_new_text_length = edit_state.InitialText.Size - 1; - } - } - - // When using 'ImGuiInputTextFlags_EnterReturnsTrue' as a special case we reapply the live buffer back to the input buffer before clearing ActiveId, even though strictly speaking it wasn't modified on this frame. - // If we didn't do that, code like InputInt() with ImGuiInputTextFlags_EnterReturnsTrue would fail. Also this allows the user to use InputText() with ImGuiInputTextFlags_EnterReturnsTrue without maintaining any user-side storage. - bool apply_edit_back_to_user_buffer = !cancel_edit || (enter_pressed && (flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0); - if (apply_edit_back_to_user_buffer) - { - // Apply new value immediately - copy modified buffer back - // Note that as soon as the input box is active, the in-widget value gets priority over any underlying modification of the input buffer - // FIXME: We actually always render 'buf' when calling DrawList->AddText, making the comment above incorrect. - // FIXME-OPT: CPU waste to do this every time the widget is active, should mark dirty state from the stb_textedit callbacks. - if (is_editable) - { - edit_state.TempBuffer.resize(edit_state.TextW.Size * 4 + 1); - ImTextStrToUtf8(edit_state.TempBuffer.Data, edit_state.TempBuffer.Size, edit_state.TextW.Data, NULL); - } - - // User callback - if ((flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory | ImGuiInputTextFlags_CallbackAlways)) != 0) - { - IM_ASSERT(callback != NULL); - - // The reason we specify the usage semantic (Completion/History) is that Completion needs to disable keyboard TABBING at the moment. - ImGuiInputTextFlags event_flag = 0; - ImGuiKey event_key = ImGuiKey_COUNT; - if ((flags & ImGuiInputTextFlags_CallbackCompletion) != 0 && IsKeyPressedMap(ImGuiKey_Tab)) - { - event_flag = ImGuiInputTextFlags_CallbackCompletion; - event_key = ImGuiKey_Tab; - } - else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_UpArrow)) - { - event_flag = ImGuiInputTextFlags_CallbackHistory; - event_key = ImGuiKey_UpArrow; - } - else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_DownArrow)) - { - event_flag = ImGuiInputTextFlags_CallbackHistory; - event_key = ImGuiKey_DownArrow; - } - else if (flags & ImGuiInputTextFlags_CallbackAlways) - event_flag = ImGuiInputTextFlags_CallbackAlways; - - if (event_flag) - { - ImGuiInputTextCallbackData callback_data; - memset(&callback_data, 0, sizeof(ImGuiInputTextCallbackData)); - callback_data.EventFlag = event_flag; - callback_data.Flags = flags; - callback_data.UserData = callback_user_data; - - callback_data.EventKey = event_key; - callback_data.Buf = edit_state.TempBuffer.Data; - callback_data.BufTextLen = edit_state.CurLenA; - callback_data.BufSize = edit_state.BufCapacityA; - callback_data.BufDirty = false; - - // We have to convert from wchar-positions to UTF-8-positions, which can be pretty slow (an incentive to ditch the ImWchar buffer, see https://github.com/nothings/stb/issues/188) - ImWchar* text = edit_state.TextW.Data; - const int utf8_cursor_pos = callback_data.CursorPos = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.cursor); - const int utf8_selection_start = callback_data.SelectionStart = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.select_start); - const int utf8_selection_end = callback_data.SelectionEnd = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.select_end); - - // Call user code - callback(&callback_data); - - // Read back what user may have modified - IM_ASSERT(callback_data.Buf == edit_state.TempBuffer.Data); // Invalid to modify those fields - IM_ASSERT(callback_data.BufSize == edit_state.BufCapacityA); - IM_ASSERT(callback_data.Flags == flags); - if (callback_data.CursorPos != utf8_cursor_pos) { edit_state.StbState.cursor = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.CursorPos); edit_state.CursorFollow = true; } - if (callback_data.SelectionStart != utf8_selection_start) { edit_state.StbState.select_start = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionStart); } - if (callback_data.SelectionEnd != utf8_selection_end) { edit_state.StbState.select_end = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionEnd); } - if (callback_data.BufDirty) - { - IM_ASSERT(callback_data.BufTextLen == (int)strlen(callback_data.Buf)); // You need to maintain BufTextLen if you change the text! - if (callback_data.BufTextLen > backup_current_text_length && is_resizable) - edit_state.TextW.resize(edit_state.TextW.Size + (callback_data.BufTextLen - backup_current_text_length)); - edit_state.CurLenW = ImTextStrFromUtf8(edit_state.TextW.Data, edit_state.TextW.Size, callback_data.Buf, NULL); - edit_state.CurLenA = callback_data.BufTextLen; // Assume correct length and valid UTF-8 from user, saves us an extra strlen() - edit_state.CursorAnimReset(); - } - } - } - - // Will copy result string if modified - if (is_editable && strcmp(edit_state.TempBuffer.Data, buf) != 0) - { - apply_new_text = edit_state.TempBuffer.Data; - apply_new_text_length = edit_state.CurLenA; - } - } - - // Copy result to user buffer - if (apply_new_text) - { - IM_ASSERT(apply_new_text_length >= 0); - if (backup_current_text_length != apply_new_text_length && is_resizable) - { - ImGuiInputTextCallbackData callback_data; - callback_data.EventFlag = ImGuiInputTextFlags_CallbackResize; - callback_data.Flags = flags; - callback_data.Buf = buf; - callback_data.BufTextLen = apply_new_text_length; - callback_data.BufSize = ImMax(buf_size, apply_new_text_length + 1); - callback_data.UserData = callback_user_data; - callback(&callback_data); - buf = callback_data.Buf; - buf_size = callback_data.BufSize; - apply_new_text_length = ImMin(callback_data.BufTextLen, buf_size - 1); - IM_ASSERT(apply_new_text_length <= buf_size); - } - - // If the underlying buffer resize was denied or not carried to the next frame, apply_new_text_length+1 may be >= buf_size. - ImStrncpy(buf, edit_state.TempBuffer.Data, ImMin(apply_new_text_length + 1, buf_size)); - value_changed = true; - } - - // Clear temporary user storage - edit_state.UserFlags = 0; - edit_state.UserCallback = NULL; - edit_state.UserCallbackData = NULL; - } - - // Release active ID at the end of the function (so e.g. pressing Return still does a final application of the value) - if (clear_active_id && g.ActiveId == id) - ClearActiveID(); - - // Render - // Select which buffer we are going to display. When ImGuiInputTextFlags_NoLiveEdit is set 'buf' might still be the old value. We set buf to NULL to prevent accidental usage from now on. - const char* buf_display = (g.ActiveId == id && is_editable) ? edit_state.TempBuffer.Data : buf; buf = NULL; - - // Set upper limit of single-line InputTextEx() at 2 million characters strings. The current pathological worst case is a long line - // without any carriage return, which would makes ImFont::RenderText() reserve too many vertices and probably crash. Avoid it altogether. - // Note that we only use this limit on single-line InputText(), so a pathologically large line on a InputTextMultiline() would still crash. - const int buf_display_max_length = 2 * 1024 * 1024; - - if (!is_multiline) - { - RenderNavHighlight(frame_bb, id); - RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); - } - - const ImVec4 clip_rect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + size.x, frame_bb.Min.y + size.y); // Not using frame_bb.Max because we have adjusted size - ImVec2 render_pos = is_multiline ? draw_window->DC.CursorPos : frame_bb.Min + style.FramePadding; - ImVec2 text_size(0.f, 0.f); - const bool is_currently_scrolling = (edit_state.ID == id && is_multiline && g.ActiveId == draw_window->GetIDNoKeepAlive("#SCROLLY")); - if (g.ActiveId == id || is_currently_scrolling) - { - edit_state.CursorAnim += io.DeltaTime; - - // This is going to be messy. We need to: - // - Display the text (this alone can be more easily clipped) - // - Handle scrolling, highlight selection, display cursor (those all requires some form of 1d->2d cursor position calculation) - // - Measure text height (for scrollbar) - // We are attempting to do most of that in **one main pass** to minimize the computation cost (non-negligible for large amount of text) + 2nd pass for selection rendering (we could merge them by an extra refactoring effort) - // FIXME: This should occur on buf_display but we'd need to maintain cursor/select_start/select_end for UTF-8. - const ImWchar* text_begin = edit_state.TextW.Data; - ImVec2 cursor_offset, select_start_offset; - - { - // Count lines + find lines numbers straddling 'cursor' and 'select_start' position. - const ImWchar* searches_input_ptr[2]; - searches_input_ptr[0] = text_begin + edit_state.StbState.cursor; - searches_input_ptr[1] = NULL; - int searches_remaining = 1; - int searches_result_line_number[2] = { -1, -999 }; - if (edit_state.StbState.select_start != edit_state.StbState.select_end) - { - searches_input_ptr[1] = text_begin + ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end); - searches_result_line_number[1] = -1; - searches_remaining++; - } - - // Iterate all lines to find our line numbers - // In multi-line mode, we never exit the loop until all lines are counted, so add one extra to the searches_remaining counter. - searches_remaining += is_multiline ? 1 : 0; - int line_count = 0; - for (const ImWchar* s = text_begin; *s != 0; s++) - if (*s == '\n') - { - line_count++; - if (searches_result_line_number[0] == -1 && s >= searches_input_ptr[0]) { searches_result_line_number[0] = line_count; if (--searches_remaining <= 0) break; } - if (searches_result_line_number[1] == -1 && s >= searches_input_ptr[1]) { searches_result_line_number[1] = line_count; if (--searches_remaining <= 0) break; } - } - line_count++; - if (searches_result_line_number[0] == -1) searches_result_line_number[0] = line_count; - if (searches_result_line_number[1] == -1) searches_result_line_number[1] = line_count; - - // Calculate 2d position by finding the beginning of the line and measuring distance - cursor_offset.x = InputTextCalcTextSizeW(ImStrbolW(searches_input_ptr[0], text_begin), searches_input_ptr[0]).x; - cursor_offset.y = searches_result_line_number[0] * g.FontSize; - if (searches_result_line_number[1] >= 0) - { - select_start_offset.x = InputTextCalcTextSizeW(ImStrbolW(searches_input_ptr[1], text_begin), searches_input_ptr[1]).x; - select_start_offset.y = searches_result_line_number[1] * g.FontSize; - } - - // Store text height (note that we haven't calculated text width at all, see GitHub issues #383, #1224) - if (is_multiline) - text_size = ImVec2(size.x, line_count * g.FontSize); - } - - // Scroll - if (edit_state.CursorFollow) - { - // Horizontal scroll in chunks of quarter width - if (!(flags & ImGuiInputTextFlags_NoHorizontalScroll)) - { - const float scroll_increment_x = size.x * 0.25f; - if (cursor_offset.x < edit_state.ScrollX) - edit_state.ScrollX = (float)(int)ImMax(0.0f, cursor_offset.x - scroll_increment_x); - else if (cursor_offset.x - size.x >= edit_state.ScrollX) - edit_state.ScrollX = (float)(int)(cursor_offset.x - size.x + scroll_increment_x); - } - else - { - edit_state.ScrollX = 0.0f; - } - - // Vertical scroll - if (is_multiline) - { - float scroll_y = draw_window->Scroll.y; - if (cursor_offset.y - g.FontSize < scroll_y) - scroll_y = ImMax(0.0f, cursor_offset.y - g.FontSize); - else if (cursor_offset.y - size.y >= scroll_y) - scroll_y = cursor_offset.y - size.y; - draw_window->DC.CursorPos.y += (draw_window->Scroll.y - scroll_y); // To avoid a frame of lag - draw_window->Scroll.y = scroll_y; - render_pos.y = draw_window->DC.CursorPos.y; - } - } - edit_state.CursorFollow = false; - const ImVec2 render_scroll = ImVec2(edit_state.ScrollX, 0.0f); - - // Draw selection - if (edit_state.StbState.select_start != edit_state.StbState.select_end) - { - const ImWchar* text_selected_begin = text_begin + ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end); - const ImWchar* text_selected_end = text_begin + ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end); - - float bg_offy_up = is_multiline ? 0.0f : -1.0f; // FIXME: those offsets should be part of the style? they don't play so well with multi-line selection. - float bg_offy_dn = is_multiline ? 0.0f : 2.0f; - ImU32 bg_color = GetColorU32(ImGuiCol_TextSelectedBg); - ImVec2 rect_pos = render_pos + select_start_offset - render_scroll; - for (const ImWchar* p = text_selected_begin; p < text_selected_end; ) - { - if (rect_pos.y > clip_rect.w + g.FontSize) - break; - if (rect_pos.y < clip_rect.y) - { - while (p < text_selected_end) - if (*p++ == '\n') - break; - } - else - { - ImVec2 rect_size = InputTextCalcTextSizeW(p, text_selected_end, &p, NULL, true); - if (rect_size.x <= 0.0f) rect_size.x = (float)(int)(g.Font->GetCharAdvance((unsigned short)' ') * 0.50f); // So we can see selected empty lines - ImRect rect(rect_pos + ImVec2(0.0f, bg_offy_up - g.FontSize), rect_pos +ImVec2(rect_size.x, bg_offy_dn)); - rect.ClipWith(clip_rect); - if (rect.Overlaps(clip_rect)) - draw_window->DrawList->AddRectFilled(rect.Min, rect.Max, bg_color); - } - rect_pos.x = render_pos.x - render_scroll.x; - rect_pos.y += g.FontSize; - } - } - - const int buf_display_len = edit_state.CurLenA; - if (is_multiline || buf_display_len < buf_display_max_length) - draw_window->DrawList->AddText(g.Font, g.FontSize, render_pos - render_scroll, GetColorU32(ImGuiCol_Text), buf_display, buf_display + buf_display_len, 0.0f, is_multiline ? NULL : &clip_rect); - - // Draw blinking cursor - bool cursor_is_visible = (!g.IO.ConfigCursorBlink) || (g.InputTextState.CursorAnim <= 0.0f) || ImFmod(g.InputTextState.CursorAnim, 1.20f) <= 0.80f; - ImVec2 cursor_screen_pos = render_pos + cursor_offset - render_scroll; - ImRect cursor_screen_rect(cursor_screen_pos.x, cursor_screen_pos.y-g.FontSize+0.5f, cursor_screen_pos.x+1.0f, cursor_screen_pos.y-1.5f); - if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect)) - draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_Text)); - - // Notify OS of text input position for advanced IME (-1 x offset so that Windows IME can cover our cursor. Bit of an extra nicety.) - if (is_editable) - { - g.PlatformImePos = ImVec2(cursor_screen_pos.x - 1, cursor_screen_pos.y - g.FontSize); - g.PlatformImePosViewport = window->Viewport; - } - } - else - { - // Render text only - const char* buf_end = NULL; - if (is_multiline) - text_size = ImVec2(size.x, InputTextCalcTextLenAndLineCount(buf_display, &buf_end) * g.FontSize); // We don't need width - else - buf_end = buf_display + strlen(buf_display); - if (is_multiline || (buf_end - buf_display) < buf_display_max_length) - draw_window->DrawList->AddText(g.Font, g.FontSize, render_pos, GetColorU32(ImGuiCol_Text), buf_display, buf_end, 0.0f, is_multiline ? NULL : &clip_rect); - } - - if (is_multiline) - { - Dummy(text_size + ImVec2(0.0f, g.FontSize)); // Always add room to scroll an extra line - EndChildFrame(); - EndGroup(); - } - - if (is_password) - PopFont(); - - // Log as text - if (g.LogEnabled && !is_password) - LogRenderedText(&render_pos, buf_display, NULL); - - if (label_size.x > 0) - RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); - - if (value_changed) - MarkItemEdited(id); - - if ((flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0) - return enter_pressed; - else - return value_changed; -} - -bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data) -{ - IM_ASSERT(!(flags & ImGuiInputTextFlags_Multiline)); // call InputTextMultiline() - return InputTextEx(label, buf, (int)buf_size, ImVec2(0,0), flags, callback, user_data); -} - -bool ImGui::InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data) -{ - return InputTextEx(label, buf, (int)buf_size, size, flags | ImGuiInputTextFlags_Multiline, callback, user_data); -} - -// NB: format here must be a simple "%xx" format string with no prefix/suffix (unlike the Drag/Slider functions "format" argument) bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* data_ptr, const void* step, const void* step_fast, const char* format, ImGuiInputTextFlags extra_flags) { ImGuiWindow* window = GetCurrentWindow(); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index a944ce6ca2bb..c218f2b683ee 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -34,6 +34,11 @@ // Forward Declarations //------------------------------------------------------------------------- +// For InputTextEx() +static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data); +static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end); +static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining = NULL, ImVec2* out_offset = NULL, bool stop_on_new_line = false); + //------------------------------------------------------------------------- // SHARED UTILITIES //------------------------------------------------------------------------- @@ -1209,6 +1214,975 @@ bool ImGui::Combo(const char* label, int* current_item, const char* items_separa // - InputTextEx() [Internal] //------------------------------------------------------------------------- +bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data) +{ + IM_ASSERT(!(flags & ImGuiInputTextFlags_Multiline)); // call InputTextMultiline() + return InputTextEx(label, buf, (int)buf_size, ImVec2(0,0), flags, callback, user_data); +} + +bool ImGui::InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data) +{ + return InputTextEx(label, buf, (int)buf_size, size, flags | ImGuiInputTextFlags_Multiline, callback, user_data); +} + +static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end) +{ + int line_count = 0; + const char* s = text_begin; + while (char c = *s++) // We are only matching for \n so we can ignore UTF-8 decoding + if (c == '\n') + line_count++; + s--; + if (s[0] != '\n' && s[0] != '\r') + line_count++; + *out_text_end = s; + return line_count; +} + +static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining, ImVec2* out_offset, bool stop_on_new_line) +{ + ImFont* font = GImGui->Font; + const float line_height = GImGui->FontSize; + const float scale = line_height / font->FontSize; + + ImVec2 text_size = ImVec2(0,0); + float line_width = 0.0f; + + const ImWchar* s = text_begin; + while (s < text_end) + { + unsigned int c = (unsigned int)(*s++); + if (c == '\n') + { + text_size.x = ImMax(text_size.x, line_width); + text_size.y += line_height; + line_width = 0.0f; + if (stop_on_new_line) + break; + continue; + } + if (c == '\r') + continue; + + const float char_width = font->GetCharAdvance((unsigned short)c) * scale; + line_width += char_width; + } + + if (text_size.x < line_width) + text_size.x = line_width; + + if (out_offset) + *out_offset = ImVec2(line_width, text_size.y + line_height); // offset allow for the possibility of sitting after a trailing \n + + if (line_width > 0 || text_size.y == 0.0f) // whereas size.y will ignore the trailing \n + text_size.y += line_height; + + if (remaining) + *remaining = s; + + return text_size; +} + +// Wrapper for stb_textedit.h to edit text (our wrapper is for: statically sized buffer, single-line, wchar characters. InputText converts between UTF-8 and wchar) +namespace ImGuiStb +{ + +static int STB_TEXTEDIT_STRINGLEN(const STB_TEXTEDIT_STRING* obj) { return obj->CurLenW; } +static ImWchar STB_TEXTEDIT_GETCHAR(const STB_TEXTEDIT_STRING* obj, int idx) { return obj->TextW[idx]; } +static float STB_TEXTEDIT_GETWIDTH(STB_TEXTEDIT_STRING* obj, int line_start_idx, int char_idx) { ImWchar c = obj->TextW[line_start_idx+char_idx]; if (c == '\n') return STB_TEXTEDIT_GETWIDTH_NEWLINE; return GImGui->Font->GetCharAdvance(c) * (GImGui->FontSize / GImGui->Font->FontSize); } +static int STB_TEXTEDIT_KEYTOTEXT(int key) { return key >= 0x10000 ? 0 : key; } +static ImWchar STB_TEXTEDIT_NEWLINE = '\n'; +static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, STB_TEXTEDIT_STRING* obj, int line_start_idx) +{ + const ImWchar* text = obj->TextW.Data; + const ImWchar* text_remaining = NULL; + const ImVec2 size = InputTextCalcTextSizeW(text + line_start_idx, text + obj->CurLenW, &text_remaining, NULL, true); + r->x0 = 0.0f; + r->x1 = size.x; + r->baseline_y_delta = size.y; + r->ymin = 0.0f; + r->ymax = size.y; + r->num_chars = (int)(text_remaining - (text + line_start_idx)); +} + +static bool is_separator(unsigned int c) { return ImCharIsBlankW(c) || c==',' || c==';' || c=='(' || c==')' || c=='{' || c=='}' || c=='[' || c==']' || c=='|'; } +static int is_word_boundary_from_right(STB_TEXTEDIT_STRING* obj, int idx) { return idx > 0 ? (is_separator( obj->TextW[idx-1] ) && !is_separator( obj->TextW[idx] ) ) : 1; } +static int STB_TEXTEDIT_MOVEWORDLEFT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx--; while (idx >= 0 && !is_word_boundary_from_right(obj, idx)) idx--; return idx < 0 ? 0 : idx; } +#ifdef __APPLE__ // FIXME: Move setting to IO structure +static int is_word_boundary_from_left(STB_TEXTEDIT_STRING* obj, int idx) { return idx > 0 ? (!is_separator( obj->TextW[idx-1] ) && is_separator( obj->TextW[idx] ) ) : 1; } +static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_left(obj, idx)) idx++; return idx > len ? len : idx; } +#else +static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_right(obj, idx)) idx++; return idx > len ? len : idx; } +#endif +#define STB_TEXTEDIT_MOVEWORDLEFT STB_TEXTEDIT_MOVEWORDLEFT_IMPL // They need to be #define for stb_textedit.h +#define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_IMPL + +static void STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING* obj, int pos, int n) +{ + ImWchar* dst = obj->TextW.Data + pos; + + // We maintain our buffer length in both UTF-8 and wchar formats + obj->CurLenA -= ImTextCountUtf8BytesFromStr(dst, dst + n); + obj->CurLenW -= n; + + // Offset remaining text + const ImWchar* src = obj->TextW.Data + pos + n; + while (ImWchar c = *src++) + *dst++ = c; + *dst = '\0'; +} + +static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const ImWchar* new_text, int new_text_len) +{ + const bool is_resizable = (obj->UserFlags & ImGuiInputTextFlags_CallbackResize) != 0; + const int text_len = obj->CurLenW; + IM_ASSERT(pos <= text_len); + + const int new_text_len_utf8 = ImTextCountUtf8BytesFromStr(new_text, new_text + new_text_len); + if (!is_resizable && (new_text_len_utf8 + obj->CurLenA + 1 > obj->BufCapacityA)) + return false; + + // Grow internal buffer if needed + if (new_text_len + text_len + 1 > obj->TextW.Size) + { + if (!is_resizable) + return false; + IM_ASSERT(text_len < obj->TextW.Size); + obj->TextW.resize(text_len + ImClamp(new_text_len * 4, 32, ImMax(256, new_text_len)) + 1); + } + + ImWchar* text = obj->TextW.Data; + if (pos != text_len) + memmove(text + pos + new_text_len, text + pos, (size_t)(text_len - pos) * sizeof(ImWchar)); + memcpy(text + pos, new_text, (size_t)new_text_len * sizeof(ImWchar)); + + obj->CurLenW += new_text_len; + obj->CurLenA += new_text_len_utf8; + obj->TextW[obj->CurLenW] = '\0'; + + return true; +} + +// We don't use an enum so we can build even with conflicting symbols (if another user of stb_textedit.h leak their STB_TEXTEDIT_K_* symbols) +#define STB_TEXTEDIT_K_LEFT 0x10000 // keyboard input to move cursor left +#define STB_TEXTEDIT_K_RIGHT 0x10001 // keyboard input to move cursor right +#define STB_TEXTEDIT_K_UP 0x10002 // keyboard input to move cursor up +#define STB_TEXTEDIT_K_DOWN 0x10003 // keyboard input to move cursor down +#define STB_TEXTEDIT_K_LINESTART 0x10004 // keyboard input to move cursor to start of line +#define STB_TEXTEDIT_K_LINEEND 0x10005 // keyboard input to move cursor to end of line +#define STB_TEXTEDIT_K_TEXTSTART 0x10006 // keyboard input to move cursor to start of text +#define STB_TEXTEDIT_K_TEXTEND 0x10007 // keyboard input to move cursor to end of text +#define STB_TEXTEDIT_K_DELETE 0x10008 // keyboard input to delete selection or character under cursor +#define STB_TEXTEDIT_K_BACKSPACE 0x10009 // keyboard input to delete selection or character left of cursor +#define STB_TEXTEDIT_K_UNDO 0x1000A // keyboard input to perform undo +#define STB_TEXTEDIT_K_REDO 0x1000B // keyboard input to perform redo +#define STB_TEXTEDIT_K_WORDLEFT 0x1000C // keyboard input to move cursor left one word +#define STB_TEXTEDIT_K_WORDRIGHT 0x1000D // keyboard input to move cursor right one word +#define STB_TEXTEDIT_K_SHIFT 0x20000 + +#define STB_TEXTEDIT_IMPLEMENTATION +#include "stb_textedit.h" + +} + +void ImGuiInputTextState::OnKeyPressed(int key) +{ + stb_textedit_key(this, &StbState, key); + CursorFollow = true; + CursorAnimReset(); +} + +ImGuiInputTextCallbackData::ImGuiInputTextCallbackData() +{ + memset(this, 0, sizeof(*this)); +} + +// Public API to manipulate UTF-8 text +// We expose UTF-8 to the user (unlike the STB_TEXTEDIT_* functions which are manipulating wchar) +// FIXME: The existence of this rarely exercised code path is a bit of a nuisance. +void ImGuiInputTextCallbackData::DeleteChars(int pos, int bytes_count) +{ + IM_ASSERT(pos + bytes_count <= BufTextLen); + char* dst = Buf + pos; + const char* src = Buf + pos + bytes_count; + while (char c = *src++) + *dst++ = c; + *dst = '\0'; + + if (CursorPos + bytes_count >= pos) + CursorPos -= bytes_count; + else if (CursorPos >= pos) + CursorPos = pos; + SelectionStart = SelectionEnd = CursorPos; + BufDirty = true; + BufTextLen -= bytes_count; +} + +void ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, const char* new_text_end) +{ + const bool is_resizable = (Flags & ImGuiInputTextFlags_CallbackResize) != 0; + const int new_text_len = new_text_end ? (int)(new_text_end - new_text) : (int)strlen(new_text); + if (new_text_len + BufTextLen >= BufSize) + { + if (!is_resizable) + return; + + // Contrary to STB_TEXTEDIT_INSERTCHARS() this is working in the UTF8 buffer, hence the midly similar code (until we remove the U16 buffer alltogether!) + ImGuiContext& g = *GImGui; + ImGuiInputTextState* edit_state = &g.InputTextState; + IM_ASSERT(edit_state->ID != 0 && g.ActiveId == edit_state->ID); + IM_ASSERT(Buf == edit_state->TempBuffer.Data); + int new_buf_size = BufTextLen + ImClamp(new_text_len * 4, 32, ImMax(256, new_text_len)) + 1; + edit_state->TempBuffer.reserve(new_buf_size + 1); + Buf = edit_state->TempBuffer.Data; + BufSize = edit_state->BufCapacityA = new_buf_size; + } + + if (BufTextLen != pos) + memmove(Buf + pos + new_text_len, Buf + pos, (size_t)(BufTextLen - pos)); + memcpy(Buf + pos, new_text, (size_t)new_text_len * sizeof(char)); + Buf[BufTextLen + new_text_len] = '\0'; + + if (CursorPos >= pos) + CursorPos += new_text_len; + SelectionStart = SelectionEnd = CursorPos; + BufDirty = true; + BufTextLen += new_text_len; +} + +// Return false to discard a character. +static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data) +{ + unsigned int c = *p_char; + + if (c < 128 && c != ' ' && !isprint((int)(c & 0xFF))) + { + bool pass = false; + pass |= (c == '\n' && (flags & ImGuiInputTextFlags_Multiline)); + pass |= (c == '\t' && (flags & ImGuiInputTextFlags_AllowTabInput)); + if (!pass) + return false; + } + + if (c >= 0xE000 && c <= 0xF8FF) // Filter private Unicode range. I don't imagine anybody would want to input them. GLFW on OSX seems to send private characters for special keys like arrow keys. + return false; + + if (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_CharsNoBlank | ImGuiInputTextFlags_CharsScientific)) + { + if (flags & ImGuiInputTextFlags_CharsDecimal) + if (!(c >= '0' && c <= '9') && (c != '.') && (c != '-') && (c != '+') && (c != '*') && (c != '/')) + return false; + + if (flags & ImGuiInputTextFlags_CharsScientific) + if (!(c >= '0' && c <= '9') && (c != '.') && (c != '-') && (c != '+') && (c != '*') && (c != '/') && (c != 'e') && (c != 'E')) + return false; + + if (flags & ImGuiInputTextFlags_CharsHexadecimal) + if (!(c >= '0' && c <= '9') && !(c >= 'a' && c <= 'f') && !(c >= 'A' && c <= 'F')) + return false; + + if (flags & ImGuiInputTextFlags_CharsUppercase) + if (c >= 'a' && c <= 'z') + *p_char = (c += (unsigned int)('A'-'a')); + + if (flags & ImGuiInputTextFlags_CharsNoBlank) + if (ImCharIsBlankW(c)) + return false; + } + + if (flags & ImGuiInputTextFlags_CallbackCharFilter) + { + ImGuiInputTextCallbackData callback_data; + memset(&callback_data, 0, sizeof(ImGuiInputTextCallbackData)); + callback_data.EventFlag = ImGuiInputTextFlags_CallbackCharFilter; + callback_data.EventChar = (ImWchar)c; + callback_data.Flags = flags; + callback_data.UserData = user_data; + if (callback(&callback_data) != 0) + return false; + *p_char = callback_data.EventChar; + if (!callback_data.EventChar) + return false; + } + + return true; +} + +// Edit a string of text +// - buf_size account for the zero-terminator, so a buf_size of 6 can hold "Hello" but not "Hello!". +// This is so we can easily call InputText() on static arrays using ARRAYSIZE() and to match +// Note that in std::string world, capacity() would omit 1 byte used by the zero-terminator. +// - When active, hold on a privately held copy of the text (and apply back to 'buf'). So changing 'buf' while the InputText is active has no effect. +// - If you want to use ImGui::InputText() with std::string, see misc/stl/imgui_stl.h +// (FIXME: Rather messy function partly because we are doing UTF8 > u16 > UTF8 conversions on the go to more easily handle stb_textedit calls. Ideally we should stay in UTF-8 all the time. See https://github.com/nothings/stb/issues/188) +bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* callback_user_data) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackHistory) && (flags & ImGuiInputTextFlags_Multiline))); // Can't use both together (they both use up/down keys) + IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackCompletion) && (flags & ImGuiInputTextFlags_AllowTabInput))); // Can't use both together (they both use tab key) + + ImGuiContext& g = *GImGui; + const ImGuiIO& io = g.IO; + const ImGuiStyle& style = g.Style; + + const bool is_multiline = (flags & ImGuiInputTextFlags_Multiline) != 0; + const bool is_editable = (flags & ImGuiInputTextFlags_ReadOnly) == 0; + const bool is_password = (flags & ImGuiInputTextFlags_Password) != 0; + const bool is_undoable = (flags & ImGuiInputTextFlags_NoUndoRedo) == 0; + const bool is_resizable = (flags & ImGuiInputTextFlags_CallbackResize) != 0; + if (is_resizable) + IM_ASSERT(callback != NULL); // Must provide a callback if you set the ImGuiInputTextFlags_CallbackResize flag! + + if (is_multiline) // Open group before calling GetID() because groups tracks id created within their scope, + BeginGroup(); + const ImGuiID id = window->GetID(label); + const ImVec2 label_size = CalcTextSize(label, NULL, true); + ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), (is_multiline ? GetTextLineHeight() * 8.0f : label_size.y) + style.FramePadding.y*2.0f); // Arbitrary default of 8 lines high for multi-line + const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size); + const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? (style.ItemInnerSpacing.x + label_size.x) : 0.0f, 0.0f)); + + ImGuiWindow* draw_window = window; + if (is_multiline) + { + ItemAdd(total_bb, id, &frame_bb); + if (!BeginChildFrame(id, frame_bb.GetSize())) + { + EndChildFrame(); + EndGroup(); + return false; + } + draw_window = GetCurrentWindow(); + draw_window->DC.NavLayerActiveMaskNext |= draw_window->DC.NavLayerCurrentMask; // This is to ensure that EndChild() will display a navigation highlight + size.x -= draw_window->ScrollbarSizes.x; + } + else + { + ItemSize(total_bb, style.FramePadding.y); + if (!ItemAdd(total_bb, id, &frame_bb)) + return false; + } + const bool hovered = ItemHoverable(frame_bb, id); + if (hovered) + g.MouseCursor = ImGuiMouseCursor_TextInput; + + // Password pushes a temporary font with only a fallback glyph + if (is_password) + { + const ImFontGlyph* glyph = g.Font->FindGlyph('*'); + ImFont* password_font = &g.InputTextPasswordFont; + password_font->FontSize = g.Font->FontSize; + password_font->Scale = g.Font->Scale; + password_font->DisplayOffset = g.Font->DisplayOffset; + password_font->Ascent = g.Font->Ascent; + password_font->Descent = g.Font->Descent; + password_font->ContainerAtlas = g.Font->ContainerAtlas; + password_font->FallbackGlyph = glyph; + password_font->FallbackAdvanceX = glyph->AdvanceX; + IM_ASSERT(password_font->Glyphs.empty() && password_font->IndexAdvanceX.empty() && password_font->IndexLookup.empty()); + PushFont(password_font); + } + + // NB: we are only allowed to access 'edit_state' if we are the active widget. + ImGuiInputTextState& edit_state = g.InputTextState; + + const bool focus_requested = FocusableItemRegister(window, id, (flags & (ImGuiInputTextFlags_CallbackCompletion|ImGuiInputTextFlags_AllowTabInput)) == 0); // Using completion callback disable keyboard tabbing + const bool focus_requested_by_code = focus_requested && (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent); + const bool focus_requested_by_tab = focus_requested && !focus_requested_by_code; + + const bool user_clicked = hovered && io.MouseClicked[0]; + const bool user_scrolled = is_multiline && g.ActiveId == 0 && edit_state.ID == id && g.ActiveIdPreviousFrame == draw_window->GetIDNoKeepAlive("#SCROLLY"); + const bool user_nav_input_start = (g.ActiveId != id) && ((g.NavInputId == id) || (g.NavActivateId == id && g.NavInputSource == ImGuiInputSource_NavKeyboard)); + + bool clear_active_id = false; + + bool select_all = (g.ActiveId != id) && ((flags & ImGuiInputTextFlags_AutoSelectAll) != 0 || user_nav_input_start) && (!is_multiline); + if (focus_requested || user_clicked || user_scrolled || user_nav_input_start) + { + if (g.ActiveId != id) + { + // Start edition + // Take a copy of the initial buffer value (both in original UTF-8 format and converted to wchar) + // From the moment we focused we are ignoring the content of 'buf' (unless we are in read-only mode) + const int prev_len_w = edit_state.CurLenW; + const int init_buf_len = (int)strlen(buf); + edit_state.TextW.resize(buf_size+1); // wchar count <= UTF-8 count. we use +1 to make sure that .Data isn't NULL so it doesn't crash. + edit_state.InitialText.resize(init_buf_len + 1); // UTF-8. we use +1 to make sure that .Data isn't NULL so it doesn't crash. + memcpy(edit_state.InitialText.Data, buf, init_buf_len + 1); + const char* buf_end = NULL; + edit_state.CurLenW = ImTextStrFromUtf8(edit_state.TextW.Data, buf_size, buf, NULL, &buf_end); + edit_state.CurLenA = (int)(buf_end - buf); // We can't get the result from ImStrncpy() above because it is not UTF-8 aware. Here we'll cut off malformed UTF-8. + edit_state.CursorAnimReset(); + + // Preserve cursor position and undo/redo stack if we come back to same widget + // FIXME: We should probably compare the whole buffer to be on the safety side. Comparing buf (utf8) and edit_state.Text (wchar). + const bool recycle_state = (edit_state.ID == id) && (prev_len_w == edit_state.CurLenW); + if (recycle_state) + { + // Recycle existing cursor/selection/undo stack but clamp position + // Note a single mouse click will override the cursor/position immediately by calling stb_textedit_click handler. + edit_state.CursorClamp(); + } + else + { + edit_state.ID = id; + edit_state.ScrollX = 0.0f; + stb_textedit_initialize_state(&edit_state.StbState, !is_multiline); + if (!is_multiline && focus_requested_by_code) + select_all = true; + } + if (flags & ImGuiInputTextFlags_AlwaysInsertMode) + edit_state.StbState.insert_mode = true; + if (!is_multiline && (focus_requested_by_tab || (user_clicked && io.KeyCtrl))) + select_all = true; + } + SetActiveID(id, window); + SetFocusID(id, window); + FocusWindow(window); + if (!is_multiline && !(flags & ImGuiInputTextFlags_CallbackHistory)) + g.ActiveIdAllowNavDirFlags |= ((1 << ImGuiDir_Up) | (1 << ImGuiDir_Down)); + } + else if (io.MouseClicked[0]) + { + // Release focus when we click outside + clear_active_id = true; + } + + bool value_changed = false; + bool enter_pressed = false; + int backup_current_text_length = 0; + + if (g.ActiveId == id) + { + if (!is_editable && !g.ActiveIdIsJustActivated) + { + // When read-only we always use the live data passed to the function + edit_state.TextW.resize(buf_size+1); + const char* buf_end = NULL; + edit_state.CurLenW = ImTextStrFromUtf8(edit_state.TextW.Data, edit_state.TextW.Size, buf, NULL, &buf_end); + edit_state.CurLenA = (int)(buf_end - buf); + edit_state.CursorClamp(); + } + + backup_current_text_length = edit_state.CurLenA; + edit_state.BufCapacityA = buf_size; + edit_state.UserFlags = flags; + edit_state.UserCallback = callback; + edit_state.UserCallbackData = callback_user_data; + + // Although we are active we don't prevent mouse from hovering other elements unless we are interacting right now with the widget. + // Down the line we should have a cleaner library-wide concept of Selected vs Active. + g.ActiveIdAllowOverlap = !io.MouseDown[0]; + g.WantTextInputNextFrame = 1; + + // Edit in progress + const float mouse_x = (io.MousePos.x - frame_bb.Min.x - style.FramePadding.x) + edit_state.ScrollX; + const float mouse_y = (is_multiline ? (io.MousePos.y - draw_window->DC.CursorPos.y - style.FramePadding.y) : (g.FontSize*0.5f)); + + const bool is_osx = io.ConfigMacOSXBehaviors; + if (select_all || (hovered && !is_osx && io.MouseDoubleClicked[0])) + { + edit_state.SelectAll(); + edit_state.SelectedAllMouseLock = true; + } + else if (hovered && is_osx && io.MouseDoubleClicked[0]) + { + // Double-click select a word only, OS X style (by simulating keystrokes) + edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT); + edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT); + } + else if (io.MouseClicked[0] && !edit_state.SelectedAllMouseLock) + { + if (hovered) + { + stb_textedit_click(&edit_state, &edit_state.StbState, mouse_x, mouse_y); + edit_state.CursorAnimReset(); + } + } + else if (io.MouseDown[0] && !edit_state.SelectedAllMouseLock && (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f)) + { + stb_textedit_drag(&edit_state, &edit_state.StbState, mouse_x, mouse_y); + edit_state.CursorAnimReset(); + edit_state.CursorFollow = true; + } + if (edit_state.SelectedAllMouseLock && !io.MouseDown[0]) + edit_state.SelectedAllMouseLock = false; + + if (io.InputCharacters[0]) + { + // Process text input (before we check for Return because using some IME will effectively send a Return?) + // We ignore CTRL inputs, but need to allow ALT+CTRL as some keyboards (e.g. German) use AltGR (which _is_ Alt+Ctrl) to input certain characters. + bool ignore_inputs = (io.KeyCtrl && !io.KeyAlt) || (is_osx && io.KeySuper); + if (!ignore_inputs && is_editable && !user_nav_input_start) + for (int n = 0; n < IM_ARRAYSIZE(io.InputCharacters) && io.InputCharacters[n]; n++) + { + // Insert character if they pass filtering + unsigned int c = (unsigned int)io.InputCharacters[n]; + if (InputTextFilterCharacter(&c, flags, callback, callback_user_data)) + edit_state.OnKeyPressed((int)c); + } + + // Consume characters + memset(g.IO.InputCharacters, 0, sizeof(g.IO.InputCharacters)); + } + } + + bool cancel_edit = false; + if (g.ActiveId == id && !g.ActiveIdIsJustActivated && !clear_active_id) + { + // Handle key-presses + const int k_mask = (io.KeyShift ? STB_TEXTEDIT_K_SHIFT : 0); + const bool is_osx = io.ConfigMacOSXBehaviors; + const bool is_shortcut_key = (is_osx ? (io.KeySuper && !io.KeyCtrl) : (io.KeyCtrl && !io.KeySuper)) && !io.KeyAlt && !io.KeyShift; // OS X style: Shortcuts using Cmd/Super instead of Ctrl + const bool is_osx_shift_shortcut = is_osx && io.KeySuper && io.KeyShift && !io.KeyCtrl && !io.KeyAlt; + const bool is_wordmove_key_down = is_osx ? io.KeyAlt : io.KeyCtrl; // OS X style: Text editing cursor movement using Alt instead of Ctrl + const bool is_startend_key_down = is_osx && io.KeySuper && !io.KeyCtrl && !io.KeyAlt; // OS X style: Line/Text Start and End using Cmd+Arrows instead of Home/End + const bool is_ctrl_key_only = io.KeyCtrl && !io.KeyShift && !io.KeyAlt && !io.KeySuper; + const bool is_shift_key_only = io.KeyShift && !io.KeyCtrl && !io.KeyAlt && !io.KeySuper; + + const bool is_cut = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_X)) || (is_shift_key_only && IsKeyPressedMap(ImGuiKey_Delete))) && is_editable && !is_password && (!is_multiline || edit_state.HasSelection()); + const bool is_copy = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_C)) || (is_ctrl_key_only && IsKeyPressedMap(ImGuiKey_Insert))) && !is_password && (!is_multiline || edit_state.HasSelection()); + const bool is_paste = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_V)) || (is_shift_key_only && IsKeyPressedMap(ImGuiKey_Insert))) && is_editable; + const bool is_undo = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_Z)) && is_editable && is_undoable); + const bool is_redo = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_Y)) || (is_osx_shift_shortcut && IsKeyPressedMap(ImGuiKey_Z))) && is_editable && is_undoable; + + if (IsKeyPressedMap(ImGuiKey_LeftArrow)) { edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINESTART : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDLEFT : STB_TEXTEDIT_K_LEFT) | k_mask); } + else if (IsKeyPressedMap(ImGuiKey_RightArrow)) { edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINEEND : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDRIGHT : STB_TEXTEDIT_K_RIGHT) | k_mask); } + else if (IsKeyPressedMap(ImGuiKey_UpArrow) && is_multiline) { if (io.KeyCtrl) SetWindowScrollY(draw_window, ImMax(draw_window->Scroll.y - g.FontSize, 0.0f)); else edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTSTART : STB_TEXTEDIT_K_UP) | k_mask); } + else if (IsKeyPressedMap(ImGuiKey_DownArrow) && is_multiline) { if (io.KeyCtrl) SetWindowScrollY(draw_window, ImMin(draw_window->Scroll.y + g.FontSize, GetScrollMaxY())); else edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTEND : STB_TEXTEDIT_K_DOWN) | k_mask); } + else if (IsKeyPressedMap(ImGuiKey_Home)) { edit_state.OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTSTART | k_mask : STB_TEXTEDIT_K_LINESTART | k_mask); } + else if (IsKeyPressedMap(ImGuiKey_End)) { edit_state.OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTEND | k_mask : STB_TEXTEDIT_K_LINEEND | k_mask); } + else if (IsKeyPressedMap(ImGuiKey_Delete) && is_editable) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask); } + else if (IsKeyPressedMap(ImGuiKey_Backspace) && is_editable) + { + if (!edit_state.HasSelection()) + { + if (is_wordmove_key_down) edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT|STB_TEXTEDIT_K_SHIFT); + else if (is_osx && io.KeySuper && !io.KeyAlt && !io.KeyCtrl) edit_state.OnKeyPressed(STB_TEXTEDIT_K_LINESTART|STB_TEXTEDIT_K_SHIFT); + } + edit_state.OnKeyPressed(STB_TEXTEDIT_K_BACKSPACE | k_mask); + } + else if (IsKeyPressedMap(ImGuiKey_Enter)) + { + bool ctrl_enter_for_new_line = (flags & ImGuiInputTextFlags_CtrlEnterForNewLine) != 0; + if (!is_multiline || (ctrl_enter_for_new_line && !io.KeyCtrl) || (!ctrl_enter_for_new_line && io.KeyCtrl)) + { + enter_pressed = clear_active_id = true; + } + else if (is_editable) + { + unsigned int c = '\n'; // Insert new line + if (InputTextFilterCharacter(&c, flags, callback, callback_user_data)) + edit_state.OnKeyPressed((int)c); + } + } + else if ((flags & ImGuiInputTextFlags_AllowTabInput) && IsKeyPressedMap(ImGuiKey_Tab) && !io.KeyCtrl && !io.KeyShift && !io.KeyAlt && is_editable) + { + unsigned int c = '\t'; // Insert TAB + if (InputTextFilterCharacter(&c, flags, callback, callback_user_data)) + edit_state.OnKeyPressed((int)c); + } + else if (IsKeyPressedMap(ImGuiKey_Escape)) + { + clear_active_id = cancel_edit = true; + } + else if (is_undo || is_redo) + { + edit_state.OnKeyPressed(is_undo ? STB_TEXTEDIT_K_UNDO : STB_TEXTEDIT_K_REDO); + edit_state.ClearSelection(); + } + else if (is_shortcut_key && IsKeyPressedMap(ImGuiKey_A)) + { + edit_state.SelectAll(); + edit_state.CursorFollow = true; + } + else if (is_cut || is_copy) + { + // Cut, Copy + if (io.SetClipboardTextFn) + { + const int ib = edit_state.HasSelection() ? ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end) : 0; + const int ie = edit_state.HasSelection() ? ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end) : edit_state.CurLenW; + edit_state.TempBuffer.resize((ie-ib) * 4 + 1); + ImTextStrToUtf8(edit_state.TempBuffer.Data, edit_state.TempBuffer.Size, edit_state.TextW.Data+ib, edit_state.TextW.Data+ie); + SetClipboardText(edit_state.TempBuffer.Data); + } + if (is_cut) + { + if (!edit_state.HasSelection()) + edit_state.SelectAll(); + edit_state.CursorFollow = true; + stb_textedit_cut(&edit_state, &edit_state.StbState); + } + } + else if (is_paste) + { + if (const char* clipboard = GetClipboardText()) + { + // Filter pasted buffer + const int clipboard_len = (int)strlen(clipboard); + ImWchar* clipboard_filtered = (ImWchar*)ImGui::MemAlloc((clipboard_len+1) * sizeof(ImWchar)); + int clipboard_filtered_len = 0; + for (const char* s = clipboard; *s; ) + { + unsigned int c; + s += ImTextCharFromUtf8(&c, s, NULL); + if (c == 0) + break; + if (c >= 0x10000 || !InputTextFilterCharacter(&c, flags, callback, callback_user_data)) + continue; + clipboard_filtered[clipboard_filtered_len++] = (ImWchar)c; + } + clipboard_filtered[clipboard_filtered_len] = 0; + if (clipboard_filtered_len > 0) // If everything was filtered, ignore the pasting operation + { + stb_textedit_paste(&edit_state, &edit_state.StbState, clipboard_filtered, clipboard_filtered_len); + edit_state.CursorFollow = true; + } + ImGui::MemFree(clipboard_filtered); + } + } + } + + if (g.ActiveId == id) + { + const char* apply_new_text = NULL; + int apply_new_text_length = 0; + if (cancel_edit) + { + // Restore initial value. Only return true if restoring to the initial value changes the current buffer contents. + if (is_editable && strcmp(buf, edit_state.InitialText.Data) != 0) + { + apply_new_text = edit_state.InitialText.Data; + apply_new_text_length = edit_state.InitialText.Size - 1; + } + } + + // When using 'ImGuiInputTextFlags_EnterReturnsTrue' as a special case we reapply the live buffer back to the input buffer before clearing ActiveId, even though strictly speaking it wasn't modified on this frame. + // If we didn't do that, code like InputInt() with ImGuiInputTextFlags_EnterReturnsTrue would fail. Also this allows the user to use InputText() with ImGuiInputTextFlags_EnterReturnsTrue without maintaining any user-side storage. + bool apply_edit_back_to_user_buffer = !cancel_edit || (enter_pressed && (flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0); + if (apply_edit_back_to_user_buffer) + { + // Apply new value immediately - copy modified buffer back + // Note that as soon as the input box is active, the in-widget value gets priority over any underlying modification of the input buffer + // FIXME: We actually always render 'buf' when calling DrawList->AddText, making the comment above incorrect. + // FIXME-OPT: CPU waste to do this every time the widget is active, should mark dirty state from the stb_textedit callbacks. + if (is_editable) + { + edit_state.TempBuffer.resize(edit_state.TextW.Size * 4 + 1); + ImTextStrToUtf8(edit_state.TempBuffer.Data, edit_state.TempBuffer.Size, edit_state.TextW.Data, NULL); + } + + // User callback + if ((flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory | ImGuiInputTextFlags_CallbackAlways)) != 0) + { + IM_ASSERT(callback != NULL); + + // The reason we specify the usage semantic (Completion/History) is that Completion needs to disable keyboard TABBING at the moment. + ImGuiInputTextFlags event_flag = 0; + ImGuiKey event_key = ImGuiKey_COUNT; + if ((flags & ImGuiInputTextFlags_CallbackCompletion) != 0 && IsKeyPressedMap(ImGuiKey_Tab)) + { + event_flag = ImGuiInputTextFlags_CallbackCompletion; + event_key = ImGuiKey_Tab; + } + else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_UpArrow)) + { + event_flag = ImGuiInputTextFlags_CallbackHistory; + event_key = ImGuiKey_UpArrow; + } + else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_DownArrow)) + { + event_flag = ImGuiInputTextFlags_CallbackHistory; + event_key = ImGuiKey_DownArrow; + } + else if (flags & ImGuiInputTextFlags_CallbackAlways) + event_flag = ImGuiInputTextFlags_CallbackAlways; + + if (event_flag) + { + ImGuiInputTextCallbackData callback_data; + memset(&callback_data, 0, sizeof(ImGuiInputTextCallbackData)); + callback_data.EventFlag = event_flag; + callback_data.Flags = flags; + callback_data.UserData = callback_user_data; + + callback_data.EventKey = event_key; + callback_data.Buf = edit_state.TempBuffer.Data; + callback_data.BufTextLen = edit_state.CurLenA; + callback_data.BufSize = edit_state.BufCapacityA; + callback_data.BufDirty = false; + + // We have to convert from wchar-positions to UTF-8-positions, which can be pretty slow (an incentive to ditch the ImWchar buffer, see https://github.com/nothings/stb/issues/188) + ImWchar* text = edit_state.TextW.Data; + const int utf8_cursor_pos = callback_data.CursorPos = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.cursor); + const int utf8_selection_start = callback_data.SelectionStart = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.select_start); + const int utf8_selection_end = callback_data.SelectionEnd = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.select_end); + + // Call user code + callback(&callback_data); + + // Read back what user may have modified + IM_ASSERT(callback_data.Buf == edit_state.TempBuffer.Data); // Invalid to modify those fields + IM_ASSERT(callback_data.BufSize == edit_state.BufCapacityA); + IM_ASSERT(callback_data.Flags == flags); + if (callback_data.CursorPos != utf8_cursor_pos) { edit_state.StbState.cursor = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.CursorPos); edit_state.CursorFollow = true; } + if (callback_data.SelectionStart != utf8_selection_start) { edit_state.StbState.select_start = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionStart); } + if (callback_data.SelectionEnd != utf8_selection_end) { edit_state.StbState.select_end = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionEnd); } + if (callback_data.BufDirty) + { + IM_ASSERT(callback_data.BufTextLen == (int)strlen(callback_data.Buf)); // You need to maintain BufTextLen if you change the text! + if (callback_data.BufTextLen > backup_current_text_length && is_resizable) + edit_state.TextW.resize(edit_state.TextW.Size + (callback_data.BufTextLen - backup_current_text_length)); + edit_state.CurLenW = ImTextStrFromUtf8(edit_state.TextW.Data, edit_state.TextW.Size, callback_data.Buf, NULL); + edit_state.CurLenA = callback_data.BufTextLen; // Assume correct length and valid UTF-8 from user, saves us an extra strlen() + edit_state.CursorAnimReset(); + } + } + } + + // Will copy result string if modified + if (is_editable && strcmp(edit_state.TempBuffer.Data, buf) != 0) + { + apply_new_text = edit_state.TempBuffer.Data; + apply_new_text_length = edit_state.CurLenA; + } + } + + // Copy result to user buffer + if (apply_new_text) + { + IM_ASSERT(apply_new_text_length >= 0); + if (backup_current_text_length != apply_new_text_length && is_resizable) + { + ImGuiInputTextCallbackData callback_data; + callback_data.EventFlag = ImGuiInputTextFlags_CallbackResize; + callback_data.Flags = flags; + callback_data.Buf = buf; + callback_data.BufTextLen = apply_new_text_length; + callback_data.BufSize = ImMax(buf_size, apply_new_text_length + 1); + callback_data.UserData = callback_user_data; + callback(&callback_data); + buf = callback_data.Buf; + buf_size = callback_data.BufSize; + apply_new_text_length = ImMin(callback_data.BufTextLen, buf_size - 1); + IM_ASSERT(apply_new_text_length <= buf_size); + } + + // If the underlying buffer resize was denied or not carried to the next frame, apply_new_text_length+1 may be >= buf_size. + ImStrncpy(buf, edit_state.TempBuffer.Data, ImMin(apply_new_text_length + 1, buf_size)); + value_changed = true; + } + + // Clear temporary user storage + edit_state.UserFlags = 0; + edit_state.UserCallback = NULL; + edit_state.UserCallbackData = NULL; + } + + // Release active ID at the end of the function (so e.g. pressing Return still does a final application of the value) + if (clear_active_id && g.ActiveId == id) + ClearActiveID(); + + // Render + // Select which buffer we are going to display. When ImGuiInputTextFlags_NoLiveEdit is set 'buf' might still be the old value. We set buf to NULL to prevent accidental usage from now on. + const char* buf_display = (g.ActiveId == id && is_editable) ? edit_state.TempBuffer.Data : buf; buf = NULL; + + // Set upper limit of single-line InputTextEx() at 2 million characters strings. The current pathological worst case is a long line + // without any carriage return, which would makes ImFont::RenderText() reserve too many vertices and probably crash. Avoid it altogether. + // Note that we only use this limit on single-line InputText(), so a pathologically large line on a InputTextMultiline() would still crash. + const int buf_display_max_length = 2 * 1024 * 1024; + + if (!is_multiline) + { + RenderNavHighlight(frame_bb, id); + RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); + } + + const ImVec4 clip_rect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + size.x, frame_bb.Min.y + size.y); // Not using frame_bb.Max because we have adjusted size + ImVec2 render_pos = is_multiline ? draw_window->DC.CursorPos : frame_bb.Min + style.FramePadding; + ImVec2 text_size(0.f, 0.f); + const bool is_currently_scrolling = (edit_state.ID == id && is_multiline && g.ActiveId == draw_window->GetIDNoKeepAlive("#SCROLLY")); + if (g.ActiveId == id || is_currently_scrolling) + { + edit_state.CursorAnim += io.DeltaTime; + + // This is going to be messy. We need to: + // - Display the text (this alone can be more easily clipped) + // - Handle scrolling, highlight selection, display cursor (those all requires some form of 1d->2d cursor position calculation) + // - Measure text height (for scrollbar) + // We are attempting to do most of that in **one main pass** to minimize the computation cost (non-negligible for large amount of text) + 2nd pass for selection rendering (we could merge them by an extra refactoring effort) + // FIXME: This should occur on buf_display but we'd need to maintain cursor/select_start/select_end for UTF-8. + const ImWchar* text_begin = edit_state.TextW.Data; + ImVec2 cursor_offset, select_start_offset; + + { + // Count lines + find lines numbers straddling 'cursor' and 'select_start' position. + const ImWchar* searches_input_ptr[2]; + searches_input_ptr[0] = text_begin + edit_state.StbState.cursor; + searches_input_ptr[1] = NULL; + int searches_remaining = 1; + int searches_result_line_number[2] = { -1, -999 }; + if (edit_state.StbState.select_start != edit_state.StbState.select_end) + { + searches_input_ptr[1] = text_begin + ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end); + searches_result_line_number[1] = -1; + searches_remaining++; + } + + // Iterate all lines to find our line numbers + // In multi-line mode, we never exit the loop until all lines are counted, so add one extra to the searches_remaining counter. + searches_remaining += is_multiline ? 1 : 0; + int line_count = 0; + for (const ImWchar* s = text_begin; *s != 0; s++) + if (*s == '\n') + { + line_count++; + if (searches_result_line_number[0] == -1 && s >= searches_input_ptr[0]) { searches_result_line_number[0] = line_count; if (--searches_remaining <= 0) break; } + if (searches_result_line_number[1] == -1 && s >= searches_input_ptr[1]) { searches_result_line_number[1] = line_count; if (--searches_remaining <= 0) break; } + } + line_count++; + if (searches_result_line_number[0] == -1) searches_result_line_number[0] = line_count; + if (searches_result_line_number[1] == -1) searches_result_line_number[1] = line_count; + + // Calculate 2d position by finding the beginning of the line and measuring distance + cursor_offset.x = InputTextCalcTextSizeW(ImStrbolW(searches_input_ptr[0], text_begin), searches_input_ptr[0]).x; + cursor_offset.y = searches_result_line_number[0] * g.FontSize; + if (searches_result_line_number[1] >= 0) + { + select_start_offset.x = InputTextCalcTextSizeW(ImStrbolW(searches_input_ptr[1], text_begin), searches_input_ptr[1]).x; + select_start_offset.y = searches_result_line_number[1] * g.FontSize; + } + + // Store text height (note that we haven't calculated text width at all, see GitHub issues #383, #1224) + if (is_multiline) + text_size = ImVec2(size.x, line_count * g.FontSize); + } + + // Scroll + if (edit_state.CursorFollow) + { + // Horizontal scroll in chunks of quarter width + if (!(flags & ImGuiInputTextFlags_NoHorizontalScroll)) + { + const float scroll_increment_x = size.x * 0.25f; + if (cursor_offset.x < edit_state.ScrollX) + edit_state.ScrollX = (float)(int)ImMax(0.0f, cursor_offset.x - scroll_increment_x); + else if (cursor_offset.x - size.x >= edit_state.ScrollX) + edit_state.ScrollX = (float)(int)(cursor_offset.x - size.x + scroll_increment_x); + } + else + { + edit_state.ScrollX = 0.0f; + } + + // Vertical scroll + if (is_multiline) + { + float scroll_y = draw_window->Scroll.y; + if (cursor_offset.y - g.FontSize < scroll_y) + scroll_y = ImMax(0.0f, cursor_offset.y - g.FontSize); + else if (cursor_offset.y - size.y >= scroll_y) + scroll_y = cursor_offset.y - size.y; + draw_window->DC.CursorPos.y += (draw_window->Scroll.y - scroll_y); // To avoid a frame of lag + draw_window->Scroll.y = scroll_y; + render_pos.y = draw_window->DC.CursorPos.y; + } + } + edit_state.CursorFollow = false; + const ImVec2 render_scroll = ImVec2(edit_state.ScrollX, 0.0f); + + // Draw selection + if (edit_state.StbState.select_start != edit_state.StbState.select_end) + { + const ImWchar* text_selected_begin = text_begin + ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end); + const ImWchar* text_selected_end = text_begin + ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end); + + float bg_offy_up = is_multiline ? 0.0f : -1.0f; // FIXME: those offsets should be part of the style? they don't play so well with multi-line selection. + float bg_offy_dn = is_multiline ? 0.0f : 2.0f; + ImU32 bg_color = GetColorU32(ImGuiCol_TextSelectedBg); + ImVec2 rect_pos = render_pos + select_start_offset - render_scroll; + for (const ImWchar* p = text_selected_begin; p < text_selected_end; ) + { + if (rect_pos.y > clip_rect.w + g.FontSize) + break; + if (rect_pos.y < clip_rect.y) + { + while (p < text_selected_end) + if (*p++ == '\n') + break; + } + else + { + ImVec2 rect_size = InputTextCalcTextSizeW(p, text_selected_end, &p, NULL, true); + if (rect_size.x <= 0.0f) rect_size.x = (float)(int)(g.Font->GetCharAdvance((unsigned short)' ') * 0.50f); // So we can see selected empty lines + ImRect rect(rect_pos + ImVec2(0.0f, bg_offy_up - g.FontSize), rect_pos +ImVec2(rect_size.x, bg_offy_dn)); + rect.ClipWith(clip_rect); + if (rect.Overlaps(clip_rect)) + draw_window->DrawList->AddRectFilled(rect.Min, rect.Max, bg_color); + } + rect_pos.x = render_pos.x - render_scroll.x; + rect_pos.y += g.FontSize; + } + } + + const int buf_display_len = edit_state.CurLenA; + if (is_multiline || buf_display_len < buf_display_max_length) + draw_window->DrawList->AddText(g.Font, g.FontSize, render_pos - render_scroll, GetColorU32(ImGuiCol_Text), buf_display, buf_display + buf_display_len, 0.0f, is_multiline ? NULL : &clip_rect); + + // Draw blinking cursor + bool cursor_is_visible = (!g.IO.ConfigCursorBlink) || (g.InputTextState.CursorAnim <= 0.0f) || ImFmod(g.InputTextState.CursorAnim, 1.20f) <= 0.80f; + ImVec2 cursor_screen_pos = render_pos + cursor_offset - render_scroll; + ImRect cursor_screen_rect(cursor_screen_pos.x, cursor_screen_pos.y-g.FontSize+0.5f, cursor_screen_pos.x+1.0f, cursor_screen_pos.y-1.5f); + if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect)) + draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_Text)); + + // Notify OS of text input position for advanced IME (-1 x offset so that Windows IME can cover our cursor. Bit of an extra nicety.) + if (is_editable) + { + g.PlatformImePos = ImVec2(cursor_screen_pos.x - 1, cursor_screen_pos.y - g.FontSize); + g.PlatformImePosViewport = window->Viewport; + } + } + else + { + // Render text only + const char* buf_end = NULL; + if (is_multiline) + text_size = ImVec2(size.x, InputTextCalcTextLenAndLineCount(buf_display, &buf_end) * g.FontSize); // We don't need width + else + buf_end = buf_display + strlen(buf_display); + if (is_multiline || (buf_end - buf_display) < buf_display_max_length) + draw_window->DrawList->AddText(g.Font, g.FontSize, render_pos, GetColorU32(ImGuiCol_Text), buf_display, buf_end, 0.0f, is_multiline ? NULL : &clip_rect); + } + + if (is_multiline) + { + Dummy(text_size + ImVec2(0.0f, g.FontSize)); // Always add room to scroll an extra line + EndChildFrame(); + EndGroup(); + } + + if (is_password) + PopFont(); + + // Log as text + if (g.LogEnabled && !is_password) + LogRenderedText(&render_pos, buf_display, NULL); + + if (label_size.x > 0) + RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); + + if (value_changed) + MarkItemEdited(id); + + if ((flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0) + return enter_pressed; + else + return value_changed; +} //------------------------------------------------------------------------- // WIDGETS: Color Editor / Picker From 521405488ba222bf53fd62704a1c92f286f119f0 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 30 Aug 2018 15:14:10 +0200 Subject: [PATCH 202/828] Refactor: Moved Slider/Drag/Input functions + support DataType stuff from imgui.cpp to imgui_widgets.cpp (#2036) --- imgui.cpp | 1288 ------------------------------------------- imgui_widgets.cpp | 1340 +++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 1302 insertions(+), 1326 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 5effa3f505a0..fed2128c0138 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -835,7 +835,6 @@ // Visual Studio warnings #ifdef _MSC_VER #pragma warning (disable: 4127) // condition expression is constant -#pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff) #pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen #endif @@ -863,24 +862,6 @@ #endif #endif -static const ImS32 IM_S32_MIN = INT_MIN; // (-2147483647 - 1), (0x80000000); -static const ImS32 IM_S32_MAX = INT_MAX; // (2147483647), (0x7FFFFFFF) -static const ImU32 IM_U32_MIN = 0; -static const ImU32 IM_U32_MAX = UINT_MAX; // (0xFFFFFFFF) -#ifdef LLONG_MIN -static const ImS64 IM_S64_MIN = LLONG_MIN; // (-9223372036854775807ll - 1ll); -static const ImS64 IM_S64_MAX = LLONG_MAX; // (9223372036854775807ll); -#else -static const ImS64 IM_S64_MIN = -9223372036854775807LL - 1; -static const ImS64 IM_S64_MAX = 9223372036854775807LL; -#endif -static const ImU64 IM_U64_MIN = 0; -#ifdef ULLONG_MAX -static const ImU64 IM_U64_MAX = ULLONG_MAX; // (0xFFFFFFFFFFFFFFFFull); -#else -static const ImU64 IM_U64_MAX = (2ULL * 9223372036854775807LL + 1); -#endif - // When using CTRL+TAB (or Gamepad Square+L/R) we delay the visual a little in order to reduce visual noise doing a fast switch. static const float NAV_WINDOWING_HIGHLIGHT_DELAY = 0.20f; // Time before the highlight and screen dimming starts fading in static const float NAV_WINDOWING_LIST_APPEAR_DELAY = 0.15f; // Time before the window list starts to appear @@ -902,10 +883,6 @@ static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* wind static void AddDrawListToDrawData(ImVector* out_list, ImDrawList* draw_list); static void AddWindowToSortedBuffer(ImVector* out_sorted_windows, ImGuiWindow* window); -static inline int DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type, const void* data_ptr, const char* format); -static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, void* arg_1, const void* arg_2); -static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* format); - namespace ImGui { static bool BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags); @@ -929,12 +906,6 @@ static void SetCurrentViewport(ImGuiWindow* window, ImGuiViewportP* static int FindPlatformMonitorForPos(const ImVec2& pos); static int FindPlatformMonitorForRect(const ImRect& r); -// Template widget behaviors -template -static bool DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed, const TYPE v_min, const TYPE v_max, const char* format, float power); - -template -static bool SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, TYPE* v, const TYPE v_min, const TYPE v_max, const char* format, float power, ImGuiSliderFlags flags, ImRect* out_grab_bb); } //----------------------------------------------------------------------------- @@ -1255,19 +1226,6 @@ void ImStrTrimBlanks(char* buf) buf[p - p_start] = 0; // Zero terminate } -template -static const char* ImAtoi(const char* src, TYPE* output) -{ - int negative = 0; - if (*src == '-') { negative = 1; src++; } - if (*src == '+') { src++; } - TYPE v = 0; - while (*src >= '0' && *src <= '9') - v = (v * 10) + (*src++ - '0'); - *output = negative ? -v : v; - return src; -} - // A) MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size). // Ideally we would test for only one of those limits at runtime depending on the behavior the vsnprintf(), but trying to deduct it at compile time sounds like a pandora can of worm. // B) When buf==NULL vsnprintf() will return the output size. @@ -8997,1252 +8955,6 @@ ImGuiID ImGui::GetID(const void* ptr_id) return GImGui->CurrentWindow->GetID(ptr_id); } -static inline int DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type, const void* data_ptr, const char* format) -{ - if (data_type == ImGuiDataType_S32 || data_type == ImGuiDataType_U32) // Signedness doesn't matter when pushing the argument - return ImFormatString(buf, buf_size, format, *(const ImU32*)data_ptr); - if (data_type == ImGuiDataType_S64 || data_type == ImGuiDataType_U64) // Signedness doesn't matter when pushing the argument - return ImFormatString(buf, buf_size, format, *(const ImU64*)data_ptr); - if (data_type == ImGuiDataType_Float) - return ImFormatString(buf, buf_size, format, *(const float*)data_ptr); - if (data_type == ImGuiDataType_Double) - return ImFormatString(buf, buf_size, format, *(const double*)data_ptr); - IM_ASSERT(0); - return 0; -} - -// FIXME: Adding support for clamping on boundaries of the data type would be nice. -static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, void* arg1, const void* arg2) -{ - IM_ASSERT(op == '+' || op == '-'); - switch (data_type) - { - case ImGuiDataType_S32: - if (op == '+') *(int*)output = *(const int*)arg1 + *(const int*)arg2; - else if (op == '-') *(int*)output = *(const int*)arg1 - *(const int*)arg2; - return; - case ImGuiDataType_U32: - if (op == '+') *(unsigned int*)output = *(const unsigned int*)arg1 + *(const ImU32*)arg2; - else if (op == '-') *(unsigned int*)output = *(const unsigned int*)arg1 - *(const ImU32*)arg2; - return; - case ImGuiDataType_S64: - if (op == '+') *(ImS64*)output = *(const ImS64*)arg1 + *(const ImS64*)arg2; - else if (op == '-') *(ImS64*)output = *(const ImS64*)arg1 - *(const ImS64*)arg2; - return; - case ImGuiDataType_U64: - if (op == '+') *(ImU64*)output = *(const ImU64*)arg1 + *(const ImU64*)arg2; - else if (op == '-') *(ImU64*)output = *(const ImU64*)arg1 - *(const ImU64*)arg2; - return; - case ImGuiDataType_Float: - if (op == '+') *(float*)output = *(const float*)arg1 + *(const float*)arg2; - else if (op == '-') *(float*)output = *(const float*)arg1 - *(const float*)arg2; - return; - case ImGuiDataType_Double: - if (op == '+') *(double*)output = *(const double*)arg1 + *(const double*)arg2; - else if (op == '-') *(double*)output = *(const double*)arg1 - *(const double*)arg2; - return; - case ImGuiDataType_COUNT: break; - } - IM_ASSERT(0); -} - -struct ImGuiDataTypeInfo -{ - size_t Size; - const char* PrintFmt; // Unused - const char* ScanFmt; -}; - -static const ImGuiDataTypeInfo GDataTypeInfo[] = -{ - { sizeof(int), "%d", "%d" }, - { sizeof(unsigned int), "%u", "%u" }, -#ifdef _MSC_VER - { sizeof(ImS64), "%I64d","%I64d" }, - { sizeof(ImU64), "%I64u","%I64u" }, -#else - { sizeof(ImS64), "%lld", "%lld" }, - { sizeof(ImU64), "%llu", "%llu" }, -#endif - { sizeof(float), "%f", "%f" }, // float are promoted to double in va_arg - { sizeof(double), "%f", "%lf" }, -}; -IM_STATIC_ASSERT(IM_ARRAYSIZE(GDataTypeInfo) == ImGuiDataType_COUNT); - -// User can input math operators (e.g. +100) to edit a numerical values. -// NB: This is _not_ a full expression evaluator. We should probably add one and replace this dumb mess.. -static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* format) -{ - while (ImCharIsBlankA(*buf)) - buf++; - - // We don't support '-' op because it would conflict with inputing negative value. - // Instead you can use +-100 to subtract from an existing value - char op = buf[0]; - if (op == '+' || op == '*' || op == '/') - { - buf++; - while (ImCharIsBlankA(*buf)) - buf++; - } - else - { - op = 0; - } - if (!buf[0]) - return false; - - // Copy the value in an opaque buffer so we can compare at the end of the function if it changed at all. - IM_ASSERT(data_type < ImGuiDataType_COUNT); - int data_backup[2]; - IM_ASSERT(GDataTypeInfo[data_type].Size <= sizeof(data_backup)); - memcpy(data_backup, data_ptr, GDataTypeInfo[data_type].Size); - - if (format == NULL) - format = GDataTypeInfo[data_type].ScanFmt; - - int arg1i = 0; - if (data_type == ImGuiDataType_S32) - { - int* v = (int*)data_ptr; - int arg0i = *v; - float arg1f = 0.0f; - if (op && sscanf(initial_value_buf, format, &arg0i) < 1) - return false; - // Store operand in a float so we can use fractional value for multipliers (*1.1), but constant always parsed as integer so we can fit big integers (e.g. 2000000003) past float precision - if (op == '+') { if (sscanf(buf, "%d", &arg1i)) *v = (int)(arg0i + arg1i); } // Add (use "+-" to subtract) - else if (op == '*') { if (sscanf(buf, "%f", &arg1f)) *v = (int)(arg0i * arg1f); } // Multiply - else if (op == '/') { if (sscanf(buf, "%f", &arg1f) && arg1f != 0.0f) *v = (int)(arg0i / arg1f); } // Divide - else { if (sscanf(buf, format, &arg1i) == 1) *v = arg1i; } // Assign constant - } - else if (data_type == ImGuiDataType_U32 || data_type == ImGuiDataType_S64 || data_type == ImGuiDataType_U64) - { - // Assign constant - // FIXME: We don't bother handling support for legacy operators since they are a little too crappy. Instead we may implement a proper expression evaluator in the future. - sscanf(buf, format, data_ptr); - } - else if (data_type == ImGuiDataType_Float) - { - // For floats we have to ignore format with precision (e.g. "%.2f") because sscanf doesn't take them in - format = "%f"; - float* v = (float*)data_ptr; - float arg0f = *v, arg1f = 0.0f; - if (op && sscanf(initial_value_buf, format, &arg0f) < 1) - return false; - if (sscanf(buf, format, &arg1f) < 1) - return false; - if (op == '+') { *v = arg0f + arg1f; } // Add (use "+-" to subtract) - else if (op == '*') { *v = arg0f * arg1f; } // Multiply - else if (op == '/') { if (arg1f != 0.0f) *v = arg0f / arg1f; } // Divide - else { *v = arg1f; } // Assign constant - } - else if (data_type == ImGuiDataType_Double) - { - format = "%lf"; // scanf differentiate float/double unlike printf which forces everything to double because of ellipsis - double* v = (double*)data_ptr; - double arg0f = *v, arg1f = 0.0; - if (op && sscanf(initial_value_buf, format, &arg0f) < 1) - return false; - if (sscanf(buf, format, &arg1f) < 1) - return false; - if (op == '+') { *v = arg0f + arg1f; } // Add (use "+-" to subtract) - else if (op == '*') { *v = arg0f * arg1f; } // Multiply - else if (op == '/') { if (arg1f != 0.0f) *v = arg0f / arg1f; } // Divide - else { *v = arg1f; } // Assign constant - } - return memcmp(data_backup, data_ptr, GDataTypeInfo[data_type].Size) != 0; -} - -// Create text input in place of a slider (when CTRL+Clicking on slider) -// FIXME: Logic is messy and confusing. -bool ImGui::InputScalarAsWidgetReplacement(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* data_ptr, const char* format) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = GetCurrentWindow(); - - // Our replacement widget will override the focus ID (registered previously to allow for a TAB focus to happen) - // On the first frame, g.ScalarAsInputTextId == 0, then on subsequent frames it becomes == id - SetActiveID(g.ScalarAsInputTextId, window); - g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); - SetHoveredID(0); - FocusableItemUnregister(window); - - char fmt_buf[32]; - char data_buf[32]; - format = ImParseFormatTrimDecorations(format, fmt_buf, IM_ARRAYSIZE(fmt_buf)); - DataTypeFormatString(data_buf, IM_ARRAYSIZE(data_buf), data_type, data_ptr, format); - ImStrTrimBlanks(data_buf); - ImGuiInputTextFlags flags = ImGuiInputTextFlags_AutoSelectAll | ((data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double) ? ImGuiInputTextFlags_CharsScientific : ImGuiInputTextFlags_CharsDecimal); - bool value_changed = InputTextEx(label, data_buf, IM_ARRAYSIZE(data_buf), bb.GetSize(), flags); - if (g.ScalarAsInputTextId == 0) // First frame we started displaying the InputText widget - { - IM_ASSERT(g.ActiveId == id); // InputText ID expected to match the Slider ID - g.ScalarAsInputTextId = g.ActiveId; - SetHoveredID(id); - } - if (value_changed) - return DataTypeApplyOpFromText(data_buf, g.InputTextState.InitialText.Data, data_type, data_ptr, NULL); - return false; -} - -// We don't use strchr() because our strings are usually very short and often start with '%' -const char* ImParseFormatFindStart(const char* fmt) -{ - while (char c = fmt[0]) - { - if (c == '%' && fmt[1] != '%') - return fmt; - else if (c == '%') - fmt++; - fmt++; - } - return fmt; -} - -const char* ImParseFormatFindEnd(const char* fmt) -{ - // Printf/scanf types modifiers: I/L/h/j/l/t/w/z. Other uppercase letters qualify as types aka end of the format. - if (fmt[0] != '%') - return fmt; - const unsigned int ignored_uppercase_mask = (1 << ('I'-'A')) | (1 << ('L'-'A')); - const unsigned int ignored_lowercase_mask = (1 << ('h'-'a')) | (1 << ('j'-'a')) | (1 << ('l'-'a')) | (1 << ('t'-'a')) | (1 << ('w'-'a')) | (1 << ('z'-'a')); - for (char c; (c = *fmt) != 0; fmt++) - { - if (c >= 'A' && c <= 'Z' && ((1 << (c - 'A')) & ignored_uppercase_mask) == 0) - return fmt + 1; - if (c >= 'a' && c <= 'z' && ((1 << (c - 'a')) & ignored_lowercase_mask) == 0) - return fmt + 1; - } - return fmt; -} - -// Extract the format out of a format string with leading or trailing decorations -// fmt = "blah blah" -> return fmt -// fmt = "%.3f" -> return fmt -// fmt = "hello %.3f" -> return fmt + 6 -// fmt = "%.3f hello" -> return buf written with "%.3f" -const char* ImParseFormatTrimDecorations(const char* fmt, char* buf, int buf_size) -{ - const char* fmt_start = ImParseFormatFindStart(fmt); - if (fmt_start[0] != '%') - return fmt; - const char* fmt_end = ImParseFormatFindEnd(fmt_start); - if (fmt_end[0] == 0) // If we only have leading decoration, we don't need to copy the data. - return fmt_start; - ImStrncpy(buf, fmt_start, ImMin((int)(fmt_end + 1 - fmt_start), buf_size)); - return buf; -} - -// Parse display precision back from the display format string -// FIXME: This is still used by some navigation code path to infer a minimum tweak step, but we should aim to rework widgets so it isn't needed. -int ImParseFormatPrecision(const char* fmt, int default_precision) -{ - fmt = ImParseFormatFindStart(fmt); - if (fmt[0] != '%') - return default_precision; - fmt++; - while (*fmt >= '0' && *fmt <= '9') - fmt++; - int precision = INT_MAX; - if (*fmt == '.') - { - fmt = ImAtoi(fmt + 1, &precision); - if (precision < 0 || precision > 99) - precision = default_precision; - } - if (*fmt == 'e' || *fmt == 'E') // Maximum precision with scientific notation - precision = -1; - if ((*fmt == 'g' || *fmt == 'G') && precision == INT_MAX) - precision = -1; - return (precision == INT_MAX) ? default_precision : precision; -} - -static float GetMinimumStepAtDecimalPrecision(int decimal_precision) -{ - static const float min_steps[10] = { 1.0f, 0.1f, 0.01f, 0.001f, 0.0001f, 0.00001f, 0.000001f, 0.0000001f, 0.00000001f, 0.000000001f }; - if (decimal_precision < 0) - return FLT_MIN; - return (decimal_precision >= 0 && decimal_precision < 10) ? min_steps[decimal_precision] : ImPow(10.0f, (float)-decimal_precision); -} - -template -static inline TYPE RoundScalarWithFormat(const char* format, ImGuiDataType data_type, TYPE v) -{ - const char* fmt_start = ImParseFormatFindStart(format); - if (fmt_start[0] != '%' || fmt_start[1] == '%') // Don't apply if the value is not visible in the format string - return v; - char v_str[64]; - ImFormatString(v_str, IM_ARRAYSIZE(v_str), fmt_start, v); - const char* p = v_str; - while (*p == ' ') - p++; - if (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double) - v = (TYPE)ImAtof(p); - else - ImAtoi(p, (SIGNEDTYPE*)&v); - return v; -} - -template -static inline float SliderBehaviorCalcRatioFromValue(ImGuiDataType data_type, TYPE v, TYPE v_min, TYPE v_max, float power, float linear_zero_pos) -{ - if (v_min == v_max) - return 0.0f; - - const bool is_power = (power != 1.0f) && (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double); - const TYPE v_clamped = (v_min < v_max) ? ImClamp(v, v_min, v_max) : ImClamp(v, v_max, v_min); - if (is_power) - { - if (v_clamped < 0.0f) - { - const float f = 1.0f - (float)((v_clamped - v_min) / (ImMin((TYPE)0, v_max) - v_min)); - return (1.0f - ImPow(f, 1.0f/power)) * linear_zero_pos; - } - else - { - const float f = (float)((v_clamped - ImMax((TYPE)0, v_min)) / (v_max - ImMax((TYPE)0, v_min))); - return linear_zero_pos + ImPow(f, 1.0f/power) * (1.0f - linear_zero_pos); - } - } - - // Linear slider - return (float)((FLOATTYPE)(v_clamped - v_min) / (FLOATTYPE)(v_max - v_min)); -} - -// FIXME: Move some of the code into SliderBehavior(). Current responsability is larger than what the equivalent DragBehaviorT<> does, we also do some rendering, etc. -template -static bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, TYPE* v, const TYPE v_min, const TYPE v_max, const char* format, float power, ImGuiSliderFlags flags, ImRect* out_grab_bb) -{ - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - - const bool is_horizontal = (flags & ImGuiSliderFlags_Vertical) == 0; - const bool is_decimal = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double); - const bool is_power = (power != 1.0f) && is_decimal; - - const float grab_padding = 2.0f; - const float slider_sz = is_horizontal ? (bb.GetWidth() - grab_padding * 2.0f) : (bb.GetHeight() - grab_padding * 2.0f); - float grab_sz = style.GrabMinSize; - SIGNEDTYPE v_range = (v_min < v_max ? v_max - v_min : v_min - v_max); - if (!is_decimal && v_range >= 0) // v_range < 0 may happen on integer overflows - grab_sz = ImMax((float)(slider_sz / (v_range + 1)), style.GrabMinSize); // For integer sliders: if possible have the grab size represent 1 unit - grab_sz = ImMin(grab_sz, slider_sz); - const float slider_usable_sz = slider_sz - grab_sz; - const float slider_usable_pos_min = (is_horizontal ? bb.Min.x : bb.Min.y) + grab_padding + grab_sz*0.5f; - const float slider_usable_pos_max = (is_horizontal ? bb.Max.x : bb.Max.y) - grab_padding - grab_sz*0.5f; - - // For power curve sliders that cross over sign boundary we want the curve to be symmetric around 0.0f - float linear_zero_pos; // 0.0->1.0f - if (is_power && v_min * v_max < 0.0f) - { - // Different sign - const FLOATTYPE linear_dist_min_to_0 = ImPow(v_min >= 0 ? (FLOATTYPE)v_min : -(FLOATTYPE)v_min, (FLOATTYPE)1.0f/power); - const FLOATTYPE linear_dist_max_to_0 = ImPow(v_max >= 0 ? (FLOATTYPE)v_max : -(FLOATTYPE)v_max, (FLOATTYPE)1.0f/power); - linear_zero_pos = (float)(linear_dist_min_to_0 / (linear_dist_min_to_0 + linear_dist_max_to_0)); - } - else - { - // Same sign - linear_zero_pos = v_min < 0.0f ? 1.0f : 0.0f; - } - - // Process interacting with the slider - bool value_changed = false; - if (g.ActiveId == id) - { - bool set_new_value = false; - float clicked_t = 0.0f; - if (g.ActiveIdSource == ImGuiInputSource_Mouse) - { - if (!g.IO.MouseDown[0]) - { - ClearActiveID(); - } - else - { - const float mouse_abs_pos = is_horizontal ? g.IO.MousePos.x : g.IO.MousePos.y; - clicked_t = (slider_usable_sz > 0.0f) ? ImClamp((mouse_abs_pos - slider_usable_pos_min) / slider_usable_sz, 0.0f, 1.0f) : 0.0f; - if (!is_horizontal) - clicked_t = 1.0f - clicked_t; - set_new_value = true; - } - } - else if (g.ActiveIdSource == ImGuiInputSource_Nav) - { - const ImVec2 delta2 = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard | ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_RepeatFast, 0.0f, 0.0f); - float delta = is_horizontal ? delta2.x : -delta2.y; - if (g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated) - { - ClearActiveID(); - } - else if (delta != 0.0f) - { - clicked_t = SliderBehaviorCalcRatioFromValue(data_type, *v, v_min, v_max, power, linear_zero_pos); - const int decimal_precision = is_decimal ? ImParseFormatPrecision(format, 3) : 0; - if ((decimal_precision > 0) || is_power) - { - delta /= 100.0f; // Gamepad/keyboard tweak speeds in % of slider bounds - if (IsNavInputDown(ImGuiNavInput_TweakSlow)) - delta /= 10.0f; - } - else - { - if ((v_range >= -100.0f && v_range <= 100.0f) || IsNavInputDown(ImGuiNavInput_TweakSlow)) - delta = ((delta < 0.0f) ? -1.0f : +1.0f) / (float)v_range; // Gamepad/keyboard tweak speeds in integer steps - else - delta /= 100.0f; - } - if (IsNavInputDown(ImGuiNavInput_TweakFast)) - delta *= 10.0f; - set_new_value = true; - if ((clicked_t >= 1.0f && delta > 0.0f) || (clicked_t <= 0.0f && delta < 0.0f)) // This is to avoid applying the saturation when already past the limits - set_new_value = false; - else - clicked_t = ImSaturate(clicked_t + delta); - } - } - - if (set_new_value) - { - TYPE v_new; - if (is_power) - { - // Account for power curve scale on both sides of the zero - if (clicked_t < linear_zero_pos) - { - // Negative: rescale to the negative range before powering - float a = 1.0f - (clicked_t / linear_zero_pos); - a = ImPow(a, power); - v_new = ImLerp(ImMin(v_max, (TYPE)0), v_min, a); - } - else - { - // Positive: rescale to the positive range before powering - float a; - if (ImFabs(linear_zero_pos - 1.0f) > 1.e-6f) - a = (clicked_t - linear_zero_pos) / (1.0f - linear_zero_pos); - else - a = clicked_t; - a = ImPow(a, power); - v_new = ImLerp(ImMax(v_min, (TYPE)0), v_max, a); - } - } - else - { - // Linear slider - if (is_decimal) - { - v_new = ImLerp(v_min, v_max, clicked_t); - } - else - { - // For integer values we want the clicking position to match the grab box so we round above - // This code is carefully tuned to work with large values (e.g. high ranges of U64) while preserving this property.. - FLOATTYPE v_new_off_f = (v_max - v_min) * clicked_t; - TYPE v_new_off_floor = (TYPE)(v_new_off_f); - TYPE v_new_off_round = (TYPE)(v_new_off_f + (FLOATTYPE)0.5); - if (!is_decimal && v_new_off_floor < v_new_off_round) - v_new = v_min + v_new_off_round; - else - v_new = v_min + v_new_off_floor; - } - } - - // Round to user desired precision based on format string - v_new = RoundScalarWithFormat(format, data_type, v_new); - - // Apply result - if (*v != v_new) - { - *v = v_new; - value_changed = true; - } - } - } - - // Output grab position so it can be displayed by the caller - float grab_t = SliderBehaviorCalcRatioFromValue(data_type, *v, v_min, v_max, power, linear_zero_pos); - if (!is_horizontal) - grab_t = 1.0f - grab_t; - const float grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t); - if (is_horizontal) - *out_grab_bb = ImRect(grab_pos - grab_sz*0.5f, bb.Min.y + grab_padding, grab_pos + grab_sz*0.5f, bb.Max.y - grab_padding); - else - *out_grab_bb = ImRect(bb.Min.x + grab_padding, grab_pos - grab_sz*0.5f, bb.Max.x - grab_padding, grab_pos + grab_sz*0.5f); - - return value_changed; -} - -// For 32-bits and larger types, slider bounds are limited to half the natural type range. -// So e.g. an integer Slider between INT_MAX-10 and INT_MAX will fail, but an integer Slider between INT_MAX/2-10 and INT_MAX/2 will be ok. -// It would be possible to lift that limitation with some work but it doesn't seem to be worth it for sliders. -bool ImGui::SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, void* v, const void* v_min, const void* v_max, const char* format, float power, ImGuiSliderFlags flags, ImRect* out_grab_bb) -{ - switch (data_type) - { - case ImGuiDataType_S32: - IM_ASSERT(*(const ImS32*)v_min >= IM_S32_MIN/2 && *(const ImS32*)v_max <= IM_S32_MAX/2); - return SliderBehaviorT(bb, id, data_type, (ImS32*)v, *(const ImS32*)v_min, *(const ImS32*)v_max, format, power, flags, out_grab_bb); - case ImGuiDataType_U32: - IM_ASSERT(*(const ImU32*)v_min <= IM_U32_MAX/2); - return SliderBehaviorT(bb, id, data_type, (ImU32*)v, *(const ImU32*)v_min, *(const ImU32*)v_max, format, power, flags, out_grab_bb); - case ImGuiDataType_S64: - IM_ASSERT(*(const ImS64*)v_min >= IM_S64_MIN/2 && *(const ImS64*)v_max <= IM_S64_MAX/2); - return SliderBehaviorT(bb, id, data_type, (ImS64*)v, *(const ImS64*)v_min, *(const ImS64*)v_max, format, power, flags, out_grab_bb); - case ImGuiDataType_U64: - IM_ASSERT(*(const ImU64*)v_min <= IM_U64_MAX/2); - return SliderBehaviorT(bb, id, data_type, (ImU64*)v, *(const ImU64*)v_min, *(const ImU64*)v_max, format, power, flags, out_grab_bb); - case ImGuiDataType_Float: - IM_ASSERT(*(const float*)v_min >= -FLT_MAX/2.0f && *(const float*)v_max <= FLT_MAX/2.0f); - return SliderBehaviorT(bb, id, data_type, (float*)v, *(const float*)v_min, *(const float*)v_max, format, power, flags, out_grab_bb); - case ImGuiDataType_Double: - IM_ASSERT(*(const double*)v_min >= -DBL_MAX/2.0f && *(const double*)v_max <= DBL_MAX/2.0f); - return SliderBehaviorT(bb, id, data_type, (double*)v, *(const double*)v_min, *(const double*)v_max, format, power, flags, out_grab_bb); - case ImGuiDataType_COUNT: break; - } - IM_ASSERT(0); - return false; -} - -// FIXME-LEGACY: Prior to 1.61 our DragInt() function internally used floats and because of this the compile-time default value for format was "%.0f". -// Even though we changed the compile-time default, we expect users to have carried %f around, which would break the display of DragInt() calls. -// To honor backward compatibility we are rewriting the format string, unless IMGUI_DISABLE_OBSOLETE_FUNCTIONS is enabled. What could possibly go wrong?! -static const char* PatchFormatStringFloatToInt(const char* fmt) -{ - if (fmt[0] == '%' && fmt[1] == '.' && fmt[2] == '0' && fmt[3] == 'f' && fmt[4] == 0) // Fast legacy path for "%.0f" which is expected to be the most common case. - return "%d"; - const char* fmt_start = ImParseFormatFindStart(fmt); // Find % (if any, and ignore %%) - const char* fmt_end = ImParseFormatFindEnd(fmt_start); // Find end of format specifier, which itself is an exercise of confidence/recklessness (because snprintf is dependent on libc or user). - if (fmt_end > fmt_start && fmt_end[-1] == 'f') - { -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - if (fmt_start == fmt && fmt_end[0] == 0) - return "%d"; - ImGuiContext& g = *GImGui; - ImFormatString(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), "%.*s%%d%s", (int)(fmt_start - fmt), fmt, fmt_end); // Honor leading and trailing decorations, but lose alignment/precision. - return g.TempBuffer; -#else - IM_ASSERT(0 && "DragInt(): Invalid format string!"); // Old versions used a default parameter of "%.0f", please replace with e.g. "%d" -#endif - } - return fmt; -} - -bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* v, const void* v_min, const void* v_max, const char* format, float power) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - const ImGuiID id = window->GetID(label); - const float w = CalcItemWidth(); - - const ImVec2 label_size = CalcTextSize(label, NULL, true); - const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f)); - const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); - - // NB- we don't call ItemSize() yet because we may turn into a text edit box below - if (!ItemAdd(total_bb, id, &frame_bb)) - { - ItemSize(total_bb, style.FramePadding.y); - return false; - } - - // Default format string when passing NULL - // Patch old "%.0f" format string to use "%d", read function comments for more details. - IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT); - if (format == NULL) - format = GDataTypeInfo[data_type].PrintFmt; - else if (data_type == ImGuiDataType_S32 && strcmp(format, "%d") != 0) - format = PatchFormatStringFloatToInt(format); - - // Tabbing or CTRL-clicking on Slider turns it into an input box - bool start_text_input = false; - const bool tab_focus_requested = FocusableItemRegister(window, id); - const bool hovered = ItemHoverable(frame_bb, id); - if (tab_focus_requested || (hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || (g.NavInputId == id && g.ScalarAsInputTextId != id)) - { - SetActiveID(id, window); - SetFocusID(id, window); - FocusWindow(window); - g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); - if (tab_focus_requested || g.IO.KeyCtrl || g.NavInputId == id) - { - start_text_input = true; - g.ScalarAsInputTextId = 0; - } - } - if (start_text_input || (g.ActiveId == id && g.ScalarAsInputTextId == id)) - return InputScalarAsWidgetReplacement(frame_bb, id, label, data_type, v, format); - - ItemSize(total_bb, style.FramePadding.y); - - // Draw frame - const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); - RenderNavHighlight(frame_bb, id); - RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, g.Style.FrameRounding); - - // Slider behavior - ImRect grab_bb; - const bool value_changed = SliderBehavior(frame_bb, id, data_type, v, v_min, v_max, format, power, ImGuiSliderFlags_None, &grab_bb); - if (value_changed) - MarkItemEdited(id); - - // Render grab - window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding); - - // Display value using user-provided display format so user can add prefix/suffix/decorations to the value. - char value_buf[64]; - const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, v, format); - RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f,0.5f)); - - if (label_size.x > 0.0f) - RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); - - return value_changed; -} - -bool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, const char* format, float power) -{ - return SliderScalar(label, ImGuiDataType_Float, v, &v_min, &v_max, format, power); -} - -bool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType data_type, void* v, const void* v_min, const void* v_max, const char* format, float power) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - const ImGuiID id = window->GetID(label); - - const ImVec2 label_size = CalcTextSize(label, NULL, true); - const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size); - const ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); - - ItemSize(bb, style.FramePadding.y); - if (!ItemAdd(frame_bb, id)) - return false; - - // Default format string when passing NULL - // Patch old "%.0f" format string to use "%d", read function comments for more details. - IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT); - if (format == NULL) - format = GDataTypeInfo[data_type].PrintFmt; - else if (data_type == ImGuiDataType_S32 && strcmp(format, "%d") != 0) - format = PatchFormatStringFloatToInt(format); - - const bool hovered = ItemHoverable(frame_bb, id); - if ((hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || g.NavInputId == id) - { - SetActiveID(id, window); - SetFocusID(id, window); - FocusWindow(window); - g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right); - } - - - // Draw frame - const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); - RenderNavHighlight(frame_bb, id); - RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, g.Style.FrameRounding); - - // Slider behavior - ImRect grab_bb; - const bool value_changed = SliderBehavior(frame_bb, id, data_type, v, v_min, v_max, format, power, ImGuiSliderFlags_Vertical, &grab_bb); - if (value_changed) - MarkItemEdited(id); - - // Render grab - window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding); - - // Display value using user-provided display format so user can add prefix/suffix/decorations to the value. - // For the vertical slider we allow centered text to overlap the frame padding - char value_buf[64]; - const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, v, format); - RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f,0.0f)); - if (label_size.x > 0.0f) - RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); - - return value_changed; -} - -bool ImGui::SliderAngle(const char* label, float* v_rad, float v_degrees_min, float v_degrees_max) -{ - float v_deg = (*v_rad) * 360.0f / (2*IM_PI); - bool value_changed = SliderFloat(label, &v_deg, v_degrees_min, v_degrees_max, "%.0f deg", 1.0f); - *v_rad = v_deg * (2*IM_PI) / 360.0f; - return value_changed; -} - -bool ImGui::SliderInt(const char* label, int* v, int v_min, int v_max, const char* format) -{ - return SliderScalar(label, ImGuiDataType_S32, v, &v_min, &v_max, format); -} - -bool ImGui::VSliderFloat(const char* label, const ImVec2& size, float* v, float v_min, float v_max, const char* format, float power) -{ - return VSliderScalar(label, size, ImGuiDataType_Float, v, &v_min, &v_max, format, power); -} - -bool ImGui::VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* format) -{ - return VSliderScalar(label, size, ImGuiDataType_S32, v, &v_min, &v_max, format); -} - -// Add multiple sliders on 1 line for compact edition of multiple components -bool ImGui::SliderScalarN(const char* label, ImGuiDataType data_type, void* v, int components, const void* v_min, const void* v_max, const char* format, float power) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - bool value_changed = false; - BeginGroup(); - PushID(label); - PushMultiItemsWidths(components); - size_t type_size = GDataTypeInfo[data_type].Size; - for (int i = 0; i < components; i++) - { - PushID(i); - value_changed |= SliderScalar("##v", data_type, v, v_min, v_max, format, power); - SameLine(0, g.Style.ItemInnerSpacing.x); - PopID(); - PopItemWidth(); - v = (void*)((char*)v + type_size); - } - PopID(); - - TextUnformatted(label, FindRenderedTextEnd(label)); - EndGroup(); - return value_changed; -} - -bool ImGui::SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* format, float power) -{ - return SliderScalarN(label, ImGuiDataType_Float, v, 2, &v_min, &v_max, format, power); -} - -bool ImGui::SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* format, float power) -{ - return SliderScalarN(label, ImGuiDataType_Float, v, 3, &v_min, &v_max, format, power); -} - -bool ImGui::SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* format, float power) -{ - return SliderScalarN(label, ImGuiDataType_Float, v, 4, &v_min, &v_max, format, power); -} - -bool ImGui::SliderInt2(const char* label, int v[2], int v_min, int v_max, const char* format) -{ - return SliderScalarN(label, ImGuiDataType_S32, v, 2, &v_min, &v_max, format); -} - -bool ImGui::SliderInt3(const char* label, int v[3], int v_min, int v_max, const char* format) -{ - return SliderScalarN(label, ImGuiDataType_S32, v, 3, &v_min, &v_max, format); -} - -bool ImGui::SliderInt4(const char* label, int v[4], int v_min, int v_max, const char* format) -{ - return SliderScalarN(label, ImGuiDataType_S32, v, 4, &v_min, &v_max, format); -} - -// This is called by DragBehavior() when the widget is active (held by mouse or being manipulated with Nav controls) -template -static bool ImGui::DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed, const TYPE v_min, const TYPE v_max, const char* format, float power) -{ - ImGuiContext& g = *GImGui; - - // Default tweak speed - bool has_min_max = (v_min != v_max) && (v_max - v_max < FLT_MAX); - if (v_speed == 0.0f && has_min_max) - v_speed = (float)((v_max - v_min) * g.DragSpeedDefaultRatio); - - // Inputs accumulates into g.DragCurrentAccum, which is flushed into the current value as soon as it makes a difference with our precision settings - float adjust_delta = 0.0f; - if (g.ActiveIdSource == ImGuiInputSource_Mouse && IsMousePosValid() && g.IO.MouseDragMaxDistanceSqr[0] > 1.0f*1.0f) - { - adjust_delta = g.IO.MouseDelta.x; - if (g.IO.KeyAlt) - adjust_delta *= 1.0f/100.0f; - if (g.IO.KeyShift) - adjust_delta *= 10.0f; - } - else if (g.ActiveIdSource == ImGuiInputSource_Nav) - { - int decimal_precision = (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double) ? ImParseFormatPrecision(format, 3) : 0; - adjust_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard|ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_RepeatFast, 1.0f/10.0f, 10.0f).x; - v_speed = ImMax(v_speed, GetMinimumStepAtDecimalPrecision(decimal_precision)); - } - adjust_delta *= v_speed; - - // Clear current value on activation - // Avoid altering values and clamping when we are _already_ past the limits and heading in the same direction, so e.g. if range is 0..255, current value is 300 and we are pushing to the right side, keep the 300. - bool is_just_activated = g.ActiveIdIsJustActivated; - bool is_already_past_limits_and_pushing_outward = has_min_max && ((*v >= v_max && adjust_delta > 0.0f) || (*v <= v_min && adjust_delta < 0.0f)); - if (is_just_activated || is_already_past_limits_and_pushing_outward) - { - g.DragCurrentAccum = 0.0f; - g.DragCurrentAccumDirty = false; - } - else if (adjust_delta != 0.0f) - { - g.DragCurrentAccum += adjust_delta; - g.DragCurrentAccumDirty = true; - } - - if (!g.DragCurrentAccumDirty) - return false; - - TYPE v_cur = *v; - FLOATTYPE v_old_ref_for_accum_remainder = (FLOATTYPE)0.0f; - - const bool is_power = (power != 1.0f && (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double) && has_min_max); - if (is_power) - { - // Offset + round to user desired precision, with a curve on the v_min..v_max range to get more precision on one side of the range - FLOATTYPE v_old_norm_curved = ImPow((FLOATTYPE)(v_cur - v_min) / (FLOATTYPE)(v_max - v_min), (FLOATTYPE)1.0f / power); - FLOATTYPE v_new_norm_curved = v_old_norm_curved + (g.DragCurrentAccum / (v_max - v_min)); - v_cur = v_min + (TYPE)ImPow(ImSaturate((float)v_new_norm_curved), power) * (v_max - v_min); - v_old_ref_for_accum_remainder = v_old_norm_curved; - } - else - { - v_cur += (TYPE)g.DragCurrentAccum; - } - - // Round to user desired precision based on format string - v_cur = RoundScalarWithFormat(format, data_type, v_cur); - - // Preserve remainder after rounding has been applied. This also allow slow tweaking of values. - g.DragCurrentAccumDirty = false; - if (is_power) - { - FLOATTYPE v_cur_norm_curved = ImPow((FLOATTYPE)(v_cur - v_min) / (FLOATTYPE)(v_max - v_min), (FLOATTYPE)1.0f / power); - g.DragCurrentAccum -= (float)(v_cur_norm_curved - v_old_ref_for_accum_remainder); - } - else - { - g.DragCurrentAccum -= (float)((SIGNEDTYPE)v_cur - (SIGNEDTYPE)*v); - } - - // Lose zero sign for float/double - if (v_cur == (TYPE)-0) - v_cur = (TYPE)0; - - // Clamp values (handle overflow/wrap-around) - if (*v != v_cur && has_min_max) - { - if (v_cur < v_min || (v_cur > *v && adjust_delta < 0.0f)) - v_cur = v_min; - if (v_cur > v_max || (v_cur < *v && adjust_delta > 0.0f)) - v_cur = v_max; - } - - // Apply result - if (*v == v_cur) - return false; - *v = v_cur; - return true; -} - -bool ImGui::DragBehavior(ImGuiID id, ImGuiDataType data_type, void* v, float v_speed, const void* v_min, const void* v_max, const char* format, float power) -{ - ImGuiContext& g = *GImGui; - if (g.ActiveId == id) - { - if (g.ActiveIdSource == ImGuiInputSource_Mouse && !g.IO.MouseDown[0]) - ClearActiveID(); - else if (g.ActiveIdSource == ImGuiInputSource_Nav && g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated) - ClearActiveID(); - } - if (g.ActiveId != id) - return false; - - switch (data_type) - { - case ImGuiDataType_S32: return DragBehaviorT(data_type, (ImS32*)v, v_speed, v_min ? *(const ImS32* )v_min : IM_S32_MIN, v_max ? *(const ImS32* )v_max : IM_S32_MAX, format, power); - case ImGuiDataType_U32: return DragBehaviorT(data_type, (ImU32*)v, v_speed, v_min ? *(const ImU32* )v_min : IM_U32_MIN, v_max ? *(const ImU32* )v_max : IM_U32_MAX, format, power); - case ImGuiDataType_S64: return DragBehaviorT(data_type, (ImS64*)v, v_speed, v_min ? *(const ImS64* )v_min : IM_S64_MIN, v_max ? *(const ImS64* )v_max : IM_S64_MAX, format, power); - case ImGuiDataType_U64: return DragBehaviorT(data_type, (ImU64*)v, v_speed, v_min ? *(const ImU64* )v_min : IM_U64_MIN, v_max ? *(const ImU64* )v_max : IM_U64_MAX, format, power); - case ImGuiDataType_Float: return DragBehaviorT(data_type, (float*)v, v_speed, v_min ? *(const float* )v_min : -FLT_MAX, v_max ? *(const float* )v_max : FLT_MAX, format, power); - case ImGuiDataType_Double: return DragBehaviorT(data_type, (double*)v, v_speed, v_min ? *(const double*)v_min : -DBL_MAX, v_max ? *(const double*)v_max : DBL_MAX, format, power); - case ImGuiDataType_COUNT: break; - } - IM_ASSERT(0); - return false; -} - -bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* v, float v_speed, const void* v_min, const void* v_max, const char* format, float power) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - if (power != 1.0f) - IM_ASSERT(v_min != NULL && v_max != NULL); // When using a power curve the drag needs to have known bounds - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - const ImGuiID id = window->GetID(label); - const float w = CalcItemWidth(); - - const ImVec2 label_size = CalcTextSize(label, NULL, true); - const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f)); - const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding); - const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); - - // NB- we don't call ItemSize() yet because we may turn into a text edit box below - if (!ItemAdd(total_bb, id, &frame_bb)) - { - ItemSize(total_bb, style.FramePadding.y); - return false; - } - const bool hovered = ItemHoverable(frame_bb, id); - - // Default format string when passing NULL - // Patch old "%.0f" format string to use "%d", read function comments for more details. - IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT); - if (format == NULL) - format = GDataTypeInfo[data_type].PrintFmt; - else if (data_type == ImGuiDataType_S32 && strcmp(format, "%d") != 0) - format = PatchFormatStringFloatToInt(format); - - // Tabbing or CTRL-clicking on Drag turns it into an input box - bool start_text_input = false; - const bool tab_focus_requested = FocusableItemRegister(window, id); - if (tab_focus_requested || (hovered && (g.IO.MouseClicked[0] || g.IO.MouseDoubleClicked[0])) || g.NavActivateId == id || (g.NavInputId == id && g.ScalarAsInputTextId != id)) - { - SetActiveID(id, window); - SetFocusID(id, window); - FocusWindow(window); - g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); - if (tab_focus_requested || g.IO.KeyCtrl || g.IO.MouseDoubleClicked[0] || g.NavInputId == id) - { - start_text_input = true; - g.ScalarAsInputTextId = 0; - } - } - if (start_text_input || (g.ActiveId == id && g.ScalarAsInputTextId == id)) - return InputScalarAsWidgetReplacement(frame_bb, id, label, data_type, v, format); - - // Actual drag behavior - ItemSize(total_bb, style.FramePadding.y); - const bool value_changed = DragBehavior(id, data_type, v, v_speed, v_min, v_max, format, power); - if (value_changed) - MarkItemEdited(id); - - // Draw frame - const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); - RenderNavHighlight(frame_bb, id); - RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, style.FrameRounding); - - // Display value using user-provided display format so user can add prefix/suffix/decorations to the value. - char value_buf[64]; - const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, v, format); - RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.5f)); - - if (label_size.x > 0.0f) - RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label); - - return value_changed; -} - -bool ImGui::DragScalarN(const char* label, ImGuiDataType data_type, void* v, int components, float v_speed, const void* v_min, const void* v_max, const char* format, float power) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - bool value_changed = false; - BeginGroup(); - PushID(label); - PushMultiItemsWidths(components); - size_t type_size = GDataTypeInfo[data_type].Size; - for (int i = 0; i < components; i++) - { - PushID(i); - value_changed |= DragScalar("##v", data_type, v, v_speed, v_min, v_max, format, power); - SameLine(0, g.Style.ItemInnerSpacing.x); - PopID(); - PopItemWidth(); - v = (void*)((char*)v + type_size); - } - PopID(); - - TextUnformatted(label, FindRenderedTextEnd(label)); - EndGroup(); - return value_changed; -} - -bool ImGui::DragFloat(const char* label, float* v, float v_speed, float v_min, float v_max, const char* format, float power) -{ - return DragScalar(label, ImGuiDataType_Float, v, v_speed, &v_min, &v_max, format, power); -} - -bool ImGui::DragFloat2(const char* label, float v[2], float v_speed, float v_min, float v_max, const char* format, float power) -{ - return DragScalarN(label, ImGuiDataType_Float, v, 2, v_speed, &v_min, &v_max, format, power); -} - -bool ImGui::DragFloat3(const char* label, float v[3], float v_speed, float v_min, float v_max, const char* format, float power) -{ - return DragScalarN(label, ImGuiDataType_Float, v, 3, v_speed, &v_min, &v_max, format, power); -} - -bool ImGui::DragFloat4(const char* label, float v[4], float v_speed, float v_min, float v_max, const char* format, float power) -{ - return DragScalarN(label, ImGuiDataType_Float, v, 4, v_speed, &v_min, &v_max, format, power); -} - -bool ImGui::DragFloatRange2(const char* label, float* v_current_min, float* v_current_max, float v_speed, float v_min, float v_max, const char* format, const char* format_max, float power) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - PushID(label); - BeginGroup(); - PushMultiItemsWidths(2); - - bool value_changed = DragFloat("##min", v_current_min, v_speed, (v_min >= v_max) ? -FLT_MAX : v_min, (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max), format, power); - PopItemWidth(); - SameLine(0, g.Style.ItemInnerSpacing.x); - value_changed |= DragFloat("##max", v_current_max, v_speed, (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min), (v_min >= v_max) ? FLT_MAX : v_max, format_max ? format_max : format, power); - PopItemWidth(); - SameLine(0, g.Style.ItemInnerSpacing.x); - - TextUnformatted(label, FindRenderedTextEnd(label)); - EndGroup(); - PopID(); - return value_changed; -} - -// NB: v_speed is float to allow adjusting the drag speed with more precision -bool ImGui::DragInt(const char* label, int* v, float v_speed, int v_min, int v_max, const char* format) -{ - return DragScalar(label, ImGuiDataType_S32, v, v_speed, &v_min, &v_max, format); -} - -bool ImGui::DragInt2(const char* label, int v[2], float v_speed, int v_min, int v_max, const char* format) -{ - return DragScalarN(label, ImGuiDataType_S32, v, 2, v_speed, &v_min, &v_max, format); -} - -bool ImGui::DragInt3(const char* label, int v[3], float v_speed, int v_min, int v_max, const char* format) -{ - return DragScalarN(label, ImGuiDataType_S32, v, 3, v_speed, &v_min, &v_max, format); -} - -bool ImGui::DragInt4(const char* label, int v[4], float v_speed, int v_min, int v_max, const char* format) -{ - return DragScalarN(label, ImGuiDataType_S32, v, 4, v_speed, &v_min, &v_max, format); -} - -bool ImGui::DragIntRange2(const char* label, int* v_current_min, int* v_current_max, float v_speed, int v_min, int v_max, const char* format, const char* format_max) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - PushID(label); - BeginGroup(); - PushMultiItemsWidths(2); - - bool value_changed = DragInt("##min", v_current_min, v_speed, (v_min >= v_max) ? INT_MIN : v_min, (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max), format); - PopItemWidth(); - SameLine(0, g.Style.ItemInnerSpacing.x); - value_changed |= DragInt("##max", v_current_max, v_speed, (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min), (v_min >= v_max) ? INT_MAX : v_max, format_max ? format_max : format); - PopItemWidth(); - SameLine(0, g.Style.ItemInnerSpacing.x); - - TextUnformatted(label, FindRenderedTextEnd(label)); - EndGroup(); - PopID(); - - return value_changed; -} - -bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* data_ptr, const void* step, const void* step_fast, const char* format, ImGuiInputTextFlags extra_flags) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - - IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT); - if (format == NULL) - format = GDataTypeInfo[data_type].PrintFmt; - - char buf[64]; - DataTypeFormatString(buf, IM_ARRAYSIZE(buf), data_type, data_ptr, format); - - bool value_changed = false; - if ((extra_flags & (ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsScientific)) == 0) - extra_flags |= ImGuiInputTextFlags_CharsDecimal; - extra_flags |= ImGuiInputTextFlags_AutoSelectAll; - - if (step != NULL) - { - const float button_size = GetFrameHeight(); - - BeginGroup(); // The only purpose of the group here is to allow the caller to query item data e.g. IsItemActive() - PushID(label); - PushItemWidth(ImMax(1.0f, CalcItemWidth() - (button_size + style.ItemInnerSpacing.x) * 2)); - if (InputText("", buf, IM_ARRAYSIZE(buf), extra_flags)) // PushId(label) + "" gives us the expected ID from outside point of view - value_changed = DataTypeApplyOpFromText(buf, g.InputTextState.InitialText.Data, data_type, data_ptr, format); - PopItemWidth(); - - // Step buttons - SameLine(0, style.ItemInnerSpacing.x); - if (ButtonEx("-", ImVec2(button_size, button_size), ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups)) - { - DataTypeApplyOp(data_type, '-', data_ptr, data_ptr, g.IO.KeyCtrl && step_fast ? step_fast : step); - value_changed = true; - } - SameLine(0, style.ItemInnerSpacing.x); - if (ButtonEx("+", ImVec2(button_size, button_size), ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups)) - { - DataTypeApplyOp(data_type, '+', data_ptr, data_ptr, g.IO.KeyCtrl && step_fast ? step_fast : step); - value_changed = true; - } - SameLine(0, style.ItemInnerSpacing.x); - TextUnformatted(label, FindRenderedTextEnd(label)); - - PopID(); - EndGroup(); - } - else - { - if (InputText(label, buf, IM_ARRAYSIZE(buf), extra_flags)) - value_changed = DataTypeApplyOpFromText(buf, g.InputTextState.InitialText.Data, data_type, data_ptr, format); - } - - return value_changed; -} - -bool ImGui::InputFloat(const char* label, float* v, float step, float step_fast, const char* format, ImGuiInputTextFlags extra_flags) -{ - extra_flags |= ImGuiInputTextFlags_CharsScientific; - return InputScalar(label, ImGuiDataType_Float, (void*)v, (void*)(step>0.0f ? &step : NULL), (void*)(step_fast>0.0f ? &step_fast : NULL), format, extra_flags); -} - -bool ImGui::InputDouble(const char* label, double* v, double step, double step_fast, const char* format, ImGuiInputTextFlags extra_flags) -{ - extra_flags |= ImGuiInputTextFlags_CharsScientific; - return InputScalar(label, ImGuiDataType_Double, (void*)v, (void*)(step>0.0 ? &step : NULL), (void*)(step_fast>0.0 ? &step_fast : NULL), format, extra_flags); -} - -bool ImGui::InputInt(const char* label, int* v, int step, int step_fast, ImGuiInputTextFlags extra_flags) -{ - // Hexadecimal input provided as a convenience but the flag name is awkward. Typically you'd use InputText() to parse your own data, if you want to handle prefixes. - const char* format = (extra_flags & ImGuiInputTextFlags_CharsHexadecimal) ? "%08X" : "%d"; - return InputScalar(label, ImGuiDataType_S32, (void*)v, (void*)(step>0 ? &step : NULL), (void*)(step_fast>0 ? &step_fast : NULL), format, extra_flags); -} - -bool ImGui::InputScalarN(const char* label, ImGuiDataType data_type, void* v, int components, const void* step, const void* step_fast, const char* format, ImGuiInputTextFlags extra_flags) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - bool value_changed = false; - BeginGroup(); - PushID(label); - PushMultiItemsWidths(components); - size_t type_size = GDataTypeInfo[data_type].Size; - for (int i = 0; i < components; i++) - { - PushID(i); - value_changed |= InputScalar("##v", data_type, v, step, step_fast, format, extra_flags); - SameLine(0, g.Style.ItemInnerSpacing.x); - PopID(); - PopItemWidth(); - v = (void*)((char*)v + type_size); - } - PopID(); - - TextUnformatted(label, FindRenderedTextEnd(label)); - EndGroup(); - return value_changed; -} - -bool ImGui::InputFloat2(const char* label, float v[2], const char* format, ImGuiInputTextFlags extra_flags) -{ - return InputScalarN(label, ImGuiDataType_Float, v, 2, NULL, NULL, format, extra_flags); -} - -bool ImGui::InputFloat3(const char* label, float v[3], const char* format, ImGuiInputTextFlags extra_flags) -{ - return InputScalarN(label, ImGuiDataType_Float, v, 3, NULL, NULL, format, extra_flags); -} - -bool ImGui::InputFloat4(const char* label, float v[4], const char* format, ImGuiInputTextFlags extra_flags) -{ - return InputScalarN(label, ImGuiDataType_Float, v, 4, NULL, NULL, format, extra_flags); -} - -// Prefer using "const char* format" directly, which is more flexible and consistent with other API. -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS -bool ImGui::InputFloat(const char* label, float* v, float step, float step_fast, int decimal_precision, ImGuiInputTextFlags extra_flags) -{ - char format[16] = "%f"; - if (decimal_precision >= 0) - ImFormatString(format, IM_ARRAYSIZE(format), "%%.%df", decimal_precision); - return InputFloat(label, v, step, step_fast, format, extra_flags); -} - -bool ImGui::InputFloat2(const char* label, float v[2], int decimal_precision, ImGuiInputTextFlags extra_flags) -{ - char format[16] = "%f"; - if (decimal_precision >= 0) - ImFormatString(format, IM_ARRAYSIZE(format), "%%.%df", decimal_precision); - return InputScalarN(label, ImGuiDataType_Float, v, 2, NULL, NULL, format, extra_flags); -} - -bool ImGui::InputFloat3(const char* label, float v[3], int decimal_precision, ImGuiInputTextFlags extra_flags) -{ - char format[16] = "%f"; - if (decimal_precision >= 0) - ImFormatString(format, IM_ARRAYSIZE(format), "%%.%df", decimal_precision); - return InputScalarN(label, ImGuiDataType_Float, v, 3, NULL, NULL, format, extra_flags); -} - -bool ImGui::InputFloat4(const char* label, float v[4], int decimal_precision, ImGuiInputTextFlags extra_flags) -{ - char format[16] = "%f"; - if (decimal_precision >= 0) - ImFormatString(format, IM_ARRAYSIZE(format), "%%.%df", decimal_precision); - return InputScalarN(label, ImGuiDataType_Float, v, 4, NULL, NULL, format, extra_flags); -} -#endif // IMGUI_DISABLE_OBSOLETE_FUNCTIONS - -bool ImGui::InputInt2(const char* label, int v[2], ImGuiInputTextFlags extra_flags) -{ - return InputScalarN(label, ImGuiDataType_S32, v, 2, NULL, NULL, "%d", extra_flags); -} - -bool ImGui::InputInt3(const char* label, int v[3], ImGuiInputTextFlags extra_flags) -{ - return InputScalarN(label, ImGuiDataType_S32, v, 3, NULL, NULL, "%d", extra_flags); -} - -bool ImGui::InputInt4(const char* label, int v[4], ImGuiInputTextFlags extra_flags) -{ - return InputScalarN(label, ImGuiDataType_S32, v, 4, NULL, NULL, "%d", extra_flags); -} - // Horizontal/vertical separating line void ImGui::Separator() { diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index c218f2b683ee..0058136174dc 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -30,15 +30,55 @@ #endif #endif +//------------------------------------------------------------------------- +// Data +//------------------------------------------------------------------------- + +// Those MIN/MAX values are not define because we need to point to them +static const ImS32 IM_S32_MIN = INT_MIN; // (-2147483647 - 1), (0x80000000); +static const ImS32 IM_S32_MAX = INT_MAX; // (2147483647), (0x7FFFFFFF) +static const ImU32 IM_U32_MIN = 0; +static const ImU32 IM_U32_MAX = UINT_MAX; // (0xFFFFFFFF) +#ifdef LLONG_MIN +static const ImS64 IM_S64_MIN = LLONG_MIN; // (-9223372036854775807ll - 1ll); +static const ImS64 IM_S64_MAX = LLONG_MAX; // (9223372036854775807ll); +#else +static const ImS64 IM_S64_MIN = -9223372036854775807LL - 1; +static const ImS64 IM_S64_MAX = 9223372036854775807LL; +#endif +static const ImU64 IM_U64_MIN = 0; +#ifdef ULLONG_MAX +static const ImU64 IM_U64_MAX = ULLONG_MAX; // (0xFFFFFFFFFFFFFFFFull); +#else +static const ImU64 IM_U64_MAX = (2ULL * 9223372036854775807LL + 1); +#endif + //------------------------------------------------------------------------- // Forward Declarations //------------------------------------------------------------------------- +// Data Type helpers +static inline int DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type, const void* data_ptr, const char* format); +static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, void* arg_1, const void* arg_2); +static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* format); + // For InputTextEx() static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data); static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end); static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining = NULL, ImVec2* out_offset = NULL, bool stop_on_new_line = false); +namespace ImGui +{ + +// Template widget behaviors +template +static bool DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed, const TYPE v_min, const TYPE v_max, const char* format, float power); + +template +static bool SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, TYPE* v, const TYPE v_min, const TYPE v_max, const char* format, float power, ImGuiSliderFlags flags, ImRect* out_grab_bb); + +} + //------------------------------------------------------------------------- // SHARED UTILITIES //------------------------------------------------------------------------- @@ -1114,6 +1154,224 @@ bool ImGui::Combo(const char* label, int* current_item, const char* items_separa // - RoundScalarWithFormat<>() //------------------------------------------------------------------------- +struct ImGuiDataTypeInfo +{ + size_t Size; + const char* PrintFmt; // Unused + const char* ScanFmt; +}; + +static const ImGuiDataTypeInfo GDataTypeInfo[] = +{ + { sizeof(int), "%d", "%d" }, + { sizeof(unsigned int), "%u", "%u" }, +#ifdef _MSC_VER + { sizeof(ImS64), "%I64d","%I64d" }, + { sizeof(ImU64), "%I64u","%I64u" }, +#else + { sizeof(ImS64), "%lld", "%lld" }, + { sizeof(ImU64), "%llu", "%llu" }, +#endif + { sizeof(float), "%f", "%f" }, // float are promoted to double in va_arg + { sizeof(double), "%f", "%lf" }, +}; +IM_STATIC_ASSERT(IM_ARRAYSIZE(GDataTypeInfo) == ImGuiDataType_COUNT); + +// FIXME-LEGACY: Prior to 1.61 our DragInt() function internally used floats and because of this the compile-time default value for format was "%.0f". +// Even though we changed the compile-time default, we expect users to have carried %f around, which would break the display of DragInt() calls. +// To honor backward compatibility we are rewriting the format string, unless IMGUI_DISABLE_OBSOLETE_FUNCTIONS is enabled. What could possibly go wrong?! +static const char* PatchFormatStringFloatToInt(const char* fmt) +{ + if (fmt[0] == '%' && fmt[1] == '.' && fmt[2] == '0' && fmt[3] == 'f' && fmt[4] == 0) // Fast legacy path for "%.0f" which is expected to be the most common case. + return "%d"; + const char* fmt_start = ImParseFormatFindStart(fmt); // Find % (if any, and ignore %%) + const char* fmt_end = ImParseFormatFindEnd(fmt_start); // Find end of format specifier, which itself is an exercise of confidence/recklessness (because snprintf is dependent on libc or user). + if (fmt_end > fmt_start && fmt_end[-1] == 'f') + { +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + if (fmt_start == fmt && fmt_end[0] == 0) + return "%d"; + ImGuiContext& g = *GImGui; + ImFormatString(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), "%.*s%%d%s", (int)(fmt_start - fmt), fmt, fmt_end); // Honor leading and trailing decorations, but lose alignment/precision. + return g.TempBuffer; +#else + IM_ASSERT(0 && "DragInt(): Invalid format string!"); // Old versions used a default parameter of "%.0f", please replace with e.g. "%d" +#endif + } + return fmt; +} + +static inline int DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type, const void* data_ptr, const char* format) +{ + if (data_type == ImGuiDataType_S32 || data_type == ImGuiDataType_U32) // Signedness doesn't matter when pushing the argument + return ImFormatString(buf, buf_size, format, *(const ImU32*)data_ptr); + if (data_type == ImGuiDataType_S64 || data_type == ImGuiDataType_U64) // Signedness doesn't matter when pushing the argument + return ImFormatString(buf, buf_size, format, *(const ImU64*)data_ptr); + if (data_type == ImGuiDataType_Float) + return ImFormatString(buf, buf_size, format, *(const float*)data_ptr); + if (data_type == ImGuiDataType_Double) + return ImFormatString(buf, buf_size, format, *(const double*)data_ptr); + IM_ASSERT(0); + return 0; +} + +// FIXME: Adding support for clamping on boundaries of the data type would be nice. +static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, void* arg1, const void* arg2) +{ + IM_ASSERT(op == '+' || op == '-'); + switch (data_type) + { + case ImGuiDataType_S32: + if (op == '+') *(int*)output = *(const int*)arg1 + *(const int*)arg2; + else if (op == '-') *(int*)output = *(const int*)arg1 - *(const int*)arg2; + return; + case ImGuiDataType_U32: + if (op == '+') *(unsigned int*)output = *(const unsigned int*)arg1 + *(const ImU32*)arg2; + else if (op == '-') *(unsigned int*)output = *(const unsigned int*)arg1 - *(const ImU32*)arg2; + return; + case ImGuiDataType_S64: + if (op == '+') *(ImS64*)output = *(const ImS64*)arg1 + *(const ImS64*)arg2; + else if (op == '-') *(ImS64*)output = *(const ImS64*)arg1 - *(const ImS64*)arg2; + return; + case ImGuiDataType_U64: + if (op == '+') *(ImU64*)output = *(const ImU64*)arg1 + *(const ImU64*)arg2; + else if (op == '-') *(ImU64*)output = *(const ImU64*)arg1 - *(const ImU64*)arg2; + return; + case ImGuiDataType_Float: + if (op == '+') *(float*)output = *(const float*)arg1 + *(const float*)arg2; + else if (op == '-') *(float*)output = *(const float*)arg1 - *(const float*)arg2; + return; + case ImGuiDataType_Double: + if (op == '+') *(double*)output = *(const double*)arg1 + *(const double*)arg2; + else if (op == '-') *(double*)output = *(const double*)arg1 - *(const double*)arg2; + return; + case ImGuiDataType_COUNT: break; + } + IM_ASSERT(0); +} + +// User can input math operators (e.g. +100) to edit a numerical values. +// NB: This is _not_ a full expression evaluator. We should probably add one and replace this dumb mess.. +static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* format) +{ + while (ImCharIsBlankA(*buf)) + buf++; + + // We don't support '-' op because it would conflict with inputing negative value. + // Instead you can use +-100 to subtract from an existing value + char op = buf[0]; + if (op == '+' || op == '*' || op == '/') + { + buf++; + while (ImCharIsBlankA(*buf)) + buf++; + } + else + { + op = 0; + } + if (!buf[0]) + return false; + + // Copy the value in an opaque buffer so we can compare at the end of the function if it changed at all. + IM_ASSERT(data_type < ImGuiDataType_COUNT); + int data_backup[2]; + IM_ASSERT(GDataTypeInfo[data_type].Size <= sizeof(data_backup)); + memcpy(data_backup, data_ptr, GDataTypeInfo[data_type].Size); + + if (format == NULL) + format = GDataTypeInfo[data_type].ScanFmt; + + int arg1i = 0; + if (data_type == ImGuiDataType_S32) + { + int* v = (int*)data_ptr; + int arg0i = *v; + float arg1f = 0.0f; + if (op && sscanf(initial_value_buf, format, &arg0i) < 1) + return false; + // Store operand in a float so we can use fractional value for multipliers (*1.1), but constant always parsed as integer so we can fit big integers (e.g. 2000000003) past float precision + if (op == '+') { if (sscanf(buf, "%d", &arg1i)) *v = (int)(arg0i + arg1i); } // Add (use "+-" to subtract) + else if (op == '*') { if (sscanf(buf, "%f", &arg1f)) *v = (int)(arg0i * arg1f); } // Multiply + else if (op == '/') { if (sscanf(buf, "%f", &arg1f) && arg1f != 0.0f) *v = (int)(arg0i / arg1f); } // Divide + else { if (sscanf(buf, format, &arg1i) == 1) *v = arg1i; } // Assign constant + } + else if (data_type == ImGuiDataType_U32 || data_type == ImGuiDataType_S64 || data_type == ImGuiDataType_U64) + { + // Assign constant + // FIXME: We don't bother handling support for legacy operators since they are a little too crappy. Instead we may implement a proper expression evaluator in the future. + sscanf(buf, format, data_ptr); + } + else if (data_type == ImGuiDataType_Float) + { + // For floats we have to ignore format with precision (e.g. "%.2f") because sscanf doesn't take them in + format = "%f"; + float* v = (float*)data_ptr; + float arg0f = *v, arg1f = 0.0f; + if (op && sscanf(initial_value_buf, format, &arg0f) < 1) + return false; + if (sscanf(buf, format, &arg1f) < 1) + return false; + if (op == '+') { *v = arg0f + arg1f; } // Add (use "+-" to subtract) + else if (op == '*') { *v = arg0f * arg1f; } // Multiply + else if (op == '/') { if (arg1f != 0.0f) *v = arg0f / arg1f; } // Divide + else { *v = arg1f; } // Assign constant + } + else if (data_type == ImGuiDataType_Double) + { + format = "%lf"; // scanf differentiate float/double unlike printf which forces everything to double because of ellipsis + double* v = (double*)data_ptr; + double arg0f = *v, arg1f = 0.0; + if (op && sscanf(initial_value_buf, format, &arg0f) < 1) + return false; + if (sscanf(buf, format, &arg1f) < 1) + return false; + if (op == '+') { *v = arg0f + arg1f; } // Add (use "+-" to subtract) + else if (op == '*') { *v = arg0f * arg1f; } // Multiply + else if (op == '/') { if (arg1f != 0.0f) *v = arg0f / arg1f; } // Divide + else { *v = arg1f; } // Assign constant + } + return memcmp(data_backup, data_ptr, GDataTypeInfo[data_type].Size) != 0; +} + +static float GetMinimumStepAtDecimalPrecision(int decimal_precision) +{ + static const float min_steps[10] = { 1.0f, 0.1f, 0.01f, 0.001f, 0.0001f, 0.00001f, 0.000001f, 0.0000001f, 0.00000001f, 0.000000001f }; + if (decimal_precision < 0) + return FLT_MIN; + return (decimal_precision >= 0 && decimal_precision < 10) ? min_steps[decimal_precision] : ImPow(10.0f, (float)-decimal_precision); +} + +template +static const char* ImAtoi(const char* src, TYPE* output) +{ + int negative = 0; + if (*src == '-') { negative = 1; src++; } + if (*src == '+') { src++; } + TYPE v = 0; + while (*src >= '0' && *src <= '9') + v = (v * 10) + (*src++ - '0'); + *output = negative ? -v : v; + return src; +} + +template +static inline TYPE RoundScalarWithFormat(const char* format, ImGuiDataType data_type, TYPE v) +{ + const char* fmt_start = ImParseFormatFindStart(format); + if (fmt_start[0] != '%' || fmt_start[1] == '%') // Don't apply if the value is not visible in the format string + return v; + char v_str[64]; + ImFormatString(v_str, IM_ARRAYSIZE(v_str), fmt_start, v); + const char* p = v_str; + while (*p == ' ') + p++; + if (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double) + v = (TYPE)ImAtof(p); + else + ImAtoi(p, (SIGNEDTYPE*)&v); + return v; +} //------------------------------------------------------------------------- // WIDGETS: Drags @@ -1133,79 +1391,1085 @@ bool ImGui::Combo(const char* label, int* current_item, const char* items_separa // - DragIntRange2() //------------------------------------------------------------------------- +// This is called by DragBehavior() when the widget is active (held by mouse or being manipulated with Nav controls) +template +static bool ImGui::DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed, const TYPE v_min, const TYPE v_max, const char* format, float power) +{ + ImGuiContext& g = *GImGui; -//------------------------------------------------------------------------- -// WIDGETS: Sliders -// - SliderBehaviorT<>() [Internal] -// - SliderBehavior() [Internal] -// - SliderScalar() -// - SliderScalarN() -// - SliderFloat() -// - SliderFloat2() -// - SliderFloat3() -// - SliderFloat4() -// - SliderAngle() -// - SliderInt() -// - SliderInt2() -// - SliderInt3() -// - SliderInt4() -// - VSliderScalar() -// - VSliderFloat() -// - VSliderInt() -//------------------------------------------------------------------------- + // Default tweak speed + bool has_min_max = (v_min != v_max) && (v_max - v_max < FLT_MAX); + if (v_speed == 0.0f && has_min_max) + v_speed = (float)((v_max - v_min) * g.DragSpeedDefaultRatio); + + // Inputs accumulates into g.DragCurrentAccum, which is flushed into the current value as soon as it makes a difference with our precision settings + float adjust_delta = 0.0f; + if (g.ActiveIdSource == ImGuiInputSource_Mouse && IsMousePosValid() && g.IO.MouseDragMaxDistanceSqr[0] > 1.0f*1.0f) + { + adjust_delta = g.IO.MouseDelta.x; + if (g.IO.KeyAlt) + adjust_delta *= 1.0f/100.0f; + if (g.IO.KeyShift) + adjust_delta *= 10.0f; + } + else if (g.ActiveIdSource == ImGuiInputSource_Nav) + { + int decimal_precision = (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double) ? ImParseFormatPrecision(format, 3) : 0; + adjust_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard|ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_RepeatFast, 1.0f/10.0f, 10.0f).x; + v_speed = ImMax(v_speed, GetMinimumStepAtDecimalPrecision(decimal_precision)); + } + adjust_delta *= v_speed; + + // Clear current value on activation + // Avoid altering values and clamping when we are _already_ past the limits and heading in the same direction, so e.g. if range is 0..255, current value is 300 and we are pushing to the right side, keep the 300. + bool is_just_activated = g.ActiveIdIsJustActivated; + bool is_already_past_limits_and_pushing_outward = has_min_max && ((*v >= v_max && adjust_delta > 0.0f) || (*v <= v_min && adjust_delta < 0.0f)); + if (is_just_activated || is_already_past_limits_and_pushing_outward) + { + g.DragCurrentAccum = 0.0f; + g.DragCurrentAccumDirty = false; + } + else if (adjust_delta != 0.0f) + { + g.DragCurrentAccum += adjust_delta; + g.DragCurrentAccumDirty = true; + } + + if (!g.DragCurrentAccumDirty) + return false; + + TYPE v_cur = *v; + FLOATTYPE v_old_ref_for_accum_remainder = (FLOATTYPE)0.0f; + + const bool is_power = (power != 1.0f && (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double) && has_min_max); + if (is_power) + { + // Offset + round to user desired precision, with a curve on the v_min..v_max range to get more precision on one side of the range + FLOATTYPE v_old_norm_curved = ImPow((FLOATTYPE)(v_cur - v_min) / (FLOATTYPE)(v_max - v_min), (FLOATTYPE)1.0f / power); + FLOATTYPE v_new_norm_curved = v_old_norm_curved + (g.DragCurrentAccum / (v_max - v_min)); + v_cur = v_min + (TYPE)ImPow(ImSaturate((float)v_new_norm_curved), power) * (v_max - v_min); + v_old_ref_for_accum_remainder = v_old_norm_curved; + } + else + { + v_cur += (TYPE)g.DragCurrentAccum; + } + + // Round to user desired precision based on format string + v_cur = RoundScalarWithFormat(format, data_type, v_cur); + + // Preserve remainder after rounding has been applied. This also allow slow tweaking of values. + g.DragCurrentAccumDirty = false; + if (is_power) + { + FLOATTYPE v_cur_norm_curved = ImPow((FLOATTYPE)(v_cur - v_min) / (FLOATTYPE)(v_max - v_min), (FLOATTYPE)1.0f / power); + g.DragCurrentAccum -= (float)(v_cur_norm_curved - v_old_ref_for_accum_remainder); + } + else + { + g.DragCurrentAccum -= (float)((SIGNEDTYPE)v_cur - (SIGNEDTYPE)*v); + } + // Lose zero sign for float/double + if (v_cur == (TYPE)-0) + v_cur = (TYPE)0; + // Clamp values (handle overflow/wrap-around) + if (*v != v_cur && has_min_max) + { + if (v_cur < v_min || (v_cur > *v && adjust_delta < 0.0f)) + v_cur = v_min; + if (v_cur > v_max || (v_cur < *v && adjust_delta > 0.0f)) + v_cur = v_max; + } + // Apply result + if (*v == v_cur) + return false; + *v = v_cur; + return true; +} +bool ImGui::DragBehavior(ImGuiID id, ImGuiDataType data_type, void* v, float v_speed, const void* v_min, const void* v_max, const char* format, float power) +{ + ImGuiContext& g = *GImGui; + if (g.ActiveId == id) + { + if (g.ActiveIdSource == ImGuiInputSource_Mouse && !g.IO.MouseDown[0]) + ClearActiveID(); + else if (g.ActiveIdSource == ImGuiInputSource_Nav && g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated) + ClearActiveID(); + } + if (g.ActiveId != id) + return false; + switch (data_type) + { + case ImGuiDataType_S32: return DragBehaviorT(data_type, (ImS32*)v, v_speed, v_min ? *(const ImS32* )v_min : IM_S32_MIN, v_max ? *(const ImS32* )v_max : IM_S32_MAX, format, power); + case ImGuiDataType_U32: return DragBehaviorT(data_type, (ImU32*)v, v_speed, v_min ? *(const ImU32* )v_min : IM_U32_MIN, v_max ? *(const ImU32* )v_max : IM_U32_MAX, format, power); + case ImGuiDataType_S64: return DragBehaviorT(data_type, (ImS64*)v, v_speed, v_min ? *(const ImS64* )v_min : IM_S64_MIN, v_max ? *(const ImS64* )v_max : IM_S64_MAX, format, power); + case ImGuiDataType_U64: return DragBehaviorT(data_type, (ImU64*)v, v_speed, v_min ? *(const ImU64* )v_min : IM_U64_MIN, v_max ? *(const ImU64* )v_max : IM_U64_MAX, format, power); + case ImGuiDataType_Float: return DragBehaviorT(data_type, (float*)v, v_speed, v_min ? *(const float* )v_min : -FLT_MAX, v_max ? *(const float* )v_max : FLT_MAX, format, power); + case ImGuiDataType_Double: return DragBehaviorT(data_type, (double*)v, v_speed, v_min ? *(const double*)v_min : -DBL_MAX, v_max ? *(const double*)v_max : DBL_MAX, format, power); + case ImGuiDataType_COUNT: break; + } + IM_ASSERT(0); + return false; +} +bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* v, float v_speed, const void* v_min, const void* v_max, const char* format, float power) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + if (power != 1.0f) + IM_ASSERT(v_min != NULL && v_max != NULL); // When using a power curve the drag needs to have known bounds + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); + const float w = CalcItemWidth(); + const ImVec2 label_size = CalcTextSize(label, NULL, true); + const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f)); + const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding); + const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); + // NB- we don't call ItemSize() yet because we may turn into a text edit box below + if (!ItemAdd(total_bb, id, &frame_bb)) + { + ItemSize(total_bb, style.FramePadding.y); + return false; + } + const bool hovered = ItemHoverable(frame_bb, id); + // Default format string when passing NULL + // Patch old "%.0f" format string to use "%d", read function comments for more details. + IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT); + if (format == NULL) + format = GDataTypeInfo[data_type].PrintFmt; + else if (data_type == ImGuiDataType_S32 && strcmp(format, "%d") != 0) + format = PatchFormatStringFloatToInt(format); + + // Tabbing or CTRL-clicking on Drag turns it into an input box + bool start_text_input = false; + const bool tab_focus_requested = FocusableItemRegister(window, id); + if (tab_focus_requested || (hovered && (g.IO.MouseClicked[0] || g.IO.MouseDoubleClicked[0])) || g.NavActivateId == id || (g.NavInputId == id && g.ScalarAsInputTextId != id)) + { + SetActiveID(id, window); + SetFocusID(id, window); + FocusWindow(window); + g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); + if (tab_focus_requested || g.IO.KeyCtrl || g.IO.MouseDoubleClicked[0] || g.NavInputId == id) + { + start_text_input = true; + g.ScalarAsInputTextId = 0; + } + } + if (start_text_input || (g.ActiveId == id && g.ScalarAsInputTextId == id)) + return InputScalarAsWidgetReplacement(frame_bb, id, label, data_type, v, format); + // Actual drag behavior + ItemSize(total_bb, style.FramePadding.y); + const bool value_changed = DragBehavior(id, data_type, v, v_speed, v_min, v_max, format, power); + if (value_changed) + MarkItemEdited(id); + // Draw frame + const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); + RenderNavHighlight(frame_bb, id); + RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, style.FrameRounding); + // Display value using user-provided display format so user can add prefix/suffix/decorations to the value. + char value_buf[64]; + const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, v, format); + RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.5f)); + if (label_size.x > 0.0f) + RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label); + return value_changed; +} +bool ImGui::DragScalarN(const char* label, ImGuiDataType data_type, void* v, int components, float v_speed, const void* v_min, const void* v_max, const char* format, float power) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; -//------------------------------------------------------------------------- -// WIDGETS: Inputs (_excepted InputText_) -// - ImParseFormatFindStart() -// - ImParseFormatFindEnd() -// - ImParseFormatTrimDecorations() -// - ImParseFormatPrecision() -// - InputScalarAsWidgetReplacement() [Internal] -// - InputScalar() -// - InputScalarN() -// - InputFloat() -// - InputFloat2() -// - InputFloat3() -// - InputFloat4() -// - InputInt() -// - InputInt2() -// - InputInt3() -// - InputInt4() -// - InputDouble() -//------------------------------------------------------------------------- + ImGuiContext& g = *GImGui; + bool value_changed = false; + BeginGroup(); + PushID(label); + PushMultiItemsWidths(components); + size_t type_size = GDataTypeInfo[data_type].Size; + for (int i = 0; i < components; i++) + { + PushID(i); + value_changed |= DragScalar("##v", data_type, v, v_speed, v_min, v_max, format, power); + SameLine(0, g.Style.ItemInnerSpacing.x); + PopID(); + PopItemWidth(); + v = (void*)((char*)v + type_size); + } + PopID(); + + TextUnformatted(label, FindRenderedTextEnd(label)); + EndGroup(); + return value_changed; +} + +bool ImGui::DragFloat(const char* label, float* v, float v_speed, float v_min, float v_max, const char* format, float power) +{ + return DragScalar(label, ImGuiDataType_Float, v, v_speed, &v_min, &v_max, format, power); +} +bool ImGui::DragFloat2(const char* label, float v[2], float v_speed, float v_min, float v_max, const char* format, float power) +{ + return DragScalarN(label, ImGuiDataType_Float, v, 2, v_speed, &v_min, &v_max, format, power); +} + +bool ImGui::DragFloat3(const char* label, float v[3], float v_speed, float v_min, float v_max, const char* format, float power) +{ + return DragScalarN(label, ImGuiDataType_Float, v, 3, v_speed, &v_min, &v_max, format, power); +} +bool ImGui::DragFloat4(const char* label, float v[4], float v_speed, float v_min, float v_max, const char* format, float power) +{ + return DragScalarN(label, ImGuiDataType_Float, v, 4, v_speed, &v_min, &v_max, format, power); +} +bool ImGui::DragFloatRange2(const char* label, float* v_current_min, float* v_current_max, float v_speed, float v_min, float v_max, const char* format, const char* format_max, float power) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + ImGuiContext& g = *GImGui; + PushID(label); + BeginGroup(); + PushMultiItemsWidths(2); + bool value_changed = DragFloat("##min", v_current_min, v_speed, (v_min >= v_max) ? -FLT_MAX : v_min, (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max), format, power); + PopItemWidth(); + SameLine(0, g.Style.ItemInnerSpacing.x); + value_changed |= DragFloat("##max", v_current_max, v_speed, (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min), (v_min >= v_max) ? FLT_MAX : v_max, format_max ? format_max : format, power); + PopItemWidth(); + SameLine(0, g.Style.ItemInnerSpacing.x); + TextUnformatted(label, FindRenderedTextEnd(label)); + EndGroup(); + PopID(); + return value_changed; +} +// NB: v_speed is float to allow adjusting the drag speed with more precision +bool ImGui::DragInt(const char* label, int* v, float v_speed, int v_min, int v_max, const char* format) +{ + return DragScalar(label, ImGuiDataType_S32, v, v_speed, &v_min, &v_max, format); +} +bool ImGui::DragInt2(const char* label, int v[2], float v_speed, int v_min, int v_max, const char* format) +{ + return DragScalarN(label, ImGuiDataType_S32, v, 2, v_speed, &v_min, &v_max, format); +} +bool ImGui::DragInt3(const char* label, int v[3], float v_speed, int v_min, int v_max, const char* format) +{ + return DragScalarN(label, ImGuiDataType_S32, v, 3, v_speed, &v_min, &v_max, format); +} +bool ImGui::DragInt4(const char* label, int v[4], float v_speed, int v_min, int v_max, const char* format) +{ + return DragScalarN(label, ImGuiDataType_S32, v, 4, v_speed, &v_min, &v_max, format); +} +bool ImGui::DragIntRange2(const char* label, int* v_current_min, int* v_current_max, float v_speed, int v_min, int v_max, const char* format, const char* format_max) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + ImGuiContext& g = *GImGui; + PushID(label); + BeginGroup(); + PushMultiItemsWidths(2); + bool value_changed = DragInt("##min", v_current_min, v_speed, (v_min >= v_max) ? INT_MIN : v_min, (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max), format); + PopItemWidth(); + SameLine(0, g.Style.ItemInnerSpacing.x); + value_changed |= DragInt("##max", v_current_max, v_speed, (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min), (v_min >= v_max) ? INT_MAX : v_max, format_max ? format_max : format); + PopItemWidth(); + SameLine(0, g.Style.ItemInnerSpacing.x); + TextUnformatted(label, FindRenderedTextEnd(label)); + EndGroup(); + PopID(); + return value_changed; +} +//------------------------------------------------------------------------- +// WIDGETS: Sliders +// - SliderBehaviorT<>() [Internal] +// - SliderBehavior() [Internal] +// - SliderScalar() +// - SliderScalarN() +// - SliderFloat() +// - SliderFloat2() +// - SliderFloat3() +// - SliderFloat4() +// - SliderAngle() +// - SliderInt() +// - SliderInt2() +// - SliderInt3() +// - SliderInt4() +// - VSliderScalar() +// - VSliderFloat() +// - VSliderInt() +//------------------------------------------------------------------------- + +template +static inline float SliderBehaviorCalcRatioFromValue(ImGuiDataType data_type, TYPE v, TYPE v_min, TYPE v_max, float power, float linear_zero_pos) +{ + if (v_min == v_max) + return 0.0f; + + const bool is_power = (power != 1.0f) && (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double); + const TYPE v_clamped = (v_min < v_max) ? ImClamp(v, v_min, v_max) : ImClamp(v, v_max, v_min); + if (is_power) + { + if (v_clamped < 0.0f) + { + const float f = 1.0f - (float)((v_clamped - v_min) / (ImMin((TYPE)0, v_max) - v_min)); + return (1.0f - ImPow(f, 1.0f/power)) * linear_zero_pos; + } + else + { + const float f = (float)((v_clamped - ImMax((TYPE)0, v_min)) / (v_max - ImMax((TYPE)0, v_min))); + return linear_zero_pos + ImPow(f, 1.0f/power) * (1.0f - linear_zero_pos); + } + } + + // Linear slider + return (float)((FLOATTYPE)(v_clamped - v_min) / (FLOATTYPE)(v_max - v_min)); +} + +// FIXME: Move some of the code into SliderBehavior(). Current responsability is larger than what the equivalent DragBehaviorT<> does, we also do some rendering, etc. +template +static bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, TYPE* v, const TYPE v_min, const TYPE v_max, const char* format, float power, ImGuiSliderFlags flags, ImRect* out_grab_bb) +{ + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + + const bool is_horizontal = (flags & ImGuiSliderFlags_Vertical) == 0; + const bool is_decimal = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double); + const bool is_power = (power != 1.0f) && is_decimal; + + const float grab_padding = 2.0f; + const float slider_sz = is_horizontal ? (bb.GetWidth() - grab_padding * 2.0f) : (bb.GetHeight() - grab_padding * 2.0f); + float grab_sz = style.GrabMinSize; + SIGNEDTYPE v_range = (v_min < v_max ? v_max - v_min : v_min - v_max); + if (!is_decimal && v_range >= 0) // v_range < 0 may happen on integer overflows + grab_sz = ImMax((float)(slider_sz / (v_range + 1)), style.GrabMinSize); // For integer sliders: if possible have the grab size represent 1 unit + grab_sz = ImMin(grab_sz, slider_sz); + const float slider_usable_sz = slider_sz - grab_sz; + const float slider_usable_pos_min = (is_horizontal ? bb.Min.x : bb.Min.y) + grab_padding + grab_sz*0.5f; + const float slider_usable_pos_max = (is_horizontal ? bb.Max.x : bb.Max.y) - grab_padding - grab_sz*0.5f; + + // For power curve sliders that cross over sign boundary we want the curve to be symmetric around 0.0f + float linear_zero_pos; // 0.0->1.0f + if (is_power && v_min * v_max < 0.0f) + { + // Different sign + const FLOATTYPE linear_dist_min_to_0 = ImPow(v_min >= 0 ? (FLOATTYPE)v_min : -(FLOATTYPE)v_min, (FLOATTYPE)1.0f/power); + const FLOATTYPE linear_dist_max_to_0 = ImPow(v_max >= 0 ? (FLOATTYPE)v_max : -(FLOATTYPE)v_max, (FLOATTYPE)1.0f/power); + linear_zero_pos = (float)(linear_dist_min_to_0 / (linear_dist_min_to_0 + linear_dist_max_to_0)); + } + else + { + // Same sign + linear_zero_pos = v_min < 0.0f ? 1.0f : 0.0f; + } + + // Process interacting with the slider + bool value_changed = false; + if (g.ActiveId == id) + { + bool set_new_value = false; + float clicked_t = 0.0f; + if (g.ActiveIdSource == ImGuiInputSource_Mouse) + { + if (!g.IO.MouseDown[0]) + { + ClearActiveID(); + } + else + { + const float mouse_abs_pos = is_horizontal ? g.IO.MousePos.x : g.IO.MousePos.y; + clicked_t = (slider_usable_sz > 0.0f) ? ImClamp((mouse_abs_pos - slider_usable_pos_min) / slider_usable_sz, 0.0f, 1.0f) : 0.0f; + if (!is_horizontal) + clicked_t = 1.0f - clicked_t; + set_new_value = true; + } + } + else if (g.ActiveIdSource == ImGuiInputSource_Nav) + { + const ImVec2 delta2 = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard | ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_RepeatFast, 0.0f, 0.0f); + float delta = is_horizontal ? delta2.x : -delta2.y; + if (g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated) + { + ClearActiveID(); + } + else if (delta != 0.0f) + { + clicked_t = SliderBehaviorCalcRatioFromValue(data_type, *v, v_min, v_max, power, linear_zero_pos); + const int decimal_precision = is_decimal ? ImParseFormatPrecision(format, 3) : 0; + if ((decimal_precision > 0) || is_power) + { + delta /= 100.0f; // Gamepad/keyboard tweak speeds in % of slider bounds + if (IsNavInputDown(ImGuiNavInput_TweakSlow)) + delta /= 10.0f; + } + else + { + if ((v_range >= -100.0f && v_range <= 100.0f) || IsNavInputDown(ImGuiNavInput_TweakSlow)) + delta = ((delta < 0.0f) ? -1.0f : +1.0f) / (float)v_range; // Gamepad/keyboard tweak speeds in integer steps + else + delta /= 100.0f; + } + if (IsNavInputDown(ImGuiNavInput_TweakFast)) + delta *= 10.0f; + set_new_value = true; + if ((clicked_t >= 1.0f && delta > 0.0f) || (clicked_t <= 0.0f && delta < 0.0f)) // This is to avoid applying the saturation when already past the limits + set_new_value = false; + else + clicked_t = ImSaturate(clicked_t + delta); + } + } + + if (set_new_value) + { + TYPE v_new; + if (is_power) + { + // Account for power curve scale on both sides of the zero + if (clicked_t < linear_zero_pos) + { + // Negative: rescale to the negative range before powering + float a = 1.0f - (clicked_t / linear_zero_pos); + a = ImPow(a, power); + v_new = ImLerp(ImMin(v_max, (TYPE)0), v_min, a); + } + else + { + // Positive: rescale to the positive range before powering + float a; + if (ImFabs(linear_zero_pos - 1.0f) > 1.e-6f) + a = (clicked_t - linear_zero_pos) / (1.0f - linear_zero_pos); + else + a = clicked_t; + a = ImPow(a, power); + v_new = ImLerp(ImMax(v_min, (TYPE)0), v_max, a); + } + } + else + { + // Linear slider + if (is_decimal) + { + v_new = ImLerp(v_min, v_max, clicked_t); + } + else + { + // For integer values we want the clicking position to match the grab box so we round above + // This code is carefully tuned to work with large values (e.g. high ranges of U64) while preserving this property.. + FLOATTYPE v_new_off_f = (v_max - v_min) * clicked_t; + TYPE v_new_off_floor = (TYPE)(v_new_off_f); + TYPE v_new_off_round = (TYPE)(v_new_off_f + (FLOATTYPE)0.5); + if (!is_decimal && v_new_off_floor < v_new_off_round) + v_new = v_min + v_new_off_round; + else + v_new = v_min + v_new_off_floor; + } + } + + // Round to user desired precision based on format string + v_new = RoundScalarWithFormat(format, data_type, v_new); + + // Apply result + if (*v != v_new) + { + *v = v_new; + value_changed = true; + } + } + } + + // Output grab position so it can be displayed by the caller + float grab_t = SliderBehaviorCalcRatioFromValue(data_type, *v, v_min, v_max, power, linear_zero_pos); + if (!is_horizontal) + grab_t = 1.0f - grab_t; + const float grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t); + if (is_horizontal) + *out_grab_bb = ImRect(grab_pos - grab_sz*0.5f, bb.Min.y + grab_padding, grab_pos + grab_sz*0.5f, bb.Max.y - grab_padding); + else + *out_grab_bb = ImRect(bb.Min.x + grab_padding, grab_pos - grab_sz*0.5f, bb.Max.x - grab_padding, grab_pos + grab_sz*0.5f); + + return value_changed; +} + +// For 32-bits and larger types, slider bounds are limited to half the natural type range. +// So e.g. an integer Slider between INT_MAX-10 and INT_MAX will fail, but an integer Slider between INT_MAX/2-10 and INT_MAX/2 will be ok. +// It would be possible to lift that limitation with some work but it doesn't seem to be worth it for sliders. +bool ImGui::SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, void* v, const void* v_min, const void* v_max, const char* format, float power, ImGuiSliderFlags flags, ImRect* out_grab_bb) +{ + switch (data_type) + { + case ImGuiDataType_S32: + IM_ASSERT(*(const ImS32*)v_min >= IM_S32_MIN/2 && *(const ImS32*)v_max <= IM_S32_MAX/2); + return SliderBehaviorT(bb, id, data_type, (ImS32*)v, *(const ImS32*)v_min, *(const ImS32*)v_max, format, power, flags, out_grab_bb); + case ImGuiDataType_U32: + IM_ASSERT(*(const ImU32*)v_min <= IM_U32_MAX/2); + return SliderBehaviorT(bb, id, data_type, (ImU32*)v, *(const ImU32*)v_min, *(const ImU32*)v_max, format, power, flags, out_grab_bb); + case ImGuiDataType_S64: + IM_ASSERT(*(const ImS64*)v_min >= IM_S64_MIN/2 && *(const ImS64*)v_max <= IM_S64_MAX/2); + return SliderBehaviorT(bb, id, data_type, (ImS64*)v, *(const ImS64*)v_min, *(const ImS64*)v_max, format, power, flags, out_grab_bb); + case ImGuiDataType_U64: + IM_ASSERT(*(const ImU64*)v_min <= IM_U64_MAX/2); + return SliderBehaviorT(bb, id, data_type, (ImU64*)v, *(const ImU64*)v_min, *(const ImU64*)v_max, format, power, flags, out_grab_bb); + case ImGuiDataType_Float: + IM_ASSERT(*(const float*)v_min >= -FLT_MAX/2.0f && *(const float*)v_max <= FLT_MAX/2.0f); + return SliderBehaviorT(bb, id, data_type, (float*)v, *(const float*)v_min, *(const float*)v_max, format, power, flags, out_grab_bb); + case ImGuiDataType_Double: + IM_ASSERT(*(const double*)v_min >= -DBL_MAX/2.0f && *(const double*)v_max <= DBL_MAX/2.0f); + return SliderBehaviorT(bb, id, data_type, (double*)v, *(const double*)v_min, *(const double*)v_max, format, power, flags, out_grab_bb); + case ImGuiDataType_COUNT: break; + } + IM_ASSERT(0); + return false; +} + +bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* v, const void* v_min, const void* v_max, const char* format, float power) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); + const float w = CalcItemWidth(); + + const ImVec2 label_size = CalcTextSize(label, NULL, true); + const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f)); + const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); + + // NB- we don't call ItemSize() yet because we may turn into a text edit box below + if (!ItemAdd(total_bb, id, &frame_bb)) + { + ItemSize(total_bb, style.FramePadding.y); + return false; + } + + // Default format string when passing NULL + // Patch old "%.0f" format string to use "%d", read function comments for more details. + IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT); + if (format == NULL) + format = GDataTypeInfo[data_type].PrintFmt; + else if (data_type == ImGuiDataType_S32 && strcmp(format, "%d") != 0) + format = PatchFormatStringFloatToInt(format); + + // Tabbing or CTRL-clicking on Slider turns it into an input box + bool start_text_input = false; + const bool tab_focus_requested = FocusableItemRegister(window, id); + const bool hovered = ItemHoverable(frame_bb, id); + if (tab_focus_requested || (hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || (g.NavInputId == id && g.ScalarAsInputTextId != id)) + { + SetActiveID(id, window); + SetFocusID(id, window); + FocusWindow(window); + g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); + if (tab_focus_requested || g.IO.KeyCtrl || g.NavInputId == id) + { + start_text_input = true; + g.ScalarAsInputTextId = 0; + } + } + if (start_text_input || (g.ActiveId == id && g.ScalarAsInputTextId == id)) + return InputScalarAsWidgetReplacement(frame_bb, id, label, data_type, v, format); + + ItemSize(total_bb, style.FramePadding.y); + + // Draw frame + const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); + RenderNavHighlight(frame_bb, id); + RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, g.Style.FrameRounding); + + // Slider behavior + ImRect grab_bb; + const bool value_changed = SliderBehavior(frame_bb, id, data_type, v, v_min, v_max, format, power, ImGuiSliderFlags_None, &grab_bb); + if (value_changed) + MarkItemEdited(id); + + // Render grab + window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding); + + // Display value using user-provided display format so user can add prefix/suffix/decorations to the value. + char value_buf[64]; + const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, v, format); + RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f,0.5f)); + + if (label_size.x > 0.0f) + RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); + + return value_changed; +} + +// Add multiple sliders on 1 line for compact edition of multiple components +bool ImGui::SliderScalarN(const char* label, ImGuiDataType data_type, void* v, int components, const void* v_min, const void* v_max, const char* format, float power) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + bool value_changed = false; + BeginGroup(); + PushID(label); + PushMultiItemsWidths(components); + size_t type_size = GDataTypeInfo[data_type].Size; + for (int i = 0; i < components; i++) + { + PushID(i); + value_changed |= SliderScalar("##v", data_type, v, v_min, v_max, format, power); + SameLine(0, g.Style.ItemInnerSpacing.x); + PopID(); + PopItemWidth(); + v = (void*)((char*)v + type_size); + } + PopID(); + + TextUnformatted(label, FindRenderedTextEnd(label)); + EndGroup(); + return value_changed; +} + +bool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, const char* format, float power) +{ + return SliderScalar(label, ImGuiDataType_Float, v, &v_min, &v_max, format, power); +} + +bool ImGui::SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* format, float power) +{ + return SliderScalarN(label, ImGuiDataType_Float, v, 2, &v_min, &v_max, format, power); +} + +bool ImGui::SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* format, float power) +{ + return SliderScalarN(label, ImGuiDataType_Float, v, 3, &v_min, &v_max, format, power); +} + +bool ImGui::SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* format, float power) +{ + return SliderScalarN(label, ImGuiDataType_Float, v, 4, &v_min, &v_max, format, power); +} + +bool ImGui::SliderAngle(const char* label, float* v_rad, float v_degrees_min, float v_degrees_max) +{ + float v_deg = (*v_rad) * 360.0f / (2*IM_PI); + bool value_changed = SliderFloat(label, &v_deg, v_degrees_min, v_degrees_max, "%.0f deg", 1.0f); + *v_rad = v_deg * (2*IM_PI) / 360.0f; + return value_changed; +} + +bool ImGui::SliderInt(const char* label, int* v, int v_min, int v_max, const char* format) +{ + return SliderScalar(label, ImGuiDataType_S32, v, &v_min, &v_max, format); +} + +bool ImGui::SliderInt2(const char* label, int v[2], int v_min, int v_max, const char* format) +{ + return SliderScalarN(label, ImGuiDataType_S32, v, 2, &v_min, &v_max, format); +} + +bool ImGui::SliderInt3(const char* label, int v[3], int v_min, int v_max, const char* format) +{ + return SliderScalarN(label, ImGuiDataType_S32, v, 3, &v_min, &v_max, format); +} + +bool ImGui::SliderInt4(const char* label, int v[4], int v_min, int v_max, const char* format) +{ + return SliderScalarN(label, ImGuiDataType_S32, v, 4, &v_min, &v_max, format); +} + +bool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType data_type, void* v, const void* v_min, const void* v_max, const char* format, float power) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); + + const ImVec2 label_size = CalcTextSize(label, NULL, true); + const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size); + const ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); + + ItemSize(bb, style.FramePadding.y); + if (!ItemAdd(frame_bb, id)) + return false; + + // Default format string when passing NULL + // Patch old "%.0f" format string to use "%d", read function comments for more details. + IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT); + if (format == NULL) + format = GDataTypeInfo[data_type].PrintFmt; + else if (data_type == ImGuiDataType_S32 && strcmp(format, "%d") != 0) + format = PatchFormatStringFloatToInt(format); + + const bool hovered = ItemHoverable(frame_bb, id); + if ((hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || g.NavInputId == id) + { + SetActiveID(id, window); + SetFocusID(id, window); + FocusWindow(window); + g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right); + } + + + // Draw frame + const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); + RenderNavHighlight(frame_bb, id); + RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, g.Style.FrameRounding); + + // Slider behavior + ImRect grab_bb; + const bool value_changed = SliderBehavior(frame_bb, id, data_type, v, v_min, v_max, format, power, ImGuiSliderFlags_Vertical, &grab_bb); + if (value_changed) + MarkItemEdited(id); + + // Render grab + window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding); + + // Display value using user-provided display format so user can add prefix/suffix/decorations to the value. + // For the vertical slider we allow centered text to overlap the frame padding + char value_buf[64]; + const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, v, format); + RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f,0.0f)); + if (label_size.x > 0.0f) + RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); + + return value_changed; +} + +bool ImGui::VSliderFloat(const char* label, const ImVec2& size, float* v, float v_min, float v_max, const char* format, float power) +{ + return VSliderScalar(label, size, ImGuiDataType_Float, v, &v_min, &v_max, format, power); +} + +bool ImGui::VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* format) +{ + return VSliderScalar(label, size, ImGuiDataType_S32, v, &v_min, &v_max, format); +} + +//------------------------------------------------------------------------- +// WIDGETS: Inputs (_excepted InputText_) +// - ImParseFormatFindStart() +// - ImParseFormatFindEnd() +// - ImParseFormatTrimDecorations() +// - ImParseFormatPrecision() +// - InputScalarAsWidgetReplacement() [Internal] +// - InputScalar() +// - InputScalarN() +// - InputFloat() +// - InputFloat2() +// - InputFloat3() +// - InputFloat4() +// - InputInt() +// - InputInt2() +// - InputInt3() +// - InputInt4() +// - InputDouble() +//------------------------------------------------------------------------- + +// We don't use strchr() because our strings are usually very short and often start with '%' +const char* ImParseFormatFindStart(const char* fmt) +{ + while (char c = fmt[0]) + { + if (c == '%' && fmt[1] != '%') + return fmt; + else if (c == '%') + fmt++; + fmt++; + } + return fmt; +} + +const char* ImParseFormatFindEnd(const char* fmt) +{ + // Printf/scanf types modifiers: I/L/h/j/l/t/w/z. Other uppercase letters qualify as types aka end of the format. + if (fmt[0] != '%') + return fmt; + const unsigned int ignored_uppercase_mask = (1 << ('I'-'A')) | (1 << ('L'-'A')); + const unsigned int ignored_lowercase_mask = (1 << ('h'-'a')) | (1 << ('j'-'a')) | (1 << ('l'-'a')) | (1 << ('t'-'a')) | (1 << ('w'-'a')) | (1 << ('z'-'a')); + for (char c; (c = *fmt) != 0; fmt++) + { + if (c >= 'A' && c <= 'Z' && ((1 << (c - 'A')) & ignored_uppercase_mask) == 0) + return fmt + 1; + if (c >= 'a' && c <= 'z' && ((1 << (c - 'a')) & ignored_lowercase_mask) == 0) + return fmt + 1; + } + return fmt; +} + +// Extract the format out of a format string with leading or trailing decorations +// fmt = "blah blah" -> return fmt +// fmt = "%.3f" -> return fmt +// fmt = "hello %.3f" -> return fmt + 6 +// fmt = "%.3f hello" -> return buf written with "%.3f" +const char* ImParseFormatTrimDecorations(const char* fmt, char* buf, int buf_size) +{ + const char* fmt_start = ImParseFormatFindStart(fmt); + if (fmt_start[0] != '%') + return fmt; + const char* fmt_end = ImParseFormatFindEnd(fmt_start); + if (fmt_end[0] == 0) // If we only have leading decoration, we don't need to copy the data. + return fmt_start; + ImStrncpy(buf, fmt_start, ImMin((int)(fmt_end + 1 - fmt_start), buf_size)); + return buf; +} + +// Parse display precision back from the display format string +// FIXME: This is still used by some navigation code path to infer a minimum tweak step, but we should aim to rework widgets so it isn't needed. +int ImParseFormatPrecision(const char* fmt, int default_precision) +{ + fmt = ImParseFormatFindStart(fmt); + if (fmt[0] != '%') + return default_precision; + fmt++; + while (*fmt >= '0' && *fmt <= '9') + fmt++; + int precision = INT_MAX; + if (*fmt == '.') + { + fmt = ImAtoi(fmt + 1, &precision); + if (precision < 0 || precision > 99) + precision = default_precision; + } + if (*fmt == 'e' || *fmt == 'E') // Maximum precision with scientific notation + precision = -1; + if ((*fmt == 'g' || *fmt == 'G') && precision == INT_MAX) + precision = -1; + return (precision == INT_MAX) ? default_precision : precision; +} + +// Create text input in place of a slider (when CTRL+Clicking on slider) +// FIXME: Logic is messy and confusing. +bool ImGui::InputScalarAsWidgetReplacement(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* data_ptr, const char* format) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + + // Our replacement widget will override the focus ID (registered previously to allow for a TAB focus to happen) + // On the first frame, g.ScalarAsInputTextId == 0, then on subsequent frames it becomes == id + SetActiveID(g.ScalarAsInputTextId, window); + g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); + SetHoveredID(0); + FocusableItemUnregister(window); + + char fmt_buf[32]; + char data_buf[32]; + format = ImParseFormatTrimDecorations(format, fmt_buf, IM_ARRAYSIZE(fmt_buf)); + DataTypeFormatString(data_buf, IM_ARRAYSIZE(data_buf), data_type, data_ptr, format); + ImStrTrimBlanks(data_buf); + ImGuiInputTextFlags flags = ImGuiInputTextFlags_AutoSelectAll | ((data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double) ? ImGuiInputTextFlags_CharsScientific : ImGuiInputTextFlags_CharsDecimal); + bool value_changed = InputTextEx(label, data_buf, IM_ARRAYSIZE(data_buf), bb.GetSize(), flags); + if (g.ScalarAsInputTextId == 0) // First frame we started displaying the InputText widget + { + IM_ASSERT(g.ActiveId == id); // InputText ID expected to match the Slider ID + g.ScalarAsInputTextId = g.ActiveId; + SetHoveredID(id); + } + if (value_changed) + return DataTypeApplyOpFromText(data_buf, g.InputTextState.InitialText.Data, data_type, data_ptr, NULL); + return false; +} + +bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* data_ptr, const void* step, const void* step_fast, const char* format, ImGuiInputTextFlags extra_flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + + IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT); + if (format == NULL) + format = GDataTypeInfo[data_type].PrintFmt; + + char buf[64]; + DataTypeFormatString(buf, IM_ARRAYSIZE(buf), data_type, data_ptr, format); + + bool value_changed = false; + if ((extra_flags & (ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsScientific)) == 0) + extra_flags |= ImGuiInputTextFlags_CharsDecimal; + extra_flags |= ImGuiInputTextFlags_AutoSelectAll; + + if (step != NULL) + { + const float button_size = GetFrameHeight(); + + BeginGroup(); // The only purpose of the group here is to allow the caller to query item data e.g. IsItemActive() + PushID(label); + PushItemWidth(ImMax(1.0f, CalcItemWidth() - (button_size + style.ItemInnerSpacing.x) * 2)); + if (InputText("", buf, IM_ARRAYSIZE(buf), extra_flags)) // PushId(label) + "" gives us the expected ID from outside point of view + value_changed = DataTypeApplyOpFromText(buf, g.InputTextState.InitialText.Data, data_type, data_ptr, format); + PopItemWidth(); + + // Step buttons + SameLine(0, style.ItemInnerSpacing.x); + if (ButtonEx("-", ImVec2(button_size, button_size), ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups)) + { + DataTypeApplyOp(data_type, '-', data_ptr, data_ptr, g.IO.KeyCtrl && step_fast ? step_fast : step); + value_changed = true; + } + SameLine(0, style.ItemInnerSpacing.x); + if (ButtonEx("+", ImVec2(button_size, button_size), ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups)) + { + DataTypeApplyOp(data_type, '+', data_ptr, data_ptr, g.IO.KeyCtrl && step_fast ? step_fast : step); + value_changed = true; + } + SameLine(0, style.ItemInnerSpacing.x); + TextUnformatted(label, FindRenderedTextEnd(label)); + + PopID(); + EndGroup(); + } + else + { + if (InputText(label, buf, IM_ARRAYSIZE(buf), extra_flags)) + value_changed = DataTypeApplyOpFromText(buf, g.InputTextState.InitialText.Data, data_type, data_ptr, format); + } + + return value_changed; +} + +bool ImGui::InputScalarN(const char* label, ImGuiDataType data_type, void* v, int components, const void* step, const void* step_fast, const char* format, ImGuiInputTextFlags extra_flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + bool value_changed = false; + BeginGroup(); + PushID(label); + PushMultiItemsWidths(components); + size_t type_size = GDataTypeInfo[data_type].Size; + for (int i = 0; i < components; i++) + { + PushID(i); + value_changed |= InputScalar("##v", data_type, v, step, step_fast, format, extra_flags); + SameLine(0, g.Style.ItemInnerSpacing.x); + PopID(); + PopItemWidth(); + v = (void*)((char*)v + type_size); + } + PopID(); + + TextUnformatted(label, FindRenderedTextEnd(label)); + EndGroup(); + return value_changed; +} + +bool ImGui::InputFloat(const char* label, float* v, float step, float step_fast, const char* format, ImGuiInputTextFlags extra_flags) +{ + extra_flags |= ImGuiInputTextFlags_CharsScientific; + return InputScalar(label, ImGuiDataType_Float, (void*)v, (void*)(step>0.0f ? &step : NULL), (void*)(step_fast>0.0f ? &step_fast : NULL), format, extra_flags); +} + +bool ImGui::InputFloat2(const char* label, float v[2], const char* format, ImGuiInputTextFlags extra_flags) +{ + return InputScalarN(label, ImGuiDataType_Float, v, 2, NULL, NULL, format, extra_flags); +} + +bool ImGui::InputFloat3(const char* label, float v[3], const char* format, ImGuiInputTextFlags extra_flags) +{ + return InputScalarN(label, ImGuiDataType_Float, v, 3, NULL, NULL, format, extra_flags); +} + +bool ImGui::InputFloat4(const char* label, float v[4], const char* format, ImGuiInputTextFlags extra_flags) +{ + return InputScalarN(label, ImGuiDataType_Float, v, 4, NULL, NULL, format, extra_flags); +} + +// Prefer using "const char* format" directly, which is more flexible and consistent with other API. +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS +bool ImGui::InputFloat(const char* label, float* v, float step, float step_fast, int decimal_precision, ImGuiInputTextFlags extra_flags) +{ + char format[16] = "%f"; + if (decimal_precision >= 0) + ImFormatString(format, IM_ARRAYSIZE(format), "%%.%df", decimal_precision); + return InputFloat(label, v, step, step_fast, format, extra_flags); +} + +bool ImGui::InputFloat2(const char* label, float v[2], int decimal_precision, ImGuiInputTextFlags extra_flags) +{ + char format[16] = "%f"; + if (decimal_precision >= 0) + ImFormatString(format, IM_ARRAYSIZE(format), "%%.%df", decimal_precision); + return InputScalarN(label, ImGuiDataType_Float, v, 2, NULL, NULL, format, extra_flags); +} + +bool ImGui::InputFloat3(const char* label, float v[3], int decimal_precision, ImGuiInputTextFlags extra_flags) +{ + char format[16] = "%f"; + if (decimal_precision >= 0) + ImFormatString(format, IM_ARRAYSIZE(format), "%%.%df", decimal_precision); + return InputScalarN(label, ImGuiDataType_Float, v, 3, NULL, NULL, format, extra_flags); +} + +bool ImGui::InputFloat4(const char* label, float v[4], int decimal_precision, ImGuiInputTextFlags extra_flags) +{ + char format[16] = "%f"; + if (decimal_precision >= 0) + ImFormatString(format, IM_ARRAYSIZE(format), "%%.%df", decimal_precision); + return InputScalarN(label, ImGuiDataType_Float, v, 4, NULL, NULL, format, extra_flags); +} +#endif // IMGUI_DISABLE_OBSOLETE_FUNCTIONS + +bool ImGui::InputInt(const char* label, int* v, int step, int step_fast, ImGuiInputTextFlags extra_flags) +{ + // Hexadecimal input provided as a convenience but the flag name is awkward. Typically you'd use InputText() to parse your own data, if you want to handle prefixes. + const char* format = (extra_flags & ImGuiInputTextFlags_CharsHexadecimal) ? "%08X" : "%d"; + return InputScalar(label, ImGuiDataType_S32, (void*)v, (void*)(step>0 ? &step : NULL), (void*)(step_fast>0 ? &step_fast : NULL), format, extra_flags); +} + +bool ImGui::InputInt2(const char* label, int v[2], ImGuiInputTextFlags extra_flags) +{ + return InputScalarN(label, ImGuiDataType_S32, v, 2, NULL, NULL, "%d", extra_flags); +} + +bool ImGui::InputInt3(const char* label, int v[3], ImGuiInputTextFlags extra_flags) +{ + return InputScalarN(label, ImGuiDataType_S32, v, 3, NULL, NULL, "%d", extra_flags); +} + +bool ImGui::InputInt4(const char* label, int v[4], ImGuiInputTextFlags extra_flags) +{ + return InputScalarN(label, ImGuiDataType_S32, v, 4, NULL, NULL, "%d", extra_flags); +} + +bool ImGui::InputDouble(const char* label, double* v, double step, double step_fast, const char* format, ImGuiInputTextFlags extra_flags) +{ + extra_flags |= ImGuiInputTextFlags_CharsScientific; + return InputScalar(label, ImGuiDataType_Double, (void*)v, (void*)(step>0.0 ? &step : NULL), (void*)(step_fast>0.0 ? &step_fast : NULL), format, extra_flags); +} //------------------------------------------------------------------------- // WIDGETS: InputText From 66b51940bd71bfe75df74747773a292790be200f Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 30 Aug 2018 15:19:24 +0200 Subject: [PATCH 203/828] Refactor: Moved README, ChangeLog, TODO files to docs/ folder + update Changelog. (#2036) --- CHANGELOG.txt => docs/CHANGELOG.txt | 15 +++++++++++++++ README.md => docs/README.md | 0 TODO.txt => docs/TODO.txt | 0 3 files changed, 15 insertions(+) rename CHANGELOG.txt => docs/CHANGELOG.txt (98%) rename README.md => docs/README.md (100%) rename TODO.txt => docs/TODO.txt (100%) diff --git a/CHANGELOG.txt b/docs/CHANGELOG.txt similarity index 98% rename from CHANGELOG.txt rename to docs/CHANGELOG.txt index 783454d37731..35472c3da493 100644 --- a/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -33,6 +33,21 @@ HOW TO UPDATE? VERSION 1.64 (in progress) ----------------------------------------------------------------------- +Changes: + +- Moved Readme, Changelog and Todo files to the docs/ folder. + If you are updating dear imgui by copying files, take the chance to delete the old files. +- Added imgui_widgets.cpp file, extracted and moved widgets code out of imgui.cpp into imgui_widgets.cpp. + Re-ordered some of the code remaining in imgui.cpp in cleared chunks. + NONE OF THE FUNCTIONS HAVE CHANGED. THE CODE IS SEMANTICALLY 100% IDENTICAL, BUT ALL FUNCTIONS WERE MOVED. + Because of this, any local modifications to imgui.cpp will likely conflict when you update. + If you have any modifications to imgui.cpp, it is suggested that you first update to 1.63, then + isolate your patches. You can peak at imgui_widgets.cpp from 1.64 to get a sense of what is included in it, + then separate your changes into several patches that can more easily be applied to 1.64 on a per-file basis. +- As a reminder, if you have any change to imgui.cpp it is a good habit to discuss them on the github + so a solution applicable on the Master branch can be found. If your company has changes that you cannot + disclose you may also contact me privately. + ----------------------------------------------------------------------- VERSION 1.63 (Released 2018-08-29) diff --git a/README.md b/docs/README.md similarity index 100% rename from README.md rename to docs/README.md diff --git a/TODO.txt b/docs/TODO.txt similarity index 100% rename from TODO.txt rename to docs/TODO.txt From a44c5f7afedbf47bd284934930be4f538cf32562 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 30 Aug 2018 15:38:55 +0200 Subject: [PATCH 204/828] Refactor: Internals: Moved various functions in imgui.cpp (#2036) --- imgui.cpp | 206 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 107 insertions(+), 99 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index fed2128c0138..524cce28ad11 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1087,7 +1087,7 @@ void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars) } //----------------------------------------------------------------------------- -// HELPERS +// HELPERS/UTILITIES //----------------------------------------------------------------------------- ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p) @@ -1488,6 +1488,68 @@ int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_e return bytes_count; } +FILE* ImFileOpen(const char* filename, const char* mode) +{ +#if defined(_WIN32) && !defined(__CYGWIN__) + // We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames. Converting both strings from UTF-8 to wchar format (using a single allocation, because we can) + const int filename_wsize = ImTextCountCharsFromUtf8(filename, NULL) + 1; + const int mode_wsize = ImTextCountCharsFromUtf8(mode, NULL) + 1; + ImVector buf; + buf.resize(filename_wsize + mode_wsize); + ImTextStrFromUtf8(&buf[0], filename_wsize, filename, NULL); + ImTextStrFromUtf8(&buf[filename_wsize], mode_wsize, mode, NULL); + return _wfopen((wchar_t*)&buf[0], (wchar_t*)&buf[filename_wsize]); +#else + return fopen(filename, mode); +#endif +} + +// Load file content into memory +// Memory allocated with ImGui::MemAlloc(), must be freed by user using ImGui::MemFree() +void* ImFileLoadToMemory(const char* filename, const char* file_open_mode, size_t* out_file_size, int padding_bytes) +{ + IM_ASSERT(filename && file_open_mode); + if (out_file_size) + *out_file_size = 0; + + FILE* f; + if ((f = ImFileOpen(filename, file_open_mode)) == NULL) + return NULL; + + long file_size_signed; + if (fseek(f, 0, SEEK_END) || (file_size_signed = ftell(f)) == -1 || fseek(f, 0, SEEK_SET)) + { + fclose(f); + return NULL; + } + + size_t file_size = (size_t)file_size_signed; + void* file_data = ImGui::MemAlloc(file_size + padding_bytes); + if (file_data == NULL) + { + fclose(f); + return NULL; + } + if (fread(file_data, 1, file_size, f) != file_size) + { + fclose(f); + ImGui::MemFree(file_data); + return NULL; + } + if (padding_bytes > 0) + memset((void*)(((char*)file_data) + file_size), 0, (size_t)padding_bytes); + + fclose(f); + if (out_file_size) + *out_file_size = file_size; + + return file_data; +} + +//----------------------------------------------------------------------------- +// COLOR FUNCTIONS +//----------------------------------------------------------------------------- + ImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in) { float s = 1.0f/255.0f; @@ -1508,38 +1570,6 @@ ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in) return out; } -ImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul) -{ - ImGuiStyle& style = GImGui->Style; - ImVec4 c = style.Colors[idx]; - c.w *= style.Alpha * alpha_mul; - return ColorConvertFloat4ToU32(c); -} - -ImU32 ImGui::GetColorU32(const ImVec4& col) -{ - ImGuiStyle& style = GImGui->Style; - ImVec4 c = col; - c.w *= style.Alpha; - return ColorConvertFloat4ToU32(c); -} - -const ImVec4& ImGui::GetStyleColorVec4(ImGuiCol idx) -{ - ImGuiStyle& style = GImGui->Style; - return style.Colors[idx]; -} - -ImU32 ImGui::GetColorU32(ImU32 col) -{ - float style_alpha = GImGui->Style.Alpha; - if (style_alpha >= 1.0f) - return col; - ImU32 a = (col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT; - a = (ImU32)(a * style_alpha); // We don't need to clamp 0..255 because Style.Alpha is in 0..1 range. - return (col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT); -} - // Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592 // Optimized http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v) @@ -1591,62 +1621,36 @@ void ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& } } -FILE* ImFileOpen(const char* filename, const char* mode) +ImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul) { -#if defined(_WIN32) && !defined(__CYGWIN__) - // We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames. Converting both strings from UTF-8 to wchar format (using a single allocation, because we can) - const int filename_wsize = ImTextCountCharsFromUtf8(filename, NULL) + 1; - const int mode_wsize = ImTextCountCharsFromUtf8(mode, NULL) + 1; - ImVector buf; - buf.resize(filename_wsize + mode_wsize); - ImTextStrFromUtf8(&buf[0], filename_wsize, filename, NULL); - ImTextStrFromUtf8(&buf[filename_wsize], mode_wsize, mode, NULL); - return _wfopen((wchar_t*)&buf[0], (wchar_t*)&buf[filename_wsize]); -#else - return fopen(filename, mode); -#endif + ImGuiStyle& style = GImGui->Style; + ImVec4 c = style.Colors[idx]; + c.w *= style.Alpha * alpha_mul; + return ColorConvertFloat4ToU32(c); } -// Load file content into memory -// Memory allocated with ImGui::MemAlloc(), must be freed by user using ImGui::MemFree() -void* ImFileLoadToMemory(const char* filename, const char* file_open_mode, size_t* out_file_size, int padding_bytes) +ImU32 ImGui::GetColorU32(const ImVec4& col) { - IM_ASSERT(filename && file_open_mode); - if (out_file_size) - *out_file_size = 0; - - FILE* f; - if ((f = ImFileOpen(filename, file_open_mode)) == NULL) - return NULL; - - long file_size_signed; - if (fseek(f, 0, SEEK_END) || (file_size_signed = ftell(f)) == -1 || fseek(f, 0, SEEK_SET)) - { - fclose(f); - return NULL; - } - - size_t file_size = (size_t)file_size_signed; - void* file_data = ImGui::MemAlloc(file_size + padding_bytes); - if (file_data == NULL) - { - fclose(f); - return NULL; - } - if (fread(file_data, 1, file_size, f) != file_size) - { - fclose(f); - ImGui::MemFree(file_data); - return NULL; - } - if (padding_bytes > 0) - memset((void*)(((char*)file_data) + file_size), 0, (size_t)padding_bytes); + ImGuiStyle& style = GImGui->Style; + ImVec4 c = col; + c.w *= style.Alpha; + return ColorConvertFloat4ToU32(c); +} - fclose(f); - if (out_file_size) - *out_file_size = file_size; +const ImVec4& ImGui::GetStyleColorVec4(ImGuiCol idx) +{ + ImGuiStyle& style = GImGui->Style; + return style.Colors[idx]; +} - return file_data; +ImU32 ImGui::GetColorU32(ImU32 col) +{ + float style_alpha = GImGui->Style.Alpha; + if (style_alpha >= 1.0f) + return col; + ImU32 a = (col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT; + a = (ImU32)(a * style_alpha); // We don't need to clamp 0..255 because Style.Alpha is in 0..1 range. + return (col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT); } //----------------------------------------------------------------------------- @@ -9220,6 +9224,26 @@ void ImGui::NewLine() window->DC.LayoutType = backup_layout_type; } +void ImGui::Indent(float indent_w) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + window->DC.Indent.x += (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing; + window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x; +} + +void ImGui::Unindent(float indent_w) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + window->DC.Indent.x -= (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing; + window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x; +} + +//----------------------------------------------------------------------------- +// COLUMNS +//----------------------------------------------------------------------------- + void ImGui::NextColumn() { ImGuiWindow* window = GetCurrentWindow(); @@ -9538,22 +9562,6 @@ void ImGui::Columns(int columns_count, const char* id, bool border) BeginColumns(id, columns_count, flags); } -void ImGui::Indent(float indent_w) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = GetCurrentWindow(); - window->DC.Indent.x += (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing; - window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x; -} - -void ImGui::Unindent(float indent_w) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = GetCurrentWindow(); - window->DC.Indent.x -= (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing; - window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x; -} - //----------------------------------------------------------------------------- // DRAG AND DROP //----------------------------------------------------------------------------- From 81bc4265e54ebc890ceea3e9b1eb31622894099c Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 30 Aug 2018 15:48:57 +0200 Subject: [PATCH 205/828] Refactor: Internals: Moved Settings functions in imgui.cpp in their own section. (#2036) --- imgui.cpp | 444 ++++++++++++++++++++++++----------------------- imgui_internal.h | 3 +- 2 files changed, 228 insertions(+), 219 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 524cce28ad11..536006ed9598 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -876,13 +876,17 @@ static void SetWindowSize(ImGuiWindow* window, const ImVec2& size, I static void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond); static void FindHoveredWindow(); static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags); -static ImGuiWindowSettings* CreateNewWindowSettings(const char* name); static void CheckStacksSize(ImGuiWindow* window, bool write); static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window, bool snap_on_edges); static void AddDrawListToDrawData(ImVector* out_list, ImDrawList* draw_list); static void AddWindowToSortedBuffer(ImVector* out_sorted_windows, ImGuiWindow* window); +// Settings +static void* SettingsHandlerWindow_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name); +static void SettingsHandlerWindow_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line); +static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf); + namespace ImGui { static bool BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags); @@ -4416,74 +4420,6 @@ void ImGui::NewFrame() Begin("Debug##Default"); } -static void* SettingsHandlerWindow_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name) -{ - ImGuiWindowSettings* settings = ImGui::FindWindowSettings(ImHash(name, 0)); - if (!settings) - settings = CreateNewWindowSettings(name); - return (void*)settings; -} - -static void SettingsHandlerWindow_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line) -{ - ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry; - float x, y; - int i; - ImU32 u1; - if (sscanf(line, "Pos=%f,%f", &x, &y) == 2) { settings->Pos = ImVec2(x, y); } - else if (sscanf(line, "Size=%f,%f", &x, &y) == 2) { settings->Size = ImMax(ImVec2(x, y), GImGui->Style.WindowMinSize); } - else if (sscanf(line, "ViewportId=0x%08X", &u1) == 1) { settings->ViewportId = u1; } - else if (sscanf(line, "ViewportPos=%f,%f", &x, &y) == 2) { settings->ViewportPos = ImVec2(x, y); } - else if (sscanf(line, "Collapsed=%d", &i) == 1) { settings->Collapsed = (i != 0); } -} - -static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf) -{ - // Gather data from windows that were active during this session - ImGuiContext& g = *imgui_ctx; - for (int i = 0; i != g.Windows.Size; i++) - { - ImGuiWindow* window = g.Windows[i]; - if (window->Flags & ImGuiWindowFlags_NoSavedSettings) - continue; - - ImGuiWindowSettings* settings = (window->SettingsIdx != -1) ? &g.SettingsWindows[window->SettingsIdx] : ImGui::FindWindowSettings(window->ID); - if (!settings) - { - settings = CreateNewWindowSettings(window->Name); - window->SettingsIdx = g.SettingsWindows.index_from_pointer(settings); - } - IM_ASSERT(settings->ID == window->ID); - settings->Pos = window->Pos - window->ViewportPos; - settings->Size = window->SizeFull; - settings->ViewportId = window->ViewportId; - settings->ViewportPos = window->ViewportPos; - settings->Collapsed = window->Collapsed; - } - - // Write a buffer - // If a window wasn't opened in this session we preserve its settings - buf->reserve(buf->size() + g.SettingsWindows.Size * 96); // ballpark reserve - for (int i = 0; i != g.SettingsWindows.Size; i++) - { - const ImGuiWindowSettings* settings = &g.SettingsWindows[i]; - const char* name = settings->Name; - if (const char* p = strstr(name, "###")) // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID() - name = p; - buf->appendf("[%s][%s]\n", handler->TypeName, name); - if (settings->ViewportId != 0 && settings->ViewportId != ImGui::IMGUI_VIEWPORT_DEFAULT_ID) - { - buf->appendf("ViewportPos=%d,%d\n", (int)settings->ViewportPos.x, (int)settings->ViewportPos.y); - buf->appendf("ViewportId=0x%08X\n", settings->ViewportId); - } - if (settings->Pos.x != 0.0f || settings->Pos.y != 0.0f || settings->ViewportId == ImGui::IMGUI_VIEWPORT_DEFAULT_ID) - buf->appendf("Pos=%d,%d\n", (int)settings->Pos.x, (int)settings->Pos.y); - buf->appendf("Size=%d,%d\n", (int)settings->Size.x, (int)settings->Size.y); - buf->appendf("Collapsed=%d\n", settings->Collapsed); - buf->appendf("\n"); - } -} - void ImGui::Initialize(ImGuiContext* context) { ImGuiContext& g = *context; @@ -4592,155 +4528,6 @@ void ImGui::Shutdown(ImGuiContext* context) g.Initialized = false; } -ImGuiWindowSettings* ImGui::FindWindowSettings(ImGuiID id) -{ - ImGuiContext& g = *GImGui; - for (int i = 0; i != g.SettingsWindows.Size; i++) - if (g.SettingsWindows[i].ID == id) - return &g.SettingsWindows[i]; - return NULL; -} - -static ImGuiWindowSettings* CreateNewWindowSettings(const char* name) -{ - ImGuiContext& g = *GImGui; - g.SettingsWindows.push_back(ImGuiWindowSettings()); - ImGuiWindowSettings* settings = &g.SettingsWindows.back(); - settings->Name = ImStrdup(name); - settings->ID = ImHash(name, 0); - return settings; -} - -void ImGui::LoadIniSettingsFromDisk(const char* ini_filename) -{ - size_t file_data_size = 0; - char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_data_size); - if (!file_data) - return; - LoadIniSettingsFromMemory(file_data, (size_t)file_data_size); - ImGui::MemFree(file_data); -} - -ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name) -{ - ImGuiContext& g = *GImGui; - const ImGuiID type_hash = ImHash(type_name, 0, 0); - for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++) - if (g.SettingsHandlers[handler_n].TypeHash == type_hash) - return &g.SettingsHandlers[handler_n]; - return NULL; -} - -// Zero-tolerance, no error reporting, cheap .ini parsing -void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(g.Initialized); - IM_ASSERT(g.SettingsLoaded == false && g.FrameCount == 0); - - // For user convenience, we allow passing a non zero-terminated string (hence the ini_size parameter). - // For our convenience and to make the code simpler, we'll also write zero-terminators within the buffer. So let's create a writable copy.. - if (ini_size == 0) - ini_size = strlen(ini_data); - char* buf = (char*)ImGui::MemAlloc(ini_size + 1); - char* buf_end = buf + ini_size; - memcpy(buf, ini_data, ini_size); - buf[ini_size] = 0; - - void* entry_data = NULL; - ImGuiSettingsHandler* entry_handler = NULL; - - char* line_end = NULL; - for (char* line = buf; line < buf_end; line = line_end + 1) - { - // Skip new lines markers, then find end of the line - while (*line == '\n' || *line == '\r') - line++; - line_end = line; - while (line_end < buf_end && *line_end != '\n' && *line_end != '\r') - line_end++; - line_end[0] = 0; - - if (line[0] == '[' && line_end > line && line_end[-1] == ']') - { - // Parse "[Type][Name]". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code. - line_end[-1] = 0; - const char* name_end = line_end - 1; - const char* type_start = line + 1; - char* type_end = (char*)(intptr_t)ImStrchrRange(type_start, name_end, ']'); - const char* name_start = type_end ? ImStrchrRange(type_end + 1, name_end, '[') : NULL; - if (!type_end || !name_start) - { - name_start = type_start; // Import legacy entries that have no type - type_start = "Window"; - } - else - { - *type_end = 0; // Overwrite first ']' - name_start++; // Skip second '[' - } - entry_handler = FindSettingsHandler(type_start); - entry_data = entry_handler ? entry_handler->ReadOpenFn(&g, entry_handler, name_start) : NULL; - } - else if (entry_handler != NULL && entry_data != NULL) - { - // Let type handler parse the line - entry_handler->ReadLineFn(&g, entry_handler, entry_data, line); - } - } - - ImGui::MemFree(buf); - g.SettingsLoaded = true; -} - -void ImGui::SaveIniSettingsToDisk(const char* ini_filename) -{ - ImGuiContext& g = *GImGui; - g.SettingsDirtyTimer = 0.0f; - if (!ini_filename) - return; - - size_t ini_data_size = 0; - const char* ini_data = SaveIniSettingsToMemory(&ini_data_size); - FILE* f = ImFileOpen(ini_filename, "wt"); - if (!f) - return; - fwrite(ini_data, sizeof(char), ini_data_size, f); - fclose(f); -} - -// Call registered handlers (e.g. SettingsHandlerWindow_WriteAll() + custom handlers) to write their stuff into a text buffer -const char* ImGui::SaveIniSettingsToMemory(size_t* out_size) -{ - ImGuiContext& g = *GImGui; - g.SettingsDirtyTimer = 0.0f; - g.SettingsIniData.Buf.resize(0); - g.SettingsIniData.Buf.push_back(0); - for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++) - { - ImGuiSettingsHandler* handler = &g.SettingsHandlers[handler_n]; - handler->WriteAllFn(&g, handler, &g.SettingsIniData); - } - if (out_size) - *out_size = (size_t)g.SettingsIniData.size(); - return g.SettingsIniData.c_str(); -} - -void ImGui::MarkIniSettingsDirty() -{ - ImGuiContext& g = *GImGui; - if (g.SettingsDirtyTimer <= 0.0f) - g.SettingsDirtyTimer = g.IO.IniSavingRate; -} - -void ImGui::MarkIniSettingsDirty(ImGuiWindow* window) -{ - ImGuiContext& g = *GImGui; - if (!(window->Flags & ImGuiWindowFlags_NoSavedSettings)) - if (g.SettingsDirtyTimer <= 0.0f) - g.SettingsDirtyTimer = g.IO.IniSavingRate; -} - // FIXME: Add a more explicit sort order in the window structure. static int IMGUI_CDECL ChildWindowComparer(const void* lhs, const void* rhs) { @@ -9849,6 +9636,227 @@ void ImGui::EndDragDropTarget() g.DragDropWithinSourceOrTarget = false; } +//----------------------------------------------------------------------------- +// SETTINGS +//----------------------------------------------------------------------------- + +void ImGui::MarkIniSettingsDirty() +{ + ImGuiContext& g = *GImGui; + if (g.SettingsDirtyTimer <= 0.0f) + g.SettingsDirtyTimer = g.IO.IniSavingRate; +} + +void ImGui::MarkIniSettingsDirty(ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; + if (!(window->Flags & ImGuiWindowFlags_NoSavedSettings)) + if (g.SettingsDirtyTimer <= 0.0f) + g.SettingsDirtyTimer = g.IO.IniSavingRate; +} + +static ImGuiWindowSettings* CreateNewWindowSettings(const char* name) +{ + ImGuiContext& g = *GImGui; + g.SettingsWindows.push_back(ImGuiWindowSettings()); + ImGuiWindowSettings* settings = &g.SettingsWindows.back(); + settings->Name = ImStrdup(name); + settings->ID = ImHash(name, 0); + return settings; +} + +ImGuiWindowSettings* ImGui::FindWindowSettings(ImGuiID id) +{ + ImGuiContext& g = *GImGui; + for (int i = 0; i != g.SettingsWindows.Size; i++) + if (g.SettingsWindows[i].ID == id) + return &g.SettingsWindows[i]; + return NULL; +} + +void ImGui::LoadIniSettingsFromDisk(const char* ini_filename) +{ + size_t file_data_size = 0; + char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_data_size); + if (!file_data) + return; + LoadIniSettingsFromMemory(file_data, (size_t)file_data_size); + ImGui::MemFree(file_data); +} + +ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name) +{ + ImGuiContext& g = *GImGui; + const ImGuiID type_hash = ImHash(type_name, 0, 0); + for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++) + if (g.SettingsHandlers[handler_n].TypeHash == type_hash) + return &g.SettingsHandlers[handler_n]; + return NULL; +} + +// Zero-tolerance, no error reporting, cheap .ini parsing +void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(g.Initialized); + IM_ASSERT(g.SettingsLoaded == false && g.FrameCount == 0); + + // For user convenience, we allow passing a non zero-terminated string (hence the ini_size parameter). + // For our convenience and to make the code simpler, we'll also write zero-terminators within the buffer. So let's create a writable copy.. + if (ini_size == 0) + ini_size = strlen(ini_data); + char* buf = (char*)ImGui::MemAlloc(ini_size + 1); + char* buf_end = buf + ini_size; + memcpy(buf, ini_data, ini_size); + buf[ini_size] = 0; + + void* entry_data = NULL; + ImGuiSettingsHandler* entry_handler = NULL; + + char* line_end = NULL; + for (char* line = buf; line < buf_end; line = line_end + 1) + { + // Skip new lines markers, then find end of the line + while (*line == '\n' || *line == '\r') + line++; + line_end = line; + while (line_end < buf_end && *line_end != '\n' && *line_end != '\r') + line_end++; + line_end[0] = 0; + + if (line[0] == '[' && line_end > line && line_end[-1] == ']') + { + // Parse "[Type][Name]". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code. + line_end[-1] = 0; + const char* name_end = line_end - 1; + const char* type_start = line + 1; + char* type_end = (char*)(intptr_t)ImStrchrRange(type_start, name_end, ']'); + const char* name_start = type_end ? ImStrchrRange(type_end + 1, name_end, '[') : NULL; + if (!type_end || !name_start) + { + name_start = type_start; // Import legacy entries that have no type + type_start = "Window"; + } + else + { + *type_end = 0; // Overwrite first ']' + name_start++; // Skip second '[' + } + entry_handler = FindSettingsHandler(type_start); + entry_data = entry_handler ? entry_handler->ReadOpenFn(&g, entry_handler, name_start) : NULL; + } + else if (entry_handler != NULL && entry_data != NULL) + { + // Let type handler parse the line + entry_handler->ReadLineFn(&g, entry_handler, entry_data, line); + } + } + + ImGui::MemFree(buf); + g.SettingsLoaded = true; +} + +void ImGui::SaveIniSettingsToDisk(const char* ini_filename) +{ + ImGuiContext& g = *GImGui; + g.SettingsDirtyTimer = 0.0f; + if (!ini_filename) + return; + + size_t ini_data_size = 0; + const char* ini_data = SaveIniSettingsToMemory(&ini_data_size); + FILE* f = ImFileOpen(ini_filename, "wt"); + if (!f) + return; + fwrite(ini_data, sizeof(char), ini_data_size, f); + fclose(f); +} + +// Call registered handlers (e.g. SettingsHandlerWindow_WriteAll() + custom handlers) to write their stuff into a text buffer +const char* ImGui::SaveIniSettingsToMemory(size_t* out_size) +{ + ImGuiContext& g = *GImGui; + g.SettingsDirtyTimer = 0.0f; + g.SettingsIniData.Buf.resize(0); + g.SettingsIniData.Buf.push_back(0); + for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++) + { + ImGuiSettingsHandler* handler = &g.SettingsHandlers[handler_n]; + handler->WriteAllFn(&g, handler, &g.SettingsIniData); + } + if (out_size) + *out_size = (size_t)g.SettingsIniData.size(); + return g.SettingsIniData.c_str(); +} + +static void* SettingsHandlerWindow_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name) +{ + ImGuiWindowSettings* settings = ImGui::FindWindowSettings(ImHash(name, 0)); + if (!settings) + settings = CreateNewWindowSettings(name); + return (void*)settings; +} + +static void SettingsHandlerWindow_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line) +{ + ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry; + float x, y; + int i; + ImU32 u1; + if (sscanf(line, "Pos=%f,%f", &x, &y) == 2) { settings->Pos = ImVec2(x, y); } + else if (sscanf(line, "Size=%f,%f", &x, &y) == 2) { settings->Size = ImMax(ImVec2(x, y), GImGui->Style.WindowMinSize); } + else if (sscanf(line, "ViewportId=0x%08X", &u1) == 1) { settings->ViewportId = u1; } + else if (sscanf(line, "ViewportPos=%f,%f", &x, &y) == 2) { settings->ViewportPos = ImVec2(x, y); } + else if (sscanf(line, "Collapsed=%d", &i) == 1) { settings->Collapsed = (i != 0); } +} + +static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf) +{ + // Gather data from windows that were active during this session + ImGuiContext& g = *imgui_ctx; + for (int i = 0; i != g.Windows.Size; i++) + { + ImGuiWindow* window = g.Windows[i]; + if (window->Flags & ImGuiWindowFlags_NoSavedSettings) + continue; + + ImGuiWindowSettings* settings = (window->SettingsIdx != -1) ? &g.SettingsWindows[window->SettingsIdx] : ImGui::FindWindowSettings(window->ID); + if (!settings) + { + settings = CreateNewWindowSettings(window->Name); + window->SettingsIdx = g.SettingsWindows.index_from_pointer(settings); + } + IM_ASSERT(settings->ID == window->ID); + settings->Pos = window->Pos - window->ViewportPos; + settings->Size = window->SizeFull; + settings->ViewportId = window->ViewportId; + settings->ViewportPos = window->ViewportPos; + settings->Collapsed = window->Collapsed; + } + + // Write a buffer + // If a window wasn't opened in this session we preserve its settings + buf->reserve(buf->size() + g.SettingsWindows.Size * 96); // ballpark reserve + for (int i = 0; i != g.SettingsWindows.Size; i++) + { + const ImGuiWindowSettings* settings = &g.SettingsWindows[i]; + const char* name = settings->Name; + if (const char* p = strstr(name, "###")) // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID() + name = p; + buf->appendf("[%s][%s]\n", handler->TypeName, name); + if (settings->ViewportId != 0 && settings->ViewportId != ImGui::IMGUI_VIEWPORT_DEFAULT_ID) + { + buf->appendf("ViewportPos=%d,%d\n", (int)settings->ViewportPos.x, (int)settings->ViewportPos.y); + buf->appendf("ViewportId=0x%08X\n", settings->ViewportId); + } + if (settings->Pos.x != 0.0f || settings->Pos.y != 0.0f || settings->ViewportId == ImGui::IMGUI_VIEWPORT_DEFAULT_ID) + buf->appendf("Pos=%d,%d\n", (int)settings->Pos.x, (int)settings->Pos.y); + buf->appendf("Size=%d,%d\n", (int)settings->Size.x, (int)settings->Size.y); + buf->appendf("Collapsed=%d\n", settings->Collapsed); + buf->appendf("\n"); + } +} + //----------------------------------------------------------------------------- // PLATFORM DEPENDENT HELPERS //----------------------------------------------------------------------------- diff --git a/imgui_internal.h b/imgui_internal.h index cf5163bad94e..ee091ee8ad41 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1204,8 +1204,9 @@ namespace ImGui // Settings IMGUI_API void MarkIniSettingsDirty(); IMGUI_API void MarkIniSettingsDirty(ImGuiWindow* window); - IMGUI_API ImGuiSettingsHandler* FindSettingsHandler(const char* type_name); + IMGUI_API ImGuiWindowSettings* CreateNewWindowSettings(const char* name); IMGUI_API ImGuiWindowSettings* FindWindowSettings(ImGuiID id); + IMGUI_API ImGuiSettingsHandler* FindSettingsHandler(const char* type_name); // Basic Accessors inline ImGuiID GetItemID() { ImGuiContext& g = *GImGui; return g.CurrentWindow->DC.LastItemId; } From adeb9931223aecd7d1bbce2f0a27628a232b2950 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 30 Aug 2018 15:51:14 +0200 Subject: [PATCH 206/828] Refactor: Internals: Moved Logging functions in imgui.cpp in their own section. (#2036) --- imgui.cpp | 358 +++++++++++++++++++++++++++--------------------------- 1 file changed, 181 insertions(+), 177 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 536006ed9598..47f8a0e94345 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4965,74 +4965,6 @@ const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end) return text_display_end; } -// Pass text data straight to log (without being displayed) -void ImGui::LogText(const char* fmt, ...) -{ - ImGuiContext& g = *GImGui; - if (!g.LogEnabled) - return; - - va_list args; - va_start(args, fmt); - if (g.LogFile) - vfprintf(g.LogFile, fmt, args); - else - g.LogClipboard.appendfv(fmt, args); - va_end(args); -} - -// Internal version that takes a position to decide on newline placement and pad items according to their depth. -// We split text into individual lines to add current tree level padding -void ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - if (!text_end) - text_end = ImGui::FindRenderedTextEnd(text, text_end); - - const bool log_new_line = ref_pos && (ref_pos->y > window->DC.LogLinePosY + 1); - if (ref_pos) - window->DC.LogLinePosY = ref_pos->y; - - const char* text_remaining = text; - if (g.LogStartDepth > window->DC.TreeDepth) // Re-adjust padding if we have popped out of our starting depth - g.LogStartDepth = window->DC.TreeDepth; - const int tree_depth = (window->DC.TreeDepth - g.LogStartDepth); - for (;;) - { - // Split the string. Each new line (after a '\n') is followed by spacing corresponding to the current depth of our log entry. - const char* line_end = text_remaining; - while (line_end < text_end) - if (*line_end == '\n') - break; - else - line_end++; - if (line_end >= text_end) - line_end = NULL; - - const bool is_first_line = (text == text_remaining); - bool is_last_line = false; - if (line_end == NULL) - { - is_last_line = true; - line_end = text_end; - } - if (line_end != NULL && !(is_last_line && (line_end - text_remaining)==0)) - { - const int char_count = (int)(line_end - text_remaining); - if (log_new_line || !is_first_line) - ImGui::LogText(IM_NEWLINE "%*s%.*s", tree_depth*4, "", char_count, text_remaining); - else - ImGui::LogText(" %.*s", char_count, text_remaining); - } - - if (is_last_line) - break; - text_remaining = line_end + 1; - } -} - // Internal ImGui functions to render text // RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText() void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash) @@ -8592,114 +8524,6 @@ void ImGui::AlignTextToFramePadding() window->DC.CurrentLineTextBaseOffset = ImMax(window->DC.CurrentLineTextBaseOffset, g.Style.FramePadding.y); } -// Start logging ImGui output to TTY -void ImGui::LogToTTY(int max_depth) -{ - ImGuiContext& g = *GImGui; - if (g.LogEnabled) - return; - ImGuiWindow* window = g.CurrentWindow; - - IM_ASSERT(g.LogFile == NULL); - g.LogFile = stdout; - g.LogEnabled = true; - g.LogStartDepth = window->DC.TreeDepth; - if (max_depth >= 0) - g.LogAutoExpandMaxDepth = max_depth; -} - -// Start logging ImGui output to given file -void ImGui::LogToFile(int max_depth, const char* filename) -{ - ImGuiContext& g = *GImGui; - if (g.LogEnabled) - return; - ImGuiWindow* window = g.CurrentWindow; - - if (!filename) - { - filename = g.IO.LogFilename; - if (!filename) - return; - } - - IM_ASSERT(g.LogFile == NULL); - g.LogFile = ImFileOpen(filename, "ab"); - if (!g.LogFile) - { - IM_ASSERT(g.LogFile != NULL); // Consider this an error - return; - } - g.LogEnabled = true; - g.LogStartDepth = window->DC.TreeDepth; - if (max_depth >= 0) - g.LogAutoExpandMaxDepth = max_depth; -} - -// Start logging ImGui output to clipboard -void ImGui::LogToClipboard(int max_depth) -{ - ImGuiContext& g = *GImGui; - if (g.LogEnabled) - return; - ImGuiWindow* window = g.CurrentWindow; - - IM_ASSERT(g.LogFile == NULL); - g.LogFile = NULL; - g.LogEnabled = true; - g.LogStartDepth = window->DC.TreeDepth; - if (max_depth >= 0) - g.LogAutoExpandMaxDepth = max_depth; -} - -void ImGui::LogFinish() -{ - ImGuiContext& g = *GImGui; - if (!g.LogEnabled) - return; - - LogText(IM_NEWLINE); - if (g.LogFile != NULL) - { - if (g.LogFile == stdout) - fflush(g.LogFile); - else - fclose(g.LogFile); - g.LogFile = NULL; - } - if (g.LogClipboard.size() > 1) - { - SetClipboardText(g.LogClipboard.begin()); - g.LogClipboard.clear(); - } - g.LogEnabled = false; -} - -// Helper to display logging buttons -void ImGui::LogButtons() -{ - ImGuiContext& g = *GImGui; - - PushID("LogButtons"); - const bool log_to_tty = Button("Log To TTY"); SameLine(); - const bool log_to_file = Button("Log To File"); SameLine(); - const bool log_to_clipboard = Button("Log To Clipboard"); SameLine(); - PushItemWidth(80.0f); - PushAllowKeyboardFocus(false); - SliderInt("Depth", &g.LogAutoExpandMaxDepth, 0, 9, NULL); - PopAllowKeyboardFocus(); - PopItemWidth(); - PopID(); - - // Start logging at the end of the function so that the buttons don't appear in the log - if (log_to_tty) - LogToTTY(g.LogAutoExpandMaxDepth); - if (log_to_file) - LogToFile(g.LogAutoExpandMaxDepth, g.IO.LogFilename); - if (log_to_clipboard) - LogToClipboard(g.LogAutoExpandMaxDepth); -} - void ImGui::PushID(const char* str_id) { ImGuiWindow* window = GetCurrentWindowRead(); @@ -8784,7 +8608,7 @@ void ImGui::Separator() window->DrawList->AddLine(bb.Min, ImVec2(bb.Max.x,bb.Min.y), GetColorU32(ImGuiCol_Separator)); if (g.LogEnabled) - LogRenderedText(NULL, IM_NEWLINE "--------------------------------"); + LogRenderedText(NULL, IM_NEWLINE "--------------------------------"); if (window->DC.ColumnsSet) { @@ -9636,6 +9460,186 @@ void ImGui::EndDragDropTarget() g.DragDropWithinSourceOrTarget = false; } +//----------------------------------------------------------------------------- +// LOGGING +//----------------------------------------------------------------------------- + +// Pass text data straight to log (without being displayed) +void ImGui::LogText(const char* fmt, ...) +{ + ImGuiContext& g = *GImGui; + if (!g.LogEnabled) + return; + + va_list args; + va_start(args, fmt); + if (g.LogFile) + vfprintf(g.LogFile, fmt, args); + else + g.LogClipboard.appendfv(fmt, args); + va_end(args); +} + +// Internal version that takes a position to decide on newline placement and pad items according to their depth. +// We split text into individual lines to add current tree level padding +void ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + + if (!text_end) + text_end = FindRenderedTextEnd(text, text_end); + + const bool log_new_line = ref_pos && (ref_pos->y > window->DC.LogLinePosY + 1); + if (ref_pos) + window->DC.LogLinePosY = ref_pos->y; + + const char* text_remaining = text; + if (g.LogStartDepth > window->DC.TreeDepth) // Re-adjust padding if we have popped out of our starting depth + g.LogStartDepth = window->DC.TreeDepth; + const int tree_depth = (window->DC.TreeDepth - g.LogStartDepth); + for (;;) + { + // Split the string. Each new line (after a '\n') is followed by spacing corresponding to the current depth of our log entry. + const char* line_end = text_remaining; + while (line_end < text_end) + if (*line_end == '\n') + break; + else + line_end++; + if (line_end >= text_end) + line_end = NULL; + + const bool is_first_line = (text == text_remaining); + bool is_last_line = false; + if (line_end == NULL) + { + is_last_line = true; + line_end = text_end; + } + if (line_end != NULL && !(is_last_line && (line_end - text_remaining)==0)) + { + const int char_count = (int)(line_end - text_remaining); + if (log_new_line || !is_first_line) + LogText(IM_NEWLINE "%*s%.*s", tree_depth*4, "", char_count, text_remaining); + else + LogText(" %.*s", char_count, text_remaining); + } + + if (is_last_line) + break; + text_remaining = line_end + 1; + } +} + +// Start logging ImGui output to TTY +void ImGui::LogToTTY(int max_depth) +{ + ImGuiContext& g = *GImGui; + if (g.LogEnabled) + return; + ImGuiWindow* window = g.CurrentWindow; + + IM_ASSERT(g.LogFile == NULL); + g.LogFile = stdout; + g.LogEnabled = true; + g.LogStartDepth = window->DC.TreeDepth; + if (max_depth >= 0) + g.LogAutoExpandMaxDepth = max_depth; +} + +// Start logging ImGui output to given file +void ImGui::LogToFile(int max_depth, const char* filename) +{ + ImGuiContext& g = *GImGui; + if (g.LogEnabled) + return; + ImGuiWindow* window = g.CurrentWindow; + + if (!filename) + { + filename = g.IO.LogFilename; + if (!filename) + return; + } + + IM_ASSERT(g.LogFile == NULL); + g.LogFile = ImFileOpen(filename, "ab"); + if (!g.LogFile) + { + IM_ASSERT(g.LogFile != NULL); // Consider this an error + return; + } + g.LogEnabled = true; + g.LogStartDepth = window->DC.TreeDepth; + if (max_depth >= 0) + g.LogAutoExpandMaxDepth = max_depth; +} + +// Start logging ImGui output to clipboard +void ImGui::LogToClipboard(int max_depth) +{ + ImGuiContext& g = *GImGui; + if (g.LogEnabled) + return; + ImGuiWindow* window = g.CurrentWindow; + + IM_ASSERT(g.LogFile == NULL); + g.LogFile = NULL; + g.LogEnabled = true; + g.LogStartDepth = window->DC.TreeDepth; + if (max_depth >= 0) + g.LogAutoExpandMaxDepth = max_depth; +} + +void ImGui::LogFinish() +{ + ImGuiContext& g = *GImGui; + if (!g.LogEnabled) + return; + + LogText(IM_NEWLINE); + if (g.LogFile != NULL) + { + if (g.LogFile == stdout) + fflush(g.LogFile); + else + fclose(g.LogFile); + g.LogFile = NULL; + } + if (g.LogClipboard.size() > 1) + { + SetClipboardText(g.LogClipboard.begin()); + g.LogClipboard.clear(); + } + g.LogEnabled = false; +} + +// Helper to display logging buttons +void ImGui::LogButtons() +{ + ImGuiContext& g = *GImGui; + + PushID("LogButtons"); + const bool log_to_tty = Button("Log To TTY"); SameLine(); + const bool log_to_file = Button("Log To File"); SameLine(); + const bool log_to_clipboard = Button("Log To Clipboard"); SameLine(); + PushItemWidth(80.0f); + PushAllowKeyboardFocus(false); + SliderInt("Depth", &g.LogAutoExpandMaxDepth, 0, 9, NULL); + PopAllowKeyboardFocus(); + PopItemWidth(); + PopID(); + + // Start logging at the end of the function so that the buttons don't appear in the log + if (log_to_tty) + LogToTTY(g.LogAutoExpandMaxDepth); + if (log_to_file) + LogToFile(g.LogAutoExpandMaxDepth, g.IO.LogFilename); + if (log_to_clipboard) + LogToClipboard(g.LogAutoExpandMaxDepth); +} + //----------------------------------------------------------------------------- // SETTINGS //----------------------------------------------------------------------------- From af002dc861f8dc34e19f88f486b4e623b0aff9c8 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 30 Aug 2018 16:11:49 +0200 Subject: [PATCH 207/828] Refactor: Internals: Moved Navigation functions in imgui.cpp in their own section. (part 1) (#2036, #787) --- imgui.cpp | 466 +++++++++++++++++++++++++++--------------------------- 1 file changed, 235 insertions(+), 231 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 47f8a0e94345..f1a9b07d68b2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2335,168 +2335,6 @@ void ImGui::ItemSize(const ImRect& bb, float text_offset_y) ItemSize(bb.GetSize(), text_offset_y); } -ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy) -{ - if (ImFabs(dx) > ImFabs(dy)) - return (dx > 0.0f) ? ImGuiDir_Right : ImGuiDir_Left; - return (dy > 0.0f) ? ImGuiDir_Down : ImGuiDir_Up; -} - -static float inline NavScoreItemDistInterval(float a0, float a1, float b0, float b1) -{ - if (a1 < b0) - return a1 - b0; - if (b1 < a0) - return a0 - b1; - return 0.0f; -} - -static void inline NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir, ImRect& r, const ImRect& clip_rect) -{ - if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right) - { - r.Min.y = ImClamp(r.Min.y, clip_rect.Min.y, clip_rect.Max.y); - r.Max.y = ImClamp(r.Max.y, clip_rect.Min.y, clip_rect.Max.y); - } - else - { - r.Min.x = ImClamp(r.Min.x, clip_rect.Min.x, clip_rect.Max.x); - r.Max.x = ImClamp(r.Max.x, clip_rect.Min.x, clip_rect.Max.x); - } -} - -// Scoring function for directional navigation. Based on https://gist.github.com/rygorous/6981057 -static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - if (g.NavLayer != window->DC.NavLayerCurrent) - return false; - - const ImRect& curr = g.NavScoringRectScreen; // Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having varied item width) - g.NavScoringCount++; - - // When entering through a NavFlattened border, we consider child window items as fully clipped for scoring - if (window->ParentWindow == g.NavWindow) - { - IM_ASSERT((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened); - if (!window->ClipRect.Contains(cand)) - return false; - cand.ClipWithFull(window->ClipRect); // This allows the scored item to not overlap other candidates in the parent window - } - - // We perform scoring on items bounding box clipped by the current clipping rectangle on the other axis (clipping on our movement axis would give us equal scores for all clipped items) - // For example, this ensure that items in one column are not reached when moving vertically from items in another column. - NavClampRectToVisibleAreaForMoveDir(g.NavMoveClipDir, cand, window->ClipRect); - - // Compute distance between boxes - // FIXME-NAV: Introducing biases for vertical navigation, needs to be removed. - float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x); - float dby = NavScoreItemDistInterval(ImLerp(cand.Min.y, cand.Max.y, 0.2f), ImLerp(cand.Min.y, cand.Max.y, 0.8f), ImLerp(curr.Min.y, curr.Max.y, 0.2f), ImLerp(curr.Min.y, curr.Max.y, 0.8f)); // Scale down on Y to keep using box-distance for vertically touching items - if (dby != 0.0f && dbx != 0.0f) - dbx = (dbx/1000.0f) + ((dbx > 0.0f) ? +1.0f : -1.0f); - float dist_box = ImFabs(dbx) + ImFabs(dby); - - // Compute distance between centers (this is off by a factor of 2, but we only compare center distances with each other so it doesn't matter) - float dcx = (cand.Min.x + cand.Max.x) - (curr.Min.x + curr.Max.x); - float dcy = (cand.Min.y + cand.Max.y) - (curr.Min.y + curr.Max.y); - float dist_center = ImFabs(dcx) + ImFabs(dcy); // L1 metric (need this for our connectedness guarantee) - - // Determine which quadrant of 'curr' our candidate item 'cand' lies in based on distance - ImGuiDir quadrant; - float dax = 0.0f, day = 0.0f, dist_axial = 0.0f; - if (dbx != 0.0f || dby != 0.0f) - { - // For non-overlapping boxes, use distance between boxes - dax = dbx; - day = dby; - dist_axial = dist_box; - quadrant = ImGetDirQuadrantFromDelta(dbx, dby); - } - else if (dcx != 0.0f || dcy != 0.0f) - { - // For overlapping boxes with different centers, use distance between centers - dax = dcx; - day = dcy; - dist_axial = dist_center; - quadrant = ImGetDirQuadrantFromDelta(dcx, dcy); - } - else - { - // Degenerate case: two overlapping buttons with same center, break ties arbitrarily (note that LastItemId here is really the _previous_ item order, but it doesn't matter) - quadrant = (window->DC.LastItemId < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right; - } - -#if IMGUI_DEBUG_NAV_SCORING - char buf[128]; - if (ImGui::IsMouseHoveringRect(cand.Min, cand.Max)) - { - ImFormatString(buf, IM_ARRAYSIZE(buf), "dbox (%.2f,%.2f->%.4f)\ndcen (%.2f,%.2f->%.4f)\nd (%.2f,%.2f->%.4f)\nnav %c, quadrant %c", dbx, dby, dist_box, dcx, dcy, dist_center, dax, day, dist_axial, "WENS"[g.NavMoveDir], "WENS"[quadrant]); - ImDrawList* draw_list = ImGui::GetOverlayDrawList(window); - draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255,200,0,100)); - draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255,255,0,200)); - draw_list->AddRectFilled(cand.Max-ImVec2(4,4), cand.Max+ImGui::CalcTextSize(buf)+ImVec2(4,4), IM_COL32(40,0,0,150)); - draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Max, ~0U, buf); - } - else if (g.IO.KeyCtrl) // Hold to preview score in matching quadrant. Press C to rotate. - { - if (IsKeyPressedMap(ImGuiKey_C)) { g.NavMoveDirLast = (ImGuiDir)((g.NavMoveDirLast + 1) & 3); g.IO.KeysDownDuration[g.IO.KeyMap[ImGuiKey_C]] = 0.01f; } - if (quadrant == g.NavMoveDir) - { - ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center); - ImDrawList* draw_list = ImGui::GetOverlayDrawList(window); - draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 200)); - draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Min, IM_COL32(255, 255, 255, 255), buf); - } - } - #endif - - // Is it in the quadrant we're interesting in moving to? - bool new_best = false; - if (quadrant == g.NavMoveDir) - { - // Does it beat the current best candidate? - if (dist_box < result->DistBox) - { - result->DistBox = dist_box; - result->DistCenter = dist_center; - return true; - } - if (dist_box == result->DistBox) - { - // Try using distance between center points to break ties - if (dist_center < result->DistCenter) - { - result->DistCenter = dist_center; - new_best = true; - } - else if (dist_center == result->DistCenter) - { - // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items - // (with higher index) to the right/downwards by an infinitesimal amount since we the current "best" button already (so it must have a lower index), - // this is fairly easy. This rule ensures that all buttons with dx==dy==0 will end up being linked in order of appearance along the x axis. - if (((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance - new_best = true; - } - } - } - - // Axial check: if 'curr' has no link at all in some direction and 'cand' lies roughly in that direction, add a tentative link. This will only be kept if no "real" matches - // are found, so it only augments the graph produced by the above method using extra links. (important, since it doesn't guarantee strong connectedness) - // This is just to avoid buttons having no links in a particular direction when there's a suitable neighbor. you get good graphs without this too. - // 2017/09/29: FIXME: This now currently only enabled inside menu bars, ideally we'd disable it everywhere. Menus in particular need to catch failure. For general navigation it feels awkward. - // Disabling it may lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option? - if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial) // Check axial match - if (g.NavLayer == 1 && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu)) - if ((g.NavMoveDir == ImGuiDir_Left && dax < 0.0f) || (g.NavMoveDir == ImGuiDir_Right && dax > 0.0f) || (g.NavMoveDir == ImGuiDir_Up && day < 0.0f) || (g.NavMoveDir == ImGuiDir_Down && day > 0.0f)) - { - result->DistAxial = dist_axial; - new_best = true; - } - - return new_best; -} - static void NavSaveLastChildNavWindow(ImGuiWindow* child_window) { ImGuiWindow* parent_window = child_window; @@ -2545,75 +2383,6 @@ void ImGui::NavMoveRequestCancel() NavUpdateAnyRequestFlag(); } -// We get there when either NavId == id, or when g.NavAnyRequest is set (which is updated by NavUpdateAnyRequestFlag above) -static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id) -{ - ImGuiContext& g = *GImGui; - //if (!g.IO.NavActive) // [2017/10/06] Removed this possibly redundant test but I am not sure of all the side-effects yet. Some of the feature here will need to work regardless of using a _NoNavInputs flag. - // return; - - const ImGuiItemFlags item_flags = window->DC.ItemFlags; - const ImRect nav_bb_rel(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos); - - // Process Init Request - if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent) - { - // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback - if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus) || g.NavInitResultId == 0) - { - g.NavInitResultId = id; - g.NavInitResultRectRel = nav_bb_rel; - } - if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus)) - { - g.NavInitRequest = false; // Found a match, clear request - NavUpdateAnyRequestFlag(); - } - } - - // Process Move Request (scoring for navigation) - // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRectScreen + scoring from a rect wrapped according to current wrapping policy) - if ((g.NavId != id || (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & ImGuiItemFlags_NoNav)) - { - ImGuiNavMoveResult* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; -#if IMGUI_DEBUG_NAV_SCORING - // [DEBUG] Score all items in NavWindow at all times - if (!g.NavMoveRequest) - g.NavMoveDir = g.NavMoveDirLast; - bool new_best = NavScoreItem(result, nav_bb) && g.NavMoveRequest; -#else - bool new_best = g.NavMoveRequest && NavScoreItem(result, nav_bb); -#endif - if (new_best) - { - result->ID = id; - result->Window = window; - result->RectRel = nav_bb_rel; - } - - const float VISIBLE_RATIO = 0.70f; - if ((g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb)) - if (ImClamp(nav_bb.Max.y, window->ClipRect.Min.y, window->ClipRect.Max.y) - ImClamp(nav_bb.Min.y, window->ClipRect.Min.y, window->ClipRect.Max.y) >= (nav_bb.Max.y - nav_bb.Min.y) * VISIBLE_RATIO) - if (NavScoreItem(&g.NavMoveResultLocalVisibleSet, nav_bb)) - { - result = &g.NavMoveResultLocalVisibleSet; - result->ID = id; - result->Window = window; - result->RectRel = nav_bb_rel; - } - } - - // Update window-relative bounding box of navigated item - if (g.NavId == id) - { - g.NavWindow = window; // Always refresh g.NavWindow, because some operations such as FocusItem() don't have a window. - g.NavLayer = window->DC.NavLayerCurrent; - g.NavIdIsAlive = true; - g.NavIdTabCounter = window->FocusIdxTabCounter; - window->NavRectRel[window->DC.NavLayerCurrent] = nav_bb_rel; // Store item bounding box (relative to window position) - } -} - // Declare item bounding box for clipping and interaction. // Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface // declare their minimum size requirement to ItemSize() and then use a larger region for drawing/interaction, which is passed to ItemAdd(). @@ -8851,6 +8620,241 @@ void ImGui::Unindent(float indent_w) window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x; } +//----------------------------------------------------------------------------- +// NAVIGATION +//----------------------------------------------------------------------------- + +ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy) +{ + if (ImFabs(dx) > ImFabs(dy)) + return (dx > 0.0f) ? ImGuiDir_Right : ImGuiDir_Left; + return (dy > 0.0f) ? ImGuiDir_Down : ImGuiDir_Up; +} + +static float inline NavScoreItemDistInterval(float a0, float a1, float b0, float b1) +{ + if (a1 < b0) + return a1 - b0; + if (b1 < a0) + return a0 - b1; + return 0.0f; +} + +static void inline NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir, ImRect& r, const ImRect& clip_rect) +{ + if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right) + { + r.Min.y = ImClamp(r.Min.y, clip_rect.Min.y, clip_rect.Max.y); + r.Max.y = ImClamp(r.Max.y, clip_rect.Min.y, clip_rect.Max.y); + } + else + { + r.Min.x = ImClamp(r.Min.x, clip_rect.Min.x, clip_rect.Max.x); + r.Max.x = ImClamp(r.Max.x, clip_rect.Min.x, clip_rect.Max.x); + } +} + +// Scoring function for directional navigation. Based on https://gist.github.com/rygorous/6981057 +static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + if (g.NavLayer != window->DC.NavLayerCurrent) + return false; + + const ImRect& curr = g.NavScoringRectScreen; // Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having varied item width) + g.NavScoringCount++; + + // When entering through a NavFlattened border, we consider child window items as fully clipped for scoring + if (window->ParentWindow == g.NavWindow) + { + IM_ASSERT((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened); + if (!window->ClipRect.Contains(cand)) + return false; + cand.ClipWithFull(window->ClipRect); // This allows the scored item to not overlap other candidates in the parent window + } + + // We perform scoring on items bounding box clipped by the current clipping rectangle on the other axis (clipping on our movement axis would give us equal scores for all clipped items) + // For example, this ensure that items in one column are not reached when moving vertically from items in another column. + NavClampRectToVisibleAreaForMoveDir(g.NavMoveClipDir, cand, window->ClipRect); + + // Compute distance between boxes + // FIXME-NAV: Introducing biases for vertical navigation, needs to be removed. + float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x); + float dby = NavScoreItemDistInterval(ImLerp(cand.Min.y, cand.Max.y, 0.2f), ImLerp(cand.Min.y, cand.Max.y, 0.8f), ImLerp(curr.Min.y, curr.Max.y, 0.2f), ImLerp(curr.Min.y, curr.Max.y, 0.8f)); // Scale down on Y to keep using box-distance for vertically touching items + if (dby != 0.0f && dbx != 0.0f) + dbx = (dbx/1000.0f) + ((dbx > 0.0f) ? +1.0f : -1.0f); + float dist_box = ImFabs(dbx) + ImFabs(dby); + + // Compute distance between centers (this is off by a factor of 2, but we only compare center distances with each other so it doesn't matter) + float dcx = (cand.Min.x + cand.Max.x) - (curr.Min.x + curr.Max.x); + float dcy = (cand.Min.y + cand.Max.y) - (curr.Min.y + curr.Max.y); + float dist_center = ImFabs(dcx) + ImFabs(dcy); // L1 metric (need this for our connectedness guarantee) + + // Determine which quadrant of 'curr' our candidate item 'cand' lies in based on distance + ImGuiDir quadrant; + float dax = 0.0f, day = 0.0f, dist_axial = 0.0f; + if (dbx != 0.0f || dby != 0.0f) + { + // For non-overlapping boxes, use distance between boxes + dax = dbx; + day = dby; + dist_axial = dist_box; + quadrant = ImGetDirQuadrantFromDelta(dbx, dby); + } + else if (dcx != 0.0f || dcy != 0.0f) + { + // For overlapping boxes with different centers, use distance between centers + dax = dcx; + day = dcy; + dist_axial = dist_center; + quadrant = ImGetDirQuadrantFromDelta(dcx, dcy); + } + else + { + // Degenerate case: two overlapping buttons with same center, break ties arbitrarily (note that LastItemId here is really the _previous_ item order, but it doesn't matter) + quadrant = (window->DC.LastItemId < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right; + } + +#if IMGUI_DEBUG_NAV_SCORING + char buf[128]; + if (ImGui::IsMouseHoveringRect(cand.Min, cand.Max)) + { + ImFormatString(buf, IM_ARRAYSIZE(buf), "dbox (%.2f,%.2f->%.4f)\ndcen (%.2f,%.2f->%.4f)\nd (%.2f,%.2f->%.4f)\nnav %c, quadrant %c", dbx, dby, dist_box, dcx, dcy, dist_center, dax, day, dist_axial, "WENS"[g.NavMoveDir], "WENS"[quadrant]); + ImDrawList* draw_list = ImGui::GetOverlayDrawList(window); + draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255,200,0,100)); + draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255,255,0,200)); + draw_list->AddRectFilled(cand.Max-ImVec2(4,4), cand.Max+ImGui::CalcTextSize(buf)+ImVec2(4,4), IM_COL32(40,0,0,150)); + draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Max, ~0U, buf); + } + else if (g.IO.KeyCtrl) // Hold to preview score in matching quadrant. Press C to rotate. + { + if (IsKeyPressedMap(ImGuiKey_C)) { g.NavMoveDirLast = (ImGuiDir)((g.NavMoveDirLast + 1) & 3); g.IO.KeysDownDuration[g.IO.KeyMap[ImGuiKey_C]] = 0.01f; } + if (quadrant == g.NavMoveDir) + { + ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center); + ImDrawList* draw_list = ImGui::GetOverlayDrawList(window); + draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 200)); + draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Min, IM_COL32(255, 255, 255, 255), buf); + } + } + #endif + + // Is it in the quadrant we're interesting in moving to? + bool new_best = false; + if (quadrant == g.NavMoveDir) + { + // Does it beat the current best candidate? + if (dist_box < result->DistBox) + { + result->DistBox = dist_box; + result->DistCenter = dist_center; + return true; + } + if (dist_box == result->DistBox) + { + // Try using distance between center points to break ties + if (dist_center < result->DistCenter) + { + result->DistCenter = dist_center; + new_best = true; + } + else if (dist_center == result->DistCenter) + { + // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items + // (with higher index) to the right/downwards by an infinitesimal amount since we the current "best" button already (so it must have a lower index), + // this is fairly easy. This rule ensures that all buttons with dx==dy==0 will end up being linked in order of appearance along the x axis. + if (((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance + new_best = true; + } + } + } + + // Axial check: if 'curr' has no link at all in some direction and 'cand' lies roughly in that direction, add a tentative link. This will only be kept if no "real" matches + // are found, so it only augments the graph produced by the above method using extra links. (important, since it doesn't guarantee strong connectedness) + // This is just to avoid buttons having no links in a particular direction when there's a suitable neighbor. you get good graphs without this too. + // 2017/09/29: FIXME: This now currently only enabled inside menu bars, ideally we'd disable it everywhere. Menus in particular need to catch failure. For general navigation it feels awkward. + // Disabling it may lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option? + if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial) // Check axial match + if (g.NavLayer == 1 && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu)) + if ((g.NavMoveDir == ImGuiDir_Left && dax < 0.0f) || (g.NavMoveDir == ImGuiDir_Right && dax > 0.0f) || (g.NavMoveDir == ImGuiDir_Up && day < 0.0f) || (g.NavMoveDir == ImGuiDir_Down && day > 0.0f)) + { + result->DistAxial = dist_axial; + new_best = true; + } + + return new_best; +} + +// We get there when either NavId == id, or when g.NavAnyRequest is set (which is updated by NavUpdateAnyRequestFlag above) +static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id) +{ + ImGuiContext& g = *GImGui; + //if (!g.IO.NavActive) // [2017/10/06] Removed this possibly redundant test but I am not sure of all the side-effects yet. Some of the feature here will need to work regardless of using a _NoNavInputs flag. + // return; + + const ImGuiItemFlags item_flags = window->DC.ItemFlags; + const ImRect nav_bb_rel(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos); + + // Process Init Request + if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent) + { + // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback + if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus) || g.NavInitResultId == 0) + { + g.NavInitResultId = id; + g.NavInitResultRectRel = nav_bb_rel; + } + if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus)) + { + g.NavInitRequest = false; // Found a match, clear request + NavUpdateAnyRequestFlag(); + } + } + + // Process Move Request (scoring for navigation) + // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRectScreen + scoring from a rect wrapped according to current wrapping policy) + if ((g.NavId != id || (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & ImGuiItemFlags_NoNav)) + { + ImGuiNavMoveResult* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; +#if IMGUI_DEBUG_NAV_SCORING + // [DEBUG] Score all items in NavWindow at all times + if (!g.NavMoveRequest) + g.NavMoveDir = g.NavMoveDirLast; + bool new_best = NavScoreItem(result, nav_bb) && g.NavMoveRequest; +#else + bool new_best = g.NavMoveRequest && NavScoreItem(result, nav_bb); +#endif + if (new_best) + { + result->ID = id; + result->Window = window; + result->RectRel = nav_bb_rel; + } + + const float VISIBLE_RATIO = 0.70f; + if ((g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb)) + if (ImClamp(nav_bb.Max.y, window->ClipRect.Min.y, window->ClipRect.Max.y) - ImClamp(nav_bb.Min.y, window->ClipRect.Min.y, window->ClipRect.Max.y) >= (nav_bb.Max.y - nav_bb.Min.y) * VISIBLE_RATIO) + if (NavScoreItem(&g.NavMoveResultLocalVisibleSet, nav_bb)) + { + result = &g.NavMoveResultLocalVisibleSet; + result->ID = id; + result->Window = window; + result->RectRel = nav_bb_rel; + } + } + + // Update window-relative bounding box of navigated item + if (g.NavId == id) + { + g.NavWindow = window; // Always refresh g.NavWindow, because some operations such as FocusItem() don't have a window. + g.NavLayer = window->DC.NavLayerCurrent; + g.NavIdIsAlive = true; + g.NavIdTabCounter = window->FocusIdxTabCounter; + window->NavRectRel[window->DC.NavLayerCurrent] = nav_bb_rel; // Store item bounding box (relative to window position) + } +} + //----------------------------------------------------------------------------- // COLUMNS //----------------------------------------------------------------------------- From 9a4234ea8e459185aeb61e5dc484a3a308c8788e Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 30 Aug 2018 16:18:13 +0200 Subject: [PATCH 208/828] Refactor: Internals: Moved Navigation functions in imgui.cpp in their own section. (part 2) (#2036, #787) --- imgui.cpp | 120 +++++++++++++++++++++++++++--------------------------- 1 file changed, 60 insertions(+), 60 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index f1a9b07d68b2..2cf5d96d04ec 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2370,19 +2370,6 @@ static inline void NavUpdateAnyRequestFlag() IM_ASSERT(g.NavWindow != NULL); } -bool ImGui::NavMoveRequestButNoResultYet() -{ - ImGuiContext& g = *GImGui; - return g.NavMoveRequest && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0; -} - -void ImGui::NavMoveRequestCancel() -{ - ImGuiContext& g = *GImGui; - g.NavMoveRequest = false; - NavUpdateAnyRequestFlag(); -} - // Declare item bounding box for clipping and interaction. // Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface // declare their minimum size requirement to ItemSize() and then use a larger region for drawing/interaction, which is passed to ItemAdd(). @@ -5610,53 +5597,6 @@ bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags fla return is_open; } -void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, const ImRect& bb_rel, ImGuiNavMoveFlags move_flags) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_None); - ImGui::NavMoveRequestCancel(); - g.NavMoveDir = move_dir; - g.NavMoveClipDir = clip_dir; - g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued; - g.NavMoveRequestFlags = move_flags; - g.NavWindow->NavRectRel[g.NavLayer] = bb_rel; -} - -void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags) -{ - ImGuiContext& g = *GImGui; - if (g.NavWindow != window || !NavMoveRequestButNoResultYet() || g.NavMoveRequestForward != ImGuiNavForward_None || g.NavLayer != 0) - return; - IM_ASSERT(move_flags != 0); // No points calling this with no wrapping - ImRect bb_rel = window->NavRectRel[0]; - - ImGuiDir clip_dir = g.NavMoveDir; - if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX))) - { - bb_rel.Min.x = bb_rel.Max.x = ImMax(window->SizeFull.x, window->SizeContents.x) - window->Scroll.x; - if (move_flags & ImGuiNavMoveFlags_WrapX) { bb_rel.TranslateY(-bb_rel.GetHeight()); clip_dir = ImGuiDir_Up; } - NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags); - } - if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX))) - { - bb_rel.Min.x = bb_rel.Max.x = -window->Scroll.x; - if (move_flags & ImGuiNavMoveFlags_WrapX) { bb_rel.TranslateY(+bb_rel.GetHeight()); clip_dir = ImGuiDir_Down; } - NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags); - } - if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY))) - { - bb_rel.Min.y = bb_rel.Max.y = ImMax(window->SizeFull.y, window->SizeContents.y) - window->Scroll.y; - if (move_flags & ImGuiNavMoveFlags_WrapY) { bb_rel.TranslateX(-bb_rel.GetWidth()); clip_dir = ImGuiDir_Left; } - NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags); - } - if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY))) - { - bb_rel.Min.y = bb_rel.Max.y = -window->Scroll.y; - if (move_flags & ImGuiNavMoveFlags_WrapY) { bb_rel.TranslateX(+bb_rel.GetWidth()); clip_dir = ImGuiDir_Right; } - NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags); - } -} - void ImGui::EndPopup() { ImGuiContext& g = *GImGui; (void)g; @@ -8855,6 +8795,66 @@ static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, con } } +bool ImGui::NavMoveRequestButNoResultYet() +{ + ImGuiContext& g = *GImGui; + return g.NavMoveRequest && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0; +} + +void ImGui::NavMoveRequestCancel() +{ + ImGuiContext& g = *GImGui; + g.NavMoveRequest = false; + NavUpdateAnyRequestFlag(); +} + +void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, const ImRect& bb_rel, ImGuiNavMoveFlags move_flags) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_None); + ImGui::NavMoveRequestCancel(); + g.NavMoveDir = move_dir; + g.NavMoveClipDir = clip_dir; + g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued; + g.NavMoveRequestFlags = move_flags; + g.NavWindow->NavRectRel[g.NavLayer] = bb_rel; +} + +void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags) +{ + ImGuiContext& g = *GImGui; + if (g.NavWindow != window || !NavMoveRequestButNoResultYet() || g.NavMoveRequestForward != ImGuiNavForward_None || g.NavLayer != 0) + return; + IM_ASSERT(move_flags != 0); // No points calling this with no wrapping + ImRect bb_rel = window->NavRectRel[0]; + + ImGuiDir clip_dir = g.NavMoveDir; + if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX))) + { + bb_rel.Min.x = bb_rel.Max.x = ImMax(window->SizeFull.x, window->SizeContents.x) - window->Scroll.x; + if (move_flags & ImGuiNavMoveFlags_WrapX) { bb_rel.TranslateY(-bb_rel.GetHeight()); clip_dir = ImGuiDir_Up; } + NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags); + } + if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX))) + { + bb_rel.Min.x = bb_rel.Max.x = -window->Scroll.x; + if (move_flags & ImGuiNavMoveFlags_WrapX) { bb_rel.TranslateY(+bb_rel.GetHeight()); clip_dir = ImGuiDir_Down; } + NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags); + } + if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY))) + { + bb_rel.Min.y = bb_rel.Max.y = ImMax(window->SizeFull.y, window->SizeContents.y) - window->Scroll.y; + if (move_flags & ImGuiNavMoveFlags_WrapY) { bb_rel.TranslateX(-bb_rel.GetWidth()); clip_dir = ImGuiDir_Left; } + NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags); + } + if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY))) + { + bb_rel.Min.y = bb_rel.Max.y = -window->Scroll.y; + if (move_flags & ImGuiNavMoveFlags_WrapY) { bb_rel.TranslateX(+bb_rel.GetWidth()); clip_dir = ImGuiDir_Right; } + NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags); + } +} + //----------------------------------------------------------------------------- // COLUMNS //----------------------------------------------------------------------------- From f5ed5478e1a462b7ac33e2c9688c54c6e8ac6379 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 30 Aug 2018 18:01:19 +0200 Subject: [PATCH 209/828] Refactor: Internals: Moved Navigation functions in imgui.cpp in their own section (extracted some code out of NavUpdate()). (part 3) (#2036, #787) --- imgui.cpp | 172 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 96 insertions(+), 76 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 2cf5d96d04ec..9df1e536bae2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -894,7 +894,11 @@ static bool BeginChildEx(const char* name, ImGuiID id, const ImVec2& static void NavUpdate(); static void NavUpdateWindowing(); static void NavUpdateWindowingList(); +static void NavUpdateMoveResult(); +static float NavUpdatePageUpPageDown(int allowed_dir_flags); +static inline void NavUpdateAnyRequestFlag(); static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id); +static ImVec2 NavCalcPreferredRefPos(); static void UpdateMouseInputs(); static void UpdateMouseWheel(); @@ -2362,7 +2366,7 @@ static void NavRestoreLayer(int layer) ImGui::NavInitWindow(g.NavWindow, true); } -static inline void NavUpdateAnyRequestFlag() +static inline void ImGui::NavUpdateAnyRequestFlag() { ImGuiContext& g = *GImGui; g.NavAnyRequest = g.NavMoveRequest || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL); @@ -2723,7 +2727,7 @@ void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit) } } -static ImVec2 NavCalcPreferredRefPos() +static ImVec2 ImGui::NavCalcPreferredRefPos() { ImGuiContext& g = *GImGui; if (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow) @@ -3073,44 +3077,7 @@ static void ImGui::NavUpdate() // Process navigation move request if (g.NavMoveRequest && (g.NavMoveResultLocal.ID != 0 || g.NavMoveResultOther.ID != 0)) - { - // Select which result to use - ImGuiNavMoveResult* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; - - // PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page. - if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) - if (g.NavMoveResultLocalVisibleSet.ID != 0 && g.NavMoveResultLocalVisibleSet.ID != g.NavId) - result = &g.NavMoveResultLocalVisibleSet; - - // Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules. - if (result != &g.NavMoveResultOther && g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow) - if ((g.NavMoveResultOther.DistBox < result->DistBox) || (g.NavMoveResultOther.DistBox == result->DistBox && g.NavMoveResultOther.DistCenter < result->DistCenter)) - result = &g.NavMoveResultOther; - IM_ASSERT(g.NavWindow && result->Window); - - // Scroll to keep newly navigated item fully into view. - if (g.NavLayer == 0) - { - ImRect rect_abs = ImRect(result->RectRel.Min + result->Window->Pos, result->RectRel.Max + result->Window->Pos); - NavScrollToBringItemIntoView(result->Window, rect_abs); - - // Estimate upcoming scroll so we can offset our result position so mouse position can be applied immediately after in NavUpdate() - ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(result->Window, false); - ImVec2 delta_scroll = result->Window->Scroll - next_scroll; - result->RectRel.Translate(delta_scroll); - - // Also scroll parent window to keep us into view if necessary (we could/should technically recurse back the whole the parent hierarchy). - if (result->Window->Flags & ImGuiWindowFlags_ChildWindow) - NavScrollToBringItemIntoView(result->Window->ParentWindow, ImRect(rect_abs.Min + delta_scroll, rect_abs.Max + delta_scroll)); - } - - // Apply result from previous frame navigation directional move request - ClearActiveID(); - g.NavWindow = result->Window; - SetNavIDWithRectRel(result->ID, g.NavLayer, result->RectRel); - g.NavJustMovedToId = result->ID; - g.NavMoveFromClampedRefRect = false; - } + NavUpdateMoveResult(); // When a forwarded move request failed, we restore the highlight that we disabled during the forward frame if (g.NavMoveRequestForward == ImGuiNavForward_ForwardActive) @@ -3242,42 +3209,8 @@ static void ImGui::NavUpdate() // PageUp/PageDown scroll float nav_scoring_rect_offset_y = 0.0f; - if (nav_keyboard_active && g.NavMoveDir == ImGuiDir_None && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget && g.NavLayer == 0) - { - ImGuiWindow* window = g.NavWindow; - bool page_up_held = IsKeyDown(g.IO.KeyMap[ImGuiKey_PageUp]) && (allowed_dir_flags & (1 << ImGuiDir_Up)); - bool page_down_held = IsKeyDown(g.IO.KeyMap[ImGuiKey_PageDown]) && (allowed_dir_flags & (1 << ImGuiDir_Down)); - if ((page_up_held && !page_down_held) || (page_down_held && !page_up_held)) - { - if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll) - { - // Fallback manual-scroll when window has no navigable item - if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageUp], true)) - SetWindowScrollY(window, window->Scroll.y - window->InnerClipRect.GetHeight()); - else if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageDown], true)) - SetWindowScrollY(window, window->Scroll.y + window->InnerClipRect.GetHeight()); - } - else - { - const ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer]; - const float page_offset_y = ImMax(0.0f, window->InnerClipRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight()); - if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageUp], true)) - { - nav_scoring_rect_offset_y = -page_offset_y; - g.NavMoveDir = ImGuiDir_Down; // Because our scoring rect is offset, we intentionally request the opposite direction (so we can always land on the last item) - g.NavMoveClipDir = ImGuiDir_Up; - g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet; - } - else if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageDown], true)) - { - nav_scoring_rect_offset_y = +page_offset_y; - g.NavMoveDir = ImGuiDir_Up; // Because our scoring rect is offset, we intentionally request the opposite direction (so we can always land on the last item) - g.NavMoveClipDir = ImGuiDir_Down; - g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet; - } - } - } - } + if (nav_keyboard_active) + nav_scoring_rect_offset_y = NavUpdatePageUpPageDown(allowed_dir_flags); if (g.NavMoveDir != ImGuiDir_None) { @@ -8855,6 +8788,93 @@ void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags mov } } +// + +static void ImGui::NavUpdateMoveResult() +{ + // Select which result to use + ImGuiContext& g = *GImGui; + ImGuiNavMoveResult* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; + + // PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page. + if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) + if (g.NavMoveResultLocalVisibleSet.ID != 0 && g.NavMoveResultLocalVisibleSet.ID != g.NavId) + result = &g.NavMoveResultLocalVisibleSet; + + // Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules. + if (result != &g.NavMoveResultOther && g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow) + if ((g.NavMoveResultOther.DistBox < result->DistBox) || (g.NavMoveResultOther.DistBox == result->DistBox && g.NavMoveResultOther.DistCenter < result->DistCenter)) + result = &g.NavMoveResultOther; + IM_ASSERT(g.NavWindow && result->Window); + + // Scroll to keep newly navigated item fully into view. + if (g.NavLayer == 0) + { + ImRect rect_abs = ImRect(result->RectRel.Min + result->Window->Pos, result->RectRel.Max + result->Window->Pos); + NavScrollToBringItemIntoView(result->Window, rect_abs); + + // Estimate upcoming scroll so we can offset our result position so mouse position can be applied immediately after in NavUpdate() + ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(result->Window, false); + ImVec2 delta_scroll = result->Window->Scroll - next_scroll; + result->RectRel.Translate(delta_scroll); + + // Also scroll parent window to keep us into view if necessary (we could/should technically recurse back the whole the parent hierarchy). + if (result->Window->Flags & ImGuiWindowFlags_ChildWindow) + NavScrollToBringItemIntoView(result->Window->ParentWindow, ImRect(rect_abs.Min + delta_scroll, rect_abs.Max + delta_scroll)); + } + + // Apply result from previous frame navigation directional move request + ClearActiveID(); + g.NavWindow = result->Window; + SetNavIDWithRectRel(result->ID, g.NavLayer, result->RectRel); + g.NavJustMovedToId = result->ID; + g.NavMoveFromClampedRefRect = false; +} + +static float ImGui::NavUpdatePageUpPageDown(int allowed_dir_flags) +{ + ImGuiContext& g = *GImGui; + if (g.NavMoveDir == ImGuiDir_None && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget && g.NavLayer == 0) + { + ImGuiWindow* window = g.NavWindow; + bool page_up_held = IsKeyDown(g.IO.KeyMap[ImGuiKey_PageUp]) && (allowed_dir_flags & (1 << ImGuiDir_Up)); + bool page_down_held = IsKeyDown(g.IO.KeyMap[ImGuiKey_PageDown]) && (allowed_dir_flags & (1 << ImGuiDir_Down)); + if ((page_up_held && !page_down_held) || (page_down_held && !page_up_held)) + { + if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll) + { + // Fallback manual-scroll when window has no navigable item + if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageUp], true)) + SetWindowScrollY(window, window->Scroll.y - window->InnerClipRect.GetHeight()); + else if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageDown], true)) + SetWindowScrollY(window, window->Scroll.y + window->InnerClipRect.GetHeight()); + } + else + { + const ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer]; + const float page_offset_y = ImMax(0.0f, window->InnerClipRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight()); + float nav_scoring_rect_offset_y = 0.0f; + if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageUp], true)) + { + nav_scoring_rect_offset_y = -page_offset_y; + g.NavMoveDir = ImGuiDir_Down; // Because our scoring rect is offset, we intentionally request the opposite direction (so we can always land on the last item) + g.NavMoveClipDir = ImGuiDir_Up; + g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet; + } + else if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageDown], true)) + { + nav_scoring_rect_offset_y = +page_offset_y; + g.NavMoveDir = ImGuiDir_Up; // Because our scoring rect is offset, we intentionally request the opposite direction (so we can always land on the last item) + g.NavMoveClipDir = ImGuiDir_Down; + g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet; + } + return nav_scoring_rect_offset_y; + } + } + } + return 0.0f; +} + //----------------------------------------------------------------------------- // COLUMNS //----------------------------------------------------------------------------- From 9093166eeac556424a56f489ee9f98891297e3bb Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 30 Aug 2018 18:04:36 +0200 Subject: [PATCH 210/828] Refactor: Internals: Removing new lines from NavUpdate solely because I couldn't find another way to get a neat diff/patch when moving it. (part 4) (#2036, #787) --- imgui.cpp | 31 ++++--------------------------- 1 file changed, 4 insertions(+), 27 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 9df1e536bae2..b59edfb448fc 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3025,19 +3025,15 @@ static void ImGui::NavUpdate() { ImGuiContext& g = *GImGui; g.IO.WantSetMousePos = false; - #if 0 if (g.NavScoringCount > 0) printf("[%05d] NavScoringCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.FrameCount, g.NavScoringCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest); #endif - + // Set input source as Gamepad when buttons are pressed before we map Keyboard (some features differs when used with Gamepad vs Keyboard) bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (g.IO.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; - - // Set input source as Gamepad when buttons are pressed before we map Keyboard (some features differs when used with Gamepad vs Keyboard) if (nav_gamepad_active) if (g.IO.NavInputs[ImGuiNavInput_Activate] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Input] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Cancel] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Menu] > 0.0f) g.NavInputSource = ImGuiInputSource_NavGamepad; - // Update Keyboard->Nav inputs mapping if (nav_keyboard_active) { @@ -3054,11 +3050,9 @@ static void ImGui::NavUpdate() if (g.IO.KeyAlt) g.IO.NavInputs[ImGuiNavInput_KeyMenu_] = 1.0f; #undef NAV_MAP_KEY } - memcpy(g.IO.NavInputsDownDurationPrev, g.IO.NavInputsDownDuration, sizeof(g.IO.NavInputsDownDuration)); for (int i = 0; i < IM_ARRAYSIZE(g.IO.NavInputs); i++) g.IO.NavInputsDownDuration[i] = (g.IO.NavInputs[i] > 0.0f) ? (g.IO.NavInputsDownDuration[i] < 0.0f ? 0.0f : g.IO.NavInputsDownDuration[i] + g.IO.DeltaTime) : -1.0f; - // Process navigation init request (select first/default focus) if (g.NavInitResultId != 0 && (!g.NavDisableHighlight || g.NavInitRequestFromMove)) { @@ -3074,11 +3068,9 @@ static void ImGui::NavUpdate() g.NavInitRequestFromMove = false; g.NavInitResultId = 0; g.NavJustMovedToId = 0; - // Process navigation move request if (g.NavMoveRequest && (g.NavMoveResultLocal.ID != 0 || g.NavMoveResultOther.ID != 0)) NavUpdateMoveResult(); - // When a forwarded move request failed, we restore the highlight that we disabled during the forward frame if (g.NavMoveRequestForward == ImGuiNavForward_ForwardActive) { @@ -3087,7 +3079,6 @@ static void ImGui::NavUpdate() g.NavDisableHighlight = false; g.NavMoveRequestForward = ImGuiNavForward_None; } - // Apply application mouse position movement, after we had a chance to process move request result. if (g.NavMousePosDirty && g.NavIdIsAlive) { @@ -3105,19 +3096,16 @@ static void ImGui::NavUpdate() g.NavIdIsAlive = false; g.NavJustTabbedId = 0; IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1); - // Store our return window (for returning from Layer 1 to Layer 0) and clear it as soon as we step back in our own Layer 0 if (g.NavWindow) NavSaveLastChildNavWindow(g.NavWindow); if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == 0) g.NavWindow->NavLastChildNavWindow = NULL; - + // Update CTRL+TAB and Windowing features (hold Square to move/resize/etc.) NavUpdateWindowing(); - // Set output flags for user application g.IO.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs); g.IO.NavVisible = (g.IO.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL) || g.NavInitRequest; - // Process NavCancel input (to close a popup, get back to parent, clear focus) if (IsNavInputPressed(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed)) { @@ -3156,7 +3144,6 @@ static void ImGui::NavUpdate() g.NavId = 0; } } - // Process manual activation request g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = 0; if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) @@ -3177,12 +3164,10 @@ static void ImGui::NavUpdate() if (g.NavActivateId != 0) IM_ASSERT(g.NavActivateDownId == g.NavActivateId); g.NavMoveRequest = false; - // Process programmatic activation request if (g.NavNextActivateId != 0) g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = g.NavNextActivateId; g.NavNextActivateId = 0; - // Initiate directional inputs request const int allowed_dir_flags = (g.ActiveId == 0) ? ~0 : g.ActiveIdAllowNavDirFlags; if (g.NavMoveRequestForward == ImGuiNavForward_None) @@ -3206,28 +3191,23 @@ static void ImGui::NavUpdate() IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_ForwardQueued); g.NavMoveRequestForward = ImGuiNavForward_ForwardActive; } - - // PageUp/PageDown scroll + // Update PageUp/PageDown scroll float nav_scoring_rect_offset_y = 0.0f; if (nav_keyboard_active) nav_scoring_rect_offset_y = NavUpdatePageUpPageDown(allowed_dir_flags); - + // If we initiate a movement request and have no current NavId, we initiate a InitDefautRequest that will be used as a fallback if the direction fails to find a match if (g.NavMoveDir != ImGuiDir_None) { g.NavMoveRequest = true; g.NavMoveDirLast = g.NavMoveDir; } - - // If we initiate a movement request and have no current NavId, we initiate a InitDefautRequest that will be used as a fallback if the direction fails to find a match if (g.NavMoveRequest && g.NavId == 0) { g.NavInitRequest = g.NavInitRequestFromMove = true; g.NavInitResultId = 0; g.NavDisableHighlight = false; } - NavUpdateAnyRequestFlag(); - // Scrolling if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget) { @@ -3256,12 +3236,10 @@ static void ImGui::NavUpdate() g.NavMoveFromClampedRefRect = true; } } - // Reset search results g.NavMoveResultLocal.Clear(); g.NavMoveResultLocalVisibleSet.Clear(); g.NavMoveResultOther.Clear(); - // When we have manually scrolled (without using navigation) and NavId becomes out of bounds, we project its bounding box to the visible area to restart navigation within visible items if (g.NavMoveRequest && g.NavMoveFromClampedRefRect && g.NavLayer == 0) { @@ -3276,7 +3254,6 @@ static void ImGui::NavUpdate() } g.NavMoveFromClampedRefRect = false; } - // For scoring we use a single segment on the left side our current item bounding box (not touching the edge to avoid box overlap with zero-spaced items) ImRect nav_rect_rel = (g.NavWindow && !g.NavWindow->NavRectRel[g.NavLayer].IsInverted()) ? g.NavWindow->NavRectRel[g.NavLayer] : ImRect(0,0,0,0); g.NavScoringRectScreen = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : ImRect(0,0,0,0); From f74d9ec7f72a6872188a06d18702b2db683f2377 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 30 Aug 2018 18:10:15 +0200 Subject: [PATCH 211/828] Refactor: Internals: Moved Navigation functions in imgui.cpp in their own section (moved NavUpdate which would not diff properly unless empty lines were removed, hence the previous patch). (part 5) (#2036, #787) --- imgui.cpp | 496 +++++++++++++++++++++++++++--------------------------- 1 file changed, 247 insertions(+), 249 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index b59edfb448fc..ed1c6560a010 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3021,254 +3021,6 @@ static void NavScrollToBringItemIntoView(ImGuiWindow* window, const ImRect& item } } -static void ImGui::NavUpdate() -{ - ImGuiContext& g = *GImGui; - g.IO.WantSetMousePos = false; -#if 0 - if (g.NavScoringCount > 0) printf("[%05d] NavScoringCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.FrameCount, g.NavScoringCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest); -#endif - // Set input source as Gamepad when buttons are pressed before we map Keyboard (some features differs when used with Gamepad vs Keyboard) - bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; - bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (g.IO.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; - if (nav_gamepad_active) - if (g.IO.NavInputs[ImGuiNavInput_Activate] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Input] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Cancel] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Menu] > 0.0f) - g.NavInputSource = ImGuiInputSource_NavGamepad; - // Update Keyboard->Nav inputs mapping - if (nav_keyboard_active) - { - #define NAV_MAP_KEY(_KEY, _NAV_INPUT) if (IsKeyDown(g.IO.KeyMap[_KEY])) { g.IO.NavInputs[_NAV_INPUT] = 1.0f; g.NavInputSource = ImGuiInputSource_NavKeyboard; } - NAV_MAP_KEY(ImGuiKey_Space, ImGuiNavInput_Activate ); - NAV_MAP_KEY(ImGuiKey_Enter, ImGuiNavInput_Input ); - NAV_MAP_KEY(ImGuiKey_Escape, ImGuiNavInput_Cancel ); - NAV_MAP_KEY(ImGuiKey_LeftArrow, ImGuiNavInput_KeyLeft_ ); - NAV_MAP_KEY(ImGuiKey_RightArrow,ImGuiNavInput_KeyRight_); - NAV_MAP_KEY(ImGuiKey_UpArrow, ImGuiNavInput_KeyUp_ ); - NAV_MAP_KEY(ImGuiKey_DownArrow, ImGuiNavInput_KeyDown_ ); - if (g.IO.KeyCtrl) g.IO.NavInputs[ImGuiNavInput_TweakSlow] = 1.0f; - if (g.IO.KeyShift) g.IO.NavInputs[ImGuiNavInput_TweakFast] = 1.0f; - if (g.IO.KeyAlt) g.IO.NavInputs[ImGuiNavInput_KeyMenu_] = 1.0f; - #undef NAV_MAP_KEY - } - memcpy(g.IO.NavInputsDownDurationPrev, g.IO.NavInputsDownDuration, sizeof(g.IO.NavInputsDownDuration)); - for (int i = 0; i < IM_ARRAYSIZE(g.IO.NavInputs); i++) - g.IO.NavInputsDownDuration[i] = (g.IO.NavInputs[i] > 0.0f) ? (g.IO.NavInputsDownDuration[i] < 0.0f ? 0.0f : g.IO.NavInputsDownDuration[i] + g.IO.DeltaTime) : -1.0f; - // Process navigation init request (select first/default focus) - if (g.NavInitResultId != 0 && (!g.NavDisableHighlight || g.NavInitRequestFromMove)) - { - // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called) - IM_ASSERT(g.NavWindow); - if (g.NavInitRequestFromMove) - SetNavIDWithRectRel(g.NavInitResultId, g.NavLayer, g.NavInitResultRectRel); - else - SetNavID(g.NavInitResultId, g.NavLayer); - g.NavWindow->NavRectRel[g.NavLayer] = g.NavInitResultRectRel; - } - g.NavInitRequest = false; - g.NavInitRequestFromMove = false; - g.NavInitResultId = 0; - g.NavJustMovedToId = 0; - // Process navigation move request - if (g.NavMoveRequest && (g.NavMoveResultLocal.ID != 0 || g.NavMoveResultOther.ID != 0)) - NavUpdateMoveResult(); - // When a forwarded move request failed, we restore the highlight that we disabled during the forward frame - if (g.NavMoveRequestForward == ImGuiNavForward_ForwardActive) - { - IM_ASSERT(g.NavMoveRequest); - if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0) - g.NavDisableHighlight = false; - g.NavMoveRequestForward = ImGuiNavForward_None; - } - // Apply application mouse position movement, after we had a chance to process move request result. - if (g.NavMousePosDirty && g.NavIdIsAlive) - { - // Set mouse position given our knowledge of the navigated item position from last frame - if ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (g.IO.BackendFlags & ImGuiBackendFlags_HasSetMousePos)) - { - if (!g.NavDisableHighlight && g.NavDisableMouseHover && g.NavWindow) - { - g.IO.MousePos = g.IO.MousePosPrev = NavCalcPreferredRefPos(); - g.IO.WantSetMousePos = true; - } - } - g.NavMousePosDirty = false; - } - g.NavIdIsAlive = false; - g.NavJustTabbedId = 0; - IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1); - // Store our return window (for returning from Layer 1 to Layer 0) and clear it as soon as we step back in our own Layer 0 - if (g.NavWindow) - NavSaveLastChildNavWindow(g.NavWindow); - if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == 0) - g.NavWindow->NavLastChildNavWindow = NULL; - // Update CTRL+TAB and Windowing features (hold Square to move/resize/etc.) - NavUpdateWindowing(); - // Set output flags for user application - g.IO.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs); - g.IO.NavVisible = (g.IO.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL) || g.NavInitRequest; - // Process NavCancel input (to close a popup, get back to parent, clear focus) - if (IsNavInputPressed(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed)) - { - if (g.ActiveId != 0) - { - ClearActiveID(); - } - else if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow) && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow) - { - // Exit child window - ImGuiWindow* child_window = g.NavWindow; - ImGuiWindow* parent_window = g.NavWindow->ParentWindow; - IM_ASSERT(child_window->ChildId != 0); - FocusWindow(parent_window); - SetNavID(child_window->ChildId, 0); - g.NavIdIsAlive = false; - if (g.NavDisableMouseHover) - g.NavMousePosDirty = true; - } - else if (g.OpenPopupStack.Size > 0) - { - // Close open popup/menu - if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal)) - ClosePopupToLevel(g.OpenPopupStack.Size - 1); - } - else if (g.NavLayer != 0) - { - // Leave the "menu" layer - NavRestoreLayer(0); - } - else - { - // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were - if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow))) - g.NavWindow->NavLastIds[0] = 0; - g.NavId = 0; - } - } - // Process manual activation request - g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = 0; - if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) - { - bool activate_down = IsNavInputDown(ImGuiNavInput_Activate); - bool activate_pressed = activate_down && IsNavInputPressed(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed); - if (g.ActiveId == 0 && activate_pressed) - g.NavActivateId = g.NavId; - if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_down) - g.NavActivateDownId = g.NavId; - if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_pressed) - g.NavActivatePressedId = g.NavId; - if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && IsNavInputPressed(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed)) - g.NavInputId = g.NavId; - } - if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) - g.NavDisableHighlight = true; - if (g.NavActivateId != 0) - IM_ASSERT(g.NavActivateDownId == g.NavActivateId); - g.NavMoveRequest = false; - // Process programmatic activation request - if (g.NavNextActivateId != 0) - g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = g.NavNextActivateId; - g.NavNextActivateId = 0; - // Initiate directional inputs request - const int allowed_dir_flags = (g.ActiveId == 0) ? ~0 : g.ActiveIdAllowNavDirFlags; - if (g.NavMoveRequestForward == ImGuiNavForward_None) - { - g.NavMoveDir = ImGuiDir_None; - g.NavMoveRequestFlags = 0; - if (g.NavWindow && !g.NavWindowingTarget && allowed_dir_flags && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) - { - if ((allowed_dir_flags & (1<Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget) - { - // *Fallback* manual-scroll with Nav directional keys when window has no navigable item - ImGuiWindow* window = g.NavWindow; - const float scroll_speed = ImFloor(window->CalcFontSize() * 100 * g.IO.DeltaTime + 0.5f); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported. - if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll && g.NavMoveRequest) - { - if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) - SetWindowScrollX(window, ImFloor(window->Scroll.x + ((g.NavMoveDir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed)); - if (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) - SetWindowScrollY(window, ImFloor(window->Scroll.y + ((g.NavMoveDir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed)); - } - - // *Normal* Manual scroll with NavScrollXXX keys - // Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds. - ImVec2 scroll_dir = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down, 1.0f/10.0f, 10.0f); - if (scroll_dir.x != 0.0f && window->ScrollbarX) - { - SetWindowScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed)); - g.NavMoveFromClampedRefRect = true; - } - if (scroll_dir.y != 0.0f) - { - SetWindowScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed)); - g.NavMoveFromClampedRefRect = true; - } - } - // Reset search results - g.NavMoveResultLocal.Clear(); - g.NavMoveResultLocalVisibleSet.Clear(); - g.NavMoveResultOther.Clear(); - // When we have manually scrolled (without using navigation) and NavId becomes out of bounds, we project its bounding box to the visible area to restart navigation within visible items - if (g.NavMoveRequest && g.NavMoveFromClampedRefRect && g.NavLayer == 0) - { - ImGuiWindow* window = g.NavWindow; - ImRect window_rect_rel(window->InnerMainRect.Min - window->Pos - ImVec2(1,1), window->InnerMainRect.Max - window->Pos + ImVec2(1,1)); - if (!window_rect_rel.Contains(window->NavRectRel[g.NavLayer])) - { - float pad = window->CalcFontSize() * 0.5f; - window_rect_rel.Expand(ImVec2(-ImMin(window_rect_rel.GetWidth(), pad), -ImMin(window_rect_rel.GetHeight(), pad))); // Terrible approximation for the intent of starting navigation from first fully visible item - window->NavRectRel[g.NavLayer].ClipWith(window_rect_rel); - g.NavId = 0; - } - g.NavMoveFromClampedRefRect = false; - } - // For scoring we use a single segment on the left side our current item bounding box (not touching the edge to avoid box overlap with zero-spaced items) - ImRect nav_rect_rel = (g.NavWindow && !g.NavWindow->NavRectRel[g.NavLayer].IsInverted()) ? g.NavWindow->NavRectRel[g.NavLayer] : ImRect(0,0,0,0); - g.NavScoringRectScreen = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : ImRect(0,0,0,0); - g.NavScoringRectScreen.TranslateY(nav_scoring_rect_offset_y); - g.NavScoringRectScreen.Min.x = ImMin(g.NavScoringRectScreen.Min.x + 1.0f, g.NavScoringRectScreen.Max.x); - g.NavScoringRectScreen.Max.x = g.NavScoringRectScreen.Min.x; - IM_ASSERT(!g.NavScoringRectScreen.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows us to remove extraneous ImFabs() calls in NavScoreItem(). - //g.OverlayDrawList.AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG] - g.NavScoringCount = 0; -#if IMGUI_DEBUG_NAV_RECTS - if (g.NavWindow) { for (int layer = 0; layer < 2; layer++) GetOverlayDrawList(g.NavWindow)->AddRect(g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Min, g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Max, IM_COL32(255,200,0,255)); } // [DEBUG] - if (g.NavWindow) { ImU32 col = (g.NavWindow->HiddenFrames == 0) ? IM_COL32(255,0,255,255) : IM_COL32(255,0,0,255); ImVec2 p = NavCalcPreferredRefPos(NULL); char buf[32]; ImFormatString(buf, 32, "%d", g.NavLayer); GetOverlayDrawList(g.NavWindow)->AddCircleFilled(p, 3.0f, col); GetOverlayDrawList(g.NavWindow)->AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); } -#endif -} - static void SetWindowViewport(ImGuiWindow* window, ImGuiViewportP* viewport) { window->Viewport = viewport; @@ -8765,7 +8517,253 @@ void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags mov } } -// +static void ImGui::NavUpdate() +{ + ImGuiContext& g = *GImGui; + g.IO.WantSetMousePos = false; +#if 0 + if (g.NavScoringCount > 0) printf("[%05d] NavScoringCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.FrameCount, g.NavScoringCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest); +#endif + // Set input source as Gamepad when buttons are pressed before we map Keyboard (some features differs when used with Gamepad vs Keyboard) + bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; + bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (g.IO.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; + if (nav_gamepad_active) + if (g.IO.NavInputs[ImGuiNavInput_Activate] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Input] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Cancel] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Menu] > 0.0f) + g.NavInputSource = ImGuiInputSource_NavGamepad; + // Update Keyboard->Nav inputs mapping + if (nav_keyboard_active) + { + #define NAV_MAP_KEY(_KEY, _NAV_INPUT) if (IsKeyDown(g.IO.KeyMap[_KEY])) { g.IO.NavInputs[_NAV_INPUT] = 1.0f; g.NavInputSource = ImGuiInputSource_NavKeyboard; } + NAV_MAP_KEY(ImGuiKey_Space, ImGuiNavInput_Activate ); + NAV_MAP_KEY(ImGuiKey_Enter, ImGuiNavInput_Input ); + NAV_MAP_KEY(ImGuiKey_Escape, ImGuiNavInput_Cancel ); + NAV_MAP_KEY(ImGuiKey_LeftArrow, ImGuiNavInput_KeyLeft_ ); + NAV_MAP_KEY(ImGuiKey_RightArrow,ImGuiNavInput_KeyRight_); + NAV_MAP_KEY(ImGuiKey_UpArrow, ImGuiNavInput_KeyUp_ ); + NAV_MAP_KEY(ImGuiKey_DownArrow, ImGuiNavInput_KeyDown_ ); + if (g.IO.KeyCtrl) g.IO.NavInputs[ImGuiNavInput_TweakSlow] = 1.0f; + if (g.IO.KeyShift) g.IO.NavInputs[ImGuiNavInput_TweakFast] = 1.0f; + if (g.IO.KeyAlt) g.IO.NavInputs[ImGuiNavInput_KeyMenu_] = 1.0f; + #undef NAV_MAP_KEY + } + memcpy(g.IO.NavInputsDownDurationPrev, g.IO.NavInputsDownDuration, sizeof(g.IO.NavInputsDownDuration)); + for (int i = 0; i < IM_ARRAYSIZE(g.IO.NavInputs); i++) + g.IO.NavInputsDownDuration[i] = (g.IO.NavInputs[i] > 0.0f) ? (g.IO.NavInputsDownDuration[i] < 0.0f ? 0.0f : g.IO.NavInputsDownDuration[i] + g.IO.DeltaTime) : -1.0f; + // Process navigation init request (select first/default focus) + if (g.NavInitResultId != 0 && (!g.NavDisableHighlight || g.NavInitRequestFromMove)) + { + // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called) + IM_ASSERT(g.NavWindow); + if (g.NavInitRequestFromMove) + SetNavIDWithRectRel(g.NavInitResultId, g.NavLayer, g.NavInitResultRectRel); + else + SetNavID(g.NavInitResultId, g.NavLayer); + g.NavWindow->NavRectRel[g.NavLayer] = g.NavInitResultRectRel; + } + g.NavInitRequest = false; + g.NavInitRequestFromMove = false; + g.NavInitResultId = 0; + g.NavJustMovedToId = 0; + // Process navigation move request + if (g.NavMoveRequest && (g.NavMoveResultLocal.ID != 0 || g.NavMoveResultOther.ID != 0)) + NavUpdateMoveResult(); + // When a forwarded move request failed, we restore the highlight that we disabled during the forward frame + if (g.NavMoveRequestForward == ImGuiNavForward_ForwardActive) + { + IM_ASSERT(g.NavMoveRequest); + if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0) + g.NavDisableHighlight = false; + g.NavMoveRequestForward = ImGuiNavForward_None; + } + // Apply application mouse position movement, after we had a chance to process move request result. + if (g.NavMousePosDirty && g.NavIdIsAlive) + { + // Set mouse position given our knowledge of the navigated item position from last frame + if ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (g.IO.BackendFlags & ImGuiBackendFlags_HasSetMousePos)) + { + if (!g.NavDisableHighlight && g.NavDisableMouseHover && g.NavWindow) + { + g.IO.MousePos = g.IO.MousePosPrev = NavCalcPreferredRefPos(); + g.IO.WantSetMousePos = true; + } + } + g.NavMousePosDirty = false; + } + g.NavIdIsAlive = false; + g.NavJustTabbedId = 0; + IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1); + // Store our return window (for returning from Layer 1 to Layer 0) and clear it as soon as we step back in our own Layer 0 + if (g.NavWindow) + NavSaveLastChildNavWindow(g.NavWindow); + if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == 0) + g.NavWindow->NavLastChildNavWindow = NULL; + // Update CTRL+TAB and Windowing features (hold Square to move/resize/etc.) + NavUpdateWindowing(); + // Set output flags for user application + g.IO.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs); + g.IO.NavVisible = (g.IO.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL) || g.NavInitRequest; + // Process NavCancel input (to close a popup, get back to parent, clear focus) + if (IsNavInputPressed(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed)) + { + if (g.ActiveId != 0) + { + ClearActiveID(); + } + else if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow) && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow) + { + // Exit child window + ImGuiWindow* child_window = g.NavWindow; + ImGuiWindow* parent_window = g.NavWindow->ParentWindow; + IM_ASSERT(child_window->ChildId != 0); + FocusWindow(parent_window); + SetNavID(child_window->ChildId, 0); + g.NavIdIsAlive = false; + if (g.NavDisableMouseHover) + g.NavMousePosDirty = true; + } + else if (g.OpenPopupStack.Size > 0) + { + // Close open popup/menu + if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal)) + ClosePopupToLevel(g.OpenPopupStack.Size - 1); + } + else if (g.NavLayer != 0) + { + // Leave the "menu" layer + NavRestoreLayer(0); + } + else + { + // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were + if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow))) + g.NavWindow->NavLastIds[0] = 0; + g.NavId = 0; + } + } + // Process manual activation request + g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = 0; + if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) + { + bool activate_down = IsNavInputDown(ImGuiNavInput_Activate); + bool activate_pressed = activate_down && IsNavInputPressed(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed); + if (g.ActiveId == 0 && activate_pressed) + g.NavActivateId = g.NavId; + if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_down) + g.NavActivateDownId = g.NavId; + if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_pressed) + g.NavActivatePressedId = g.NavId; + if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && IsNavInputPressed(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed)) + g.NavInputId = g.NavId; + } + if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) + g.NavDisableHighlight = true; + if (g.NavActivateId != 0) + IM_ASSERT(g.NavActivateDownId == g.NavActivateId); + g.NavMoveRequest = false; + // Process programmatic activation request + if (g.NavNextActivateId != 0) + g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = g.NavNextActivateId; + g.NavNextActivateId = 0; + // Initiate directional inputs request + const int allowed_dir_flags = (g.ActiveId == 0) ? ~0 : g.ActiveIdAllowNavDirFlags; + if (g.NavMoveRequestForward == ImGuiNavForward_None) + { + g.NavMoveDir = ImGuiDir_None; + g.NavMoveRequestFlags = 0; + if (g.NavWindow && !g.NavWindowingTarget && allowed_dir_flags && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) + { + if ((allowed_dir_flags & (1<Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget) + { + // *Fallback* manual-scroll with Nav directional keys when window has no navigable item + ImGuiWindow* window = g.NavWindow; + const float scroll_speed = ImFloor(window->CalcFontSize() * 100 * g.IO.DeltaTime + 0.5f); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported. + if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll && g.NavMoveRequest) + { + if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) + SetWindowScrollX(window, ImFloor(window->Scroll.x + ((g.NavMoveDir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed)); + if (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) + SetWindowScrollY(window, ImFloor(window->Scroll.y + ((g.NavMoveDir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed)); + } + + // *Normal* Manual scroll with NavScrollXXX keys + // Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds. + ImVec2 scroll_dir = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down, 1.0f/10.0f, 10.0f); + if (scroll_dir.x != 0.0f && window->ScrollbarX) + { + SetWindowScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed)); + g.NavMoveFromClampedRefRect = true; + } + if (scroll_dir.y != 0.0f) + { + SetWindowScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed)); + g.NavMoveFromClampedRefRect = true; + } + } + // Reset search results + g.NavMoveResultLocal.Clear(); + g.NavMoveResultLocalVisibleSet.Clear(); + g.NavMoveResultOther.Clear(); + // When we have manually scrolled (without using navigation) and NavId becomes out of bounds, we project its bounding box to the visible area to restart navigation within visible items + if (g.NavMoveRequest && g.NavMoveFromClampedRefRect && g.NavLayer == 0) + { + ImGuiWindow* window = g.NavWindow; + ImRect window_rect_rel(window->InnerMainRect.Min - window->Pos - ImVec2(1,1), window->InnerMainRect.Max - window->Pos + ImVec2(1,1)); + if (!window_rect_rel.Contains(window->NavRectRel[g.NavLayer])) + { + float pad = window->CalcFontSize() * 0.5f; + window_rect_rel.Expand(ImVec2(-ImMin(window_rect_rel.GetWidth(), pad), -ImMin(window_rect_rel.GetHeight(), pad))); // Terrible approximation for the intent of starting navigation from first fully visible item + window->NavRectRel[g.NavLayer].ClipWith(window_rect_rel); + g.NavId = 0; + } + g.NavMoveFromClampedRefRect = false; + } + // For scoring we use a single segment on the left side our current item bounding box (not touching the edge to avoid box overlap with zero-spaced items) + ImRect nav_rect_rel = (g.NavWindow && !g.NavWindow->NavRectRel[g.NavLayer].IsInverted()) ? g.NavWindow->NavRectRel[g.NavLayer] : ImRect(0,0,0,0); + g.NavScoringRectScreen = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : ImRect(0,0,0,0); + g.NavScoringRectScreen.TranslateY(nav_scoring_rect_offset_y); + g.NavScoringRectScreen.Min.x = ImMin(g.NavScoringRectScreen.Min.x + 1.0f, g.NavScoringRectScreen.Max.x); + g.NavScoringRectScreen.Max.x = g.NavScoringRectScreen.Min.x; + IM_ASSERT(!g.NavScoringRectScreen.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows us to remove extraneous ImFabs() calls in NavScoreItem(). + //g.OverlayDrawList.AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG] + g.NavScoringCount = 0; +#if IMGUI_DEBUG_NAV_RECTS + if (g.NavWindow) { for (int layer = 0; layer < 2; layer++) GetOverlayDrawList(g.NavWindow)->AddRect(g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Min, g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Max, IM_COL32(255,200,0,255)); } // [DEBUG] + if (g.NavWindow) { ImU32 col = (g.NavWindow->HiddenFrames == 0) ? IM_COL32(255,0,255,255) : IM_COL32(255,0,0,255); ImVec2 p = NavCalcPreferredRefPos(NULL); char buf[32]; ImFormatString(buf, 32, "%d", g.NavLayer); GetOverlayDrawList(g.NavWindow)->AddCircleFilled(p, 3.0f, col); GetOverlayDrawList(g.NavWindow)->AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); } +#endif +} static void ImGui::NavUpdateMoveResult() { From 172e426ba97fbce81b6b35f2bad847cc25ad8dd6 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 30 Aug 2018 18:13:34 +0200 Subject: [PATCH 212/828] Refactor: Internals: Restored new lines in NavUpdate() ... (part 6) (#2036, #787) --- imgui.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/imgui.cpp b/imgui.cpp index ed1c6560a010..17f02f4e3e16 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -8524,12 +8524,14 @@ static void ImGui::NavUpdate() #if 0 if (g.NavScoringCount > 0) printf("[%05d] NavScoringCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.FrameCount, g.NavScoringCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest); #endif + // Set input source as Gamepad when buttons are pressed before we map Keyboard (some features differs when used with Gamepad vs Keyboard) bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (g.IO.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; if (nav_gamepad_active) if (g.IO.NavInputs[ImGuiNavInput_Activate] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Input] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Cancel] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Menu] > 0.0f) g.NavInputSource = ImGuiInputSource_NavGamepad; + // Update Keyboard->Nav inputs mapping if (nav_keyboard_active) { @@ -8549,6 +8551,7 @@ static void ImGui::NavUpdate() memcpy(g.IO.NavInputsDownDurationPrev, g.IO.NavInputsDownDuration, sizeof(g.IO.NavInputsDownDuration)); for (int i = 0; i < IM_ARRAYSIZE(g.IO.NavInputs); i++) g.IO.NavInputsDownDuration[i] = (g.IO.NavInputs[i] > 0.0f) ? (g.IO.NavInputsDownDuration[i] < 0.0f ? 0.0f : g.IO.NavInputsDownDuration[i] + g.IO.DeltaTime) : -1.0f; + // Process navigation init request (select first/default focus) if (g.NavInitResultId != 0 && (!g.NavDisableHighlight || g.NavInitRequestFromMove)) { @@ -8564,9 +8567,11 @@ static void ImGui::NavUpdate() g.NavInitRequestFromMove = false; g.NavInitResultId = 0; g.NavJustMovedToId = 0; + // Process navigation move request if (g.NavMoveRequest && (g.NavMoveResultLocal.ID != 0 || g.NavMoveResultOther.ID != 0)) NavUpdateMoveResult(); + // When a forwarded move request failed, we restore the highlight that we disabled during the forward frame if (g.NavMoveRequestForward == ImGuiNavForward_ForwardActive) { @@ -8575,6 +8580,7 @@ static void ImGui::NavUpdate() g.NavDisableHighlight = false; g.NavMoveRequestForward = ImGuiNavForward_None; } + // Apply application mouse position movement, after we had a chance to process move request result. if (g.NavMousePosDirty && g.NavIdIsAlive) { @@ -8592,16 +8598,20 @@ static void ImGui::NavUpdate() g.NavIdIsAlive = false; g.NavJustTabbedId = 0; IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1); + // Store our return window (for returning from Layer 1 to Layer 0) and clear it as soon as we step back in our own Layer 0 if (g.NavWindow) NavSaveLastChildNavWindow(g.NavWindow); if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == 0) g.NavWindow->NavLastChildNavWindow = NULL; + // Update CTRL+TAB and Windowing features (hold Square to move/resize/etc.) NavUpdateWindowing(); + // Set output flags for user application g.IO.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs); g.IO.NavVisible = (g.IO.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL) || g.NavInitRequest; + // Process NavCancel input (to close a popup, get back to parent, clear focus) if (IsNavInputPressed(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed)) { @@ -8640,6 +8650,7 @@ static void ImGui::NavUpdate() g.NavId = 0; } } + // Process manual activation request g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = 0; if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) @@ -8660,10 +8671,12 @@ static void ImGui::NavUpdate() if (g.NavActivateId != 0) IM_ASSERT(g.NavActivateDownId == g.NavActivateId); g.NavMoveRequest = false; + // Process programmatic activation request if (g.NavNextActivateId != 0) g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = g.NavNextActivateId; g.NavNextActivateId = 0; + // Initiate directional inputs request const int allowed_dir_flags = (g.ActiveId == 0) ? ~0 : g.ActiveIdAllowNavDirFlags; if (g.NavMoveRequestForward == ImGuiNavForward_None) @@ -8687,10 +8700,12 @@ static void ImGui::NavUpdate() IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_ForwardQueued); g.NavMoveRequestForward = ImGuiNavForward_ForwardActive; } + // Update PageUp/PageDown scroll float nav_scoring_rect_offset_y = 0.0f; if (nav_keyboard_active) nav_scoring_rect_offset_y = NavUpdatePageUpPageDown(allowed_dir_flags); + // If we initiate a movement request and have no current NavId, we initiate a InitDefautRequest that will be used as a fallback if the direction fails to find a match if (g.NavMoveDir != ImGuiDir_None) { @@ -8704,6 +8719,7 @@ static void ImGui::NavUpdate() g.NavDisableHighlight = false; } NavUpdateAnyRequestFlag(); + // Scrolling if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget) { @@ -8732,10 +8748,12 @@ static void ImGui::NavUpdate() g.NavMoveFromClampedRefRect = true; } } + // Reset search results g.NavMoveResultLocal.Clear(); g.NavMoveResultLocalVisibleSet.Clear(); g.NavMoveResultOther.Clear(); + // When we have manually scrolled (without using navigation) and NavId becomes out of bounds, we project its bounding box to the visible area to restart navigation within visible items if (g.NavMoveRequest && g.NavMoveFromClampedRefRect && g.NavLayer == 0) { @@ -8750,6 +8768,7 @@ static void ImGui::NavUpdate() } g.NavMoveFromClampedRefRect = false; } + // For scoring we use a single segment on the left side our current item bounding box (not touching the edge to avoid box overlap with zero-spaced items) ImRect nav_rect_rel = (g.NavWindow && !g.NavWindow->NavRectRel[g.NavLayer].IsInverted()) ? g.NavWindow->NavRectRel[g.NavLayer] : ImRect(0,0,0,0); g.NavScoringRectScreen = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : ImRect(0,0,0,0); From b6cdfef442f3db016a1ef64a4af63c8ddd3c8c83 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 30 Aug 2018 18:18:24 +0200 Subject: [PATCH 213/828] Refactor: Internals: Moved Navigation functions in imgui.cpp in their own section. (part 7) (#2036, #787) --- imgui.cpp | 221 +++++++++++++++++++++++++++--------------------------- 1 file changed, 110 insertions(+), 111 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 17f02f4e3e16..e767866b0869 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2761,44 +2761,6 @@ static ImGuiWindow* FindWindowNavFocusable(int i_start, int i_stop, int dir) // return NULL; } -float ImGui::GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode) -{ - ImGuiContext& g = *GImGui; - if (mode == ImGuiInputReadMode_Down) - return g.IO.NavInputs[n]; // Instant, read analog input (0.0f..1.0f, as provided by user) - - const float t = g.IO.NavInputsDownDuration[n]; - if (t < 0.0f && mode == ImGuiInputReadMode_Released) // Return 1.0f when just released, no repeat, ignore analog input. - return (g.IO.NavInputsDownDurationPrev[n] >= 0.0f ? 1.0f : 0.0f); - if (t < 0.0f) - return 0.0f; - if (mode == ImGuiInputReadMode_Pressed) // Return 1.0f when just pressed, no repeat, ignore analog input. - return (t == 0.0f) ? 1.0f : 0.0f; - if (mode == ImGuiInputReadMode_Repeat) - return (float)CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 0.80f, g.IO.KeyRepeatRate * 0.80f); - if (mode == ImGuiInputReadMode_RepeatSlow) - return (float)CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 1.00f, g.IO.KeyRepeatRate * 2.00f); - if (mode == ImGuiInputReadMode_RepeatFast) - return (float)CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 0.80f, g.IO.KeyRepeatRate * 0.30f); - return 0.0f; -} - -ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor, float fast_factor) -{ - ImVec2 delta(0.0f, 0.0f); - if (dir_sources & ImGuiNavDirSourceFlags_Keyboard) - delta += ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight_, mode) - GetNavInputAmount(ImGuiNavInput_KeyLeft_, mode), GetNavInputAmount(ImGuiNavInput_KeyDown_, mode) - GetNavInputAmount(ImGuiNavInput_KeyUp_, mode)); - if (dir_sources & ImGuiNavDirSourceFlags_PadDPad) - delta += ImVec2(GetNavInputAmount(ImGuiNavInput_DpadRight, mode) - GetNavInputAmount(ImGuiNavInput_DpadLeft, mode), GetNavInputAmount(ImGuiNavInput_DpadDown, mode) - GetNavInputAmount(ImGuiNavInput_DpadUp, mode)); - if (dir_sources & ImGuiNavDirSourceFlags_PadLStick) - delta += ImVec2(GetNavInputAmount(ImGuiNavInput_LStickRight, mode) - GetNavInputAmount(ImGuiNavInput_LStickLeft, mode), GetNavInputAmount(ImGuiNavInput_LStickDown, mode) - GetNavInputAmount(ImGuiNavInput_LStickUp, mode)); - if (slow_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakSlow)) - delta *= slow_factor; - if (fast_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakFast)) - delta *= fast_factor; - return delta; -} - static void NavUpdateWindowingHighlightWindow(int focus_change_dir) { ImGuiContext& g = *GImGui; @@ -2936,7 +2898,6 @@ static void ImGui::NavUpdateWindowing() ImGuiWindow* new_nav_window = g.NavWindow; while ((new_nav_window->DC.NavLayerActiveMask & (1 << 1)) == 0 && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0) new_nav_window = new_nav_window->ParentWindow; - if (new_nav_window != g.NavWindow) { ImGuiWindow* old_nav_window = g.NavWindow; @@ -2949,78 +2910,6 @@ static void ImGui::NavUpdateWindowing() } } -// Window has already passed the IsWindowNavFocusable() -static const char* GetFallbackWindowNameForWindowingList(ImGuiWindow* window) -{ - if (window->Flags & ImGuiWindowFlags_Popup) - return "(Popup)"; - if ((window->Flags & ImGuiWindowFlags_MenuBar) && strcmp(window->Name, "##MainMenuBar") == 0) - return "(Main menu bar)"; - return "(Untitled)"; -} - -// Overlay displayed when using CTRL+TAB. Called by EndFrame(). -void ImGui::NavUpdateWindowingList() -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(g.NavWindowingTarget != NULL); - - if (g.NavWindowingTimer < NAV_WINDOWING_LIST_APPEAR_DELAY) - return; - - if (g.NavWindowingList == NULL) - g.NavWindowingList = FindWindowByName("###NavWindowingList"); - ImGuiViewportP* viewport = /*g.NavWindow ? g.NavWindow->Viewport :*/ (ImGuiViewportP*)GetMainViewport(); - SetNextWindowSizeConstraints(ImVec2(viewport->Size.x * 0.20f, viewport->Size.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX)); - SetNextWindowPos(viewport->Pos + viewport->Size * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f)); - PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f); - Begin("###NavWindowingList", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings); - for (int n = g.Windows.Size - 1; n >= 0; n--) - { - ImGuiWindow* window = g.Windows[n]; - if (!IsWindowNavFocusable(window)) - continue; - const char* label = window->Name; - if (label == FindRenderedTextEnd(label)) - label = GetFallbackWindowNameForWindowingList(window); - Selectable(label, g.NavWindowingTarget == window); - } - End(); - PopStyleVar(); -} - -// Scroll to keep newly navigated item fully into view -// NB: We modify rect_rel by the amount we scrolled for, so it is immediately updated. -static void NavScrollToBringItemIntoView(ImGuiWindow* window, const ImRect& item_rect) -{ - ImRect window_rect(window->InnerMainRect.Min - ImVec2(1, 1), window->InnerMainRect.Max + ImVec2(1, 1)); - //GetOverlayDrawList(window)->AddRect(window_rect_rel.Min, window_rect_rel.Max, IM_COL32_WHITE); // [DEBUG] - if (window_rect.Contains(item_rect)) - return; - - ImGuiContext& g = *GImGui; - if (window->ScrollbarX && item_rect.Min.x < window_rect.Min.x) - { - window->ScrollTarget.x = item_rect.Min.x - window->Pos.x + window->Scroll.x - g.Style.ItemSpacing.x; - window->ScrollTargetCenterRatio.x = 0.0f; - } - else if (window->ScrollbarX && item_rect.Max.x >= window_rect.Max.x) - { - window->ScrollTarget.x = item_rect.Max.x - window->Pos.x + window->Scroll.x + g.Style.ItemSpacing.x; - window->ScrollTargetCenterRatio.x = 1.0f; - } - if (item_rect.Min.y < window_rect.Min.y) - { - window->ScrollTarget.y = item_rect.Min.y - window->Pos.y + window->Scroll.y - g.Style.ItemSpacing.y; - window->ScrollTargetCenterRatio.y = 0.0f; - } - else if (item_rect.Max.y >= window_rect.Max.y) - { - window->ScrollTarget.y = item_rect.Max.y - window->Pos.y + window->Scroll.y + g.Style.ItemSpacing.y; - window->ScrollTargetCenterRatio.y = 1.0f; - } -} - static void SetWindowViewport(ImGuiWindow* window, ImGuiViewportP* viewport) { window->Viewport = viewport; @@ -8517,6 +8406,76 @@ void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags mov } } +float ImGui::GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode) +{ + ImGuiContext& g = *GImGui; + if (mode == ImGuiInputReadMode_Down) + return g.IO.NavInputs[n]; // Instant, read analog input (0.0f..1.0f, as provided by user) + + const float t = g.IO.NavInputsDownDuration[n]; + if (t < 0.0f && mode == ImGuiInputReadMode_Released) // Return 1.0f when just released, no repeat, ignore analog input. + return (g.IO.NavInputsDownDurationPrev[n] >= 0.0f ? 1.0f : 0.0f); + if (t < 0.0f) + return 0.0f; + if (mode == ImGuiInputReadMode_Pressed) // Return 1.0f when just pressed, no repeat, ignore analog input. + return (t == 0.0f) ? 1.0f : 0.0f; + if (mode == ImGuiInputReadMode_Repeat) + return (float)CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 0.80f, g.IO.KeyRepeatRate * 0.80f); + if (mode == ImGuiInputReadMode_RepeatSlow) + return (float)CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 1.00f, g.IO.KeyRepeatRate * 2.00f); + if (mode == ImGuiInputReadMode_RepeatFast) + return (float)CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 0.80f, g.IO.KeyRepeatRate * 0.30f); + return 0.0f; +} + +ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor, float fast_factor) +{ + ImVec2 delta(0.0f, 0.0f); + if (dir_sources & ImGuiNavDirSourceFlags_Keyboard) + delta += ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight_, mode) - GetNavInputAmount(ImGuiNavInput_KeyLeft_, mode), GetNavInputAmount(ImGuiNavInput_KeyDown_, mode) - GetNavInputAmount(ImGuiNavInput_KeyUp_, mode)); + if (dir_sources & ImGuiNavDirSourceFlags_PadDPad) + delta += ImVec2(GetNavInputAmount(ImGuiNavInput_DpadRight, mode) - GetNavInputAmount(ImGuiNavInput_DpadLeft, mode), GetNavInputAmount(ImGuiNavInput_DpadDown, mode) - GetNavInputAmount(ImGuiNavInput_DpadUp, mode)); + if (dir_sources & ImGuiNavDirSourceFlags_PadLStick) + delta += ImVec2(GetNavInputAmount(ImGuiNavInput_LStickRight, mode) - GetNavInputAmount(ImGuiNavInput_LStickLeft, mode), GetNavInputAmount(ImGuiNavInput_LStickDown, mode) - GetNavInputAmount(ImGuiNavInput_LStickUp, mode)); + if (slow_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakSlow)) + delta *= slow_factor; + if (fast_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakFast)) + delta *= fast_factor; + return delta; +} + +// Scroll to keep newly navigated item fully into view +// NB: We modify rect_rel by the amount we scrolled for, so it is immediately updated. +static void NavScrollToBringItemIntoView(ImGuiWindow* window, const ImRect& item_rect) +{ + ImRect window_rect(window->InnerMainRect.Min - ImVec2(1, 1), window->InnerMainRect.Max + ImVec2(1, 1)); + //GetOverlayDrawList(window)->AddRect(window_rect_rel.Min, window_rect_rel.Max, IM_COL32_WHITE); // [DEBUG] + if (window_rect.Contains(item_rect)) + return; + + ImGuiContext& g = *GImGui; + if (window->ScrollbarX && item_rect.Min.x < window_rect.Min.x) + { + window->ScrollTarget.x = item_rect.Min.x - window->Pos.x + window->Scroll.x - g.Style.ItemSpacing.x; + window->ScrollTargetCenterRatio.x = 0.0f; + } + else if (window->ScrollbarX && item_rect.Max.x >= window_rect.Max.x) + { + window->ScrollTarget.x = item_rect.Max.x - window->Pos.x + window->Scroll.x + g.Style.ItemSpacing.x; + window->ScrollTargetCenterRatio.x = 1.0f; + } + if (item_rect.Min.y < window_rect.Min.y) + { + window->ScrollTarget.y = item_rect.Min.y - window->Pos.y + window->Scroll.y - g.Style.ItemSpacing.y; + window->ScrollTargetCenterRatio.y = 0.0f; + } + else if (item_rect.Max.y >= window_rect.Max.y) + { + window->ScrollTarget.y = item_rect.Max.y - window->Pos.y + window->Scroll.y + g.Style.ItemSpacing.y; + window->ScrollTargetCenterRatio.y = 1.0f; + } +} + static void ImGui::NavUpdate() { ImGuiContext& g = *GImGui; @@ -8869,6 +8828,46 @@ static float ImGui::NavUpdatePageUpPageDown(int allowed_dir_flags) return 0.0f; } +// Window has already passed the IsWindowNavFocusable() +static const char* GetFallbackWindowNameForWindowingList(ImGuiWindow* window) +{ + if (window->Flags & ImGuiWindowFlags_Popup) + return "(Popup)"; + if ((window->Flags & ImGuiWindowFlags_MenuBar) && strcmp(window->Name, "##MainMenuBar") == 0) + return "(Main menu bar)"; + return "(Untitled)"; +} + +// Overlay displayed when using CTRL+TAB. Called by EndFrame(). +void ImGui::NavUpdateWindowingList() +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(g.NavWindowingTarget != NULL); + + if (g.NavWindowingTimer < NAV_WINDOWING_LIST_APPEAR_DELAY) + return; + + if (g.NavWindowingList == NULL) + g.NavWindowingList = FindWindowByName("###NavWindowingList"); + ImGuiViewportP* viewport = /*g.NavWindow ? g.NavWindow->Viewport :*/ (ImGuiViewportP*)GetMainViewport(); + SetNextWindowSizeConstraints(ImVec2(viewport->Size.x * 0.20f, viewport->Size.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX)); + SetNextWindowPos(viewport->Pos + viewport->Size * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f)); + PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f); + Begin("###NavWindowingList", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings); + for (int n = g.Windows.Size - 1; n >= 0; n--) + { + ImGuiWindow* window = g.Windows[n]; + if (!IsWindowNavFocusable(window)) + continue; + const char* label = window->Name; + if (label == FindRenderedTextEnd(label)) + label = GetFallbackWindowNameForWindowingList(window); + Selectable(label, g.NavWindowingTarget == window); + } + End(); + PopStyleVar(); +} + //----------------------------------------------------------------------------- // COLUMNS //----------------------------------------------------------------------------- From fefcc77f13c871a4a409511c6aec4fb7365efa7c Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 30 Aug 2018 18:20:19 +0200 Subject: [PATCH 214/828] Refactor: Internals: Moved Navigation functions in imgui.cpp in their own section. (part 8) (#2036, #787) --- imgui.cpp | 414 +++++++++++++++++++++++++++--------------------------- 1 file changed, 207 insertions(+), 207 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index e767866b0869..b4756e6d1dcf 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2703,213 +2703,6 @@ ImDrawListSharedData* ImGui::GetDrawListSharedData() return &GImGui->DrawListSharedData; } -// This needs to be called before we submit any widget (aka in or before Begin) -void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(window == g.NavWindow); - bool init_for_nav = false; - if (!(window->Flags & ImGuiWindowFlags_NoNavInputs)) - if (!(window->Flags & ImGuiWindowFlags_ChildWindow) || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit) - init_for_nav = true; - if (init_for_nav) - { - SetNavID(0, g.NavLayer); - g.NavInitRequest = true; - g.NavInitRequestFromMove = false; - g.NavInitResultId = 0; - g.NavInitResultRectRel = ImRect(); - NavUpdateAnyRequestFlag(); - } - else - { - g.NavId = window->NavLastIds[0]; - } -} - -static ImVec2 ImGui::NavCalcPreferredRefPos() -{ - ImGuiContext& g = *GImGui; - if (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow) - { - IM_ASSERT(ImGui::IsMousePosValid()); // This will probably trigger at some point, please share your repro! - return ImFloor(g.IO.MousePos); - } - - // When navigation is active and mouse is disabled, decide on an arbitrary position around the bottom left of the currently navigated item - const ImRect& rect_rel = g.NavWindow->NavRectRel[g.NavLayer]; - ImVec2 pos = g.NavWindow->Pos + ImVec2(rect_rel.Min.x + ImMin(g.Style.FramePadding.x*4, rect_rel.GetWidth()), rect_rel.Max.y - ImMin(g.Style.FramePadding.y, rect_rel.GetHeight())); - ImRect visible_rect = g.NavWindow->Viewport->GetRect(); - return ImFloor(ImClamp(pos, visible_rect.Min, visible_rect.Max)); // ImFloor() is important because non-integer mouse position application in back-end might be lossy and result in undesirable non-zero delta. -} - -static int FindWindowIndex(ImGuiWindow* window) // FIXME-OPT O(N) -{ - ImGuiContext& g = *GImGui; - for (int i = g.Windows.Size-1; i >= 0; i--) - if (g.Windows[i] == window) - return i; - return -1; -} - -static ImGuiWindow* FindWindowNavFocusable(int i_start, int i_stop, int dir) // FIXME-OPT O(N) -{ - ImGuiContext& g = *GImGui; - for (int i = i_start; i >= 0 && i < g.Windows.Size && i != i_stop; i += dir) - if (ImGui::IsWindowNavFocusable(g.Windows[i])) - return g.Windows[i]; - return NULL; -} - -static void NavUpdateWindowingHighlightWindow(int focus_change_dir) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(g.NavWindowingTarget); - if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal) - return; - - const int i_current = FindWindowIndex(g.NavWindowingTarget); - ImGuiWindow* window_target = FindWindowNavFocusable(i_current + focus_change_dir, -INT_MAX, focus_change_dir); - if (!window_target) - window_target = FindWindowNavFocusable((focus_change_dir < 0) ? (g.Windows.Size - 1) : 0, i_current, focus_change_dir); - if (window_target) // Don't reset windowing target if there's a single window in the list - g.NavWindowingTarget = g.NavWindowingTargetAnim = window_target; - g.NavWindowingToggleLayer = false; -} - -// Window management mode (hold to: change focus/move/resize, tap to: toggle menu layer) -static void ImGui::NavUpdateWindowing() -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* apply_focus_window = NULL; - bool apply_toggle_layer = false; - - ImGuiWindow* modal_window = GetFrontMostPopupModal(); - if (modal_window != NULL) - { - g.NavWindowingTarget = NULL; - return; - } - - // Fade out - if (g.NavWindowingTargetAnim && g.NavWindowingTarget == NULL) - { - g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha - g.IO.DeltaTime * 10.0f, 0.0f); - if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f) - g.NavWindowingTargetAnim = NULL; - } - - // Start CTRL-TAB or Square+L/R window selection - bool start_windowing_with_gamepad = !g.NavWindowingTarget && IsNavInputPressed(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed); - bool start_windowing_with_keyboard = !g.NavWindowingTarget && g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard); - if (start_windowing_with_gamepad || start_windowing_with_keyboard) - if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.Windows.Size - 1, -INT_MAX, -1)) - { - g.NavWindowingTarget = g.NavWindowingTargetAnim = window; - g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f; - g.NavWindowingToggleLayer = start_windowing_with_keyboard ? false : true; - g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_NavKeyboard : ImGuiInputSource_NavGamepad; - } - - // Gamepad update - g.NavWindowingTimer += g.IO.DeltaTime; - if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavGamepad) - { - // Highlight only appears after a brief time holding the button, so that a fast tap on PadMenu (to toggle NavLayer) doesn't add visual noise - g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); - - // Select window to focus - const int focus_change_dir = (int)IsNavInputPressed(ImGuiNavInput_FocusPrev, ImGuiInputReadMode_RepeatSlow) - (int)IsNavInputPressed(ImGuiNavInput_FocusNext, ImGuiInputReadMode_RepeatSlow); - if (focus_change_dir != 0) - { - NavUpdateWindowingHighlightWindow(focus_change_dir); - g.NavWindowingHighlightAlpha = 1.0f; - } - - // Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered front-most) - if (!IsNavInputDown(ImGuiNavInput_Menu)) - { - g.NavWindowingToggleLayer &= (g.NavWindowingHighlightAlpha < 1.0f); // Once button was held long enough we don't consider it a tap-to-toggle-layer press anymore. - if (g.NavWindowingToggleLayer && g.NavWindow) - apply_toggle_layer = true; - else if (!g.NavWindowingToggleLayer) - apply_focus_window = g.NavWindowingTarget; - g.NavWindowingTarget = NULL; - } - } - - // Keyboard: Focus - if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavKeyboard) - { - // Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise - g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); // 1.0f - if (IsKeyPressedMap(ImGuiKey_Tab, true)) - NavUpdateWindowingHighlightWindow(g.IO.KeyShift ? +1 : -1); - if (!g.IO.KeyCtrl) - apply_focus_window = g.NavWindowingTarget; - } - - // Keyboard: Press and Release ALT to toggle menu layer - // FIXME: We lack an explicit IO variable for "is the imgui window focused", so compare mouse validity to detect the common case of back-end clearing releases all keys on ALT-TAB - if ((g.ActiveId == 0 || g.ActiveIdAllowOverlap) && IsNavInputPressed(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Released)) - if (IsMousePosValid(&g.IO.MousePos) == IsMousePosValid(&g.IO.MousePosPrev)) - apply_toggle_layer = true; - - // Move window - if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove)) - { - ImVec2 move_delta; - if (g.NavInputSource == ImGuiInputSource_NavKeyboard && !g.IO.KeyShift) - move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down); - if (g.NavInputSource == ImGuiInputSource_NavGamepad) - move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down); - if (move_delta.x != 0.0f || move_delta.y != 0.0f) - { - const float NAV_MOVE_SPEED = 800.0f; - const float move_speed = ImFloor(NAV_MOVE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y)); // FIXME: Doesn't code variable framerate very well - g.NavWindowingTarget->RootWindow->Pos += move_delta * move_speed; - g.NavDisableMouseHover = true; - MarkIniSettingsDirty(g.NavWindowingTarget); - } - } - - // Apply final focus - if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindow)) - { - g.NavDisableHighlight = false; - g.NavDisableMouseHover = true; - apply_focus_window = NavRestoreLastChildNavWindow(apply_focus_window); - ClosePopupsOverWindow(apply_focus_window); - FocusWindow(apply_focus_window); - if (apply_focus_window->NavLastIds[0] == 0) - NavInitWindow(apply_focus_window, false); - - // If the window only has a menu layer, select it directly - if (apply_focus_window->DC.NavLayerActiveMask == (1 << 1)) - g.NavLayer = 1; - } - if (apply_focus_window) - g.NavWindowingTarget = NULL; - - // Apply menu/layer toggle - if (apply_toggle_layer && g.NavWindow) - { - // Move to parent menu if necessary - ImGuiWindow* new_nav_window = g.NavWindow; - while ((new_nav_window->DC.NavLayerActiveMask & (1 << 1)) == 0 && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0) - new_nav_window = new_nav_window->ParentWindow; - if (new_nav_window != g.NavWindow) - { - ImGuiWindow* old_nav_window = g.NavWindow; - FocusWindow(new_nav_window); - new_nav_window->NavLastChildNavWindow = old_nav_window; - } - g.NavDisableHighlight = false; - g.NavDisableMouseHover = true; - NavRestoreLayer((g.NavWindow->DC.NavLayerActiveMask & (1 << 1)) ? (g.NavLayer ^ 1) : 0); - } -} - static void SetWindowViewport(ImGuiWindow* window, ImGuiViewportP* viewport) { window->Viewport = viewport; @@ -8406,6 +8199,46 @@ void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags mov } } +// This needs to be called before we submit any widget (aka in or before Begin) +void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(window == g.NavWindow); + bool init_for_nav = false; + if (!(window->Flags & ImGuiWindowFlags_NoNavInputs)) + if (!(window->Flags & ImGuiWindowFlags_ChildWindow) || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit) + init_for_nav = true; + if (init_for_nav) + { + SetNavID(0, g.NavLayer); + g.NavInitRequest = true; + g.NavInitRequestFromMove = false; + g.NavInitResultId = 0; + g.NavInitResultRectRel = ImRect(); + NavUpdateAnyRequestFlag(); + } + else + { + g.NavId = window->NavLastIds[0]; + } +} + +static ImVec2 ImGui::NavCalcPreferredRefPos() +{ + ImGuiContext& g = *GImGui; + if (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow) + { + IM_ASSERT(ImGui::IsMousePosValid()); // This will probably trigger at some point, please share your repro! + return ImFloor(g.IO.MousePos); + } + + // When navigation is active and mouse is disabled, decide on an arbitrary position around the bottom left of the currently navigated item + const ImRect& rect_rel = g.NavWindow->NavRectRel[g.NavLayer]; + ImVec2 pos = g.NavWindow->Pos + ImVec2(rect_rel.Min.x + ImMin(g.Style.FramePadding.x*4, rect_rel.GetWidth()), rect_rel.Max.y - ImMin(g.Style.FramePadding.y, rect_rel.GetHeight())); + ImRect visible_rect = g.NavWindow->Viewport->GetRect(); + return ImFloor(ImClamp(pos, visible_rect.Min, visible_rect.Max)); // ImFloor() is important because non-integer mouse position application in back-end might be lossy and result in undesirable non-zero delta. +} + float ImGui::GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode) { ImGuiContext& g = *GImGui; @@ -8828,6 +8661,173 @@ static float ImGui::NavUpdatePageUpPageDown(int allowed_dir_flags) return 0.0f; } +static int FindWindowIndex(ImGuiWindow* window) // FIXME-OPT O(N) +{ + ImGuiContext& g = *GImGui; + for (int i = g.Windows.Size-1; i >= 0; i--) + if (g.Windows[i] == window) + return i; + return -1; +} + +static ImGuiWindow* FindWindowNavFocusable(int i_start, int i_stop, int dir) // FIXME-OPT O(N) +{ + ImGuiContext& g = *GImGui; + for (int i = i_start; i >= 0 && i < g.Windows.Size && i != i_stop; i += dir) + if (ImGui::IsWindowNavFocusable(g.Windows[i])) + return g.Windows[i]; + return NULL; +} + +static void NavUpdateWindowingHighlightWindow(int focus_change_dir) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(g.NavWindowingTarget); + if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal) + return; + + const int i_current = FindWindowIndex(g.NavWindowingTarget); + ImGuiWindow* window_target = FindWindowNavFocusable(i_current + focus_change_dir, -INT_MAX, focus_change_dir); + if (!window_target) + window_target = FindWindowNavFocusable((focus_change_dir < 0) ? (g.Windows.Size - 1) : 0, i_current, focus_change_dir); + if (window_target) // Don't reset windowing target if there's a single window in the list + g.NavWindowingTarget = g.NavWindowingTargetAnim = window_target; + g.NavWindowingToggleLayer = false; +} + +// Window management mode (hold to: change focus/move/resize, tap to: toggle menu layer) +static void ImGui::NavUpdateWindowing() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* apply_focus_window = NULL; + bool apply_toggle_layer = false; + + ImGuiWindow* modal_window = GetFrontMostPopupModal(); + if (modal_window != NULL) + { + g.NavWindowingTarget = NULL; + return; + } + + // Fade out + if (g.NavWindowingTargetAnim && g.NavWindowingTarget == NULL) + { + g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha - g.IO.DeltaTime * 10.0f, 0.0f); + if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f) + g.NavWindowingTargetAnim = NULL; + } + + // Start CTRL-TAB or Square+L/R window selection + bool start_windowing_with_gamepad = !g.NavWindowingTarget && IsNavInputPressed(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed); + bool start_windowing_with_keyboard = !g.NavWindowingTarget && g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard); + if (start_windowing_with_gamepad || start_windowing_with_keyboard) + if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.Windows.Size - 1, -INT_MAX, -1)) + { + g.NavWindowingTarget = g.NavWindowingTargetAnim = window; + g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f; + g.NavWindowingToggleLayer = start_windowing_with_keyboard ? false : true; + g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_NavKeyboard : ImGuiInputSource_NavGamepad; + } + + // Gamepad update + g.NavWindowingTimer += g.IO.DeltaTime; + if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavGamepad) + { + // Highlight only appears after a brief time holding the button, so that a fast tap on PadMenu (to toggle NavLayer) doesn't add visual noise + g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); + + // Select window to focus + const int focus_change_dir = (int)IsNavInputPressed(ImGuiNavInput_FocusPrev, ImGuiInputReadMode_RepeatSlow) - (int)IsNavInputPressed(ImGuiNavInput_FocusNext, ImGuiInputReadMode_RepeatSlow); + if (focus_change_dir != 0) + { + NavUpdateWindowingHighlightWindow(focus_change_dir); + g.NavWindowingHighlightAlpha = 1.0f; + } + + // Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered front-most) + if (!IsNavInputDown(ImGuiNavInput_Menu)) + { + g.NavWindowingToggleLayer &= (g.NavWindowingHighlightAlpha < 1.0f); // Once button was held long enough we don't consider it a tap-to-toggle-layer press anymore. + if (g.NavWindowingToggleLayer && g.NavWindow) + apply_toggle_layer = true; + else if (!g.NavWindowingToggleLayer) + apply_focus_window = g.NavWindowingTarget; + g.NavWindowingTarget = NULL; + } + } + + // Keyboard: Focus + if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavKeyboard) + { + // Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise + g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); // 1.0f + if (IsKeyPressedMap(ImGuiKey_Tab, true)) + NavUpdateWindowingHighlightWindow(g.IO.KeyShift ? +1 : -1); + if (!g.IO.KeyCtrl) + apply_focus_window = g.NavWindowingTarget; + } + + // Keyboard: Press and Release ALT to toggle menu layer + // FIXME: We lack an explicit IO variable for "is the imgui window focused", so compare mouse validity to detect the common case of back-end clearing releases all keys on ALT-TAB + if ((g.ActiveId == 0 || g.ActiveIdAllowOverlap) && IsNavInputPressed(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Released)) + if (IsMousePosValid(&g.IO.MousePos) == IsMousePosValid(&g.IO.MousePosPrev)) + apply_toggle_layer = true; + + // Move window + if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove)) + { + ImVec2 move_delta; + if (g.NavInputSource == ImGuiInputSource_NavKeyboard && !g.IO.KeyShift) + move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down); + if (g.NavInputSource == ImGuiInputSource_NavGamepad) + move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down); + if (move_delta.x != 0.0f || move_delta.y != 0.0f) + { + const float NAV_MOVE_SPEED = 800.0f; + const float move_speed = ImFloor(NAV_MOVE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y)); // FIXME: Doesn't code variable framerate very well + g.NavWindowingTarget->RootWindow->Pos += move_delta * move_speed; + g.NavDisableMouseHover = true; + MarkIniSettingsDirty(g.NavWindowingTarget); + } + } + + // Apply final focus + if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindow)) + { + g.NavDisableHighlight = false; + g.NavDisableMouseHover = true; + apply_focus_window = NavRestoreLastChildNavWindow(apply_focus_window); + ClosePopupsOverWindow(apply_focus_window); + FocusWindow(apply_focus_window); + if (apply_focus_window->NavLastIds[0] == 0) + NavInitWindow(apply_focus_window, false); + + // If the window only has a menu layer, select it directly + if (apply_focus_window->DC.NavLayerActiveMask == (1 << 1)) + g.NavLayer = 1; + } + if (apply_focus_window) + g.NavWindowingTarget = NULL; + + // Apply menu/layer toggle + if (apply_toggle_layer && g.NavWindow) + { + // Move to parent menu if necessary + ImGuiWindow* new_nav_window = g.NavWindow; + while ((new_nav_window->DC.NavLayerActiveMask & (1 << 1)) == 0 && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0) + new_nav_window = new_nav_window->ParentWindow; + if (new_nav_window != g.NavWindow) + { + ImGuiWindow* old_nav_window = g.NavWindow; + FocusWindow(new_nav_window); + new_nav_window->NavLastChildNavWindow = old_nav_window; + } + g.NavDisableHighlight = false; + g.NavDisableMouseHover = true; + NavRestoreLayer((g.NavWindow->DC.NavLayerActiveMask & (1 << 1)) ? (g.NavLayer ^ 1) : 0); + } +} + // Window has already passed the IsWindowNavFocusable() static const char* GetFallbackWindowNameForWindowingList(ImGuiWindow* window) { From 346f83e014cbcca372f397a0bd65745d32ad4367 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 30 Aug 2018 18:24:33 +0200 Subject: [PATCH 215/828] Refactor: Internals: Moved Navigation functions in imgui.cpp in their own section. DONE! (part 9) (#2036, #787) --- imgui.cpp | 72 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 37 insertions(+), 35 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index b4756e6d1dcf..da7528e8f7e5 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -899,6 +899,8 @@ static float NavUpdatePageUpPageDown(int allowed_dir_flags); static inline void NavUpdateAnyRequestFlag(); static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id); static ImVec2 NavCalcPreferredRefPos(); +static void NavSaveLastChildNavWindow(ImGuiWindow* nav_window); +static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window); static void UpdateMouseInputs(); static void UpdateMouseWheel(); @@ -2339,41 +2341,6 @@ void ImGui::ItemSize(const ImRect& bb, float text_offset_y) ItemSize(bb.GetSize(), text_offset_y); } -static void NavSaveLastChildNavWindow(ImGuiWindow* child_window) -{ - ImGuiWindow* parent_window = child_window; - while (parent_window && (parent_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (parent_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0) - parent_window = parent_window->ParentWindow; - if (parent_window && parent_window != child_window) - parent_window->NavLastChildNavWindow = child_window; -} - -// Call when we are expected to land on Layer 0 after FocusWindow() -static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window) -{ - return window->NavLastChildNavWindow ? window->NavLastChildNavWindow : window; -} - -static void NavRestoreLayer(int layer) -{ - ImGuiContext& g = *GImGui; - g.NavLayer = layer; - if (layer == 0) - g.NavWindow = NavRestoreLastChildNavWindow(g.NavWindow); - if (layer == 0 && g.NavWindow->NavLastIds[0] != 0) - ImGui::SetNavIDWithRectRel(g.NavWindow->NavLastIds[0], layer, g.NavWindow->NavRectRel[0]); - else - ImGui::NavInitWindow(g.NavWindow, true); -} - -static inline void ImGui::NavUpdateAnyRequestFlag() -{ - ImGuiContext& g = *GImGui; - g.NavAnyRequest = g.NavMoveRequest || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL); - if (g.NavAnyRequest) - IM_ASSERT(g.NavWindow != NULL); -} - // Declare item bounding box for clipping and interaction. // Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface // declare their minimum size requirement to ItemSize() and then use a larger region for drawing/interaction, which is passed to ItemAdd(). @@ -8199,6 +8166,41 @@ void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags mov } } +static void ImGui::NavSaveLastChildNavWindow(ImGuiWindow* nav_window) +{ + ImGuiWindow* parent_window = nav_window; + while (parent_window && (parent_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (parent_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0) + parent_window = parent_window->ParentWindow; + if (parent_window && parent_window != nav_window) + parent_window->NavLastChildNavWindow = nav_window; +} + +// Call when we are expected to land on Layer 0 after FocusWindow() +static ImGuiWindow* ImGui::NavRestoreLastChildNavWindow(ImGuiWindow* window) +{ + return window->NavLastChildNavWindow ? window->NavLastChildNavWindow : window; +} + +static void NavRestoreLayer(int layer) +{ + ImGuiContext& g = *GImGui; + g.NavLayer = layer; + if (layer == 0) + g.NavWindow = ImGui::NavRestoreLastChildNavWindow(g.NavWindow); + if (layer == 0 && g.NavWindow->NavLastIds[0] != 0) + ImGui::SetNavIDWithRectRel(g.NavWindow->NavLastIds[0], layer, g.NavWindow->NavRectRel[0]); + else + ImGui::NavInitWindow(g.NavWindow, true); +} + +static inline void ImGui::NavUpdateAnyRequestFlag() +{ + ImGuiContext& g = *GImGui; + g.NavAnyRequest = g.NavMoveRequest || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL); + if (g.NavAnyRequest) + IM_ASSERT(g.NavWindow != NULL); +} + // This needs to be called before we submit any widget (aka in or before Begin) void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit) { From a58e4dfd0f55e9dc91f270ab9586d79d091f9272 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 30 Aug 2018 20:42:50 +0200 Subject: [PATCH 216/828] Refactor: Moved Scrollbar function from imgui.cpp to imgui_widgets.cpp, added file index (#2036) --- imgui.cpp | 110 --------------------------------------- imgui_draw.cpp | 2 +- imgui_widgets.cpp | 128 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 129 insertions(+), 111 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index da7528e8f7e5..d3753b68a66c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6475,116 +6475,6 @@ void ImGui::End() SetCurrentViewport(g.CurrentWindow, g.CurrentWindow->Viewport); } -// Vertical scrollbar -// The entire piece of code below is rather confusing because: -// - We handle absolute seeking (when first clicking outside the grab) and relative manipulation (afterward or when clicking inside the grab) -// - We store values as normalized ratio and in a form that allows the window content to change while we are holding on a scrollbar -// - We handle both horizontal and vertical scrollbars, which makes the terminology not ideal. -void ImGui::Scrollbar(ImGuiLayoutType direction) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - const bool horizontal = (direction == ImGuiLayoutType_Horizontal); - const ImGuiStyle& style = g.Style; - const ImGuiID id = window->GetID(horizontal ? "#SCROLLX" : "#SCROLLY"); - - // Render background - bool other_scrollbar = (horizontal ? window->ScrollbarY : window->ScrollbarX); - float other_scrollbar_size_w = other_scrollbar ? style.ScrollbarSize : 0.0f; - const ImRect window_rect = window->Rect(); - const float border_size = window->WindowBorderSize; - ImRect bb = horizontal - ? ImRect(window->Pos.x + border_size, window_rect.Max.y - style.ScrollbarSize, window_rect.Max.x - other_scrollbar_size_w - border_size, window_rect.Max.y - border_size) - : ImRect(window_rect.Max.x - style.ScrollbarSize, window->Pos.y + border_size, window_rect.Max.x - border_size, window_rect.Max.y - other_scrollbar_size_w - border_size); - if (!horizontal) - bb.Min.y += window->TitleBarHeight() + ((window->Flags & ImGuiWindowFlags_MenuBar) ? window->MenuBarHeight() : 0.0f); - if (bb.GetWidth() <= 0.0f || bb.GetHeight() <= 0.0f) - return; - - int window_rounding_corners; - if (horizontal) - window_rounding_corners = ImDrawCornerFlags_BotLeft | (other_scrollbar ? 0 : ImDrawCornerFlags_BotRight); - else - window_rounding_corners = (((window->Flags & ImGuiWindowFlags_NoTitleBar) && !(window->Flags & ImGuiWindowFlags_MenuBar)) ? ImDrawCornerFlags_TopRight : 0) | (other_scrollbar ? 0 : ImDrawCornerFlags_BotRight); - window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_ScrollbarBg), window->WindowRounding, window_rounding_corners); - bb.Expand(ImVec2(-ImClamp((float)(int)((bb.Max.x - bb.Min.x - 2.0f) * 0.5f), 0.0f, 3.0f), -ImClamp((float)(int)((bb.Max.y - bb.Min.y - 2.0f) * 0.5f), 0.0f, 3.0f))); - - // V denote the main, longer axis of the scrollbar (= height for a vertical scrollbar) - float scrollbar_size_v = horizontal ? bb.GetWidth() : bb.GetHeight(); - float scroll_v = horizontal ? window->Scroll.x : window->Scroll.y; - float win_size_avail_v = (horizontal ? window->SizeFull.x : window->SizeFull.y) - other_scrollbar_size_w; - float win_size_contents_v = horizontal ? window->SizeContents.x : window->SizeContents.y; - - // Calculate the height of our grabbable box. It generally represent the amount visible (vs the total scrollable amount) - // But we maintain a minimum size in pixel to allow for the user to still aim inside. - IM_ASSERT(ImMax(win_size_contents_v, win_size_avail_v) > 0.0f); // Adding this assert to check if the ImMax(XXX,1.0f) is still needed. PLEASE CONTACT ME if this triggers. - const float win_size_v = ImMax(ImMax(win_size_contents_v, win_size_avail_v), 1.0f); - const float grab_h_pixels = ImClamp(scrollbar_size_v * (win_size_avail_v / win_size_v), style.GrabMinSize, scrollbar_size_v); - const float grab_h_norm = grab_h_pixels / scrollbar_size_v; - - // Handle input right away. None of the code of Begin() is relying on scrolling position before calling Scrollbar(). - bool held = false; - bool hovered = false; - const bool previously_held = (g.ActiveId == id); - ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_NoNavFocus); - - float scroll_max = ImMax(1.0f, win_size_contents_v - win_size_avail_v); - float scroll_ratio = ImSaturate(scroll_v / scroll_max); - float grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v; - if (held && grab_h_norm < 1.0f) - { - float scrollbar_pos_v = horizontal ? bb.Min.x : bb.Min.y; - float mouse_pos_v = horizontal ? g.IO.MousePos.x : g.IO.MousePos.y; - float* click_delta_to_grab_center_v = horizontal ? &g.ScrollbarClickDeltaToGrabCenter.x : &g.ScrollbarClickDeltaToGrabCenter.y; - - // Click position in scrollbar normalized space (0.0f->1.0f) - const float clicked_v_norm = ImSaturate((mouse_pos_v - scrollbar_pos_v) / scrollbar_size_v); - SetHoveredID(id); - - bool seek_absolute = false; - if (!previously_held) - { - // On initial click calculate the distance between mouse and the center of the grab - if (clicked_v_norm >= grab_v_norm && clicked_v_norm <= grab_v_norm + grab_h_norm) - { - *click_delta_to_grab_center_v = clicked_v_norm - grab_v_norm - grab_h_norm*0.5f; - } - else - { - seek_absolute = true; - *click_delta_to_grab_center_v = 0.0f; - } - } - - // Apply scroll - // It is ok to modify Scroll here because we are being called in Begin() after the calculation of SizeContents and before setting up our starting position - const float scroll_v_norm = ImSaturate((clicked_v_norm - *click_delta_to_grab_center_v - grab_h_norm*0.5f) / (1.0f - grab_h_norm)); - scroll_v = (float)(int)(0.5f + scroll_v_norm * scroll_max);//(win_size_contents_v - win_size_v)); - if (horizontal) - window->Scroll.x = scroll_v; - else - window->Scroll.y = scroll_v; - - // Update values for rendering - scroll_ratio = ImSaturate(scroll_v / scroll_max); - grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v; - - // Update distance to grab now that we have seeked and saturated - if (seek_absolute) - *click_delta_to_grab_center_v = clicked_v_norm - grab_v_norm - grab_h_norm*0.5f; - } - - // Render - const ImU32 grab_col = GetColorU32(held ? ImGuiCol_ScrollbarGrabActive : hovered ? ImGuiCol_ScrollbarGrabHovered : ImGuiCol_ScrollbarGrab); - ImRect grab_rect; - if (horizontal) - grab_rect = ImRect(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm), bb.Min.y, ImMin(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm) + grab_h_pixels, window_rect.Max.x), bb.Max.y); - else - grab_rect = ImRect(bb.Min.x, ImLerp(bb.Min.y, bb.Max.y, grab_v_norm), bb.Max.x, ImMin(ImLerp(bb.Min.y, bb.Max.y, grab_v_norm) + grab_h_pixels, window_rect.Max.y)); - window->DrawList->AddRectFilled(grab_rect.Min, grab_rect.Max, grab_col, style.ScrollbarRounding); -} - void ImGui::BringWindowToFront(ImGuiWindow* window) { ImGuiContext& g = *GImGui; diff --git a/imgui_draw.cpp b/imgui_draw.cpp index c70530560a05..fec936a715e2 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1,7 +1,7 @@ // dear imgui, v1.64 WIP // (drawing and font code) -// Contains implementation for +// Index of this file: // - Default styles // - ImDrawList // - ImDrawData diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 9dafd29925eb..069a23c48163 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1,6 +1,23 @@ // dear imgui, v1.64 WIP // (widgets code) +// Index of this file: +// - Widgets: Text, etc. +// - Widgets: Button, Image, Checkbox, RadioButton, ProgressBar, Bullet, etc. +// - Widgets: ComboBox +// - Data Type and Data Formatting Helpers +// - Widgets: DragScalar, DragFloat, DragInt, etc. +// - Widgets: SliderScalar, SliderFloat, SliderInt, etc. +// - Widgets: InputScalar, InputFloat, InputInt, etc. +// - Widgets: InputText, InputTextMultiline +// - Widgets: ColorEdit, ColorPicker, ColorButton, etc. +// - Widgets: TreeNode, TreePush, TreePop, etc. +// - Widgets: Selectable +// - Widgets: ListBox +// - Widgets: PlotLines, PlotHistogram +// - Widgets: Value +// - Widgets: MenuItem, BeginMenu, EndMenu, etc. + #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) #define _CRT_SECURE_NO_WARNINGS #endif @@ -347,6 +364,7 @@ void ImGui::BulletTextV(const char* fmt, va_list args) // - ArrowButton() // - CloseButton() [Internal] // - CollapseButton() [Internal] +// - Scrollbar() [Internal] // - Image() // - ImageButton() // - Checkbox() @@ -673,6 +691,116 @@ bool ImGui::CollapseButton(ImGuiID id, const ImVec2& pos) return pressed; } +// Vertical/Horizontal scrollbar +// The entire piece of code below is rather confusing because: +// - We handle absolute seeking (when first clicking outside the grab) and relative manipulation (afterward or when clicking inside the grab) +// - We store values as normalized ratio and in a form that allows the window content to change while we are holding on a scrollbar +// - We handle both horizontal and vertical scrollbars, which makes the terminology not ideal. +void ImGui::Scrollbar(ImGuiLayoutType direction) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + + const bool horizontal = (direction == ImGuiLayoutType_Horizontal); + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(horizontal ? "#SCROLLX" : "#SCROLLY"); + + // Render background + bool other_scrollbar = (horizontal ? window->ScrollbarY : window->ScrollbarX); + float other_scrollbar_size_w = other_scrollbar ? style.ScrollbarSize : 0.0f; + const ImRect window_rect = window->Rect(); + const float border_size = window->WindowBorderSize; + ImRect bb = horizontal + ? ImRect(window->Pos.x + border_size, window_rect.Max.y - style.ScrollbarSize, window_rect.Max.x - other_scrollbar_size_w - border_size, window_rect.Max.y - border_size) + : ImRect(window_rect.Max.x - style.ScrollbarSize, window->Pos.y + border_size, window_rect.Max.x - border_size, window_rect.Max.y - other_scrollbar_size_w - border_size); + if (!horizontal) + bb.Min.y += window->TitleBarHeight() + ((window->Flags & ImGuiWindowFlags_MenuBar) ? window->MenuBarHeight() : 0.0f); + if (bb.GetWidth() <= 0.0f || bb.GetHeight() <= 0.0f) + return; + + int window_rounding_corners; + if (horizontal) + window_rounding_corners = ImDrawCornerFlags_BotLeft | (other_scrollbar ? 0 : ImDrawCornerFlags_BotRight); + else + window_rounding_corners = (((window->Flags & ImGuiWindowFlags_NoTitleBar) && !(window->Flags & ImGuiWindowFlags_MenuBar)) ? ImDrawCornerFlags_TopRight : 0) | (other_scrollbar ? 0 : ImDrawCornerFlags_BotRight); + window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_ScrollbarBg), window->WindowRounding, window_rounding_corners); + bb.Expand(ImVec2(-ImClamp((float)(int)((bb.Max.x - bb.Min.x - 2.0f) * 0.5f), 0.0f, 3.0f), -ImClamp((float)(int)((bb.Max.y - bb.Min.y - 2.0f) * 0.5f), 0.0f, 3.0f))); + + // V denote the main, longer axis of the scrollbar (= height for a vertical scrollbar) + float scrollbar_size_v = horizontal ? bb.GetWidth() : bb.GetHeight(); + float scroll_v = horizontal ? window->Scroll.x : window->Scroll.y; + float win_size_avail_v = (horizontal ? window->SizeFull.x : window->SizeFull.y) - other_scrollbar_size_w; + float win_size_contents_v = horizontal ? window->SizeContents.x : window->SizeContents.y; + + // Calculate the height of our grabbable box. It generally represent the amount visible (vs the total scrollable amount) + // But we maintain a minimum size in pixel to allow for the user to still aim inside. + IM_ASSERT(ImMax(win_size_contents_v, win_size_avail_v) > 0.0f); // Adding this assert to check if the ImMax(XXX,1.0f) is still needed. PLEASE CONTACT ME if this triggers. + const float win_size_v = ImMax(ImMax(win_size_contents_v, win_size_avail_v), 1.0f); + const float grab_h_pixels = ImClamp(scrollbar_size_v * (win_size_avail_v / win_size_v), style.GrabMinSize, scrollbar_size_v); + const float grab_h_norm = grab_h_pixels / scrollbar_size_v; + + // Handle input right away. None of the code of Begin() is relying on scrolling position before calling Scrollbar(). + bool held = false; + bool hovered = false; + const bool previously_held = (g.ActiveId == id); + ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_NoNavFocus); + + float scroll_max = ImMax(1.0f, win_size_contents_v - win_size_avail_v); + float scroll_ratio = ImSaturate(scroll_v / scroll_max); + float grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v; + if (held && grab_h_norm < 1.0f) + { + float scrollbar_pos_v = horizontal ? bb.Min.x : bb.Min.y; + float mouse_pos_v = horizontal ? g.IO.MousePos.x : g.IO.MousePos.y; + float* click_delta_to_grab_center_v = horizontal ? &g.ScrollbarClickDeltaToGrabCenter.x : &g.ScrollbarClickDeltaToGrabCenter.y; + + // Click position in scrollbar normalized space (0.0f->1.0f) + const float clicked_v_norm = ImSaturate((mouse_pos_v - scrollbar_pos_v) / scrollbar_size_v); + SetHoveredID(id); + + bool seek_absolute = false; + if (!previously_held) + { + // On initial click calculate the distance between mouse and the center of the grab + if (clicked_v_norm >= grab_v_norm && clicked_v_norm <= grab_v_norm + grab_h_norm) + { + *click_delta_to_grab_center_v = clicked_v_norm - grab_v_norm - grab_h_norm*0.5f; + } + else + { + seek_absolute = true; + *click_delta_to_grab_center_v = 0.0f; + } + } + + // Apply scroll + // It is ok to modify Scroll here because we are being called in Begin() after the calculation of SizeContents and before setting up our starting position + const float scroll_v_norm = ImSaturate((clicked_v_norm - *click_delta_to_grab_center_v - grab_h_norm*0.5f) / (1.0f - grab_h_norm)); + scroll_v = (float)(int)(0.5f + scroll_v_norm * scroll_max);//(win_size_contents_v - win_size_v)); + if (horizontal) + window->Scroll.x = scroll_v; + else + window->Scroll.y = scroll_v; + + // Update values for rendering + scroll_ratio = ImSaturate(scroll_v / scroll_max); + grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v; + + // Update distance to grab now that we have seeked and saturated + if (seek_absolute) + *click_delta_to_grab_center_v = clicked_v_norm - grab_v_norm - grab_h_norm*0.5f; + } + + // Render + const ImU32 grab_col = GetColorU32(held ? ImGuiCol_ScrollbarGrabActive : hovered ? ImGuiCol_ScrollbarGrabHovered : ImGuiCol_ScrollbarGrab); + ImRect grab_rect; + if (horizontal) + grab_rect = ImRect(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm), bb.Min.y, ImMin(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm) + grab_h_pixels, window_rect.Max.x), bb.Max.y); + else + grab_rect = ImRect(bb.Min.x, ImLerp(bb.Min.y, bb.Max.y, grab_v_norm), bb.Max.x, ImMin(ImLerp(bb.Min.y, bb.Max.y, grab_v_norm) + grab_h_pixels, window_rect.Max.y)); + window->DrawList->AddRectFilled(grab_rect.Min, grab_rect.Max, grab_col, style.ScrollbarRounding); +} + void ImGui::Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col) { ImGuiWindow* window = GetCurrentWindow(); From b1f263b34b00033e99a5277a6e44ff09391600e0 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 30 Aug 2018 20:58:04 +0200 Subject: [PATCH 217/828] Refactor: Internals: Moved Popup functions in imgui.cpp in their own section. (part 1) (#2036) --- imgui.cpp | 308 +++++++++++++++++++++++++++--------------------------- 1 file changed, 155 insertions(+), 153 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index d3753b68a66c..f7cc2558b684 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4709,132 +4709,6 @@ void ImGui::EndTooltip() End(); } -// Mark popup as open (toggle toward open state). -// Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block. -// Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level). -// One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL) -void ImGui::OpenPopupEx(ImGuiID id) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* parent_window = g.CurrentWindow; - int current_stack_size = g.CurrentPopupStack.Size; - ImGuiPopupRef popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack. - popup_ref.PopupId = id; - popup_ref.Window = NULL; - popup_ref.ParentWindow = parent_window; - popup_ref.OpenFrameCount = g.FrameCount; - popup_ref.OpenParentId = parent_window->IDStack.back(); - popup_ref.OpenMousePos = g.IO.MousePos; - popup_ref.OpenPopupPos = NavCalcPreferredRefPos(); - - //printf("[%05d] OpenPopupEx(0x%08X)\n", g.FrameCount, id); - if (g.OpenPopupStack.Size < current_stack_size + 1) - { - g.OpenPopupStack.push_back(popup_ref); - } - else - { - // Gently handle the user mistakenly calling OpenPopup() every frame. It is a programming mistake! However, if we were to run the regular code path, the ui - // would become completely unusable because the popup will always be in hidden-while-calculating-size state _while_ claiming focus. Which would be a very confusing - // situation for the programmer. Instead, we silently allow the popup to proceed, it will keep reappearing and the programming error will be more obvious to understand. - if (g.OpenPopupStack[current_stack_size].PopupId == id && g.OpenPopupStack[current_stack_size].OpenFrameCount == g.FrameCount - 1) - { - g.OpenPopupStack[current_stack_size].OpenFrameCount = popup_ref.OpenFrameCount; - } - else - { - // Close child popups if any, then flag popup for open/reopen - g.OpenPopupStack.resize(current_stack_size + 1); - g.OpenPopupStack[current_stack_size] = popup_ref; - } - - // When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by ClosePopupsOverWindow(). - // This is equivalent to what ClosePopupToLevel() does. - //if (g.OpenPopupStack[current_stack_size].PopupId == id) - // FocusWindow(parent_window); - } -} - -void ImGui::OpenPopup(const char* str_id) -{ - ImGuiContext& g = *GImGui; - OpenPopupEx(g.CurrentWindow->GetID(str_id)); -} - -void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window) -{ - ImGuiContext& g = *GImGui; - if (g.OpenPopupStack.empty()) - return; - - // When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it. - // Don't close our own child popup windows. - int n = 0; - if (ref_window) - { - for (n = 0; n < g.OpenPopupStack.Size; n++) - { - ImGuiPopupRef& popup = g.OpenPopupStack[n]; - if (!popup.Window) - continue; - IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0); - if (popup.Window->Flags & ImGuiWindowFlags_ChildWindow) - continue; - - // Trim the stack if popups are not direct descendant of the reference window (which is often the NavWindow) - bool has_focus = false; - for (int m = n; m < g.OpenPopupStack.Size && !has_focus; m++) - has_focus = (g.OpenPopupStack[m].Window && g.OpenPopupStack[m].Window->RootWindow == ref_window->RootWindow); - if (!has_focus) - break; - } - } - if (n < g.OpenPopupStack.Size) // This test is not required but it allows to set a convenient breakpoint on the block below - ClosePopupToLevel(n); -} - -ImGuiWindow* ImGui::GetFrontMostPopupModal() -{ - ImGuiContext& g = *GImGui; - for (int n = g.OpenPopupStack.Size-1; n >= 0; n--) - if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window) - if (popup->Flags & ImGuiWindowFlags_Modal) - return popup; - return NULL; -} - -void ImGui::ClosePopupToLevel(int remaining) -{ - IM_ASSERT(remaining >= 0); - ImGuiContext& g = *GImGui; - ImGuiWindow* focus_window = (remaining > 0) ? g.OpenPopupStack[remaining-1].Window : g.OpenPopupStack[0].ParentWindow; - if (g.NavLayer == 0) - focus_window = NavRestoreLastChildNavWindow(focus_window); - FocusWindow(focus_window); - focus_window->DC.NavHideHighlightOneFrame = true; - g.OpenPopupStack.resize(remaining); -} - -void ImGui::ClosePopup(ImGuiID id) -{ - if (!IsPopupOpen(id)) - return; - ImGuiContext& g = *GImGui; - ClosePopupToLevel(g.OpenPopupStack.Size - 1); -} - -// Close the popup we have begin-ed into. -void ImGui::CloseCurrentPopup() -{ - ImGuiContext& g = *GImGui; - int popup_idx = g.CurrentPopupStack.Size - 1; - if (popup_idx < 0 || popup_idx >= g.OpenPopupStack.Size || g.CurrentPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId) - return; - while (popup_idx > 0 && g.OpenPopupStack[popup_idx].Window && (g.OpenPopupStack[popup_idx].Window->Flags & ImGuiWindowFlags_ChildMenu)) - popup_idx--; - ClosePopupToLevel(popup_idx); -} - bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags) { ImGuiContext& g = *GImGui; @@ -4868,18 +4742,6 @@ bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags) return BeginPopupEx(g.CurrentWindow->GetID(str_id), flags|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings); } -bool ImGui::IsPopupOpen(ImGuiID id) -{ - ImGuiContext& g = *GImGui; - return g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].PopupId == id; -} - -bool ImGui::IsPopupOpen(const char* str_id) -{ - ImGuiContext& g = *GImGui; - return g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].PopupId == g.CurrentWindow->GetID(str_id); -} - bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags) { ImGuiContext& g = *GImGui; @@ -4904,7 +4766,6 @@ bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags fla ClosePopup(id); return false; } - return is_open; } @@ -4920,19 +4781,6 @@ void ImGui::EndPopup() End(); } -bool ImGui::OpenPopupOnItemClick(const char* str_id, int mouse_button) -{ - ImGuiWindow* window = GImGui->CurrentWindow; - if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) - { - ImGuiID id = str_id ? window->GetID(str_id) : window->DC.LastItemId; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict! - IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item) - OpenPopupEx(id); - return true; - } - return false; -} - // This is a helper to handle the simplest case of associating one named popup to one given widget. // You may want to handle this on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters). // You can pass a NULL str_id to use the identifier of the last item. @@ -5010,7 +4858,6 @@ static bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size SetActiveID(id+1, child_window); // Steal ActiveId with a dummy id so that key-press won't activate child item g.ActiveIdSource = ImGuiInputSource_Nav; } - return ret; } @@ -7761,6 +7608,161 @@ void ImGui::Unindent(float indent_w) window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x; } +//----------------------------------------------------------------------------- +// POPUPS +//----------------------------------------------------------------------------- + +bool ImGui::IsPopupOpen(ImGuiID id) +{ + ImGuiContext& g = *GImGui; + return g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].PopupId == id; +} + +bool ImGui::IsPopupOpen(const char* str_id) +{ + ImGuiContext& g = *GImGui; + return g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].PopupId == g.CurrentWindow->GetID(str_id); +} + +ImGuiWindow* ImGui::GetFrontMostPopupModal() +{ + ImGuiContext& g = *GImGui; + for (int n = g.OpenPopupStack.Size-1; n >= 0; n--) + if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window) + if (popup->Flags & ImGuiWindowFlags_Modal) + return popup; + return NULL; +} + +void ImGui::OpenPopup(const char* str_id) +{ + ImGuiContext& g = *GImGui; + OpenPopupEx(g.CurrentWindow->GetID(str_id)); +} + +// Mark popup as open (toggle toward open state). +// Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block. +// Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level). +// One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL) +void ImGui::OpenPopupEx(ImGuiID id) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* parent_window = g.CurrentWindow; + int current_stack_size = g.CurrentPopupStack.Size; + ImGuiPopupRef popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack. + popup_ref.PopupId = id; + popup_ref.Window = NULL; + popup_ref.ParentWindow = parent_window; + popup_ref.OpenFrameCount = g.FrameCount; + popup_ref.OpenParentId = parent_window->IDStack.back(); + popup_ref.OpenMousePos = g.IO.MousePos; + popup_ref.OpenPopupPos = NavCalcPreferredRefPos(); + + //printf("[%05d] OpenPopupEx(0x%08X)\n", g.FrameCount, id); + if (g.OpenPopupStack.Size < current_stack_size + 1) + { + g.OpenPopupStack.push_back(popup_ref); + } + else + { + // Gently handle the user mistakenly calling OpenPopup() every frame. It is a programming mistake! However, if we were to run the regular code path, the ui + // would become completely unusable because the popup will always be in hidden-while-calculating-size state _while_ claiming focus. Which would be a very confusing + // situation for the programmer. Instead, we silently allow the popup to proceed, it will keep reappearing and the programming error will be more obvious to understand. + if (g.OpenPopupStack[current_stack_size].PopupId == id && g.OpenPopupStack[current_stack_size].OpenFrameCount == g.FrameCount - 1) + { + g.OpenPopupStack[current_stack_size].OpenFrameCount = popup_ref.OpenFrameCount; + } + else + { + // Close child popups if any, then flag popup for open/reopen + g.OpenPopupStack.resize(current_stack_size + 1); + g.OpenPopupStack[current_stack_size] = popup_ref; + } + + // When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by ClosePopupsOverWindow(). + // This is equivalent to what ClosePopupToLevel() does. + //if (g.OpenPopupStack[current_stack_size].PopupId == id) + // FocusWindow(parent_window); + } +} + +bool ImGui::OpenPopupOnItemClick(const char* str_id, int mouse_button) +{ + ImGuiWindow* window = GImGui->CurrentWindow; + if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) + { + ImGuiID id = str_id ? window->GetID(str_id) : window->DC.LastItemId; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict! + IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item) + OpenPopupEx(id); + return true; + } + return false; +} + +void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window) +{ + ImGuiContext& g = *GImGui; + if (g.OpenPopupStack.empty()) + return; + + // When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it. + // Don't close our own child popup windows. + int n = 0; + if (ref_window) + { + for (n = 0; n < g.OpenPopupStack.Size; n++) + { + ImGuiPopupRef& popup = g.OpenPopupStack[n]; + if (!popup.Window) + continue; + IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0); + if (popup.Window->Flags & ImGuiWindowFlags_ChildWindow) + continue; + + // Trim the stack if popups are not direct descendant of the reference window (which is often the NavWindow) + bool has_focus = false; + for (int m = n; m < g.OpenPopupStack.Size && !has_focus; m++) + has_focus = (g.OpenPopupStack[m].Window && g.OpenPopupStack[m].Window->RootWindow == ref_window->RootWindow); + if (!has_focus) + break; + } + } + if (n < g.OpenPopupStack.Size) // This test is not required but it allows to set a convenient breakpoint on the block below + ClosePopupToLevel(n); +} + +void ImGui::ClosePopupToLevel(int remaining) +{ + IM_ASSERT(remaining >= 0); + ImGuiContext& g = *GImGui; + ImGuiWindow* focus_window = (remaining > 0) ? g.OpenPopupStack[remaining-1].Window : g.OpenPopupStack[0].ParentWindow; + if (g.NavLayer == 0) + focus_window = NavRestoreLastChildNavWindow(focus_window); + FocusWindow(focus_window); + focus_window->DC.NavHideHighlightOneFrame = true; + g.OpenPopupStack.resize(remaining); +} + +void ImGui::ClosePopup(ImGuiID id) +{ + if (!IsPopupOpen(id)) + return; + ImGuiContext& g = *GImGui; + ClosePopupToLevel(g.OpenPopupStack.Size - 1); +} + +// Close the popup we have begin-ed into. +void ImGui::CloseCurrentPopup() +{ + ImGuiContext& g = *GImGui; + int popup_idx = g.CurrentPopupStack.Size - 1; + if (popup_idx < 0 || popup_idx >= g.OpenPopupStack.Size || g.CurrentPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId) + return; + while (popup_idx > 0 && g.OpenPopupStack[popup_idx].Window && (g.OpenPopupStack[popup_idx].Window->Flags & ImGuiWindowFlags_ChildMenu)) + popup_idx--; + ClosePopupToLevel(popup_idx); +} + //----------------------------------------------------------------------------- // NAVIGATION //----------------------------------------------------------------------------- From d7c04ccbfb2f039d0b5e2978dca646c47750e51c Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 30 Aug 2018 20:59:24 +0200 Subject: [PATCH 218/828] Refactor: Internals: Moved Popup functions in imgui.cpp in their own section. (part 2) (#2036) --- imgui.cpp | 212 +++++++++++++++++++++++++++--------------------------- 1 file changed, 106 insertions(+), 106 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index f7cc2558b684..0a889913bfd6 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4709,112 +4709,6 @@ void ImGui::EndTooltip() End(); } -bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags) -{ - ImGuiContext& g = *GImGui; - if (!IsPopupOpen(id)) - { - g.NextWindowData.Clear(); // We behave like Begin() and need to consume those values - return false; - } - - char name[20]; - if (extra_flags & ImGuiWindowFlags_ChildMenu) - ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.CurrentPopupStack.Size); // Recycle windows based on depth - else - ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame - - bool is_open = Begin(name, NULL, extra_flags | ImGuiWindowFlags_Popup); - if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display) - EndPopup(); - - return is_open; -} - -bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags) -{ - ImGuiContext& g = *GImGui; - if (g.OpenPopupStack.Size <= g.CurrentPopupStack.Size) // Early out for performance - { - g.NextWindowData.Clear(); // We behave like Begin() and need to consume those values - return false; - } - return BeginPopupEx(g.CurrentWindow->GetID(str_id), flags|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings); -} - -bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - const ImGuiID id = window->GetID(name); - if (!IsPopupOpen(id)) - { - g.NextWindowData.Clear(); // We behave like Begin() and need to consume those values - return false; - } - - // Center modal windows by default - // FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window. - if (g.NextWindowData.PosCond == 0) - SetNextWindowPos(window->Viewport->GetCenter(), ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); - - bool is_open = Begin(name, p_open, flags | ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings); - if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display) - { - EndPopup(); - if (is_open) - ClosePopup(id); - return false; - } - return is_open; -} - -void ImGui::EndPopup() -{ - ImGuiContext& g = *GImGui; (void)g; - IM_ASSERT(g.CurrentWindow->Flags & ImGuiWindowFlags_Popup); // Mismatched BeginPopup()/EndPopup() calls - IM_ASSERT(g.CurrentPopupStack.Size > 0); - - // Make all menus and popups wrap around for now, may need to expose that policy. - NavMoveRequestTryWrapping(g.CurrentWindow, ImGuiNavMoveFlags_LoopY); - - End(); -} - -// This is a helper to handle the simplest case of associating one named popup to one given widget. -// You may want to handle this on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters). -// You can pass a NULL str_id to use the identifier of the last item. -bool ImGui::BeginPopupContextItem(const char* str_id, int mouse_button) -{ - ImGuiWindow* window = GImGui->CurrentWindow; - ImGuiID id = str_id ? window->GetID(str_id) : window->DC.LastItemId; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict! - IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item) - if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) - OpenPopupEx(id); - return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings); -} - -bool ImGui::BeginPopupContextWindow(const char* str_id, int mouse_button, bool also_over_items) -{ - if (!str_id) - str_id = "window_context"; - ImGuiID id = GImGui->CurrentWindow->GetID(str_id); - if (IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) - if (also_over_items || !IsAnyItemHovered()) - OpenPopupEx(id); - return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings); -} - -bool ImGui::BeginPopupContextVoid(const char* str_id, int mouse_button) -{ - if (!str_id) - str_id = "void_context"; - ImGuiID id = GImGui->CurrentWindow->GetID(str_id); - if (IsMouseReleased(mouse_button) && !IsWindowHovered(ImGuiHoveredFlags_AnyWindow)) - OpenPopupEx(id); - return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings); -} - static bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags) { ImGuiContext& g = *GImGui; @@ -7763,6 +7657,112 @@ void ImGui::CloseCurrentPopup() ClosePopupToLevel(popup_idx); } +bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags) +{ + ImGuiContext& g = *GImGui; + if (!IsPopupOpen(id)) + { + g.NextWindowData.Clear(); // We behave like Begin() and need to consume those values + return false; + } + + char name[20]; + if (extra_flags & ImGuiWindowFlags_ChildMenu) + ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.CurrentPopupStack.Size); // Recycle windows based on depth + else + ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame + + bool is_open = Begin(name, NULL, extra_flags | ImGuiWindowFlags_Popup); + if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display) + EndPopup(); + + return is_open; +} + +bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags) +{ + ImGuiContext& g = *GImGui; + if (g.OpenPopupStack.Size <= g.CurrentPopupStack.Size) // Early out for performance + { + g.NextWindowData.Clear(); // We behave like Begin() and need to consume those values + return false; + } + return BeginPopupEx(g.CurrentWindow->GetID(str_id), flags|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings); +} + +bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + const ImGuiID id = window->GetID(name); + if (!IsPopupOpen(id)) + { + g.NextWindowData.Clear(); // We behave like Begin() and need to consume those values + return false; + } + + // Center modal windows by default + // FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window. + if (g.NextWindowData.PosCond == 0) + SetNextWindowPos(window->Viewport->GetCenter(), ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); + + bool is_open = Begin(name, p_open, flags | ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings); + if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display) + { + EndPopup(); + if (is_open) + ClosePopup(id); + return false; + } + return is_open; +} + +void ImGui::EndPopup() +{ + ImGuiContext& g = *GImGui; (void)g; + IM_ASSERT(g.CurrentWindow->Flags & ImGuiWindowFlags_Popup); // Mismatched BeginPopup()/EndPopup() calls + IM_ASSERT(g.CurrentPopupStack.Size > 0); + + // Make all menus and popups wrap around for now, may need to expose that policy. + NavMoveRequestTryWrapping(g.CurrentWindow, ImGuiNavMoveFlags_LoopY); + + End(); +} + +// This is a helper to handle the simplest case of associating one named popup to one given widget. +// You may want to handle this on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters). +// You can pass a NULL str_id to use the identifier of the last item. +bool ImGui::BeginPopupContextItem(const char* str_id, int mouse_button) +{ + ImGuiWindow* window = GImGui->CurrentWindow; + ImGuiID id = str_id ? window->GetID(str_id) : window->DC.LastItemId; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict! + IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item) + if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) + OpenPopupEx(id); + return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings); +} + +bool ImGui::BeginPopupContextWindow(const char* str_id, int mouse_button, bool also_over_items) +{ + if (!str_id) + str_id = "window_context"; + ImGuiID id = GImGui->CurrentWindow->GetID(str_id); + if (IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) + if (also_over_items || !IsAnyItemHovered()) + OpenPopupEx(id); + return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings); +} + +bool ImGui::BeginPopupContextVoid(const char* str_id, int mouse_button) +{ + if (!str_id) + str_id = "void_context"; + ImGuiID id = GImGui->CurrentWindow->GetID(str_id); + if (IsMouseReleased(mouse_button) && !IsWindowHovered(ImGuiHoveredFlags_AnyWindow)) + OpenPopupEx(id); + return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings); +} + //----------------------------------------------------------------------------- // NAVIGATION //----------------------------------------------------------------------------- From 00262d51ad8d6692afed7aa9609b56280382b964 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 30 Aug 2018 21:00:51 +0200 Subject: [PATCH 219/828] Refactor: Internals: Moved Popup functions in imgui.cpp in their own section. (part 3) (#2036) --- imgui.cpp | 244 +++++++++++++++++++++++++++--------------------------- 1 file changed, 122 insertions(+), 122 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 0a889913bfd6..3eb02eee918e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4842,128 +4842,6 @@ static void CheckStacksSize(ImGuiWindow* window, bool write) IM_ASSERT(p_backup == window->DC.StackSizesBackup + IM_ARRAYSIZE(window->DC.StackSizesBackup)); } -// r_avoid = the rectangle to avoid (e.g. for tooltip it is a rectangle around the mouse cursor which we want to avoid. for popups it's a small point around the cursor.) -// r_outer = the visible area rectangle, minus safe area padding. If our popup size won't fit because of safe area padding we ignore it. -// (r_outer is usually equivalent to the viewport rectangle minus padding, but when multi-viewports are enabled and monitor -// information are available, it may represent the entire platform monitor from the frame of reference of the current viewport. -// this allows us to have tooltips/popups displayed out of the parent viewport.) -ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy) -{ - ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size); - //GImGui->OverlayDrawList.AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255)); - //GImGui->OverlayDrawList.AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255)); - - // Combo Box policy (we want a connecting edge) - if (policy == ImGuiPopupPositionPolicy_ComboBox) - { - const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Down, ImGuiDir_Right, ImGuiDir_Left, ImGuiDir_Up }; - for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++) - { - const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n]; - if (n != -1 && dir == *last_dir) // Already tried this direction? - continue; - ImVec2 pos; - if (dir == ImGuiDir_Down) pos = ImVec2(r_avoid.Min.x, r_avoid.Max.y); // Below, Toward Right (default) - if (dir == ImGuiDir_Right) pos = ImVec2(r_avoid.Min.x, r_avoid.Min.y - size.y); // Above, Toward Right - if (dir == ImGuiDir_Left) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Max.y); // Below, Toward Left - if (dir == ImGuiDir_Up) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Min.y - size.y); // Above, Toward Left - if (!r_outer.Contains(ImRect(pos, pos + size))) - continue; - *last_dir = dir; - return pos; - } - } - - // Default popup policy - const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Right, ImGuiDir_Down, ImGuiDir_Up, ImGuiDir_Left }; - for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++) - { - const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n]; - if (n != -1 && dir == *last_dir) // Already tried this direction? - continue; - float avail_w = (dir == ImGuiDir_Left ? r_avoid.Min.x : r_outer.Max.x) - (dir == ImGuiDir_Right ? r_avoid.Max.x : r_outer.Min.x); - float avail_h = (dir == ImGuiDir_Up ? r_avoid.Min.y : r_outer.Max.y) - (dir == ImGuiDir_Down ? r_avoid.Max.y : r_outer.Min.y); - if (avail_w < size.x || avail_h < size.y) - continue; - ImVec2 pos; - pos.x = (dir == ImGuiDir_Left) ? r_avoid.Min.x - size.x : (dir == ImGuiDir_Right) ? r_avoid.Max.x : base_pos_clamped.x; - pos.y = (dir == ImGuiDir_Up) ? r_avoid.Min.y - size.y : (dir == ImGuiDir_Down) ? r_avoid.Max.y : base_pos_clamped.y; - *last_dir = dir; - return pos; - } - - // Fallback, try to keep within display - *last_dir = ImGuiDir_None; - ImVec2 pos = ref_pos; - pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x); - pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y); - return pos; -} - -ImRect ImGui::GetWindowAllowedExtentRect(ImGuiWindow* window) -{ - ImGuiContext& g = *GImGui; - ImRect r_screen; - if (window->ViewportAllowPlatformMonitorExtend >= 0) - { - // Extent with be in the frame of reference of the given viewport (so Min is likely to be negative here) - const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[window->ViewportAllowPlatformMonitorExtend]; - r_screen.Min = monitor.WorkPos; - r_screen.Max = monitor.WorkPos + monitor.WorkSize; - } - else - { - r_screen.Min = window->Viewport->Pos; - r_screen.Max = window->Viewport->Pos + window->Viewport->Size; - } - ImVec2 padding = g.Style.DisplaySafeAreaPadding; - r_screen.Expand(ImVec2((r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f)); - return r_screen; -} - -ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window) -{ - ImGuiContext& g = *GImGui; - if (window->Flags & ImGuiWindowFlags_ChildMenu) - { - // Child menus typically request _any_ position within the parent menu item, and then our FindBestWindowPosForPopup() function will move the new menu outside the parent bounds. - // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu. - ImGuiWindow* parent_window = window->ParentWindow; - float horizontal_overlap = g.Style.ItemSpacing.x; // We want some overlap to convey the relative depth of each menu (currently the amount of overlap is hard-coded to style.ItemSpacing.x). - ImRect r_outer = GetWindowAllowedExtentRect(window); - ImRect r_avoid; - if (parent_window->DC.MenuBarAppending) - r_avoid = ImRect(-FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight(), FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight() + parent_window->MenuBarHeight()); - else - r_avoid = ImRect(parent_window->Pos.x + horizontal_overlap, -FLT_MAX, parent_window->Pos.x + parent_window->Size.x - horizontal_overlap - parent_window->ScrollbarSizes.x, FLT_MAX); - return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid); - } - if (window->Flags & ImGuiWindowFlags_Popup) - { - ImRect r_outer = GetWindowAllowedExtentRect(window); - ImRect r_avoid = ImRect(window->Pos.x - 1, window->Pos.y - 1, window->Pos.x + 1, window->Pos.y + 1); - return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid); - } - if (window->Flags & ImGuiWindowFlags_Tooltip) - { - // Position tooltip (always follows mouse) - float sc = g.Style.MouseCursorScale; - ImVec2 ref_pos = NavCalcPreferredRefPos(); - ImRect r_outer = GetWindowAllowedExtentRect(window); - ImRect r_avoid; - if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos)) - r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8); - else - r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24 * sc, ref_pos.y + 24 * sc); // FIXME: Hard-coded based on mouse cursor shape expectation. Exact dimension not very important. - ImVec2 pos = FindBestWindowPosForPopupEx(ref_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid); - if (window->AutoPosLastDirection == ImGuiDir_None) - pos = ref_pos + ImVec2(2, 2); // If there's not enough room, for tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible. - return pos; - } - IM_ASSERT(0); - return window->Pos; -} - static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled) { window->SetWindowPosAllowFlags = enabled ? (window->SetWindowPosAllowFlags | flags) : (window->SetWindowPosAllowFlags & ~flags); @@ -7763,6 +7641,128 @@ bool ImGui::BeginPopupContextVoid(const char* str_id, int mouse_button) return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings); } +// r_avoid = the rectangle to avoid (e.g. for tooltip it is a rectangle around the mouse cursor which we want to avoid. for popups it's a small point around the cursor.) +// r_outer = the visible area rectangle, minus safe area padding. If our popup size won't fit because of safe area padding we ignore it. +// (r_outer is usually equivalent to the viewport rectangle minus padding, but when multi-viewports are enabled and monitor +// information are available, it may represent the entire platform monitor from the frame of reference of the current viewport. +// this allows us to have tooltips/popups displayed out of the parent viewport.) +ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy) +{ + ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size); + //GImGui->OverlayDrawList.AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255)); + //GImGui->OverlayDrawList.AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255)); + + // Combo Box policy (we want a connecting edge) + if (policy == ImGuiPopupPositionPolicy_ComboBox) + { + const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Down, ImGuiDir_Right, ImGuiDir_Left, ImGuiDir_Up }; + for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++) + { + const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n]; + if (n != -1 && dir == *last_dir) // Already tried this direction? + continue; + ImVec2 pos; + if (dir == ImGuiDir_Down) pos = ImVec2(r_avoid.Min.x, r_avoid.Max.y); // Below, Toward Right (default) + if (dir == ImGuiDir_Right) pos = ImVec2(r_avoid.Min.x, r_avoid.Min.y - size.y); // Above, Toward Right + if (dir == ImGuiDir_Left) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Max.y); // Below, Toward Left + if (dir == ImGuiDir_Up) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Min.y - size.y); // Above, Toward Left + if (!r_outer.Contains(ImRect(pos, pos + size))) + continue; + *last_dir = dir; + return pos; + } + } + + // Default popup policy + const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Right, ImGuiDir_Down, ImGuiDir_Up, ImGuiDir_Left }; + for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++) + { + const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n]; + if (n != -1 && dir == *last_dir) // Already tried this direction? + continue; + float avail_w = (dir == ImGuiDir_Left ? r_avoid.Min.x : r_outer.Max.x) - (dir == ImGuiDir_Right ? r_avoid.Max.x : r_outer.Min.x); + float avail_h = (dir == ImGuiDir_Up ? r_avoid.Min.y : r_outer.Max.y) - (dir == ImGuiDir_Down ? r_avoid.Max.y : r_outer.Min.y); + if (avail_w < size.x || avail_h < size.y) + continue; + ImVec2 pos; + pos.x = (dir == ImGuiDir_Left) ? r_avoid.Min.x - size.x : (dir == ImGuiDir_Right) ? r_avoid.Max.x : base_pos_clamped.x; + pos.y = (dir == ImGuiDir_Up) ? r_avoid.Min.y - size.y : (dir == ImGuiDir_Down) ? r_avoid.Max.y : base_pos_clamped.y; + *last_dir = dir; + return pos; + } + + // Fallback, try to keep within display + *last_dir = ImGuiDir_None; + ImVec2 pos = ref_pos; + pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x); + pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y); + return pos; +} + +ImRect ImGui::GetWindowAllowedExtentRect(ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; + ImRect r_screen; + if (window->ViewportAllowPlatformMonitorExtend >= 0) + { + // Extent with be in the frame of reference of the given viewport (so Min is likely to be negative here) + const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[window->ViewportAllowPlatformMonitorExtend]; + r_screen.Min = monitor.WorkPos; + r_screen.Max = monitor.WorkPos + monitor.WorkSize; + } + else + { + r_screen.Min = window->Viewport->Pos; + r_screen.Max = window->Viewport->Pos + window->Viewport->Size; + } + ImVec2 padding = g.Style.DisplaySafeAreaPadding; + r_screen.Expand(ImVec2((r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f)); + return r_screen; +} + +ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; + if (window->Flags & ImGuiWindowFlags_ChildMenu) + { + // Child menus typically request _any_ position within the parent menu item, and then our FindBestWindowPosForPopup() function will move the new menu outside the parent bounds. + // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu. + ImGuiWindow* parent_window = window->ParentWindow; + float horizontal_overlap = g.Style.ItemSpacing.x; // We want some overlap to convey the relative depth of each menu (currently the amount of overlap is hard-coded to style.ItemSpacing.x). + ImRect r_outer = GetWindowAllowedExtentRect(window); + ImRect r_avoid; + if (parent_window->DC.MenuBarAppending) + r_avoid = ImRect(-FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight(), FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight() + parent_window->MenuBarHeight()); + else + r_avoid = ImRect(parent_window->Pos.x + horizontal_overlap, -FLT_MAX, parent_window->Pos.x + parent_window->Size.x - horizontal_overlap - parent_window->ScrollbarSizes.x, FLT_MAX); + return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid); + } + if (window->Flags & ImGuiWindowFlags_Popup) + { + ImRect r_outer = GetWindowAllowedExtentRect(window); + ImRect r_avoid = ImRect(window->Pos.x - 1, window->Pos.y - 1, window->Pos.x + 1, window->Pos.y + 1); + return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid); + } + if (window->Flags & ImGuiWindowFlags_Tooltip) + { + // Position tooltip (always follows mouse) + float sc = g.Style.MouseCursorScale; + ImVec2 ref_pos = NavCalcPreferredRefPos(); + ImRect r_outer = GetWindowAllowedExtentRect(window); + ImRect r_avoid; + if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos)) + r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8); + else + r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24 * sc, ref_pos.y + 24 * sc); // FIXME: Hard-coded based on mouse cursor shape expectation. Exact dimension not very important. + ImVec2 pos = FindBestWindowPosForPopupEx(ref_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid); + if (window->AutoPosLastDirection == ImGuiDir_None) + pos = ref_pos + ImVec2(2, 2); // If there's not enough room, for tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible. + return pos; + } + IM_ASSERT(0); + return window->Pos; +} + //----------------------------------------------------------------------------- // NAVIGATION //----------------------------------------------------------------------------- From c83391262e1de9d5024978db5f38d693ae62005f Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 30 Aug 2018 21:03:51 +0200 Subject: [PATCH 220/828] Refactor: Internals: Moved Tooltip functions in imgui.cpp in their own section + comments. (#2036) --- imgui.cpp | 138 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 72 insertions(+), 66 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 3eb02eee918e..41c7352e46bf 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2050,6 +2050,7 @@ bool ImGuiListClipper::Step() //----------------------------------------------------------------------------- // ImGuiWindow +// (This type has very few helper methods but otherwise is mostly a dumb struct) //----------------------------------------------------------------------------- ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) @@ -2166,7 +2167,8 @@ ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs) } //----------------------------------------------------------------------------- -// Internal API exposed in imgui_internal.h +// MAIN CODE +// (this category is still too large and badly ordered, needs some tidying up) //----------------------------------------------------------------------------- static void SetCurrentWindow(ImGuiWindow* window) @@ -4644,71 +4646,6 @@ ImVec2 ImGui::GetItemRectSize() return window->DC.LastItemRect.GetSize(); } -// Not exposed publicly as BeginTooltip() because bool parameters are evil. Let's see if other needs arise first. -void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, bool override_previous_tooltip) -{ - ImGuiContext& g = *GImGui; - char window_name[16]; - ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", g.TooltipOverrideCount); - if (override_previous_tooltip) - if (ImGuiWindow* window = FindWindowByName(window_name)) - if (window->Active) - { - // Hide previous tooltip from being displayed. We can't easily "reset" the content of a window so we create a new one. - window->Hidden = true; - window->HiddenFramesRegular = 1; - ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", ++g.TooltipOverrideCount); - } - ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip|ImGuiWindowFlags_NoInputs|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoNav; - Begin(window_name, NULL, flags | extra_flags); -} - -void ImGui::SetTooltipV(const char* fmt, va_list args) -{ - ImGuiContext& g = *GImGui; - if (g.DragDropWithinSourceOrTarget) - BeginTooltip(); - else - BeginTooltipEx(0, true); - TextV(fmt, args); - EndTooltip(); -} - -void ImGui::SetTooltip(const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - SetTooltipV(fmt, args); - va_end(args); -} - -void ImGui::BeginTooltip() -{ - ImGuiContext& g = *GImGui; - if (g.DragDropWithinSourceOrTarget) - { - // The default tooltip position is a little offset to give space to see the context menu (it's also clamped within the current viewport/monitor) - // In the context of a dragging tooltip we try to reduce that offset and we enforce following the cursor. - // Whatever we do we want to call SetNextWindowPos() to enforce a tooltip position and disable clipping the tooltip without our display area, like regular tooltip do. - //ImVec2 tooltip_pos = g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding; - ImVec2 tooltip_pos = g.IO.MousePos + ImVec2(16 * g.Style.MouseCursorScale, 8 * g.Style.MouseCursorScale); - SetNextWindowPos(tooltip_pos); - SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f); - //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkboard has issue with transparent colors :( - BeginTooltipEx(0, true); - } - else - { - BeginTooltipEx(0, false); - } -} - -void ImGui::EndTooltip() -{ - IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip); // Mismatched BeginTooltip()/EndTooltip() calls - End(); -} - static bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags) { ImGuiContext& g = *GImGui; @@ -7380,6 +7317,75 @@ void ImGui::Unindent(float indent_w) window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x; } +//----------------------------------------------------------------------------- +// TOOLTIP +//----------------------------------------------------------------------------- + +void ImGui::BeginTooltip() +{ + ImGuiContext& g = *GImGui; + if (g.DragDropWithinSourceOrTarget) + { + // The default tooltip position is a little offset to give space to see the context menu (it's also clamped within the current viewport/monitor) + // In the context of a dragging tooltip we try to reduce that offset and we enforce following the cursor. + // Whatever we do we want to call SetNextWindowPos() to enforce a tooltip position and disable clipping the tooltip without our display area, like regular tooltip do. + //ImVec2 tooltip_pos = g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding; + ImVec2 tooltip_pos = g.IO.MousePos + ImVec2(16 * g.Style.MouseCursorScale, 8 * g.Style.MouseCursorScale); + SetNextWindowPos(tooltip_pos); + SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f); + //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkboard has issue with transparent colors :( + BeginTooltipEx(0, true); + } + else + { + BeginTooltipEx(0, false); + } +} + +// Not exposed publicly as BeginTooltip() because bool parameters are evil. Let's see if other needs arise first. +void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, bool override_previous_tooltip) +{ + ImGuiContext& g = *GImGui; + char window_name[16]; + ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", g.TooltipOverrideCount); + if (override_previous_tooltip) + if (ImGuiWindow* window = FindWindowByName(window_name)) + if (window->Active) + { + // Hide previous tooltip from being displayed. We can't easily "reset" the content of a window so we create a new one. + window->Hidden = true; + window->HiddenFramesRegular = 1; + ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", ++g.TooltipOverrideCount); + } + ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip|ImGuiWindowFlags_NoInputs|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoNav; + Begin(window_name, NULL, flags | extra_flags); +} + +void ImGui::EndTooltip() +{ + IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip); // Mismatched BeginTooltip()/EndTooltip() calls + End(); +} + +void ImGui::SetTooltipV(const char* fmt, va_list args) +{ + ImGuiContext& g = *GImGui; + if (g.DragDropWithinSourceOrTarget) + BeginTooltip(); + else + BeginTooltipEx(0, true); + TextV(fmt, args); + EndTooltip(); +} + +void ImGui::SetTooltip(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + SetTooltipV(fmt, args); + va_end(args); +} + //----------------------------------------------------------------------------- // POPUPS //----------------------------------------------------------------------------- From 5207afa0ddbda35d44734cee5254448a54a57952 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 31 Aug 2018 11:03:37 +0200 Subject: [PATCH 221/828] Refactor: Internals: Moved Viewport functions in imgui.cpp in their own section. (part 2) (#2036, #1542) --- imgui.cpp | 988 +++++++++++++++++++++++++++--------------------------- 1 file changed, 497 insertions(+), 491 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index d5a141dfa44b..4633754e559a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -917,6 +917,7 @@ namespace ImGui { static bool BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags); +// Navigation static void NavUpdate(); static void NavUpdateWindowing(); static void NavUpdateWindowingList(); @@ -939,6 +940,7 @@ static void UpdateViewports(); static void UpdateSelectWindowViewport(ImGuiWindow* window); static void UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* host_viewport); static void SetCurrentViewport(ImGuiWindow* window, ImGuiViewportP* viewport); +static bool GetWindowAlwaysWantOwnViewport(ImGuiWindow* window); static int FindPlatformMonitorForPos(const ImVec2& pos); static int FindPlatformMonitorForRect(const ImRect& r); @@ -2687,45 +2689,6 @@ ImDrawListSharedData* ImGui::GetDrawListSharedData() return &GImGui->DrawListSharedData; } -static void SetWindowViewport(ImGuiWindow* window, ImGuiViewportP* viewport) -{ - window->Viewport = viewport; - window->ViewportId = viewport->ID; - window->ViewportOwned = (viewport->Window == window); -} - -static bool GetWindowAlwaysWantOwnViewport(ImGuiWindow* window) -{ - // Tooltips and menus are not automatically forced into their own viewport when the NoMerge flag is set, however the multiplication of viewports makes them more likely to protude and create their own. - ImGuiContext& g = *GImGui; - if ((g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsNoMerge) && (g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) - //if (window->DockStatus == ImGuiDockStatus_Floating) - if ((window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip)) == 0) - return true; - return false; -} - -static void ImGui::UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* viewport) -{ - ImGuiContext& g = *GImGui; - if (!(g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) - return; - if (!(viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) || window->Viewport == viewport) - return; - if (!viewport->GetRect().Contains(window->Rect())) - return; - if (GetWindowAlwaysWantOwnViewport(window)) - return; - - // Move to the existing viewport, Move child/hosted windows as well (FIXME-OPT: iterate child) - ImGuiViewportP* old_viewport = window->Viewport; - if (window->ViewportOwned) - for (int n = 0; n < g.Windows.Size; n++) - if (g.Windows[n]->Viewport == old_viewport) - SetWindowViewport(g.Windows[n], viewport); - SetWindowViewport(window, viewport); -} - void ImGui::StartMouseMovingWindow(ImGuiWindow* window) { // Set ActiveId even if the _NoMove flag is set. Without it, dragging away from a window with _NoMove would activate hover on other windows. @@ -2830,410 +2793,108 @@ static void ScaleWindow(ImGuiWindow* window, float scale) window->SizeContents = ImFloor(window->SizeContents * scale); } -// Scale all windows (position, size). Use when e.g. changing DPI. (This is a lossy operation!) -void ImGui::ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale) +static bool IsWindowActiveAndVisible(ImGuiWindow* window) { - ImGuiContext& g = *GImGui; - if (viewport->Window) - { - ScaleWindow(viewport->Window, scale); - } - else - { - for (int i = 0; i != g.Windows.Size; i++) - if (g.Windows[i]->Viewport == viewport) - ScaleWindow(g.Windows[i], scale); - } + return (window->Active) && (!window->Hidden); } -static void ImGui::UpdateViewports() +static void ImGui::UpdateMouseInputs() { ImGuiContext& g = *GImGui; - IM_ASSERT(g.PlatformIO.Viewports.Size <= g.Viewports.Size); - // Update main viewport with current platform position and size - ImGuiViewportP* main_viewport = g.Viewports[0]; - IM_ASSERT(main_viewport->ID == IMGUI_VIEWPORT_DEFAULT_ID); - ImVec2 main_viewport_platform_pos = ImVec2(0.0f, 0.0f); - if ((g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) - main_viewport_platform_pos = g.PlatformIO.Platform_GetWindowPos(main_viewport); - AddUpdateViewport(NULL, IMGUI_VIEWPORT_DEFAULT_ID, main_viewport_platform_pos, g.IO.DisplaySize, ImGuiViewportFlags_CanHostOtherWindows); + // If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta + if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MousePosPrev)) + g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev; + else + g.IO.MouseDelta = ImVec2(0.0f, 0.0f); + if (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f) + g.NavDisableMouseHover = false; - g.CurrentViewport = NULL; - g.MouseViewport = NULL; - for (int n = 0; n < g.Viewports.Size; n++) + g.IO.MousePosPrev = g.IO.MousePos; + for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++) { - // Erase unused viewports - ImGuiViewportP* viewport = g.Viewports[n]; - viewport->Idx = n; - - if (n > 0 && viewport->LastFrameActive < g.FrameCount - 2) - { - // Clear references to this viewport in windows (window->ViewportId becomes the master data) - for (int window_n = 0; window_n < g.Windows.Size; window_n++) - if (g.Windows[window_n]->Viewport == viewport) - g.Windows[window_n]->Viewport = NULL; - g.Viewports.erase(g.Viewports.Data + n); - - // Destroy - if (viewport == g.MouseLastHoveredViewport) g.MouseLastHoveredViewport = NULL; - IM_ASSERT(viewport->RendererUserData == NULL && viewport->PlatformUserData == NULL && viewport->PlatformHandle == NULL); - IM_ASSERT(g.PlatformIO.Viewports.contains(viewport) == false); - IM_DELETE(viewport); - n--; - continue; - } - - if ((g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) + g.IO.MouseClicked[i] = g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] < 0.0f; + g.IO.MouseReleased[i] = !g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] >= 0.0f; + g.IO.MouseDownDurationPrev[i] = g.IO.MouseDownDuration[i]; + g.IO.MouseDownDuration[i] = g.IO.MouseDown[i] ? (g.IO.MouseDownDuration[i] < 0.0f ? 0.0f : g.IO.MouseDownDuration[i] + g.IO.DeltaTime) : -1.0f; + g.IO.MouseDoubleClicked[i] = false; + if (g.IO.MouseClicked[i]) { - // Apply Position and Size (from Platform Window to ImGui) if requested. - // We do it early in the frame instead of waiting for UpdatePlatformWindows() to avoid a frame of lag when moving/resizing using OS facilities. - if (viewport->PlatformRequestMove) - viewport->Pos = g.PlatformIO.Platform_GetWindowPos(viewport); - if (viewport->PlatformRequestResize) - viewport->Size = g.PlatformIO.Platform_GetWindowSize(viewport); - - // Translate imgui windows when a Host Viewport has been moved - ImVec2 delta = viewport->Pos - viewport->LastPos; - if ((viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) && (delta.x != 0.0f || delta.y != 0.0f)) - for (int window_n = 0; window_n < g.Windows.Size; window_n++) - if (g.Windows[window_n]->Viewport == viewport) - TranslateWindow(g.Windows[window_n], delta); - - // Update monitor (we'll use this info to clamp windows and save windows lost in a removed monitor) - viewport->PlatformMonitor = FindPlatformMonitorForRect(viewport->GetRect()); + if ((float)(g.Time - g.IO.MouseClickedTime[i]) < g.IO.MouseDoubleClickTime) + { + ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f); + if (ImLengthSqr(delta_from_click_pos) < g.IO.MouseDoubleClickMaxDist * g.IO.MouseDoubleClickMaxDist) + g.IO.MouseDoubleClicked[i] = true; + g.IO.MouseClickedTime[i] = -FLT_MAX; // so the third click isn't turned into a double-click + } + else + { + g.IO.MouseClickedTime[i] = g.Time; + } + g.IO.MouseClickedPos[i] = g.IO.MousePos; + g.IO.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f); + g.IO.MouseDragMaxDistanceSqr[i] = 0.0f; } - - // Update DPI scale - float new_dpi_scale; - if (g.PlatformIO.Platform_GetWindowDpiScale) - new_dpi_scale = g.PlatformIO.Platform_GetWindowDpiScale(viewport); - else - new_dpi_scale = (viewport->DpiScale != 0.0f) ? viewport->DpiScale : 1.0f; - if (viewport->DpiScale != 0.0f && new_dpi_scale != viewport->DpiScale) + else if (g.IO.MouseDown[i]) { - float scale_factor = new_dpi_scale / viewport->DpiScale; - if (g.IO.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleViewports) - ScaleWindowsInViewport(viewport, scale_factor); - //if (viewport == GetMainViewport()) - // g.PlatformInterface.SetWindowSize(viewport, viewport->Size * scale_factor); - - // Scale our window moving pivot so that the window will rescale roughly around the mouse position. - // FIXME-VIEWPORT: This currently creates a resizing feedback loop when a window is straddling a DPI transition border. - // (Minor: since our sizes do not perfectly linearly scale, deferring the click offset scale until we know the actual window scale ratio may get us slightly more precise mouse positioning.) - //if (g.MovingWindow != NULL && g.MovingWindow->Viewport == viewport) - // g.ActiveIdClickOffset = ImFloor(g.ActiveIdClickOffset * scale_factor); + // Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold + ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f); + g.IO.MouseDragMaxDistanceSqr[i] = ImMax(g.IO.MouseDragMaxDistanceSqr[i], ImLengthSqr(delta_from_click_pos)); + g.IO.MouseDragMaxDistanceAbs[i].x = ImMax(g.IO.MouseDragMaxDistanceAbs[i].x, delta_from_click_pos.x < 0.0f ? -delta_from_click_pos.x : delta_from_click_pos.x); + g.IO.MouseDragMaxDistanceAbs[i].y = ImMax(g.IO.MouseDragMaxDistanceAbs[i].y, delta_from_click_pos.y < 0.0f ? -delta_from_click_pos.y : delta_from_click_pos.y); } - viewport->DpiScale = new_dpi_scale; + if (g.IO.MouseClicked[i]) // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation + g.NavDisableMouseHover = false; } +} - if (!(g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) - { - g.MouseViewport = main_viewport; +void ImGui::UpdateMouseWheel() +{ + ImGuiContext& g = *GImGui; + if (!g.HoveredWindow || g.HoveredWindow->Collapsed) + return; + if (g.IO.MouseWheel == 0.0f && g.IO.MouseWheelH == 0.0f) return; - } - // Mouse handling: decide on the actual mouse viewport for this frame between the active/focused viewport and the hovered viewport. - // Note that 'viewport_hovered' should skip over any viewport that has the ImGuiViewportFlags_NoInputs flags set. - ImGuiViewportP* viewport_hovered = NULL; - if (g.IO.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport) + // If a child window has the ImGuiWindowFlags_NoScrollWithMouse flag, we give a chance to scroll its parent (unless either ImGuiWindowFlags_NoInputs or ImGuiWindowFlags_NoScrollbar are also set). + ImGuiWindow* window = g.HoveredWindow; + ImGuiWindow* scroll_window = window; + while ((scroll_window->Flags & ImGuiWindowFlags_ChildWindow) && (scroll_window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(scroll_window->Flags & ImGuiWindowFlags_NoScrollbar) && !(scroll_window->Flags & ImGuiWindowFlags_NoInputs) && scroll_window->ParentWindow) + scroll_window = scroll_window->ParentWindow; + const bool scroll_allowed = !(scroll_window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(scroll_window->Flags & ImGuiWindowFlags_NoInputs); + + if (g.IO.MouseWheel != 0.0f) { - viewport_hovered = g.IO.MouseHoveredViewport ? FindViewportByID(g.IO.MouseHoveredViewport) : NULL; - if (viewport_hovered && (viewport_hovered->Flags & ImGuiViewportFlags_NoInputs)) + if (g.IO.KeyCtrl && g.IO.FontAllowUserScaling) { - // Back-end failed at honoring its contract if it returned a viewport with the _NoInputs flag. - IM_ASSERT(0); - viewport_hovered = FindViewportHoveredFromPlatformWindowStack(g.IO.MousePos); + // Zoom / Scale window + const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f); + const float scale = new_font_scale / window->FontWindowScale; + window->FontWindowScale = new_font_scale; + + const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size; + window->Pos += offset; + window->Size *= scale; + window->SizeFull *= scale; + } + else if (!g.IO.KeyCtrl && scroll_allowed) + { + // Mouse wheel vertical scrolling + float scroll_amount = 5 * scroll_window->CalcFontSize(); + scroll_amount = (float)(int)ImMin(scroll_amount, (scroll_window->ContentsRegionRect.GetHeight() + scroll_window->WindowPadding.y * 2.0f) * 0.67f); + SetWindowScrollY(scroll_window, scroll_window->Scroll.y - g.IO.MouseWheel * scroll_amount); } } - else + if (g.IO.MouseWheelH != 0.0f && scroll_allowed && !g.IO.KeyCtrl) { - // If the back-end doesn't know how to honor ImGuiViewportFlags_NoInputs, we do a search ourselves. Note that this search: - // A) won't take account of the possibility that non-imgui windows may be in-between our dragged window and our target window. - // B) uses LastFrameAsRefViewport as a flawed replacement for the last time a window was focused (we could/should fix that by introducing Focus functions in PlatformIO) - viewport_hovered = FindViewportHoveredFromPlatformWindowStack(g.IO.MousePos); + // Mouse wheel horizontal scrolling (for hardware that supports it) + float scroll_amount = scroll_window->CalcFontSize(); + SetWindowScrollX(scroll_window, scroll_window->Scroll.x - g.IO.MouseWheelH * scroll_amount); } - if (viewport_hovered != NULL) - g.MouseLastHoveredViewport = viewport_hovered; - else if (g.MouseLastHoveredViewport == NULL) - g.MouseLastHoveredViewport = g.Viewports[0]; - - // Update mouse reference viewport - // (when moving a window we aim at its viewport, but this will be overwritten below if we go in drag and drop mode) - if (g.MovingWindow) - g.MouseViewport = g.MovingWindow->Viewport; - else - g.MouseViewport = g.MouseLastHoveredViewport; - - // When dragging something, always refer to the last hovered viewport. - // - when releasing a moving window we will revert to aiming behind (at viewport_hovered) - // - when we are between viewports, our dragged preview will tend to show in the last viewport _even_ if we don't have tooltips in their viewports (when lacking monitor info) - // - consider the case of holding on a menu item to browse child menus: even thou a mouse button is held, there's no active id because menu items only react on mouse release. - const bool is_mouse_dragging_with_an_expected_destination = g.DragDropActive; - if (is_mouse_dragging_with_an_expected_destination && viewport_hovered == NULL) - viewport_hovered = g.MouseLastHoveredViewport; - if (is_mouse_dragging_with_an_expected_destination || g.ActiveId == 0 || !ImGui::IsAnyMouseDown()) - if (viewport_hovered != NULL && viewport_hovered != g.MouseViewport && !(viewport_hovered->Flags & ImGuiViewportFlags_NoInputs)) - g.MouseViewport = viewport_hovered; - - IM_ASSERT(g.MouseViewport != NULL); -} - -static bool IsWindowActiveAndVisible(ImGuiWindow* window) -{ - return (window->Active) && (!window->Hidden); } -void ImGui::UpdatePlatformWindows() -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(g.FrameCountEnded == g.FrameCount && "Forgot to call Render() or EndFrame() before UpdatePlatformWindows()?"); - IM_ASSERT(g.FrameCountPlatformEnded < g.FrameCount); - g.FrameCountPlatformEnded = g.FrameCount; - g.Viewports[0]->LastPos = g.Viewports[0]->Pos; - if (!(g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) - return; - - // Create/resize/destroy platform windows to match each active viewport. - // Skip the main viewport (index 0), which is always fully handled by the application! - for (int i = 1; i < g.Viewports.Size; i++) - { - ImGuiViewportP* viewport = g.Viewports[i]; - viewport->LastPos = viewport->Pos; - - // Destroy platform window if the viewport hasn't been submitted or if it is hosting a hidden window (the implicit Debug window will be registered its viewport then be disabled) - bool destroy_platform_window = false; - destroy_platform_window |= (viewport->LastFrameActive < g.FrameCount - 1); - destroy_platform_window |= (viewport->Window && !IsWindowActiveAndVisible(viewport->Window)); - if (destroy_platform_window) - { - if (viewport->CreatedPlatformWindow && g.PlatformIO.Renderer_DestroyWindow) - g.PlatformIO.Renderer_DestroyWindow(viewport); - if (viewport->CreatedPlatformWindow && g.PlatformIO.Platform_DestroyWindow) - g.PlatformIO.Platform_DestroyWindow(viewport); - viewport->CreatedPlatformWindow = false; - IM_ASSERT(viewport->RendererUserData == NULL); - IM_ASSERT(viewport->PlatformUserData == NULL && viewport->PlatformHandle == NULL); - continue; - } - if (viewport->LastFrameActive < g.FrameCount) - continue; - - // New windows that appears directly in a new viewport won't always have a size on their frame - if (viewport->Size.x <= 0 || viewport->Size.y <= 0) - continue; - - // Update viewport flags - if (viewport->Window != NULL) - { - bool topmost = (viewport->Window->Flags & ImGuiWindowFlags_Tooltip) != 0; - bool no_task_bar_icon = (g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsNoTaskBarIcons) != 0 || (viewport->Window->Flags & (ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) != 0; - viewport->Flags = topmost ? (viewport->Flags | ImGuiViewportFlags_TopMost) : (viewport->Flags & ~ImGuiViewportFlags_TopMost); - viewport->Flags = no_task_bar_icon ? (viewport->Flags | ImGuiViewportFlags_NoTaskBarIcon) : (viewport->Flags & ~ImGuiViewportFlags_NoTaskBarIcon); - } - - // Create window - bool is_new_window = (viewport->CreatedPlatformWindow == false); - if (is_new_window) - { - g.PlatformIO.Platform_CreateWindow(viewport); - if (g.PlatformIO.Renderer_CreateWindow != NULL) - g.PlatformIO.Renderer_CreateWindow(viewport); - viewport->RendererLastSize = viewport->Size; - viewport->CreatedPlatformWindow = true; - } - - // Apply Position and Size (from ImGui to Platform/Renderer back-ends) - if (!viewport->PlatformRequestMove) - g.PlatformIO.Platform_SetWindowPos(viewport, viewport->Pos); - if (!viewport->PlatformRequestResize) - g.PlatformIO.Platform_SetWindowSize(viewport, viewport->Size); - if (g.PlatformIO.Renderer_SetWindowSize && (viewport->RendererLastSize.x != viewport->Size.x || viewport->RendererLastSize.y != viewport->Size.y)) - g.PlatformIO.Renderer_SetWindowSize(viewport, viewport->Size); - viewport->RendererLastSize = viewport->Size; - - // Update title bar (if it changed) - if (ImGuiWindow* window_for_title = viewport->Window) - { - const char* title_begin = window_for_title->Name; - char* title_end = (char*)(intptr_t)FindRenderedTextEnd(title_begin); - const ImGuiID title_hash = ImHash(title_begin, (int)(title_end - title_begin)); - if (viewport->LastNameHash != title_hash) - { - char title_end_backup_c = *title_end; - *title_end = 0; // Cut existing buffer short instead of doing an alloc/free - g.PlatformIO.Platform_SetWindowTitle(viewport, title_begin); - *title_end = title_end_backup_c; - viewport->LastNameHash = title_hash; - } - } - - // Update alpha - if (viewport->LastAlpha != viewport->Alpha && g.PlatformIO.Platform_SetWindowAlpha) - g.PlatformIO.Platform_SetWindowAlpha(viewport, viewport->Alpha); - viewport->LastAlpha = viewport->Alpha; - - // Show window. On startup ensure platform window don't get focus - if (is_new_window) - { - if (g.FrameCount < 2) - viewport->Flags |= ImGuiViewportFlags_NoFocusOnAppearing; - g.PlatformIO.Platform_ShowWindow(viewport); - } - - // Even without focus, we assume the window becomes front-most. This is used by our platform z-order heuristic when io.MouseHoveredViewport is not available. - if (is_new_window && viewport->LastFrontMostStampCount != g.WindowsFrontMostStampCount) - viewport->LastFrontMostStampCount = ++g.WindowsFrontMostStampCount; - - // Clear request flags - viewport->PlatformRequestClose = viewport->PlatformRequestMove = viewport->PlatformRequestResize = false; - } - - // Update our implicit z-order knowledge of platform windows, which is used when the back-end cannot provide io.MouseHoveredViewport. - if (g.PlatformIO.Platform_GetWindowFocus != NULL) - { - ImGuiViewportP* focused_viewport = NULL; - for (int i = 0; i < g.Viewports.Size && focused_viewport == NULL; i++) - if (g.Viewports[i]->PlatformUserData != NULL || g.Viewports[i]->PlatformHandle != NULL || g.Viewports[i]->CreatedPlatformWindow) - if (g.PlatformIO.Platform_GetWindowFocus(g.Viewports[i])) - focused_viewport = g.Viewports[i]; - if (focused_viewport && g.PlatformLastFocusedViewport != focused_viewport->ID) - { - if (focused_viewport->LastFrontMostStampCount != g.WindowsFrontMostStampCount) - focused_viewport->LastFrontMostStampCount = ++g.WindowsFrontMostStampCount; - g.PlatformLastFocusedViewport = focused_viewport->ID; - } - } -} - -// This is a default/basic function for performing the rendering/swap of multiple platform windows. -// Custom renderers may prefer to not call this function at all, and instead iterate the publicly exposed platform data and handle rendering/sync themselves. -// The Render/Swap functions stored in ImGuiPlatformIO are merely here to allow for this helper to exist, but you can do it yourself: -// -// ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); -// for (int i = 1; i < platform_io.Viewports.Size; i++) -// MyRenderFunction(platform_io.Viewports[i], my_args); -// for (int i = 1; i < platform_io.Viewports.Size; i++) -// MySwapBufferFunction(platform_io.Viewports[i], my_args); -// -void ImGui::RenderPlatformWindowsDefault(void* platform_render_arg, void* renderer_render_arg) -{ - if (!(ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) - return; - - // Skip the main viewport (index 0), which is always fully handled by the application! - ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); - for (int i = 1; i < platform_io.Viewports.Size; i++) - { - ImGuiViewport* viewport = platform_io.Viewports[i]; - if (platform_io.Platform_RenderWindow) platform_io.Platform_RenderWindow(viewport, platform_render_arg); - if (platform_io.Renderer_RenderWindow) platform_io.Renderer_RenderWindow(viewport, renderer_render_arg); - } - for (int i = 1; i < platform_io.Viewports.Size; i++) - { - ImGuiViewport* viewport = platform_io.Viewports[i]; - if (platform_io.Platform_SwapBuffers) platform_io.Platform_SwapBuffers(viewport, platform_render_arg); - if (platform_io.Renderer_SwapBuffers) platform_io.Renderer_SwapBuffers(viewport, renderer_render_arg); - } -} - -static void ImGui::UpdateMouseInputs() -{ - ImGuiContext& g = *GImGui; - - // If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta - if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MousePosPrev)) - g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev; - else - g.IO.MouseDelta = ImVec2(0.0f, 0.0f); - if (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f) - g.NavDisableMouseHover = false; - - g.IO.MousePosPrev = g.IO.MousePos; - for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++) - { - g.IO.MouseClicked[i] = g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] < 0.0f; - g.IO.MouseReleased[i] = !g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] >= 0.0f; - g.IO.MouseDownDurationPrev[i] = g.IO.MouseDownDuration[i]; - g.IO.MouseDownDuration[i] = g.IO.MouseDown[i] ? (g.IO.MouseDownDuration[i] < 0.0f ? 0.0f : g.IO.MouseDownDuration[i] + g.IO.DeltaTime) : -1.0f; - g.IO.MouseDoubleClicked[i] = false; - if (g.IO.MouseClicked[i]) - { - if ((float)(g.Time - g.IO.MouseClickedTime[i]) < g.IO.MouseDoubleClickTime) - { - ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f); - if (ImLengthSqr(delta_from_click_pos) < g.IO.MouseDoubleClickMaxDist * g.IO.MouseDoubleClickMaxDist) - g.IO.MouseDoubleClicked[i] = true; - g.IO.MouseClickedTime[i] = -FLT_MAX; // so the third click isn't turned into a double-click - } - else - { - g.IO.MouseClickedTime[i] = g.Time; - } - g.IO.MouseClickedPos[i] = g.IO.MousePos; - g.IO.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f); - g.IO.MouseDragMaxDistanceSqr[i] = 0.0f; - } - else if (g.IO.MouseDown[i]) - { - // Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold - ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f); - g.IO.MouseDragMaxDistanceSqr[i] = ImMax(g.IO.MouseDragMaxDistanceSqr[i], ImLengthSqr(delta_from_click_pos)); - g.IO.MouseDragMaxDistanceAbs[i].x = ImMax(g.IO.MouseDragMaxDistanceAbs[i].x, delta_from_click_pos.x < 0.0f ? -delta_from_click_pos.x : delta_from_click_pos.x); - g.IO.MouseDragMaxDistanceAbs[i].y = ImMax(g.IO.MouseDragMaxDistanceAbs[i].y, delta_from_click_pos.y < 0.0f ? -delta_from_click_pos.y : delta_from_click_pos.y); - } - if (g.IO.MouseClicked[i]) // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation - g.NavDisableMouseHover = false; - } -} - -void ImGui::UpdateMouseWheel() -{ - ImGuiContext& g = *GImGui; - if (!g.HoveredWindow || g.HoveredWindow->Collapsed) - return; - if (g.IO.MouseWheel == 0.0f && g.IO.MouseWheelH == 0.0f) - return; - - // If a child window has the ImGuiWindowFlags_NoScrollWithMouse flag, we give a chance to scroll its parent (unless either ImGuiWindowFlags_NoInputs or ImGuiWindowFlags_NoScrollbar are also set). - ImGuiWindow* window = g.HoveredWindow; - ImGuiWindow* scroll_window = window; - while ((scroll_window->Flags & ImGuiWindowFlags_ChildWindow) && (scroll_window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(scroll_window->Flags & ImGuiWindowFlags_NoScrollbar) && !(scroll_window->Flags & ImGuiWindowFlags_NoInputs) && scroll_window->ParentWindow) - scroll_window = scroll_window->ParentWindow; - const bool scroll_allowed = !(scroll_window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(scroll_window->Flags & ImGuiWindowFlags_NoInputs); - - if (g.IO.MouseWheel != 0.0f) - { - if (g.IO.KeyCtrl && g.IO.FontAllowUserScaling) - { - // Zoom / Scale window - const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f); - const float scale = new_font_scale / window->FontWindowScale; - window->FontWindowScale = new_font_scale; - - const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size; - window->Pos += offset; - window->Size *= scale; - window->SizeFull *= scale; - } - else if (!g.IO.KeyCtrl && scroll_allowed) - { - // Mouse wheel vertical scrolling - float scroll_amount = 5 * scroll_window->CalcFontSize(); - scroll_amount = (float)(int)ImMin(scroll_amount, (scroll_window->ContentsRegionRect.GetHeight() + scroll_window->WindowPadding.y * 2.0f) * 0.67f); - SetWindowScrollY(scroll_window, scroll_window->Scroll.y - g.IO.MouseWheel * scroll_amount); - } - } - if (g.IO.MouseWheelH != 0.0f && scroll_allowed && !g.IO.KeyCtrl) - { - // Mouse wheel horizontal scrolling (for hardware that supports it) - float scroll_amount = scroll_window->CalcFontSize(); - SetWindowScrollX(scroll_window, scroll_window->Scroll.x - g.IO.MouseWheelH * scroll_amount); - } -} - -// The reason this is exposed in imgui_internal.h is: on touch-based system that don't have hovering, we want to dispatch inputs to the right target (imgui vs imgui+app) -void ImGui::UpdateHoveredWindowAndCaptureFlags() +// The reason this is exposed in imgui_internal.h is: on touch-based system that don't have hovering, we want to dispatch inputs to the right target (imgui vs imgui+app) +void ImGui::UpdateHoveredWindowAndCaptureFlags() { ImGuiContext& g = *GImGui; @@ -3530,26 +3191,8 @@ void ImGui::Initialize(ImGuiContext* context) g.Initialized = true; } -void ImGui::DestroyPlatformWindows() -{ - // We call the destroy window on the main viewport (index 0) to give a chance to the back-end to clear any data - // have stored in e.g. PlatformHandle. - // It is expected that the back-end stored a flag to remember that it doesn't own the window created for the main viewport, - // and won't destroy the underlying platform/renderer data (e.g. - ImGuiContext& g = *GImGui; - for (int i = 0; i < g.Viewports.Size; i++) - if (g.Viewports[i]->CreatedPlatformWindow) - { - if (g.PlatformIO.Renderer_DestroyWindow) - g.PlatformIO.Renderer_DestroyWindow(g.Viewports[i]); - if (g.PlatformIO.Platform_DestroyWindow) - g.PlatformIO.Platform_DestroyWindow(g.Viewports[i]); - g.Viewports[i]->CreatedPlatformWindow = false; - } -} - -// This function is merely here to free heap allocations. -void ImGui::Shutdown(ImGuiContext* context) +// This function is merely here to free heap allocations. +void ImGui::Shutdown(ImGuiContext* context) { // The fonts atlas can be used prior to calling NewFrame(), so we clear it even if g.Initialized is FALSE (which would happen if we never called NewFrame) ImGuiContext& g = *context; @@ -3939,58 +3582,6 @@ void ImGui::Render() #endif } -ImGuiViewport* ImGui::GetMainViewport() -{ - ImGuiContext& g = *GImGui; - return g.Viewports[0]; -} - -ImGuiViewportP* ImGui::FindViewportByID(ImGuiID id) -{ - ImGuiContext& g = *GImGui; - for (int n = 0; n < g.Viewports.Size; n++) - if (g.Viewports[n]->ID == id) - return g.Viewports[n]; - return NULL; -} - -ImGuiViewport* ImGui::FindViewportByPlatformHandle(void* platform_handle) -{ - ImGuiContext& g = *GImGui; - for (int i = 0; i != g.Viewports.Size; i++) - if (g.Viewports[i]->PlatformHandle == platform_handle) - return g.Viewports[i]; - return NULL; -} - -void ImGui::SetCurrentViewport(ImGuiWindow* current_window, ImGuiViewportP* viewport) -{ - ImGuiContext& g = *GImGui; - if (viewport) - { - // First window submitted gets viewport ownership - // [2018-07-18] This is problematic: e.g. a drag and drop tooltip may steal viewport of the window it is hovered. Disabling, will rework in Docking branch. - (void)current_window; - /* - if (viewport->LastFrameActive < g.FrameCount && viewport->Window != current_window && viewport->Window != NULL && current_window != NULL) - { - //printf("[%05d] Window '%s' steal Viewport %08X from Window '%s'\n", g.FrameCount, current_window->Name, viewport->ID, viewport->Window->Name); - viewport->Window = current_window; - viewport->ID = current_window->ID; - } - */ - viewport->LastFrameActive = g.FrameCount; - } - if (g.CurrentViewport == viewport) - return; - g.CurrentViewport = viewport; - - // Notify platform layer of viewport changes - // FIXME-DPI: This is only currently used for experimenting with handling of multiple DPI - if (g.CurrentViewport && g.PlatformIO.Platform_OnChangedViewport) - g.PlatformIO.Platform_OnChangedViewport(g.CurrentViewport); -} - ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const ImVec2& pos, const ImVec2& size, ImGuiViewportFlags flags) { ImGuiContext& g = *GImGui; @@ -7784,6 +7375,421 @@ ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window) return window->Pos; } +//----------------------------------------------------------------------------- +// VIEWPORTS / PLATFORM WINDOWS +//----------------------------------------------------------------------------- + +ImGuiViewport* ImGui::GetMainViewport() +{ + ImGuiContext& g = *GImGui; + return g.Viewports[0]; +} + +ImGuiViewportP* ImGui::FindViewportByID(ImGuiID id) +{ + ImGuiContext& g = *GImGui; + for (int n = 0; n < g.Viewports.Size; n++) + if (g.Viewports[n]->ID == id) + return g.Viewports[n]; + return NULL; +} + +ImGuiViewport* ImGui::FindViewportByPlatformHandle(void* platform_handle) +{ + ImGuiContext& g = *GImGui; + for (int i = 0; i != g.Viewports.Size; i++) + if (g.Viewports[i]->PlatformHandle == platform_handle) + return g.Viewports[i]; + return NULL; +} + +void ImGui::SetCurrentViewport(ImGuiWindow* current_window, ImGuiViewportP* viewport) +{ + ImGuiContext& g = *GImGui; + if (viewport) + { + // First window submitted gets viewport ownership + // [2018-07-18] This is problematic: e.g. a drag and drop tooltip may steal viewport of the window it is hovered. Disabling, will rework in Docking branch. + (void)current_window; + /* + if (viewport->LastFrameActive < g.FrameCount && viewport->Window != current_window && viewport->Window != NULL && current_window != NULL) + { + //printf("[%05d] Window '%s' steal Viewport %08X from Window '%s'\n", g.FrameCount, current_window->Name, viewport->ID, viewport->Window->Name); + viewport->Window = current_window; + viewport->ID = current_window->ID; + } + */ + viewport->LastFrameActive = g.FrameCount; + } + if (g.CurrentViewport == viewport) + return; + g.CurrentViewport = viewport; + + // Notify platform layer of viewport changes + // FIXME-DPI: This is only currently used for experimenting with handling of multiple DPI + if (g.CurrentViewport && g.PlatformIO.Platform_OnChangedViewport) + g.PlatformIO.Platform_OnChangedViewport(g.CurrentViewport); +} + +static void SetWindowViewport(ImGuiWindow* window, ImGuiViewportP* viewport) +{ + window->Viewport = viewport; + window->ViewportId = viewport->ID; + window->ViewportOwned = (viewport->Window == window); +} + +static bool ImGui::GetWindowAlwaysWantOwnViewport(ImGuiWindow* window) +{ + // Tooltips and menus are not automatically forced into their own viewport when the NoMerge flag is set, however the multiplication of viewports makes them more likely to protude and create their own. + ImGuiContext& g = *GImGui; + if ((g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsNoMerge) && (g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) + //if (window->DockStatus == ImGuiDockStatus_Floating) + if ((window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip)) == 0) + return true; + return false; +} + +static void ImGui::UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* viewport) +{ + ImGuiContext& g = *GImGui; + if (!(g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) + return; + if (!(viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) || window->Viewport == viewport) + return; + if (!viewport->GetRect().Contains(window->Rect())) + return; + if (GetWindowAlwaysWantOwnViewport(window)) + return; + + // Move to the existing viewport, Move child/hosted windows as well (FIXME-OPT: iterate child) + ImGuiViewportP* old_viewport = window->Viewport; + if (window->ViewportOwned) + for (int n = 0; n < g.Windows.Size; n++) + if (g.Windows[n]->Viewport == old_viewport) + SetWindowViewport(g.Windows[n], viewport); + SetWindowViewport(window, viewport); +} + +// Scale all windows (position, size). Use when e.g. changing DPI. (This is a lossy operation!) +void ImGui::ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale) +{ + ImGuiContext& g = *GImGui; + if (viewport->Window) + { + ScaleWindow(viewport->Window, scale); + } + else + { + for (int i = 0; i != g.Windows.Size; i++) + if (g.Windows[i]->Viewport == viewport) + ScaleWindow(g.Windows[i], scale); + } +} + +static void ImGui::UpdateViewports() +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(g.PlatformIO.Viewports.Size <= g.Viewports.Size); + + // Update main viewport with current platform position and size + ImGuiViewportP* main_viewport = g.Viewports[0]; + IM_ASSERT(main_viewport->ID == IMGUI_VIEWPORT_DEFAULT_ID); + ImVec2 main_viewport_platform_pos = ImVec2(0.0f, 0.0f); + if ((g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) + main_viewport_platform_pos = g.PlatformIO.Platform_GetWindowPos(main_viewport); + AddUpdateViewport(NULL, IMGUI_VIEWPORT_DEFAULT_ID, main_viewport_platform_pos, g.IO.DisplaySize, ImGuiViewportFlags_CanHostOtherWindows); + + g.CurrentViewport = NULL; + g.MouseViewport = NULL; + for (int n = 0; n < g.Viewports.Size; n++) + { + // Erase unused viewports + ImGuiViewportP* viewport = g.Viewports[n]; + viewport->Idx = n; + + if (n > 0 && viewport->LastFrameActive < g.FrameCount - 2) + { + // Clear references to this viewport in windows (window->ViewportId becomes the master data) + for (int window_n = 0; window_n < g.Windows.Size; window_n++) + if (g.Windows[window_n]->Viewport == viewport) + g.Windows[window_n]->Viewport = NULL; + g.Viewports.erase(g.Viewports.Data + n); + + // Destroy + if (viewport == g.MouseLastHoveredViewport) g.MouseLastHoveredViewport = NULL; + IM_ASSERT(viewport->RendererUserData == NULL && viewport->PlatformUserData == NULL && viewport->PlatformHandle == NULL); + IM_ASSERT(g.PlatformIO.Viewports.contains(viewport) == false); + IM_DELETE(viewport); + n--; + continue; + } + + if ((g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) + { + // Apply Position and Size (from Platform Window to ImGui) if requested. + // We do it early in the frame instead of waiting for UpdatePlatformWindows() to avoid a frame of lag when moving/resizing using OS facilities. + if (viewport->PlatformRequestMove) + viewport->Pos = g.PlatformIO.Platform_GetWindowPos(viewport); + if (viewport->PlatformRequestResize) + viewport->Size = g.PlatformIO.Platform_GetWindowSize(viewport); + + // Translate imgui windows when a Host Viewport has been moved + ImVec2 delta = viewport->Pos - viewport->LastPos; + if ((viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) && (delta.x != 0.0f || delta.y != 0.0f)) + for (int window_n = 0; window_n < g.Windows.Size; window_n++) + if (g.Windows[window_n]->Viewport == viewport) + TranslateWindow(g.Windows[window_n], delta); + + // Update monitor (we'll use this info to clamp windows and save windows lost in a removed monitor) + viewport->PlatformMonitor = FindPlatformMonitorForRect(viewport->GetRect()); + } + + // Update DPI scale + float new_dpi_scale; + if (g.PlatformIO.Platform_GetWindowDpiScale) + new_dpi_scale = g.PlatformIO.Platform_GetWindowDpiScale(viewport); + else + new_dpi_scale = (viewport->DpiScale != 0.0f) ? viewport->DpiScale : 1.0f; + if (viewport->DpiScale != 0.0f && new_dpi_scale != viewport->DpiScale) + { + float scale_factor = new_dpi_scale / viewport->DpiScale; + if (g.IO.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleViewports) + ScaleWindowsInViewport(viewport, scale_factor); + //if (viewport == GetMainViewport()) + // g.PlatformInterface.SetWindowSize(viewport, viewport->Size * scale_factor); + + // Scale our window moving pivot so that the window will rescale roughly around the mouse position. + // FIXME-VIEWPORT: This currently creates a resizing feedback loop when a window is straddling a DPI transition border. + // (Minor: since our sizes do not perfectly linearly scale, deferring the click offset scale until we know the actual window scale ratio may get us slightly more precise mouse positioning.) + //if (g.MovingWindow != NULL && g.MovingWindow->Viewport == viewport) + // g.ActiveIdClickOffset = ImFloor(g.ActiveIdClickOffset * scale_factor); + } + viewport->DpiScale = new_dpi_scale; + } + + if (!(g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) + { + g.MouseViewport = main_viewport; + return; + } + + // Mouse handling: decide on the actual mouse viewport for this frame between the active/focused viewport and the hovered viewport. + // Note that 'viewport_hovered' should skip over any viewport that has the ImGuiViewportFlags_NoInputs flags set. + ImGuiViewportP* viewport_hovered = NULL; + if (g.IO.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport) + { + viewport_hovered = g.IO.MouseHoveredViewport ? FindViewportByID(g.IO.MouseHoveredViewport) : NULL; + if (viewport_hovered && (viewport_hovered->Flags & ImGuiViewportFlags_NoInputs)) + { + // Back-end failed at honoring its contract if it returned a viewport with the _NoInputs flag. + IM_ASSERT(0); + viewport_hovered = FindViewportHoveredFromPlatformWindowStack(g.IO.MousePos); + } + } + else + { + // If the back-end doesn't know how to honor ImGuiViewportFlags_NoInputs, we do a search ourselves. Note that this search: + // A) won't take account of the possibility that non-imgui windows may be in-between our dragged window and our target window. + // B) uses LastFrameAsRefViewport as a flawed replacement for the last time a window was focused (we could/should fix that by introducing Focus functions in PlatformIO) + viewport_hovered = FindViewportHoveredFromPlatformWindowStack(g.IO.MousePos); + } + if (viewport_hovered != NULL) + g.MouseLastHoveredViewport = viewport_hovered; + else if (g.MouseLastHoveredViewport == NULL) + g.MouseLastHoveredViewport = g.Viewports[0]; + + // Update mouse reference viewport + // (when moving a window we aim at its viewport, but this will be overwritten below if we go in drag and drop mode) + if (g.MovingWindow) + g.MouseViewport = g.MovingWindow->Viewport; + else + g.MouseViewport = g.MouseLastHoveredViewport; + + // When dragging something, always refer to the last hovered viewport. + // - when releasing a moving window we will revert to aiming behind (at viewport_hovered) + // - when we are between viewports, our dragged preview will tend to show in the last viewport _even_ if we don't have tooltips in their viewports (when lacking monitor info) + // - consider the case of holding on a menu item to browse child menus: even thou a mouse button is held, there's no active id because menu items only react on mouse release. + const bool is_mouse_dragging_with_an_expected_destination = g.DragDropActive; + if (is_mouse_dragging_with_an_expected_destination && viewport_hovered == NULL) + viewport_hovered = g.MouseLastHoveredViewport; + if (is_mouse_dragging_with_an_expected_destination || g.ActiveId == 0 || !ImGui::IsAnyMouseDown()) + if (viewport_hovered != NULL && viewport_hovered != g.MouseViewport && !(viewport_hovered->Flags & ImGuiViewportFlags_NoInputs)) + g.MouseViewport = viewport_hovered; + + IM_ASSERT(g.MouseViewport != NULL); +} + +void ImGui::UpdatePlatformWindows() +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(g.FrameCountEnded == g.FrameCount && "Forgot to call Render() or EndFrame() before UpdatePlatformWindows()?"); + IM_ASSERT(g.FrameCountPlatformEnded < g.FrameCount); + g.FrameCountPlatformEnded = g.FrameCount; + g.Viewports[0]->LastPos = g.Viewports[0]->Pos; + if (!(g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) + return; + + // Create/resize/destroy platform windows to match each active viewport. + // Skip the main viewport (index 0), which is always fully handled by the application! + for (int i = 1; i < g.Viewports.Size; i++) + { + ImGuiViewportP* viewport = g.Viewports[i]; + viewport->LastPos = viewport->Pos; + + // Destroy platform window if the viewport hasn't been submitted or if it is hosting a hidden window (the implicit Debug window will be registered its viewport then be disabled) + bool destroy_platform_window = false; + destroy_platform_window |= (viewport->LastFrameActive < g.FrameCount - 1); + destroy_platform_window |= (viewport->Window && !IsWindowActiveAndVisible(viewport->Window)); + if (destroy_platform_window) + { + if (viewport->CreatedPlatformWindow && g.PlatformIO.Renderer_DestroyWindow) + g.PlatformIO.Renderer_DestroyWindow(viewport); + if (viewport->CreatedPlatformWindow && g.PlatformIO.Platform_DestroyWindow) + g.PlatformIO.Platform_DestroyWindow(viewport); + viewport->CreatedPlatformWindow = false; + IM_ASSERT(viewport->RendererUserData == NULL); + IM_ASSERT(viewport->PlatformUserData == NULL && viewport->PlatformHandle == NULL); + continue; + } + if (viewport->LastFrameActive < g.FrameCount) + continue; + + // New windows that appears directly in a new viewport won't always have a size on their frame + if (viewport->Size.x <= 0 || viewport->Size.y <= 0) + continue; + + // Update viewport flags + if (viewport->Window != NULL) + { + bool topmost = (viewport->Window->Flags & ImGuiWindowFlags_Tooltip) != 0; + bool no_task_bar_icon = (g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsNoTaskBarIcons) != 0 || (viewport->Window->Flags & (ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) != 0; + viewport->Flags = topmost ? (viewport->Flags | ImGuiViewportFlags_TopMost) : (viewport->Flags & ~ImGuiViewportFlags_TopMost); + viewport->Flags = no_task_bar_icon ? (viewport->Flags | ImGuiViewportFlags_NoTaskBarIcon) : (viewport->Flags & ~ImGuiViewportFlags_NoTaskBarIcon); + } + + // Create window + bool is_new_window = (viewport->CreatedPlatformWindow == false); + if (is_new_window) + { + g.PlatformIO.Platform_CreateWindow(viewport); + if (g.PlatformIO.Renderer_CreateWindow != NULL) + g.PlatformIO.Renderer_CreateWindow(viewport); + viewport->RendererLastSize = viewport->Size; + viewport->CreatedPlatformWindow = true; + } + + // Apply Position and Size (from ImGui to Platform/Renderer back-ends) + if (!viewport->PlatformRequestMove) + g.PlatformIO.Platform_SetWindowPos(viewport, viewport->Pos); + if (!viewport->PlatformRequestResize) + g.PlatformIO.Platform_SetWindowSize(viewport, viewport->Size); + if (g.PlatformIO.Renderer_SetWindowSize && (viewport->RendererLastSize.x != viewport->Size.x || viewport->RendererLastSize.y != viewport->Size.y)) + g.PlatformIO.Renderer_SetWindowSize(viewport, viewport->Size); + viewport->RendererLastSize = viewport->Size; + + // Update title bar (if it changed) + if (ImGuiWindow* window_for_title = viewport->Window) + { + const char* title_begin = window_for_title->Name; + char* title_end = (char*)(intptr_t)FindRenderedTextEnd(title_begin); + const ImGuiID title_hash = ImHash(title_begin, (int)(title_end - title_begin)); + if (viewport->LastNameHash != title_hash) + { + char title_end_backup_c = *title_end; + *title_end = 0; // Cut existing buffer short instead of doing an alloc/free + g.PlatformIO.Platform_SetWindowTitle(viewport, title_begin); + *title_end = title_end_backup_c; + viewport->LastNameHash = title_hash; + } + } + + // Update alpha + if (viewport->LastAlpha != viewport->Alpha && g.PlatformIO.Platform_SetWindowAlpha) + g.PlatformIO.Platform_SetWindowAlpha(viewport, viewport->Alpha); + viewport->LastAlpha = viewport->Alpha; + + // Show window. On startup ensure platform window don't get focus + if (is_new_window) + { + if (g.FrameCount < 2) + viewport->Flags |= ImGuiViewportFlags_NoFocusOnAppearing; + g.PlatformIO.Platform_ShowWindow(viewport); + } + + // Even without focus, we assume the window becomes front-most. This is used by our platform z-order heuristic when io.MouseHoveredViewport is not available. + if (is_new_window && viewport->LastFrontMostStampCount != g.WindowsFrontMostStampCount) + viewport->LastFrontMostStampCount = ++g.WindowsFrontMostStampCount; + + // Clear request flags + viewport->PlatformRequestClose = viewport->PlatformRequestMove = viewport->PlatformRequestResize = false; + } + + // Update our implicit z-order knowledge of platform windows, which is used when the back-end cannot provide io.MouseHoveredViewport. + if (g.PlatformIO.Platform_GetWindowFocus != NULL) + { + ImGuiViewportP* focused_viewport = NULL; + for (int i = 0; i < g.Viewports.Size && focused_viewport == NULL; i++) + if (g.Viewports[i]->PlatformUserData != NULL || g.Viewports[i]->PlatformHandle != NULL || g.Viewports[i]->CreatedPlatformWindow) + if (g.PlatformIO.Platform_GetWindowFocus(g.Viewports[i])) + focused_viewport = g.Viewports[i]; + if (focused_viewport && g.PlatformLastFocusedViewport != focused_viewport->ID) + { + if (focused_viewport->LastFrontMostStampCount != g.WindowsFrontMostStampCount) + focused_viewport->LastFrontMostStampCount = ++g.WindowsFrontMostStampCount; + g.PlatformLastFocusedViewport = focused_viewport->ID; + } + } +} + +// This is a default/basic function for performing the rendering/swap of multiple platform windows. +// Custom renderers may prefer to not call this function at all, and instead iterate the publicly exposed platform data and handle rendering/sync themselves. +// The Render/Swap functions stored in ImGuiPlatformIO are merely here to allow for this helper to exist, but you can do it yourself: +// +// ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); +// for (int i = 1; i < platform_io.Viewports.Size; i++) +// MyRenderFunction(platform_io.Viewports[i], my_args); +// for (int i = 1; i < platform_io.Viewports.Size; i++) +// MySwapBufferFunction(platform_io.Viewports[i], my_args); +// +void ImGui::RenderPlatformWindowsDefault(void* platform_render_arg, void* renderer_render_arg) +{ + if (!(ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) + return; + + // Skip the main viewport (index 0), which is always fully handled by the application! + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + for (int i = 1; i < platform_io.Viewports.Size; i++) + { + ImGuiViewport* viewport = platform_io.Viewports[i]; + if (platform_io.Platform_RenderWindow) platform_io.Platform_RenderWindow(viewport, platform_render_arg); + if (platform_io.Renderer_RenderWindow) platform_io.Renderer_RenderWindow(viewport, renderer_render_arg); + } + for (int i = 1; i < platform_io.Viewports.Size; i++) + { + ImGuiViewport* viewport = platform_io.Viewports[i]; + if (platform_io.Platform_SwapBuffers) platform_io.Platform_SwapBuffers(viewport, platform_render_arg); + if (platform_io.Renderer_SwapBuffers) platform_io.Renderer_SwapBuffers(viewport, renderer_render_arg); + } +} + +void ImGui::DestroyPlatformWindows() +{ + // We call the destroy window on the main viewport (index 0) to give a chance to the back-end to clear any data + // have stored in e.g. PlatformHandle. + // It is expected that the back-end stored a flag to remember that it doesn't own the window created for the main viewport, + // and won't destroy the underlying platform/renderer data (e.g. + ImGuiContext& g = *GImGui; + for (int i = 0; i < g.Viewports.Size; i++) + if (g.Viewports[i]->CreatedPlatformWindow) + { + if (g.PlatformIO.Renderer_DestroyWindow) + g.PlatformIO.Renderer_DestroyWindow(g.Viewports[i]); + if (g.PlatformIO.Platform_DestroyWindow) + g.PlatformIO.Platform_DestroyWindow(g.Viewports[i]); + g.Viewports[i]->CreatedPlatformWindow = false; + } +} + //----------------------------------------------------------------------------- // NAVIGATION //----------------------------------------------------------------------------- From fd90afef4302265db2e64a77440c256e8d9f0c22 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 31 Aug 2018 11:08:48 +0200 Subject: [PATCH 222/828] Refactor: Internals: Moved Viewport functions in imgui.cpp in their own section. (part 3) (#2036, #1542) --- imgui.cpp | 320 +++++++++++++++++++++++++++--------------------------- 1 file changed, 160 insertions(+), 160 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 4633754e559a..1e02c839e929 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -56,6 +56,7 @@ CODE - Main Code (most of the code! lots of stuff, needs tidying up) - Tooltips - Popups +- Viewports, Platform Windows - Navigation - Columns - Drag and Drop @@ -911,7 +912,6 @@ static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, // Platform Dependents default implementation for IO functions static const char* GetClipboardTextFn_DefaultImpl(void* user_data); static void SetClipboardTextFn_DefaultImpl(void* user_data, const char* text); -static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y); namespace ImGui { @@ -3582,53 +3582,6 @@ void ImGui::Render() #endif } -ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const ImVec2& pos, const ImVec2& size, ImGuiViewportFlags flags) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(id != 0); - - ImGuiViewportP* viewport = (ImGuiViewportP*)FindViewportByID(id); - if (viewport) - { - viewport->Pos = pos; - viewport->Size = size; - } - else - { - // New viewport - viewport = IM_NEW(ImGuiViewportP)(); - viewport->ID = id; - viewport->Idx = g.Viewports.Size; - viewport->Pos = viewport->LastPos = pos; - viewport->Size = size; - viewport->PlatformMonitor = FindPlatformMonitorForRect(viewport->GetRect()); - g.Viewports.push_back(viewport); - - if (window && (window->Flags & ImGuiWindowFlags_NoFocusOnAppearing)) - flags |= ImGuiViewportFlags_NoFocusOnAppearing; - - // We normally setup for all viewports in NewFrame() but here need to handle the mid-frame creation of a new viewport. - // We need to extend the fullscreen clip rect so the OverlayDrawList clip is correct for that the first frame - g.DrawListSharedData.ClipRectFullscreen.z = ImMax(g.DrawListSharedData.ClipRectFullscreen.z, viewport->Pos.x + viewport->Size.x); - g.DrawListSharedData.ClipRectFullscreen.w = ImMax(g.DrawListSharedData.ClipRectFullscreen.w, viewport->Pos.y + viewport->Size.y); - - // Request an initial DpiScale before the OS platform window creation - // This is so we can select an appropriate font size on the first frame of our window lifetime - if (g.PlatformIO.Platform_GetWindowDpiScale) - viewport->DpiScale = g.PlatformIO.Platform_GetWindowDpiScale(viewport); - } - - viewport->Window = window; - viewport->Flags = flags; - viewport->LastFrameActive = g.FrameCount; - IM_ASSERT(window == NULL || viewport->ID == window->ID); - - if (window != NULL) - window->ViewportOwned = true; - - return viewport; -} - const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end) { const char* text_display_end = text; @@ -4636,118 +4589,6 @@ static int ImGui::FindPlatformMonitorForRect(const ImRect& rect) return best_monitor_n; } -// FIXME-VIEWPORT: This is all super messy and ought to be clarified or rewritten. -static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) -{ - ImGuiContext& g = *GImGui; - ImGuiWindowFlags flags = window->Flags; - window->ViewportAllowPlatformMonitorExtend = -1; - - // Restore main viewport if multi-viewport is not supported by the back-end - ImGuiViewportP* main_viewport = g.Viewports[0]; - if (!(g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) - { - window->Viewport = main_viewport; - window->ViewportId = main_viewport->ID; - window->ViewportOwned = false; - return; - } - - // Merge into host viewports (after moving, resizing) - if (window->ViewportOwned && window->ViewportTryMerge && g.ActiveId == 0) - { - UpdateTryMergeWindowIntoHostViewport(window, g.Viewports[0]); - window->ViewportTryMerge = false; - } - window->ViewportOwned = false; - - // Appearing popups reset their viewport so they can inherit again - if ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && window->Appearing) - { - window->Viewport = NULL; - window->ViewportId = 0; - } - - if (!g.NextWindowData.ViewportCond) - { - // By default inherit from parent window - if (window->Viewport == NULL && window->ParentWindow) - window->Viewport = window->ParentWindow->Viewport; - - // Attempt to restore saved viewport id (= window that hasn't been activated yet), try to restore the viewport based on saved 'window->ViewportPos' restored from .ini file - if (window->Viewport == NULL && window->ViewportId != 0) - { - window->Viewport = FindViewportByID(window->ViewportId); - if (window->Viewport == NULL && window->ViewportPos.x != FLT_MAX && window->ViewportPos.y != FLT_MAX) - window->Viewport = AddUpdateViewport(window, window->ID, window->ViewportPos, window->Size, ImGuiViewportFlags_NoDecoration); - } - } - - if (g.NextWindowData.ViewportCond) - { - // Code explicitly request a viewport - window->Viewport = FindViewportByID(g.NextWindowData.ViewportId); - window->ViewportId = g.NextWindowData.ViewportId; // Store ID even if Viewport isn't resolved yet. - } - else if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_ChildMenu)) - { - // Always inherit viewport from parent window - window->Viewport = window->ParentWindow->Viewport; - } - else if (flags & ImGuiWindowFlags_Tooltip) - { - window->Viewport = g.MouseViewport; - } - else if (g.MovingWindow && g.MovingWindow->RootWindow == window && IsMousePosValid()) - { - // Transition to our own viewport when leaving our host boundaries + set the NoInputs flag (which will be cleared in UpdateMovingWindow when releasing the mouse) - // If we are already in our own viewport, if need to set the NoInputs flag. - // If we have no viewport (which happens when detaching a docked node) immediately create one. - // We test for 'window->Viewport->Window == window' instead of 'window->ViewportOwned' because ViewportOwned is not valid during this function. - bool has_viewport = (window->Viewport != NULL); - bool own_viewport = has_viewport && (window->Viewport->Window == window); - bool leave_host_viewport = has_viewport && !own_viewport && !window->Viewport->GetRect().Contains(window->Rect()); - bool move_from_own_viewport = has_viewport && own_viewport && !(window->Viewport->Flags & ImGuiViewportFlags_NoInputs); - if (!has_viewport || leave_host_viewport || move_from_own_viewport) - window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoDecoration | ImGuiViewportFlags_NoFocusOnAppearing | ImGuiViewportFlags_NoInputs); - } - else if (GetWindowAlwaysWantOwnViewport(window)) - { - window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoDecoration); - } - - // Mark window as allowed to protrude outside of its viewport and into the current monitor - // We need to take account of the possibility that mouse may become invalid. - const bool use_mouse_ref = (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow); - if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) - { - ImVec2 mouse_ref = (flags & ImGuiWindowFlags_Tooltip) ? g.IO.MousePos : g.CurrentPopupStack.back().OpenMousePos; - bool mouse_valid = IsMousePosValid(&mouse_ref); - if ((window->Appearing || (flags & ImGuiWindowFlags_Tooltip)) && (!use_mouse_ref || mouse_valid)) - window->ViewportAllowPlatformMonitorExtend = FindPlatformMonitorForPos((use_mouse_ref && mouse_valid) ? mouse_ref : NavCalcPreferredRefPos()); - else - window->ViewportAllowPlatformMonitorExtend = window->Viewport->PlatformMonitor; - } - if (window->ViewportTrySplit && window->ViewportAllowPlatformMonitorExtend < 0) - window->ViewportAllowPlatformMonitorExtend = window->Viewport->PlatformMonitor; - window->ViewportTrySplit = false; - - // Fallback to default viewport - if (window->Viewport == NULL) - window->Viewport = main_viewport; - - // Update flags - window->ViewportOwned = (window == window->Viewport->Window); - if (window->ViewportOwned) - window->Viewport->Flags |= ImGuiViewportFlags_NoDecoration; - - // If the OS window has a title bar, hide our imgui title bar - if (window->ViewportOwned && !(window->Viewport->Flags & ImGuiViewportFlags_NoDecoration)) - window->Flags |= ImGuiWindowFlags_NoTitleBar; - - window->ViewportId = window->Viewport->ID; -} - struct ImGuiResizeGripDef { ImVec2 CornerPos; @@ -7619,6 +7460,165 @@ static void ImGui::UpdateViewports() IM_ASSERT(g.MouseViewport != NULL); } +ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const ImVec2& pos, const ImVec2& size, ImGuiViewportFlags flags) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(id != 0); + + ImGuiViewportP* viewport = (ImGuiViewportP*)FindViewportByID(id); + if (viewport) + { + viewport->Pos = pos; + viewport->Size = size; + } + else + { + // New viewport + viewport = IM_NEW(ImGuiViewportP)(); + viewport->ID = id; + viewport->Idx = g.Viewports.Size; + viewport->Pos = viewport->LastPos = pos; + viewport->Size = size; + viewport->PlatformMonitor = FindPlatformMonitorForRect(viewport->GetRect()); + g.Viewports.push_back(viewport); + + if (window && (window->Flags & ImGuiWindowFlags_NoFocusOnAppearing)) + flags |= ImGuiViewportFlags_NoFocusOnAppearing; + + // We normally setup for all viewports in NewFrame() but here need to handle the mid-frame creation of a new viewport. + // We need to extend the fullscreen clip rect so the OverlayDrawList clip is correct for that the first frame + g.DrawListSharedData.ClipRectFullscreen.z = ImMax(g.DrawListSharedData.ClipRectFullscreen.z, viewport->Pos.x + viewport->Size.x); + g.DrawListSharedData.ClipRectFullscreen.w = ImMax(g.DrawListSharedData.ClipRectFullscreen.w, viewport->Pos.y + viewport->Size.y); + + // Request an initial DpiScale before the OS platform window creation + // This is so we can select an appropriate font size on the first frame of our window lifetime + if (g.PlatformIO.Platform_GetWindowDpiScale) + viewport->DpiScale = g.PlatformIO.Platform_GetWindowDpiScale(viewport); + } + + viewport->Window = window; + viewport->Flags = flags; + viewport->LastFrameActive = g.FrameCount; + IM_ASSERT(window == NULL || viewport->ID == window->ID); + + if (window != NULL) + window->ViewportOwned = true; + + return viewport; +} + +// FIXME-VIEWPORT: This is all super messy and ought to be clarified or rewritten. +static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; + ImGuiWindowFlags flags = window->Flags; + window->ViewportAllowPlatformMonitorExtend = -1; + + // Restore main viewport if multi-viewport is not supported by the back-end + ImGuiViewportP* main_viewport = g.Viewports[0]; + if (!(g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) + { + window->Viewport = main_viewport; + window->ViewportId = main_viewport->ID; + window->ViewportOwned = false; + return; + } + + // Merge into host viewports (after moving, resizing) + if (window->ViewportOwned && window->ViewportTryMerge && g.ActiveId == 0) + { + UpdateTryMergeWindowIntoHostViewport(window, g.Viewports[0]); + window->ViewportTryMerge = false; + } + window->ViewportOwned = false; + + // Appearing popups reset their viewport so they can inherit again + if ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && window->Appearing) + { + window->Viewport = NULL; + window->ViewportId = 0; + } + + if (!g.NextWindowData.ViewportCond) + { + // By default inherit from parent window + if (window->Viewport == NULL && window->ParentWindow) + window->Viewport = window->ParentWindow->Viewport; + + // Attempt to restore saved viewport id (= window that hasn't been activated yet), try to restore the viewport based on saved 'window->ViewportPos' restored from .ini file + if (window->Viewport == NULL && window->ViewportId != 0) + { + window->Viewport = FindViewportByID(window->ViewportId); + if (window->Viewport == NULL && window->ViewportPos.x != FLT_MAX && window->ViewportPos.y != FLT_MAX) + window->Viewport = AddUpdateViewport(window, window->ID, window->ViewportPos, window->Size, ImGuiViewportFlags_NoDecoration); + } + } + + if (g.NextWindowData.ViewportCond) + { + // Code explicitly request a viewport + window->Viewport = FindViewportByID(g.NextWindowData.ViewportId); + window->ViewportId = g.NextWindowData.ViewportId; // Store ID even if Viewport isn't resolved yet. + } + else if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_ChildMenu)) + { + // Always inherit viewport from parent window + window->Viewport = window->ParentWindow->Viewport; + } + else if (flags & ImGuiWindowFlags_Tooltip) + { + window->Viewport = g.MouseViewport; + } + else if (g.MovingWindow && g.MovingWindow->RootWindow == window && IsMousePosValid()) + { + // Transition to our own viewport when leaving our host boundaries + set the NoInputs flag (which will be cleared in UpdateMovingWindow when releasing the mouse) + // If we are already in our own viewport, if need to set the NoInputs flag. + // If we have no viewport (which happens when detaching a docked node) immediately create one. + // We test for 'window->Viewport->Window == window' instead of 'window->ViewportOwned' because ViewportOwned is not valid during this function. + bool has_viewport = (window->Viewport != NULL); + bool own_viewport = has_viewport && (window->Viewport->Window == window); + bool leave_host_viewport = has_viewport && !own_viewport && !window->Viewport->GetRect().Contains(window->Rect()); + bool move_from_own_viewport = has_viewport && own_viewport && !(window->Viewport->Flags & ImGuiViewportFlags_NoInputs); + if (!has_viewport || leave_host_viewport || move_from_own_viewport) + window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoDecoration | ImGuiViewportFlags_NoFocusOnAppearing | ImGuiViewportFlags_NoInputs); + } + else if (GetWindowAlwaysWantOwnViewport(window)) + { + window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoDecoration); + } + + // Mark window as allowed to protrude outside of its viewport and into the current monitor + // We need to take account of the possibility that mouse may become invalid. + const bool use_mouse_ref = (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow); + if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) + { + ImVec2 mouse_ref = (flags & ImGuiWindowFlags_Tooltip) ? g.IO.MousePos : g.CurrentPopupStack.back().OpenMousePos; + bool mouse_valid = IsMousePosValid(&mouse_ref); + if ((window->Appearing || (flags & ImGuiWindowFlags_Tooltip)) && (!use_mouse_ref || mouse_valid)) + window->ViewportAllowPlatformMonitorExtend = FindPlatformMonitorForPos((use_mouse_ref && mouse_valid) ? mouse_ref : NavCalcPreferredRefPos()); + else + window->ViewportAllowPlatformMonitorExtend = window->Viewport->PlatformMonitor; + } + if (window->ViewportTrySplit && window->ViewportAllowPlatformMonitorExtend < 0) + window->ViewportAllowPlatformMonitorExtend = window->Viewport->PlatformMonitor; + window->ViewportTrySplit = false; + + // Fallback to default viewport + if (window->Viewport == NULL) + window->Viewport = main_viewport; + + // Update flags + window->ViewportOwned = (window == window->Viewport->Window); + if (window->ViewportOwned) + window->Viewport->Flags |= ImGuiViewportFlags_NoDecoration; + + // If the OS window has a title bar, hide our imgui title bar + if (window->ViewportOwned && !(window->Viewport->Flags & ImGuiViewportFlags_NoDecoration)) + window->Flags |= ImGuiWindowFlags_NoTitleBar; + + window->ViewportId = window->Viewport->ID; +} + void ImGui::UpdatePlatformWindows() { ImGuiContext& g = *GImGui; From 1007873d945159ebcaba767f53708b29b1b410fe Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 3 Sep 2018 19:51:22 +0200 Subject: [PATCH 223/828] Misc: merge minor/inconsequential stuff from Docking branch to reduce drift. --- imgui.cpp | 41 ++++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index b713227ce0c5..1a2abed73c04 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3180,7 +3180,7 @@ void ImGui::Initialize(ImGuiContext* context) ini_handler.ReadOpenFn = SettingsHandlerWindow_ReadOpen; ini_handler.ReadLineFn = SettingsHandlerWindow_ReadLine; ini_handler.WriteAllFn = SettingsHandlerWindow_WriteAll; - g.SettingsHandlers.push_front(ini_handler); + g.SettingsHandlers.push_back(ini_handler); // Create default viewport ImGuiViewportP* viewport = IM_NEW(ImGuiViewportP)(); @@ -3746,7 +3746,7 @@ void ImGui::RenderBullet(ImVec2 pos) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - window->DrawList->AddCircleFilled(pos, GImGui->FontSize*0.20f, GetColorU32(ImGuiCol_Text), 8); + window->DrawList->AddCircleFilled(pos, g.FontSize*0.20f, GetColorU32(ImGuiCol_Text), 8); } void ImGui::RenderCheckMark(ImVec2 pos, ImU32 col, float sz) @@ -4771,8 +4771,10 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window = CreateNewWindow(name, size_on_first_use, flags); } - // Update name when it changes (which can only happen with the "###" operator), but only if it is meant to be displayed to the end user, else there is no point. - if (!window_just_created && window->Viewport && window->Viewport->Window == window && strcmp(name, window->Name) != 0) + // Update stored window name when it changes (which can only happen with the "###" operator). + // Only if it is meant to be displayed to the end user in a different place than the title bar (which already always display the 'name' parameter) + bool window_title_visible_elsewhere = (window->Viewport && window->Viewport->Window == window); + if (!window_just_created && window_title_visible_elsewhere && strcmp(name, window->Name) != 0) { IM_DELETE(window->Name); window->Name = ImStrdup(name); @@ -4791,18 +4793,14 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) { window->FlagsPreviousFrame = window->Flags; window->Flags = (ImGuiWindowFlags)flags; + window->BeginOrderWithinParent = 0; + window->BeginOrderWithinContext = g.WindowsActiveCount++; } else { flags = window->Flags; } - // Parent window is latched only on the first call to Begin() of the frame, so further append-calls can be done from a different window stack - ImGuiWindow* parent_window_in_stack = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back(); - ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow; - IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow)); - window->HasCloseButton = (p_open != NULL); - // Update the Appearing flag bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesForResize > 0); @@ -4816,6 +4814,11 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if (window->Appearing) SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true); + // Parent window is latched only on the first call to Begin() of the frame, so further append-calls can be done from a different window stack + ImGuiWindow* parent_window_in_stack = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back(); + ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow; + IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow)); + // Add to stack // We intentionally set g.CurrentWindow to NULL to prevent usage until when the viewport is set, then will call SetCurrentWindow() g.CurrentWindowStack.push_back(window); @@ -4883,8 +4886,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) UpdateWindowParentAndRootLinks(window, flags, parent_window); window->Active = true; - window->BeginOrderWithinParent = 0; - window->BeginOrderWithinContext = g.WindowsActiveCount++; + window->HasCloseButton = (p_open != NULL); window->ClipRect = ImVec4(-FLT_MAX,-FLT_MAX,+FLT_MAX,+FLT_MAX); window->LastFrameActive = current_frame; window->IDStack.resize(1); @@ -5393,7 +5395,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->InnerClipRect.Max.x = ImFloor(0.5f + window->InnerMainRect.Max.x - ImMax(0.0f, ImFloor(window->WindowPadding.x*0.5f - window->WindowBorderSize))); window->InnerClipRect.Max.y = ImFloor(0.5f + window->InnerMainRect.Max.y); - // After Begin() we fill the last item / hovered data based on title bar data. It is a standard behavior (to allow creation of context menus on title bar only, etc.). + // We fill last item data based on Title Bar or Tab, in order for IsItemHovered() and IsItemActive() to be usable after Begin(). + // This is useful to allow creating context menus on title bar only, etc. window->DC.LastItemId = window->MoveId; window->DC.LastItemStatusFlags = IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0; window->DC.LastItemRect = title_bar_rect; @@ -9668,7 +9671,8 @@ void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size) while (line_end < buf_end && *line_end != '\n' && *line_end != '\r') line_end++; line_end[0] = 0; - + if (line[0] == ';') + continue; if (line[0] == '[' && line_end > line && line_end[-1] == ']') { // Parse "[Type][Name]". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code. @@ -9758,6 +9762,7 @@ static void SettingsHandlerWindow_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf) { // Gather data from windows that were active during this session + // (if a window wasn't opened in this session we preserve its settings) ImGuiContext& g = *imgui_ctx; for (int i = 0; i != g.Windows.Size; i++) { @@ -9779,8 +9784,7 @@ static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSetting settings->Collapsed = window->Collapsed; } - // Write a buffer - // If a window wasn't opened in this session we preserve its settings + // Write to text buffer buf->reserve(buf->size() + g.SettingsWindows.Size * 96); // ballpark reserve for (int i = 0; i != g.SettingsWindows.Size; i++) { @@ -9914,7 +9918,7 @@ static void RenderViewportThumbnail(ImDrawList* draw_list, ImGuiViewportP* viewp ImRect thumb_r = thumb_window->Rect(); ImRect title_r = thumb_window->TitleBarRect(); ImRect thumb_r_scaled = ImRect(ImFloor(off + thumb_r.Min * scale), ImFloor(off + thumb_r.Max * scale)); - ImRect title_r_scaled = ImRect(ImFloor(off + title_r.Min * scale), ImFloor(off + ImVec2(title_r.Max.x, title_r.Min.y) * scale) + ImVec2(0,5)); // Exagerate title bar height + ImRect title_r_scaled = ImRect(ImFloor(off + title_r.Min * scale), ImFloor(off + ImVec2(title_r.Max.x, title_r.Min.y) * scale) + ImVec2(0,5)); // Exaggerate title bar height thumb_r_scaled.ClipWithFull(bb); title_r_scaled.ClipWithFull(bb); const bool window_is_focused = (g.NavWindow && thumb_window->RootWindowForTitleBarHighlight == g.NavWindow->RootWindowForTitleBarHighlight); @@ -9961,7 +9965,6 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::Text("%d allocations", io.MetricsActiveAllocations); ImGui::Checkbox("Show clipping rectangles when hovering draw commands", &show_draw_cmd_clip_rects); ImGui::Checkbox("Ctrl shows window begin order", &show_window_begin_order); - ImGui::Separator(); struct Funcs @@ -10064,7 +10067,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::BulletText("NavRectRel[0]: (%.1f,%.1f)(%.1f,%.1f)", window->NavRectRel[0].Min.x, window->NavRectRel[0].Min.y, window->NavRectRel[0].Max.x, window->NavRectRel[0].Max.y); else ImGui::BulletText("NavRectRel[0]: "); - ImGui::BulletText("Viewport: %d, ViewportId: 0x%08X, ViewportPos: (%.1f,%.1f)", window->Viewport ? window->Viewport->Idx : -1, window->ViewportId, window->ViewportPos.x, window->ViewportPos.y); + ImGui::BulletText("Viewport: %d%s, ViewportId: 0x%08X, ViewportPos: (%.1f,%.1f)", window->Viewport ? window->Viewport->Idx : -1, window->ViewportOwned ? " (Owned)" : "", window->ViewportId, window->ViewportPos.x, window->ViewportPos.y); ImGui::BulletText("ViewportMonitor: %d", window->Viewport ? window->Viewport->PlatformMonitor : -1); if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow"); if (window->ParentWindow != NULL) NodeWindow(window->ParentWindow, "ParentWindow"); From 897e5c6231af3cc3e45c21bd77818c53019b3d59 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 5 Sep 2018 11:08:44 +0200 Subject: [PATCH 224/828] Viewport: Moved code following refactor. (#2036, #1542) --- imgui.cpp | 110 +++++++++++++++++++++++------------------------ imgui_internal.h | 2 +- 2 files changed, 56 insertions(+), 56 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 1a2abed73c04..242990734973 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2112,9 +2112,9 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) Appearing = false; Hidden = false; HasCloseButton = false; + BeginCount = 0; BeginOrderWithinParent = -1; BeginOrderWithinContext = -1; - BeginCount = 0; PopupId = 0; AutoFitFramesX = AutoFitFramesY = -1; AutoFitOnlyGrows = false; @@ -2757,23 +2757,6 @@ void ImGui::UpdateMouseMovingWindow() } } -// If the back-end doesn't set MouseLastHoveredViewport or doesn't honor ImGuiViewportFlags_NoInputs, we do a search ourselves. -// A) It won't take account of the possibility that non-imgui windows may be in-between our dragged window and our target window. -// B) It requires Platform_GetWindowFocus to be implemented by back-end. -static ImGuiViewportP* FindViewportHoveredFromPlatformWindowStack(const ImVec2 mouse_platform_pos) -{ - ImGuiContext& g = *GImGui; - ImGuiViewportP* best_candidate = NULL; - for (int n = 0; n < g.Viewports.Size; n++) - { - ImGuiViewportP* viewport = g.Viewports[n]; - if (!(viewport->Flags & ImGuiViewportFlags_NoInputs) && viewport->GetRect().Contains(mouse_platform_pos)) - if (best_candidate == NULL || best_candidate->LastFrontMostStampCount < viewport->LastFrontMostStampCount) - best_candidate = viewport; - } - return best_candidate; -} - static void TranslateWindow(ImGuiWindow* window, const ImVec2& delta) { window->Pos += delta; @@ -4556,42 +4539,6 @@ static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& co *out_size = size_constrained; } -static int ImGui::FindPlatformMonitorForPos(const ImVec2& pos) -{ - ImGuiContext& g = *GImGui; - for (int monitor_n = 0; monitor_n < g.PlatformIO.Monitors.Size; monitor_n++) - { - const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[monitor_n]; - if (ImRect(monitor.MainPos, monitor.MainPos + monitor.MainSize).Contains(pos)) - return monitor_n; - } - return -1; -} - -// Search for the monitor with the largest intersection area with the given rectangle -// We generally try to avoid searching loops but the monitor count should be very small here -static int ImGui::FindPlatformMonitorForRect(const ImRect& rect) -{ - ImGuiContext& g = *GImGui; - float surface_threshold = rect.GetWidth() * rect.GetHeight() * 0.5f; - int best_monitor_n = -1; - float best_monitor_surface = 0.001f; - for (int monitor_n = 0; monitor_n < g.PlatformIO.Monitors.Size && best_monitor_surface < surface_threshold; monitor_n++) - { - const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[monitor_n]; - if (ImRect(monitor.MainPos, monitor.MainPos + monitor.MainSize).Contains(rect)) - return monitor_n; - ImRect overlapping_rect = rect; - overlapping_rect.ClipWithFull(ImRect(monitor.MainPos, monitor.MainPos + monitor.MainSize)); - float overlapping_surface = overlapping_rect.GetWidth() * overlapping_rect.GetHeight(); - if (overlapping_surface < best_monitor_surface) - continue; - best_monitor_surface = overlapping_surface; - best_monitor_n = monitor_n; - } - return best_monitor_n; -} - struct ImGuiResizeGripDef { ImVec2 CornerPos; @@ -7333,6 +7280,23 @@ void ImGui::ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale) } } +// If the back-end doesn't set MouseLastHoveredViewport or doesn't honor ImGuiViewportFlags_NoInputs, we do a search ourselves. +// A) It won't take account of the possibility that non-imgui windows may be in-between our dragged window and our target window. +// B) It requires Platform_GetWindowFocus to be implemented by back-end. +static ImGuiViewportP* FindViewportHoveredFromPlatformWindowStack(const ImVec2 mouse_platform_pos) +{ + ImGuiContext& g = *GImGui; + ImGuiViewportP* best_candidate = NULL; + for (int n = 0; n < g.Viewports.Size; n++) + { + ImGuiViewportP* viewport = g.Viewports[n]; + if (!(viewport->Flags & ImGuiViewportFlags_NoInputs) && viewport->GetRect().Contains(mouse_platform_pos)) + if (best_candidate == NULL || best_candidate->LastFrontMostStampCount < viewport->LastFrontMostStampCount) + best_candidate = viewport; + } + return best_candidate; +} + static void ImGui::UpdateViewports() { ImGuiContext& g = *GImGui; @@ -7747,6 +7711,42 @@ void ImGui::UpdatePlatformWindows() } } +static int ImGui::FindPlatformMonitorForPos(const ImVec2& pos) +{ + ImGuiContext& g = *GImGui; + for (int monitor_n = 0; monitor_n < g.PlatformIO.Monitors.Size; monitor_n++) + { + const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[monitor_n]; + if (ImRect(monitor.MainPos, monitor.MainPos + monitor.MainSize).Contains(pos)) + return monitor_n; + } + return -1; +} + +// Search for the monitor with the largest intersection area with the given rectangle +// We generally try to avoid searching loops but the monitor count should be very small here +static int ImGui::FindPlatformMonitorForRect(const ImRect& rect) +{ + ImGuiContext& g = *GImGui; + float surface_threshold = rect.GetWidth() * rect.GetHeight() * 0.5f; + int best_monitor_n = -1; + float best_monitor_surface = 0.001f; + for (int monitor_n = 0; monitor_n < g.PlatformIO.Monitors.Size && best_monitor_surface < surface_threshold; monitor_n++) + { + const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[monitor_n]; + if (ImRect(monitor.MainPos, monitor.MainPos + monitor.MainSize).Contains(rect)) + return monitor_n; + ImRect overlapping_rect = rect; + overlapping_rect.ClipWithFull(ImRect(monitor.MainPos, monitor.MainPos + monitor.MainSize)); + float overlapping_surface = overlapping_rect.GetWidth() * overlapping_rect.GetHeight(); + if (overlapping_surface < best_monitor_surface) + continue; + best_monitor_surface = overlapping_surface; + best_monitor_n = monitor_n; + } + return best_monitor_n; +} + // This is a default/basic function for performing the rendering/swap of multiple platform windows. // Custom renderers may prefer to not call this function at all, and instead iterate the publicly exposed platform data and handle rendering/sync themselves. // The Render/Swap functions stored in ImGuiPlatformIO are merely here to allow for this helper to exist, but you can do it yourself: @@ -9894,7 +9894,7 @@ static void SetClipboardTextFn_DefaultImpl(void*, const char* text) #endif //----------------------------------------------------------------------------- -// HELP, METRICS +// METRICS/DEBUG WINDOW //----------------------------------------------------------------------------- static void RenderViewportThumbnail(ImDrawList* draw_list, ImGuiViewportP* viewport, const ImRect& bb) diff --git a/imgui_internal.h b/imgui_internal.h index f84598ae88db..7b29f178160d 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1073,9 +1073,9 @@ struct IMGUI_API ImGuiWindow bool Appearing; // Set during the frame where the window is appearing (or re-appearing) bool Hidden; // Do not display (== (HiddenFramesForResize > 0) || bool HasCloseButton; // Set when the window has a close button (p_open != NULL) + int BeginCount; // Number of Begin() during the current frame (generally 0 or 1, 1+ if appending via multiple Begin/End pairs) int BeginOrderWithinParent; // Order within immediate parent window, if we are a child window. Otherwise 0. int BeginOrderWithinContext; // Order within entire imgui context. This is mostly used for debugging submission order related issues. - int BeginCount; // Number of Begin() during the current frame (generally 0 or 1, 1+ if appending via multiple Begin/End pairs) ImGuiID PopupId; // ID in the popup stack when this window is used as a popup/menu (because we use generic Name/ID for recycling) int AutoFitFramesX, AutoFitFramesY; bool AutoFitOnlyGrows; From a8488078478839d94778499fb3be0bd28c42003c Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 5 Sep 2018 11:51:45 +0200 Subject: [PATCH 225/828] Minor inconsequential merges from Master/Docking branches --- imgui.cpp | 8 ++++---- imgui.h | 2 +- imgui_demo.cpp | 12 ++++++------ imgui_draw.cpp | 1 + 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 7c987d52a769..c946895b0f36 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3852,8 +3852,9 @@ void ImGui::CalcListClipping(int items_count, float items_height, int* out_items } // Find window given position, search front-to-back -// FIXME: Note that we have an inconsequential lag here: OuterRectClipped is updated in Begin(), so windows moved programatically with SetWindowPos() and not SetNextWindowPos() -// will have that rectangle lagging by a frame at the time FindHoveredWindow() is called, aka before the next Begin(). Moving window isn't affected. +// FIXME: Note that we have an inconsequential lag here: OuterRectClipped is updated in Begin(), so windows moved programatically +// with SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is +// called, aka before the next Begin(). Moving window isn't affected. static void FindHoveredWindow() { ImGuiContext& g = *GImGui; @@ -8209,7 +8210,7 @@ ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInput static void NavScrollToBringItemIntoView(ImGuiWindow* window, const ImRect& item_rect) { ImRect window_rect(window->InnerMainRect.Min - ImVec2(1, 1), window->InnerMainRect.Max + ImVec2(1, 1)); - //GetOverlayDrawList(window)->AddRect(window_rect_rel.Min, window_rect_rel.Max, IM_COL32_WHITE); // [DEBUG] + //GetOverlayDrawList(window)->AddRect(window_rect.Min, window_rect.Max, IM_COL32_WHITE); // [DEBUG] if (window_rect.Contains(item_rect)) return; @@ -9700,7 +9701,6 @@ void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size) entry_handler->ReadLineFn(&g, entry_handler, entry_data, line); } } - ImGui::MemFree(buf); g.SettingsLoaded = true; } diff --git a/imgui.h b/imgui.h index 2d88ab4f4a55..9a9578e3dbac 100644 --- a/imgui.h +++ b/imgui.h @@ -115,7 +115,7 @@ typedef int ImGuiHoveredFlags; // -> enum ImGuiHoveredFlags_ // Flags: f typedef int ImGuiInputTextFlags; // -> enum ImGuiInputTextFlags_ // Flags: for InputText*() typedef int ImGuiSelectableFlags; // -> enum ImGuiSelectableFlags_ // Flags: for Selectable() typedef int ImGuiTreeNodeFlags; // -> enum ImGuiTreeNodeFlags_ // Flags: for TreeNode*(),CollapsingHeader() -typedef int ImGuiViewportFlags; // -> ImGuiViewportFlags_ // Flags: for ImGuiViewport +typedef int ImGuiViewportFlags; // -> enum ImGuiViewportFlags_ // Flags: for ImGuiViewport typedef int ImGuiWindowFlags; // -> enum ImGuiWindowFlags_ // Flags: for Begin*() typedef int (*ImGuiInputTextCallback)(ImGuiInputTextCallbackData *data); typedef void (*ImGuiSizeCallback)(ImGuiSizeCallbackData* data); diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 9049078f1ec5..1827c9b474fe 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3405,7 +3405,7 @@ static void ShowExampleAppWindowTitles(bool*) // Demonstrate using the low-level ImDrawList to draw custom shapes. static void ShowExampleAppCustomRendering(bool* p_open) { - ImGui::SetNextWindowSize(ImVec2(350,560), ImGuiCond_FirstUseEver); + ImGui::SetNextWindowSize(ImVec2(350, 560), ImGuiCond_FirstUseEver); if (!ImGui::Begin("Example: Custom rendering", p_open)) { ImGui::End(); @@ -3422,7 +3422,7 @@ static void ShowExampleAppCustomRendering(bool* p_open) ImGui::Text("Primitives"); static float sz = 36.0f; static float thickness = 4.0f; - static ImVec4 col = ImVec4(1.0f,1.0f,0.4f,1.0f); + static ImVec4 col = ImVec4(1.0f, 1.0f, 0.4f, 1.0f); ImGui::DragFloat("Size", &sz, 0.2f, 2.0f, 72.0f, "%.0f"); ImGui::DragFloat("Thickness", &thickness, 0.05f, 1.0f, 8.0f, "%.02f"); ImGui::ColorEdit3("Color", &col.x); @@ -3472,8 +3472,8 @@ static void ShowExampleAppCustomRendering(bool* p_open) ImVec2 canvas_size = ImGui::GetContentRegionAvail(); // Resize canvas to what's available if (canvas_size.x < 50.0f) canvas_size.x = 50.0f; if (canvas_size.y < 50.0f) canvas_size.y = 50.0f; - draw_list->AddRectFilledMultiColor(canvas_pos, ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + canvas_size.y), IM_COL32(50,50,50,255), IM_COL32(50,50,60,255), IM_COL32(60,60,70,255), IM_COL32(50,50,60,255)); - draw_list->AddRect(canvas_pos, ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + canvas_size.y), IM_COL32(255,255,255,255)); + draw_list->AddRectFilledMultiColor(canvas_pos, ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + canvas_size.y), IM_COL32(50, 50, 50, 255), IM_COL32(50, 50, 60, 255), IM_COL32(60, 60, 70, 255), IM_COL32(50, 50, 60, 255)); + draw_list->AddRect(canvas_pos, ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + canvas_size.y), IM_COL32(255, 255, 255, 255)); bool adding_preview = false; ImGui::InvisibleButton("canvas", canvas_size); @@ -3499,9 +3499,9 @@ static void ShowExampleAppCustomRendering(bool* p_open) points.pop_back(); } } - draw_list->PushClipRect(canvas_pos, ImVec2(canvas_pos.x+canvas_size.x, canvas_pos.y+canvas_size.y), true); // clip lines within the canvas (if we resize it, etc.) + draw_list->PushClipRect(canvas_pos, ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + canvas_size.y), true); // clip lines within the canvas (if we resize it, etc.) for (int i = 0; i < points.Size - 1; i += 2) - draw_list->AddLine(ImVec2(canvas_pos.x + points[i].x, canvas_pos.y + points[i].y), ImVec2(canvas_pos.x + points[i+1].x, canvas_pos.y + points[i+1].y), IM_COL32(255,255,0,255), 2.0f); + draw_list->AddLine(ImVec2(canvas_pos.x + points[i].x, canvas_pos.y + points[i].y), ImVec2(canvas_pos.x + points[i + 1].x, canvas_pos.y + points[i + 1].y), IM_COL32(255, 255, 0, 255), 2.0f); draw_list->PopClipRect(); if (adding_preview) points.pop_back(); diff --git a/imgui_draw.cpp b/imgui_draw.cpp index afae4fe61ac6..3203cb0372d7 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -2,6 +2,7 @@ // (drawing and font code) /* + Index of this file: - Cruft for stb_truetype/stb_rectpack implementation - Style functions (default style) From 8601c39571e4fccc66205e6d9e14f97ea4f93397 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 5 Sep 2018 17:50:55 +0200 Subject: [PATCH 226/828] Viewport: Making the code a little more sturdy (flag changes) + added descriptions. (#1542) --- imgui.cpp | 37 +++++++++++++++++++------------------ imgui.h | 2 +- imgui_demo.cpp | 3 +++ imgui_internal.h | 1 + 4 files changed, 24 insertions(+), 19 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 5e96c68d9348..59d5eb03a9bc 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7328,11 +7328,12 @@ static void ImGui::UpdateViewports() for (int window_n = 0; window_n < g.Windows.Size; window_n++) if (g.Windows[window_n]->Viewport == viewport) g.Windows[window_n]->Viewport = NULL; + if (viewport == g.MouseLastHoveredViewport) + g.MouseLastHoveredViewport = NULL; g.Viewports.erase(g.Viewports.Data + n); // Destroy - if (viewport == g.MouseLastHoveredViewport) g.MouseLastHoveredViewport = NULL; - IM_ASSERT(viewport->RendererUserData == NULL && viewport->PlatformUserData == NULL && viewport->PlatformHandle == NULL); + DestroyPlatformWindow(viewport); // In most circumstances the platform window will already be destroyed here. IM_ASSERT(g.PlatformIO.Viewports.contains(viewport) == false); IM_DELETE(viewport); n--; @@ -7616,13 +7617,7 @@ void ImGui::UpdatePlatformWindows() destroy_platform_window |= (viewport->Window && !IsWindowActiveAndVisible(viewport->Window)); if (destroy_platform_window) { - if (viewport->CreatedPlatformWindow && g.PlatformIO.Renderer_DestroyWindow) - g.PlatformIO.Renderer_DestroyWindow(viewport); - if (viewport->CreatedPlatformWindow && g.PlatformIO.Platform_DestroyWindow) - g.PlatformIO.Platform_DestroyWindow(viewport); - viewport->CreatedPlatformWindow = false; - IM_ASSERT(viewport->RendererUserData == NULL); - IM_ASSERT(viewport->PlatformUserData == NULL && viewport->PlatformHandle == NULL); + DestroyPlatformWindow(viewport); continue; } if (viewport->LastFrameActive < g.FrameCount) @@ -7782,22 +7777,28 @@ void ImGui::RenderPlatformWindowsDefault(void* platform_render_arg, void* render } } +void ImGui::DestroyPlatformWindow(ImGuiViewportP* viewport) +{ + ImGuiContext& g = *GImGui; + if (viewport->CreatedPlatformWindow && g.PlatformIO.Renderer_DestroyWindow) + g.PlatformIO.Renderer_DestroyWindow(viewport); + if (viewport->CreatedPlatformWindow && g.PlatformIO.Platform_DestroyWindow) + g.PlatformIO.Platform_DestroyWindow(viewport); + viewport->CreatedPlatformWindow = false; + IM_ASSERT(viewport->RendererUserData == NULL); + IM_ASSERT(viewport->PlatformUserData == NULL && viewport->PlatformHandle == NULL); +} + void ImGui::DestroyPlatformWindows() { // We call the destroy window on the main viewport (index 0) to give a chance to the back-end to clear any data // have stored in e.g. PlatformHandle. - // It is expected that the back-end stored a flag to remember that it doesn't own the window created for the main viewport, - // and won't destroy the underlying platform/renderer data (e.g. + // It is expected that the back-end stored a flag to remember that it doesn't own the window created for the + // main viewport, and won't destroy the underlying platform/renderer data. ImGuiContext& g = *GImGui; for (int i = 0; i < g.Viewports.Size; i++) if (g.Viewports[i]->CreatedPlatformWindow) - { - if (g.PlatformIO.Renderer_DestroyWindow) - g.PlatformIO.Renderer_DestroyWindow(g.Viewports[i]); - if (g.PlatformIO.Platform_DestroyWindow) - g.PlatformIO.Platform_DestroyWindow(g.Viewports[i]); - g.Viewports[i]->CreatedPlatformWindow = false; - } + DestroyPlatformWindow(g.Viewports[i]); } //----------------------------------------------------------------------------- diff --git a/imgui.h b/imgui.h index eabf00d09199..4fc2e05a075a 100644 --- a/imgui.h +++ b/imgui.h @@ -880,7 +880,7 @@ enum ImGuiConfigFlags_ // [BETA] Viewports ImGuiConfigFlags_ViewportsEnable = 1 << 10, // Viewport enable flags (require both ImGuiConfigFlags_PlatformHasViewports + ImGuiConfigFlags_RendererHasViewports set by the respective back-ends) ImGuiConfigFlags_ViewportsNoTaskBarIcons= 1 << 11, // Disable task bars icons for all secondary viewports (will set ImGuiViewportFlags_NoTaskBarIcon on them) - ImGuiConfigFlags_ViewportsNoMerge = 1 << 12, // All floating windows _always_ have create their own viewport and platform window. + ImGuiConfigFlags_ViewportsNoMerge = 1 << 12, // All floating windows will always create their own viewport and platform window. ImGuiConfigFlags_DpiEnableScaleViewports= 1 << 13, // FIXME-DPI: Reposition and resize imgui windows when the DpiScale of a viewport changed (mostly useful for the main viewport hosting other window). Note that resizing the main window itself is up to your application. ImGuiConfigFlags_DpiEnableScaleFonts = 1 << 14, // FIXME-DPI: Request bitmap-scaled fonts to match DpiScale. This is a very low-quality workaround. The correct way to handle DPI is _currently_ to replace the atlas and/or fonts in the Platform_OnChangedViewport callback, but this is all early work in progress. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 29eef928c679..28ae9561e822 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -284,8 +284,11 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::CheckboxFlags("io.ConfigFlags: NoMouseCursorChange", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_NoMouseCursorChange); ImGui::SameLine(); ShowHelpMarker("Instruct back-end to not alter mouse cursor shape and visibility."); ImGui::CheckboxFlags("io.ConfigFlags: ViewportsEnable", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_ViewportsEnable); + ImGui::SameLine(); ShowHelpMarker("Toggling this at runtime is normally unsupported (it will offset your windows)."); ImGui::CheckboxFlags("io.ConfigFlags: ViewportsNoTaskBarIcons", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_ViewportsNoTaskBarIcons); + ImGui::SameLine(); ShowHelpMarker("Toggling this at runtime is normally unsupported (most platform back-ends won't refresh the task bar icon state right away)."); ImGui::CheckboxFlags("io.ConfigFlags: ViewportsNoMerge", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_ViewportsNoMerge); + ImGui::SameLine(); ShowHelpMarker("All floating windows will always create their own viewport and platform window."); ImGui::Checkbox("io.ConfigInputTextCursorBlink", &io.ConfigInputTextCursorBlink); ImGui::SameLine(); ShowHelpMarker("Set to false to disable blinking cursor, for users who consider it distracting"); ImGui::Checkbox("io.ConfigResizeWindowsFromEdges [beta]", &io.ConfigResizeWindowsFromEdges); diff --git a/imgui_internal.h b/imgui_internal.h index f742896fed60..a524bca06221 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1199,6 +1199,7 @@ namespace ImGui // Viewports IMGUI_API ImGuiViewportP* FindViewportByID(ImGuiID id); IMGUI_API void ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale); + IMGUI_API void DestroyPlatformWindow(ImGuiViewportP* viewport); IMGUI_API void ShowViewportThumbnails(); // Settings From 52e0de84a90066e71f2ec8b644586f65535091ac Mon Sep 17 00:00:00 2001 From: omar Date: Sun, 17 Jun 2018 20:34:07 +0200 Subject: [PATCH 227/828] Internals: Split RenderTextClipped into two functions. --- imgui.cpp | 30 +++++++++++++++++------------- imgui_internal.h | 1 + 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index f14dfc502852..a46c646d9adb 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2133,17 +2133,8 @@ void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end // Default clip_rect uses (pos_min,pos_max) // Handle clipping on CPU immediately (vs typically let the GPU clip the triangles that are overlapping the clipping rectangle edges) -void ImGui::RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align, const ImRect* clip_rect) +void ImGui::RenderTextClippedEx(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_display_end, const ImVec2* text_size_if_known, const ImVec2& align, const ImRect* clip_rect) { - // Hide anything after a '##' string - const char* text_display_end = FindRenderedTextEnd(text, text_end); - const int text_len = (int)(text_display_end - text); - if (text_len == 0) - return; - - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - // Perform CPU side clipping for single clipped element to avoid using scissor state ImVec2 pos = pos_min; const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f); @@ -2162,14 +2153,27 @@ void ImGui::RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, cons if (need_clipping) { ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y); - window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect); + draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect); } else { - window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL); + draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL); } +} + +void ImGui::RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align, const ImRect* clip_rect) +{ + // Hide anything after a '##' string + const char* text_display_end = FindRenderedTextEnd(text, text_end); + const int text_len = (int)(text_display_end - text); + if (text_len == 0) + return; + + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + RenderTextClippedEx(window->DrawList, pos_min, pos_max, text, text_display_end, text_size_if_known, align, clip_rect); if (g.LogEnabled) - LogRenderedText(&pos, text, text_display_end); + LogRenderedText(&pos_min, text, text_display_end); } // Render a rectangle shaped with optional rounding and borders diff --git a/imgui_internal.h b/imgui_internal.h index cedf1c32aa02..9384b1a4a5aa 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1215,6 +1215,7 @@ namespace ImGui IMGUI_API void RenderText(ImVec2 pos, const char* text, const char* text_end = NULL, bool hide_text_after_hash = true); IMGUI_API void RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width); IMGUI_API void RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0,0), const ImRect* clip_rect = NULL); + IMGUI_API void RenderTextClippedEx(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0, 0), const ImRect* clip_rect = NULL); IMGUI_API void RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border = true, float rounding = 0.0f); IMGUI_API void RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding = 0.0f); IMGUI_API void RenderColorRectWithAlphaCheckerboard(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, float grid_step, ImVec2 grid_off, float rounding = 0.0f, int rounding_corners_flags = ~0); From 5070c769b8af673cbb5bd695aebda8562b89f11f Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 2 Jul 2018 22:53:59 +0200 Subject: [PATCH 228/828] Internals: Windows hidden with HiddenFramesRegular (but NOT HiddenFramesForResize) preserve their SizeContents, so restoring a auto-resize window after it's been hidden by tabs won't reset its size for a frame. Arguable. Let's see how it goes. (Followup to b48e295bddbf965d7382ec5578ed05d2fe601114) --- imgui.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/imgui.cpp b/imgui.cpp index a46c646d9adb..ec8c0727dc79 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4242,6 +4242,9 @@ static ImVec2 CalcSizeAfterConstraint(ImGuiWindow* window, ImVec2 new_size) static ImVec2 CalcSizeContents(ImGuiWindow* window) { + if (window->Hidden && window->HiddenFramesForResize == 0 && window->HiddenFramesRegular > 0) + return window->SizeContents; + ImVec2 sz; sz.x = (float)(int)((window->SizeContentsExplicit.x != 0.0f) ? window->SizeContentsExplicit.x : (window->DC.CursorMaxPos.x - window->Pos.x + window->Scroll.x)); sz.y = (float)(int)((window->SizeContentsExplicit.y != 0.0f) ? window->SizeContentsExplicit.y : (window->DC.CursorMaxPos.y - window->Pos.y + window->Scroll.y)); From 741bdf151a39bfd0938b2d310572917d1f8fb3fe Mon Sep 17 00:00:00 2001 From: omar Date: Sun, 17 Jun 2018 21:10:10 +0200 Subject: [PATCH 229/828] Added ImGuiWindowFlags_UnsavedDocument flags. --- imgui.cpp | 13 +++++++++++-- imgui.h | 1 + 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index ec8c0727dc79..9172a1f78de8 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5004,8 +5004,11 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DC.NavLayerCurrentMask >>= 1; window->DC.ItemFlags = item_flags_backup; - // Title text (FIXME: refactor text alignment facilities along with RenderText helpers, this is too much code for what it does.) - ImVec2 text_size = CalcTextSize(name, NULL, true); + // Title bar text (with: horizontal alignment, avoiding collapse/close button, optional "unsaved document" marker) + // FIXME: Refactor text alignment facilities along with RenderText helpers, this is too much code.. + const char* UNSAVED_DOCUMENT_MARKER = "*"; + float marker_size_x = (flags & ImGuiWindowFlags_UnsavedDocument) ? CalcTextSize(UNSAVED_DOCUMENT_MARKER, NULL, false).x : 0.0f; + ImVec2 text_size = CalcTextSize(name, NULL, true) + ImVec2(marker_size_x, 0.0f); ImRect text_r = title_bar_rect; float pad_left = (flags & ImGuiWindowFlags_NoCollapse) ? style.FramePadding.x : (style.FramePadding.x + g.FontSize + style.ItemInnerSpacing.x); float pad_right = (p_open == NULL) ? style.FramePadding.x : (style.FramePadding.x + g.FontSize + style.ItemInnerSpacing.x); @@ -5016,6 +5019,12 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) ImRect clip_rect = text_r; clip_rect.Max.x = window->Pos.x + window->Size.x - (p_open ? title_bar_rect.GetHeight() - 3 : style.FramePadding.x); // Match the size of CloseButton() RenderTextClipped(text_r.Min, text_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_rect); + if (flags & ImGuiWindowFlags_UnsavedDocument) + { + ImVec2 marker_pos = ImVec2(ImMax(text_r.Min.x, text_r.Min.x + (text_r.GetWidth() - text_size.x) * style.WindowTitleAlign.x) + text_size.x, text_r.Min.y) + ImVec2(2 - marker_size_x, 0.0f); + ImVec2 off = ImVec2(0.0f, (float)(int)(-g.FontSize * 0.25f)); + RenderTextClipped(marker_pos + off, text_r.Max + off, UNSAVED_DOCUMENT_MARKER, NULL, NULL, ImVec2(0, style.WindowTitleAlign.y), &clip_rect); + } } // Save clipped aabb so we can access it in constant-time in FindHoveredWindow() diff --git a/imgui.h b/imgui.h index 991c20fc5bc4..a843f1a2f342 100644 --- a/imgui.h +++ b/imgui.h @@ -623,6 +623,7 @@ enum ImGuiWindowFlags_ ImGuiWindowFlags_NoNavInputs = 1 << 18, // No gamepad/keyboard navigation within the window ImGuiWindowFlags_NoNavFocus = 1 << 19, // No focusing toward this window with gamepad/keyboard navigation (e.g. skipped by CTRL+TAB) ImGuiWindowFlags_NoNav = ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus, + ImGuiWindowFlags_UnsavedDocument = 1 << 20, // Append '*' to title without affecting the ID, as a convenience to avoid using the ### operator. When used in a tab/docking context, tab is selected on closure and closure is deferred by one frame to allow code to cancel the closure (with a confirmation popup, etc.) without flicker. // [Internal] ImGuiWindowFlags_NavFlattened = 1 << 23, // [BETA] Allow gamepad/keyboard navigation to cross over parent border to this child (only use on child that have no scrolling!) From c039a77d218d1653c5c0ffd9e8107b2a138d105f Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 6 Sep 2018 15:34:03 +0200 Subject: [PATCH 230/828] Internals: Added ImPool helper structure. Moved ImVec1f. --- imgui_internal.h | 41 +++++++++++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/imgui_internal.h b/imgui_internal.h index 9384b1a4a5aa..530653d0f7f1 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -211,18 +211,32 @@ static inline ImVec2 ImRotate(const ImVec2& v, float cos_a, float sin_a) static inline float ImLinearSweep(float current, float target, float speed) { if (current < target) return ImMin(current + speed, target); if (current > target) return ImMax(current - speed, target); return current; } static inline ImVec2 ImMul(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x * rhs.x, lhs.y * rhs.y); } +// Helper: ImPool<>. Basic keyed storage for contiguous instances, slow/amortized insertion, O(1) indexable, O(Log N) queries by ID over a dense/hot buffer +// Creation/Erasure invalidate all pointers, but indexes are valid as long as the object lifetime. +template +struct IMGUI_API ImPool +{ + ImVector Data; // Contiguous data + ImGuiStorage Map; // ID->Index + int FreeIdx; // Next free idx to use + + ImPool() { FreeIdx = 0; } + ~ImPool() { Clear(); } + T* GetByKey(ImGuiID key) { int idx = Map.GetInt(key, -1); return (idx != -1) ? &Data[idx] : NULL; } + T* GetByIndex(int n) { return &Data[n]; } + int GetIndex(const T* p) const { IM_ASSERT(p >= Data.Data && p < Data.Data + Data.Size); return (int)(p - Data.Data); } + T* GetOrAddByKey(ImGuiID key) { int* p_idx = Map.GetIntRef(key, -1); if (*p_idx != -1) return &Data[*p_idx]; *p_idx = FreeIdx; return Add(); } + void Clear() { for (int n = 0; n < Map.Data.Size; n++) { int idx = Map.Data[n].val_i; if (idx != -1) Data[idx].~T(); } Map.Clear(); Data.clear(); FreeIdx = 0; } + T* Add() { int idx = FreeIdx; if (idx == Data.Size) { Data.resize(Data.Size + 1); FreeIdx++; } else { FreeIdx = *(int*)&Data[idx]; } IM_PLACEMENT_NEW(&Data[idx]) T(); return &Data[idx]; } + void Remove(ImGuiID key, const T* p) { Remove(key, GetIndex(p)); } + void Remove(ImGuiID key, int idx) { Data[idx].~T(); *(int*)&Data[idx] = FreeIdx; FreeIdx = idx; Map.SetInt(key, -1); } +}; +typedef int ImPoolIdx; + //----------------------------------------------------------------------------- // Types //----------------------------------------------------------------------------- -// 1D vector (this odd construct is used to facilitate the transition between 1D and 2D and maintenance of some patches) -struct ImVec1 -{ - float x; - ImVec1() { x = 0.0f; } - ImVec1(float _x) { x = _x; } -}; - enum ImGuiButtonFlags_ { ImGuiButtonFlags_None = 0, @@ -366,6 +380,14 @@ enum ImGuiPopupPositionPolicy ImGuiPopupPositionPolicy_ComboBox }; +// 1D vector (this odd construct is used to facilitate the transition between 1D and 2D and maintenance of some patches) +struct ImVec1 +{ + float x; + ImVec1() { x = 0.0f; } + ImVec1(float _x) { x = _x; } +}; + // 2D axis aligned bounding-box // NB: we can't rely on ImVec2 math operators being available here struct IMGUI_API ImRect @@ -632,7 +654,10 @@ struct ImGuiNextWindowData } }; +//----------------------------------------------------------------------------- // Main imgui context +//----------------------------------------------------------------------------- + struct ImGuiContext { bool Initialized; From 58d46e1fe653b7583b9959e505a21dac6944c7fd Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 6 Sep 2018 15:35:03 +0200 Subject: [PATCH 231/828] Tabs: Added BeginTabBar(), EndTabBar(), BeginTabItem(), EndTabItem() + demo. (#261, #351) --- docs/CHANGELOG.txt | 11 + imgui.cpp | 42 +++ imgui.h | 44 ++- imgui_demo.cpp | 348 ++++++++++++++++++ imgui_draw.cpp | 24 ++ imgui_internal.h | 92 +++++ imgui_widgets.cpp | 879 ++++++++++++++++++++++++++++++++++++++++++++- 7 files changed, 1437 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 91c5b5de7a26..dddf478955fd 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -29,6 +29,17 @@ HOW TO UPDATE? - Please report any issue! +----------------------------------------------------------------------- + DOCKING BRANCH (In Progress) +----------------------------------------------------------------------- + +- Added BeginTabBar(), EndTabBar(), BeginTabItem(), EndTabItem(), SetTabItemClosed() API. (#261, #351) +- Added ImGuiWindowFlags_UnsavedDocument window flag to append '*' to title without altering the ID, + as a convenience to avoid using the ### operator. +- Demo: Added Layout->Tabs demo code. (#261, #351) +- Demo: Added "Documents" example app showcasing possible use for tabs. (#261, #351) + + ----------------------------------------------------------------------- VERSION 1.66 (In Progress) ----------------------------------------------------------------------- diff --git a/imgui.cpp b/imgui.cpp index 9172a1f78de8..4721b232d329 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -990,6 +990,7 @@ ImGuiStyle::ImGuiStyle() FramePadding = ImVec2(4,3); // Padding within a framed rectangle (used by most widgets) FrameRounding = 0.0f; // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets). FrameBorderSize = 0.0f; // Thickness of border around frames. Generally set to 0.0f or 1.0f. Other values not well tested. + TabBorderSize = 0.0f; // Thickness of border around tabs. ItemSpacing = ImVec2(8,4); // Horizontal and vertical spacing between widgets/lines ItemInnerSpacing = ImVec2(4,4); // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label) TouchExtraPadding = ImVec2(0,0); // Expand reactive bounding box for touch-based system where touch position is not accurate enough. Unfortunately we don't sort widgets so priority on overlap will always be given to the first widget. So don't grow this too much! @@ -5493,6 +5494,11 @@ const char* ImGui::GetStyleColorName(ImGuiCol idx) case ImGuiCol_ResizeGrip: return "ResizeGrip"; case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered"; case ImGuiCol_ResizeGripActive: return "ResizeGripActive"; + case ImGuiCol_Tab: return "Tab"; + case ImGuiCol_TabHovered: return "TabHovered"; + case ImGuiCol_TabActive: return "TabActive"; + case ImGuiCol_TabUnfocused: return "TabUnfocused"; + case ImGuiCol_TabUnfocusedActive: return "TabUnfocusedActive"; case ImGuiCol_PlotLines: return "PlotLines"; case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered"; case ImGuiCol_PlotHistogram: return "PlotHistogram"; @@ -8797,6 +8803,36 @@ void ImGui::ShowMetricsWindow(bool* p_open) struct Funcs { + static void NodeTabBar(ImGuiTabBar* tab_bar) + { + // Previous window list + char buf[256]; + char* p = buf; + const char* buf_end = buf + IM_ARRAYSIZE(buf); + p += ImFormatString(p, buf_end - p, "TabBar (%d tabs)%s", + tab_bar->Tabs.Size, (tab_bar->PrevFrameVisible < ImGui::GetFrameCount() - 2) ? " *Inactive*" : ""); + if (tab_bar->Flags & ImGuiTabBarFlags_DockNode) + { + p += ImFormatString(p, buf_end - p, " { "); + for (int tab_n = 0; tab_n < ImMin(tab_bar->Tabs.Size, 3); tab_n++) + p += ImFormatString(p, buf_end - p, "%s'%s'", tab_n > 0 ? ", " : "", tab_bar->Tabs[tab_n].Window->Name); + p += ImFormatString(p, buf_end - p, (tab_bar->Tabs.Size > 3) ? " ... }" : " } "); + } + if (ImGui::TreeNode(tab_bar, "%s", buf)) + { + for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++) + { + const ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; + ImGui::PushID(tab); + if (ImGui::SmallButton("<")) { TabBarQueueChangeTabOrder(tab_bar, tab, -1); } ImGui::SameLine(0, 2); + if (ImGui::SmallButton(">")) { TabBarQueueChangeTabOrder(tab_bar, tab, +1); } ImGui::SameLine(); + ImGui::Text("%02d%c Tab 0x%08X '%s'", tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID, tab->Window ? tab->Window->Name : "N/A"); + ImGui::PopID(); + } + ImGui::TreePop(); + } + } + static void NodeDrawList(ImGuiWindow* window, ImDrawList* draw_list, const char* label) { bool node_open = ImGui::TreeNode(draw_list, "%s: '%s' %d vtx, %d indices, %d cmds", label, draw_list->_OwnerName ? draw_list->_OwnerName : "", draw_list->VtxBuffer.Size, draw_list->IdxBuffer.Size, draw_list->CmdBuffer.Size); @@ -8936,6 +8972,12 @@ void ImGui::ShowMetricsWindow(bool* p_open) } ImGui::TreePop(); } + if (ImGui::TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.Data.Size)) + { + for (int n = 0; n < g.TabBars.Data.Size; n++) + Funcs::NodeTabBar(g.TabBars.GetByIndex(n)); + ImGui::TreePop(); + } if (ImGui::TreeNode("Internal state")) { const char* input_source_names[] = { "None", "Mouse", "Nav", "NavKeyboard", "NavGamepad" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT); diff --git a/imgui.h b/imgui.h index a843f1a2f342..62941edf12d6 100644 --- a/imgui.h +++ b/imgui.h @@ -26,6 +26,7 @@ #define IMGUI_VERSION "1.66 WIP" #define IMGUI_VERSION_NUM 16600 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert)) +#define IMGUI_HAS_TABS 1 // Docking WIP branch // Define attributes of all API symbols declarations (e.g. for DLL under Windows) // IMGUI_API is used for core imgui functions, IMGUI_IMPL_API is used for the default bindings files (imgui_impl_xxx.h) @@ -110,6 +111,8 @@ typedef int ImGuiFocusedFlags; // -> enum ImGuiFocusedFlags_ // Flags: f typedef int ImGuiHoveredFlags; // -> enum ImGuiHoveredFlags_ // Flags: for IsItemHovered(), IsWindowHovered() etc. typedef int ImGuiInputTextFlags; // -> enum ImGuiInputTextFlags_ // Flags: for InputText*() typedef int ImGuiSelectableFlags; // -> enum ImGuiSelectableFlags_ // Flags: for Selectable() +typedef int ImGuiTabBarFlags; // -> enum ImGuiTabBarFlags_ // Flags: for BeginTabBar() +typedef int ImGuiTabItemFlags; // -> enum ImGuiTabItemFlags_ // Flags: for BeginTabItem() typedef int ImGuiTreeNodeFlags; // -> enum ImGuiTreeNodeFlags_ // Flags: for TreeNode*(),CollapsingHeader() typedef int ImGuiWindowFlags; // -> enum ImGuiWindowFlags_ // Flags: for Begin*() typedef int (*ImGuiInputTextCallback)(ImGuiInputTextCallbackData *data); @@ -493,6 +496,14 @@ namespace ImGui IMGUI_API void SetColumnOffset(int column_index, float offset_x); // set position of column line (in pixels, from the left side of the contents region). pass -1 to use current column IMGUI_API int GetColumnsCount(); + // Tabs + // [BETA API] API may evolve. + IMGUI_API bool BeginTabBar(const char* str_id, ImGuiTabBarFlags flags = 0); // create and append into a TabBar + IMGUI_API void EndTabBar(); + IMGUI_API bool BeginTabItem(const char* label, bool* p_open = NULL, ImGuiTabItemFlags = 0);// create a Tab. Returns true if the Tab is selected. + IMGUI_API void EndTabItem(); // only call EndTabItem() if BeginTabItem() returns true! + IMGUI_API void SetTabItemClosed(const char* tab_or_docked_window_label); // notify TabBar or Docking system of a closed tab/window ahead (useful to reduce visual flicker on reorderable tab bars). For tab-bar: call after BeginTabBar() and before Tab submissions. Otherwise call with a window name. + // Logging/Capture: all text output from interface is captured to tty/file/clipboard. By default, tree nodes are automatically opened during logging. IMGUI_API void LogToTTY(int max_depth = -1); // start logging to tty IMGUI_API void LogToFile(int max_depth = -1, const char* filename = NULL); // start logging to file @@ -623,7 +634,7 @@ enum ImGuiWindowFlags_ ImGuiWindowFlags_NoNavInputs = 1 << 18, // No gamepad/keyboard navigation within the window ImGuiWindowFlags_NoNavFocus = 1 << 19, // No focusing toward this window with gamepad/keyboard navigation (e.g. skipped by CTRL+TAB) ImGuiWindowFlags_NoNav = ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus, - ImGuiWindowFlags_UnsavedDocument = 1 << 20, // Append '*' to title without affecting the ID, as a convenience to avoid using the ### operator. When used in a tab/docking context, tab is selected on closure and closure is deferred by one frame to allow code to cancel the closure (with a confirmation popup, etc.) without flicker. + ImGuiWindowFlags_UnsavedDocument = 1 << 20, // Append '*' to title without altering the ID, as a convenience to avoid using the ### operator. When used in a tab/docking context, tab is selected on closure and closure is deferred by one frame to allow code to cancel the closure (with a confirmation popup, etc.) without flicker. // [Internal] ImGuiWindowFlags_NavFlattened = 1 << 23, // [BETA] Allow gamepad/keyboard navigation to cross over parent border to this child (only use on child that have no scrolling!) @@ -715,6 +726,31 @@ enum ImGuiComboFlags_ ImGuiComboFlags_HeightMask_ = ImGuiComboFlags_HeightSmall | ImGuiComboFlags_HeightRegular | ImGuiComboFlags_HeightLarge | ImGuiComboFlags_HeightLargest }; +// Flags for ImGui::BeginTabBar() +enum ImGuiTabBarFlags_ +{ + ImGuiTabBarFlags_None = 0, + ImGuiTabBarFlags_Reorderable = 1 << 0, // Allow manually dragging tabs to re-order them + New tabs are appended at the end of list + ImGuiTabBarFlags_AutoSelectNewTabs = 1 << 1, // Automatically select new tabs when they appear + ImGuiTabBarFlags_NoCloseWithMiddleMouseButton = 1 << 2, // Disable behavior of closing tabs (that are submitted with p_open != NULL) with middle mouse button. You can still repro this behavior on user's side with if (IsItemHovered() && IsMouseClicked(2)) *p_open = false. + ImGuiTabBarFlags_NoTabListPopupButton = 1 << 3, + ImGuiTabBarFlags_NoTabListScrollingButtons = 1 << 4, + ImGuiTabBarFlags_FittingPolicyResizeDown = 1 << 5, // Resize tabs when they don't fit + ImGuiTabBarFlags_FittingPolicyScroll = 1 << 6, // Add scroll buttons when tabs don't fit + ImGuiTabBarFlags_FittingPolicyMask_ = ImGuiTabBarFlags_FittingPolicyResizeDown | ImGuiTabBarFlags_FittingPolicyScroll, + ImGuiTabBarFlags_FittingPolicyDefault_ = ImGuiTabBarFlags_FittingPolicyResizeDown +}; + +// Flags for ImGui::BeginTabItem() +enum ImGuiTabItemFlags_ +{ + ImGuiTabItemFlags_None = 0, + ImGuiTabItemFlags_UnsavedDocument = 1 << 0, // Append '*' to title without affecting the ID, as a convenience to avoid using the ### operator. Also: tab is selected on closure and closure is deferred by one frame to allow code to undo it without flicker. + ImGuiTabItemFlags_SetSelected = 1 << 1, // Trigger flag to programatically make the tab selected when calling BeginTabItem() + ImGuiTabItemFlags_NoCloseWithMiddleMouseButton = 1 << 2, // Disable behavior of closing tabs (that are submitted with p_open != NULL) with middle mouse button. You can still repro this behavior on user's side with if (IsItemHovered() && IsMouseClicked(2)) *p_open = false. + ImGuiTabItemFlags_NoPushId = 1 << 3 // Don't call PushID(tab->ID)/PopID() on BeginTabItem()/EndTabItem() +}; + // Flags for ImGui::IsWindowFocused() enum ImGuiFocusedFlags_ { @@ -909,6 +945,11 @@ enum ImGuiCol_ ImGuiCol_ResizeGrip, ImGuiCol_ResizeGripHovered, ImGuiCol_ResizeGripActive, + ImGuiCol_Tab, + ImGuiCol_TabHovered, + ImGuiCol_TabActive, + ImGuiCol_TabUnfocused, + ImGuiCol_TabUnfocusedActive, ImGuiCol_PlotLines, ImGuiCol_PlotLinesHovered, ImGuiCol_PlotHistogram, @@ -1052,6 +1093,7 @@ struct ImGuiStyle ImVec2 FramePadding; // Padding within a framed rectangle (used by most widgets). float FrameRounding; // Radius of frame corners rounding. Set to 0.0f to have rectangular frame (used by most widgets). float FrameBorderSize; // Thickness of border around frames. Generally set to 0.0f or 1.0f. (Other values are not well tested and more CPU/GPU costly). + float TabBorderSize; // Thickness of border around tabs. ImVec2 ItemSpacing; // Horizontal and vertical spacing between widgets/lines. ImVec2 ItemInnerSpacing; // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label). ImVec2 TouchExtraPadding; // Expand reactive bounding box for touch-based system where touch position is not accurate enough. Unfortunately we don't sort widgets so priority on overlap will always be given to the first widget. So don't grow this too much! diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 2cce0dd9e34e..b809b8e45034 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -36,6 +36,7 @@ Index of this file: // [SECTION] Example App: Simple Overlay / ShowExampleAppSimpleOverlay() // [SECTION] Example App: Manipulating Window Titles / ShowExampleAppWindowTitles() // [SECTION] Example App: Custom Rendering using ImDrawList API / ShowExampleAppCustomRendering() +// [SECTION] Example App: Documents Handling / ShowExampleAppDocuments() */ @@ -98,6 +99,7 @@ Index of this file: #if !defined(IMGUI_DISABLE_DEMO_WINDOWS) // Forward Declarations +static void ShowExampleAppDocuments(bool* p_open); static void ShowExampleAppMainMenuBar(); static void ShowExampleAppConsole(bool* p_open); static void ShowExampleAppLog(bool* p_open); @@ -157,6 +159,7 @@ void ImGui::ShowUserGuide() void ImGui::ShowDemoWindow(bool* p_open) { // Examples Apps (accessible from the "Examples" menu) + static bool show_app_documents = false; static bool show_app_main_menu_bar = false; static bool show_app_console = false; static bool show_app_log = false; @@ -169,6 +172,7 @@ void ImGui::ShowDemoWindow(bool* p_open) static bool show_app_window_titles = false; static bool show_app_custom_rendering = false; + if (show_app_documents) ShowExampleAppDocuments(&show_app_documents); if (show_app_main_menu_bar) ShowExampleAppMainMenuBar(); if (show_app_console) ShowExampleAppConsole(&show_app_console); if (show_app_log) ShowExampleAppLog(&show_app_log); @@ -256,6 +260,7 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::MenuItem("Simple overlay", NULL, &show_app_simple_overlay); ImGui::MenuItem("Manipulating window titles", NULL, &show_app_window_titles); ImGui::MenuItem("Custom rendering", NULL, &show_app_custom_rendering); + ImGui::MenuItem("Documents", NULL, &show_app_documents); ImGui::EndMenu(); } if (ImGui::BeginMenu("Help")) @@ -1663,6 +1668,72 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::TreePop(); } + if (ImGui::TreeNode("Tabs")) + { + if (ImGui::TreeNode("Basic")) + { + ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_None; + ImGui::BeginTabBar("MyTabBar", tab_bar_flags); + if (ImGui::BeginTabItem("Avocado")) + { + ImGui::Text("This is the Avocado tab!\nblah blah blah blah blah"); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("Broccoli")) + { + ImGui::Text("This is the Broccoli tab!\nblah blah blah blah blah"); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("Cucumber")) + { + ImGui::Text("This is the Cucumber tab!\nblah blah blah blah blah"); + ImGui::EndTabItem(); + } + ImGui::EndTabBar(); + ImGui::Separator(); + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Advanced & Close Button")) + { + // Expose a couple of the available flags. In most cases you may just call BeginTabBar() with no flags (0). + static ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_Reorderable; + ImGui::CheckboxFlags("ImGuiTabBarFlags_Reorderable", (unsigned int*)&tab_bar_flags, ImGuiTabBarFlags_Reorderable); + ImGui::CheckboxFlags("ImGuiTabBarFlags_AutoSelectNewTabs", (unsigned int*)&tab_bar_flags, ImGuiTabBarFlags_AutoSelectNewTabs); + ImGui::CheckboxFlags("ImGuiTabBarFlags_NoCloseWithMiddleMouseButton", (unsigned int*)&tab_bar_flags, ImGuiTabBarFlags_NoCloseWithMiddleMouseButton); + if ((tab_bar_flags & ImGuiTabBarFlags_FittingPolicyMask_) == 0) + tab_bar_flags |= ImGuiTabBarFlags_FittingPolicyDefault_; + if (ImGui::CheckboxFlags("ImGuiTabBarFlags_FittingPolicyResizeDown", (unsigned int*)&tab_bar_flags, ImGuiTabBarFlags_FittingPolicyResizeDown)) + tab_bar_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyResizeDown); + if (ImGui::CheckboxFlags("ImGuiTabBarFlags_FittingPolicyScroll", (unsigned int*)&tab_bar_flags, ImGuiTabBarFlags_FittingPolicyScroll)) + tab_bar_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyScroll); + + // Tab Bar + const char* names[4] = { "Artichoke", "Beetroot", "Celery", "Daikon" }; + static bool opened[4] = { true, true, true, true }; // Persistent user state + for (int n = 0; n < IM_ARRAYSIZE(opened); n++) + { + if (n > 0) { ImGui::SameLine(); } + ImGui::Checkbox(names[n], &opened[n]); + } + + // Passing a bool* to BeginTabItem() is similar to passing one to Begin(): the underlying bool will be set to false when the tab is closed. + ImGui::BeginTabBar("MyTabBar", tab_bar_flags); + for (int n = 0; n < IM_ARRAYSIZE(opened); n++) + if (opened[n] && ImGui::BeginTabItem(names[n], &opened[n])) + { + ImGui::Text("This is the %s tab!", names[n]); + if (n & 1) + ImGui::Text("I am an odd tab."); + ImGui::EndTabItem(); + } + ImGui::EndTabBar(); + ImGui::Separator(); + ImGui::TreePop(); + } + ImGui::TreePop(); + } + if (ImGui::TreeNode("Groups")) { ImGui::TextWrapped("(Using ImGui::BeginGroup()/EndGroup() to layout items. BeginGroup() basically locks the horizontal position. EndGroup() bundles the whole group so that you can use functions such as IsItemHovered() on it.)"); @@ -2497,6 +2568,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) ImGui::SliderFloat("ChildBorderSize", &style.ChildBorderSize, 0.0f, 1.0f, "%.0f"); ImGui::SliderFloat("PopupBorderSize", &style.PopupBorderSize, 0.0f, 1.0f, "%.0f"); ImGui::SliderFloat("FrameBorderSize", &style.FrameBorderSize, 0.0f, 1.0f, "%.0f"); + ImGui::SliderFloat("TabBorderSize", &style.TabBorderSize, 0.0f, 1.0f, "%.0f"); ImGui::Text("Rounding"); ImGui::SliderFloat("WindowRounding", &style.WindowRounding, 0.0f, 14.0f, "%.0f"); ImGui::SliderFloat("ChildRounding", &style.ChildRounding, 0.0f, 16.0f, "%.0f"); @@ -3579,6 +3651,282 @@ static void ShowExampleAppCustomRendering(bool* p_open) ImGui::End(); } +//----------------------------------------------------------------------------- +// [SECTION] Example App: Documents Handling / ShowExampleAppDocuments() +//----------------------------------------------------------------------------- + +// Simplified structure to mimic a Document model +struct MyDocument +{ + const char* Name; // Document title + bool Open; // Set when the document is open (in this demo, we keep an array of all available documents to simplify the demo) + bool OpenPrev; // Copy of Open from last update. + bool Dirty; // Set when the document has been modified + bool WantClose; // Set when the document + ImVec4 Color; // An arbitrary variable associated to the document + + MyDocument(const char* name, bool open = true, const ImVec4& color = ImVec4(1.0f,1.0f,1.0f,1.0f)) + { + Name = name; + Open = OpenPrev = open; + Dirty = false; + WantClose = false; + Color = color; + } + void DoOpen() { Open = true; } + void DoQueueClose() { WantClose = true; } + void DoForceClose() { Open = false; Dirty = false; } + void DoSave() { Dirty = false; } + + // Display dummy contents for the Document + static void DisplayContents(MyDocument* doc) + { + ImGui::PushID(doc); + ImGui::Text("Document \"%s\"", doc->Name); + ImGui::PushStyleColor(ImGuiCol_Text, doc->Color); + ImGui::TextWrapped("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."); + ImGui::PopStyleColor(); + if (ImGui::Button("Modify", ImVec2(100, 0))) + doc->Dirty = true; + ImGui::SameLine(); + if (ImGui::Button("Save", ImVec2(100, 0))) + doc->DoSave(); + ImGui::ColorEdit3("color", &doc->Color.x); // Useful to test drag and drop and hold-dragged-to-open-tab behavior. + ImGui::PopID(); + } + + // Display context menu for the Document + static void DisplayContextMenu(MyDocument* doc) + { + if (!ImGui::BeginPopupContextItem()) + return; + + char buf[256]; + sprintf(buf, "Save %s", doc->Name); + if (ImGui::MenuItem(buf, "CTRL+S", false, doc->Open)) + doc->DoSave(); + if (ImGui::MenuItem("Close", "CTRL+W", false, doc->Open)) + doc->DoQueueClose(); + ImGui::EndPopup(); + } +}; + +struct ExampleAppDocuments +{ + ImVector Documents; + + ExampleAppDocuments() + { + Documents.push_back(MyDocument("Radish", true, ImVec4(1.0f, 1.0f, 1.0f, 1.0f))); + Documents.push_back(MyDocument("Eggplant", true, ImVec4(0.8f, 0.5f, 1.0f, 1.0f))); + Documents.push_back(MyDocument("Carrot", true, ImVec4(1.0f, 0.8f, 0.5f, 1.0f))); + Documents.push_back(MyDocument("Tomato", false, ImVec4(1.0f, 0.3f, 0.4f, 1.0f))); + Documents.push_back(MyDocument("A Rather Long Title", false)); + Documents.push_back(MyDocument("Some Document", false)); + } +}; + +// [Optional] Notify the system of Tabs/Windows of closure that happened outside the regular tab interface +// If a tab has been closed programmatically (aka closed from another source such as the Checkbox() in the demo, as opposed +// to clicking on the regular tab closing button) and stops being submitted, it will take a frame for the tab bar to notice its absence. +// During this frame there will be a gap in the tab bar, and if the tab that has disappeared was the selected one, the tab bar +// will report no selected tab during the frame. This will effectively give the impression of a flicker for one frame. +// We call SetTabItemClosed() to manually notify the Tab Bar or Docking system of removed tabs to avoid this glitch. +// Note that this completely optional, and only affect tab bars with the ImGuiTabBarFlags_Reorderable flag. +static void NotifyOfDocumentsClosedElsewhere(ExampleAppDocuments& app) +{ + for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++) + { + MyDocument* doc = &app.Documents[doc_n]; + if (!doc->Open && doc->OpenPrev) + ImGui::SetTabItemClosed(doc->Name); + doc->OpenPrev = doc->Open; + } +} + +void ShowExampleAppDocuments(bool* p_open) +{ + static ExampleAppDocuments app; + + ImGui::Begin("Examples: Documents", p_open, ImGuiWindowFlags_MenuBar); + + // Options + enum Target + { + Target_None, + Target_Tab // Create document as a local tab into a local tab bar + }; + static Target opt_target = Target_Tab; + static bool opt_reorderable = true; + static ImGuiTabBarFlags opt_fitting_flags = ImGuiTabBarFlags_FittingPolicyDefault_; + + // Menu + if (ImGui::BeginMenuBar()) + { + if (ImGui::BeginMenu("File")) + { + int open_count = 0; + for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++) + open_count += app.Documents[doc_n].Open ? 1 : 0; + + if (ImGui::BeginMenu("Open", open_count < app.Documents.Size)) + { + for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++) + { + MyDocument* doc = &app.Documents[doc_n]; + if (!doc->Open) + if (ImGui::MenuItem(doc->Name)) + doc->DoOpen(); + } + ImGui::EndMenu(); + } + if (ImGui::MenuItem("Close All Documents", NULL, false, open_count > 0)) + for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++) + app.Documents[doc_n].DoQueueClose(); + if (ImGui::MenuItem("Exit", "Alt+F4")) {} + ImGui::EndMenu(); + } + ImGui::EndMenuBar(); + } + + // [Debug] List documents with one checkbox for each + for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++) + { + MyDocument* doc = &app.Documents[doc_n]; + if (doc_n > 0) + ImGui::SameLine(); + ImGui::PushID(doc); + if (ImGui::Checkbox(doc->Name, &doc->Open)) + if (!doc->Open) + doc->DoForceClose(); + ImGui::PopID(); + } + ImGui::PushItemWidth(ImGui::GetFontSize() * 12); + ImGui::Combo("Output", (int*)&opt_target, "None\0TabBar+Tabs\0"); + ImGui::PopItemWidth(); + if (opt_target == Target_Tab) { ImGui::SameLine(); ImGui::Checkbox("Reorderable Tabs", &opt_reorderable); } + + ImGui::Separator(); + + // Tabs + if (opt_target == Target_Tab) + { + ImGuiTabBarFlags tab_bar_flags = (opt_fitting_flags) | (opt_reorderable ? ImGuiTabBarFlags_Reorderable : 0); + ImGui::BeginTabBar("##tabs", tab_bar_flags); + + if (opt_reorderable) + NotifyOfDocumentsClosedElsewhere(app); + + // [DEBUG] Stress tests + //if ((ImGui::GetFrameCount() % 30) == 0) docs[1].Open ^= 1; // [DEBUG] Automatically show/hide a tab. Test various interactions e.g. dragging with this on. + //if (ImGui::GetIO().KeyCtrl) ImGui::SetTabItemSelected(docs[1].Name); // [DEBUG] Test SetTabItemSelected(), probably not very useful as-is anyway.. + + // Submit Tabs + for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++) + { + MyDocument* doc = &app.Documents[doc_n]; + if (!doc->Open) + continue; + + ImGuiTabItemFlags tab_flags = (doc->Dirty ? ImGuiTabItemFlags_UnsavedDocument : 0); + bool visible = ImGui::BeginTabItem(doc->Name, &doc->Open, tab_flags); + + // Cancel attempt to close when unsaved add to save queue so we can display a popup. + if (!doc->Open && doc->Dirty) + { + doc->Open = true; + doc->DoQueueClose(); + } + + MyDocument::DisplayContextMenu(doc); + if (visible) + { + MyDocument::DisplayContents(doc); + ImGui::EndTabItem(); + } + } + + ImGui::EndTabBar(); + } + + // Update closing queue + static ImVector close_queue; + if (close_queue.empty()) + { + // Close queue is locked once we started a popup + for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++) + { + MyDocument* doc = &app.Documents[doc_n]; + if (doc->WantClose) + { + doc->WantClose = false; + close_queue.push_back(doc); + } + } + } + + // Display closing confirmation UI + if (!close_queue.empty()) + { + int close_queue_unsaved_documents = 0; + for (int n = 0; n < close_queue.Size; n++) + if (close_queue[n]->Dirty) + close_queue_unsaved_documents++; + + if (close_queue_unsaved_documents == 0) + { + // Close documents when all are unsaved + for (int n = 0; n < close_queue.Size; n++) + close_queue[n]->DoForceClose(); + close_queue.clear(); + } + else + { + if (!ImGui::IsPopupOpen("Save?")) + ImGui::OpenPopup("Save?"); + if (ImGui::BeginPopupModal("Save?")) + { + ImGui::Text("Save change to the following items?"); + ImGui::PushItemWidth(-1.0f); + ImGui::ListBoxHeader("##", close_queue_unsaved_documents, 6); + for (int n = 0; n < close_queue.Size; n++) + if (close_queue[n]->Dirty) + ImGui::Text("%s", close_queue[n]->Name); + ImGui::ListBoxFooter(); + + if (ImGui::Button("Yes", ImVec2(80, 0))) + { + for (int n = 0; n < close_queue.Size; n++) + { + if (close_queue[n]->Dirty) + close_queue[n]->DoSave(); + close_queue[n]->DoForceClose(); + } + close_queue.clear(); + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + if (ImGui::Button("No", ImVec2(80, 0))) + { + for (int n = 0; n < close_queue.Size; n++) + close_queue[n]->DoForceClose(); + close_queue.clear(); + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + if (ImGui::Button("Cancel", ImVec2(80, 0))) + { + close_queue.clear(); + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + } + } + + ImGui::End(); +} + // End of Demo code #else diff --git a/imgui_draw.cpp b/imgui_draw.cpp index dd30b47664c7..e7eff2e9bcfa 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -205,6 +205,11 @@ void ImGui::StyleColorsDark(ImGuiStyle* dst) colors[ImGuiCol_ResizeGrip] = ImVec4(0.26f, 0.59f, 0.98f, 0.25f); colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.67f); colors[ImGuiCol_ResizeGripActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f); + colors[ImGuiCol_Tab] = ImLerp(colors[ImGuiCol_Header], colors[ImGuiCol_TitleBgActive], 0.80f); + colors[ImGuiCol_TabActive] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f); + colors[ImGuiCol_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f); + colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f); + colors[ImGuiCol_TabHovered] = colors[ImGuiCol_HeaderHovered]; colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f); colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f); colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); @@ -255,6 +260,11 @@ void ImGui::StyleColorsClassic(ImGuiStyle* dst) colors[ImGuiCol_ResizeGrip] = ImVec4(1.00f, 1.00f, 1.00f, 0.16f); colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.78f, 0.82f, 1.00f, 0.60f); colors[ImGuiCol_ResizeGripActive] = ImVec4(0.78f, 0.82f, 1.00f, 0.90f); + colors[ImGuiCol_Tab] = ImLerp(colors[ImGuiCol_Header], colors[ImGuiCol_TitleBgActive], 0.80f); + colors[ImGuiCol_TabActive] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f); + colors[ImGuiCol_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f); + colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f); + colors[ImGuiCol_TabHovered] = colors[ImGuiCol_HeaderHovered]; colors[ImGuiCol_PlotLines] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); colors[ImGuiCol_PlotLinesHovered] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); @@ -306,6 +316,11 @@ void ImGui::StyleColorsLight(ImGuiStyle* dst) colors[ImGuiCol_ResizeGrip] = ImVec4(0.80f, 0.80f, 0.80f, 0.56f); colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.67f); colors[ImGuiCol_ResizeGripActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f); + colors[ImGuiCol_Tab] = ImLerp(colors[ImGuiCol_Header], colors[ImGuiCol_TitleBgActive], 0.90f); + colors[ImGuiCol_TabActive] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f); + colors[ImGuiCol_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f); + colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f); + colors[ImGuiCol_TabHovered] = colors[ImGuiCol_HeaderHovered]; colors[ImGuiCol_PlotLines] = ImVec4(0.39f, 0.39f, 0.39f, 1.00f); colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f); colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); @@ -2818,6 +2833,7 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col // - RenderMouseCursor() // - RenderArrowPointingAt() // - RenderRectFilledRangeH() +// - RenderPixelEllipsis() //----------------------------------------------------------------------------- void ImGui::RenderMouseCursor(ImDrawList* draw_list, ImVec2 pos, float scale, ImGuiMouseCursor mouse_cursor) @@ -2926,6 +2942,14 @@ void ImGui::RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, Im draw_list->PathFillConvex(col); } +// FIXME: Rendering an ellipsis "..." is a surprisingly tricky problem... can't rely on font glyph having it, and regular dot are typically too wide. +// If we render a dot/shape ourselves it comes with the risk that it wouldn't match the boldness or positioning of what the font uses... +void ImGui::RenderPixelEllipsis(ImDrawList* draw_list, ImFont* font, ImVec2 pos, int count, ImU32 col) +{ + pos.y += (float)(int)(font->DisplayOffset.y + font->Ascent + 0.5f - 1.0f); + for (int dot_n = 0; dot_n < count; dot_n++) + draw_list->AddRectFilled(ImVec2(pos.x + dot_n * 2.0f, pos.y), ImVec2(pos.x + dot_n * 2.0f + 1.0f, pos.y + 1.0f), col); +} //----------------------------------------------------------------------------- // [SECTION] Decompression code diff --git a/imgui_internal.h b/imgui_internal.h index 530653d0f7f1..fe84c35dcd9a 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -49,6 +49,8 @@ struct ImGuiNextWindowData; // Storage for SetNexWindow** functions struct ImGuiPopupRef; // Storage for current popup stack struct ImGuiSettingsHandler; // Storage for one type registered in the .ini file struct ImGuiStyleMod; // Stacked style modifier, backup of modified data so we can restore it +struct ImGuiTabBar; // Storage for a tab bar +struct ImGuiTabItem; // Storage for a tab item (within a tab bar) struct ImGuiWindow; // Storage for one window struct ImGuiWindowTempData; // Temporary storage for one window (that's the data which in theory we could ditch at the end of the frame) struct ImGuiWindowSettings; // Storage for window settings stored in .ini file (we keep one of those even if the actual window wasn't instanced during this session) @@ -654,6 +656,16 @@ struct ImGuiNextWindowData } }; +//----------------------------------------------------------------------------- +// Docking, Tabs +//----------------------------------------------------------------------------- + +struct ImGuiTabBarSortItem +{ + int Index; + float Width; +}; + //----------------------------------------------------------------------------- // Main imgui context //----------------------------------------------------------------------------- @@ -776,6 +788,11 @@ struct ImGuiContext ImVector DragDropPayloadBufHeap; // We don't expose the ImVector<> directly unsigned char DragDropPayloadBufLocal[8]; // Local buffer for small payloads + // Tab bars + ImPool TabBars; + ImVector CurrentTabBar; + ImVector TabSortByWidthBuffer; + // Widget state ImGuiInputTextState InputTextState; ImFont InputTextPasswordFont; @@ -1123,6 +1140,67 @@ struct ImGuiItemHoveredDataBackup void Restore() const { ImGuiWindow* window = GImGui->CurrentWindow; window->DC.LastItemId = LastItemId; window->DC.LastItemStatusFlags = LastItemStatusFlags; window->DC.LastItemRect = LastItemRect; window->DC.LastItemDisplayRect = LastItemDisplayRect; } }; +//----------------------------------------------------------------------------- +// Tab Bar, Tab Item +//----------------------------------------------------------------------------- + +enum ImGuiTabBarFlagsPrivate_ +{ + ImGuiTabBarFlags_DockNode = 1 << 20, // Part of a dock node + ImGuiTabBarFlags_DockNodeExplicitRoot = 1 << 21, // Part of an explicit dock node + ImGuiTabBarFlags_IsFocused = 1 << 22, + ImGuiTabBarFlags_SaveSettings = 1 << 23 // FIXME: Settings are handled by the docking system, this only request the tab bar to mark settings dirty when reordering tabs +}; + +enum ImGuiTabItemFlagsPrivate_ +{ + ImGuiTabItemFlags_DockedWindow = 1 << 20, + ImGuiTabItemFlags_Preview = 1 << 21 +}; + +// Storage for one active tab item (sizeof() 32~40 bytes) +struct ImGuiTabItem +{ + ImGuiID ID; + ImGuiTabItemFlags Flags; + ImGuiWindow* Window; // When TabItem is part of a DockNode's TabBar, we hold on to a window. + int LastFrameVisible; + int LastFrameSelected; // This allows us to infer an ordered list of the last activated tabs with little maintenance + float Offset; // Position relative to beginning of tab + float Width; // Width currently displayed + float WidthContents; // Width of actual contents, stored during BeginTabItem() call + + ImGuiTabItem() { ID = Flags = 0; Window = NULL; LastFrameVisible = LastFrameSelected = -1; Offset = Width = WidthContents = 0.0f; } +}; + +// Storage for a tab bar (sizeof() 92~96 bytes) +struct ImGuiTabBar +{ + ImVector Tabs; + ImGuiID ID; // Zero for tab-bars used by docking + ImGuiID SelectedTabId; // Selected tab + ImGuiID NextSelectedTabId; + ImGuiID VisibleTabId; // Can occasionally be != SelectedTabId (e.g. when previewing contents for CTRL+TAB preview) + ImGuiID WantFocusTabId; // Request focus for the window associated to this tab. Used and only honored by DockNode (meaningless for standalone tab bars) + int CurrFrameVisible; + int PrevFrameVisible; + ImRect BarRect; + float ContentsHeight; + float OffsetMax; // Distance from BarRect.Min.x, locked during layout + float OffsetNextTab; // Distance from BarRect.Min.x, incremented with each BeginTabItem() call, not used if ImGuiTabBarFlags_Reorderable if set. + float ScrollingAnim; + float ScrollingTarget; + ImGuiTabBarFlags Flags; + ImGuiID ReorderRequestTabId; + int ReorderRequestDir; + bool WantLayout; + bool VisibleTabWasSubmitted; + short LastTabItemIdx; // For BeginTabItem()/EndTabItem() + + ImGuiTabBar(); + int GetTabOrder(const ImGuiTabItem* tab) const { return Tabs.index_from_pointer(tab); } +}; + //----------------------------------------------------------------------------- // Internal API // No guarantee of forward compatibility here. @@ -1234,6 +1312,19 @@ namespace ImGui IMGUI_API void EndColumns(); // close columns IMGUI_API void PushColumnClipRect(int column_index = -1); + // Tab Bars + IMGUI_API void ShowDockingDebug(); + IMGUI_API bool BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& bb, ImGuiTabBarFlags flags); + IMGUI_API ImGuiTabItem* TabBarFindTabByID(ImGuiTabBar* tab_bar, ImGuiID tab_id); + IMGUI_API void TabBarAddTab(ImGuiTabBar* tab_bar, ImGuiWindow* window); + IMGUI_API void TabBarRemoveTab(ImGuiTabBar* tab_bar, ImGuiID tab_id); + IMGUI_API void TabBarCloseTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab); + IMGUI_API void TabBarQueueChangeTabOrder(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, int dir); + IMGUI_API bool TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags, ImGuiWindow* docked_window); + IMGUI_API ImVec2 TabItemCalcSize(const char* label, bool has_close_button); + IMGUI_API void TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImU32 col); + IMGUI_API bool TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, const char* label, ImGuiID tab_id, ImGuiID close_button_id); + // Render helpers // AVOID USING OUTSIDE OF IMGUI.CPP! NOT FOR PUBLIC CONSUMPTION. THOSE FUNCTIONS ARE A MESS. THEIR SIGNATURE AND BEHAVIOR WILL CHANGE, THEY NEED TO BE REFACTORED INTO SOMETHING DECENT. // NB: All position are in absolute pixels coordinates (we are never using window coordinates internally) @@ -1255,6 +1346,7 @@ namespace ImGui IMGUI_API void RenderMouseCursor(ImDrawList* draw_list, ImVec2 pos, float scale, ImGuiMouseCursor mouse_cursor = ImGuiMouseCursor_Arrow); IMGUI_API void RenderArrowPointingAt(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, ImGuiDir direction, ImU32 col); IMGUI_API void RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, ImU32 col, float x_start_norm, float x_end_norm, float rounding); + IMGUI_API void RenderPixelEllipsis(ImDrawList* draw_list, ImFont* font, ImVec2 pos, int count, ImU32 col); // Widgets IMGUI_API bool ButtonEx(const char* label, const ImVec2& size_arg = ImVec2(0,0), ImGuiButtonFlags flags = 0); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index f264396e3082..8be3daa1b70d 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -22,6 +22,8 @@ Index of this file: // [SECTION] Widgets: PlotLines, PlotHistogram // [SECTION] Widgets: Value helpers // [SECTION] Widgets: MenuItem, BeginMenu, EndMenu, etc. +// [SECTION] Widgets: BeginTabBar, EndTabBar, etc. +// [SECTION] Widgets: BeginTabItem, EndTabItem, etc. */ @@ -1238,7 +1240,6 @@ bool ImGui::SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float return held; } - //------------------------------------------------------------------------- // [SECTION] Widgets: Combo Box //------------------------------------------------------------------------- @@ -2612,7 +2613,6 @@ bool ImGui::InputScalarAsWidgetReplacement(const ImRect& bb, ImGuiID id, const c return false; } -// NB: format here must be a simple "%xx" format string with no prefix/suffix (unlike the Drag/Slider functions "format" argument) bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* data_ptr, const void* step, const void* step_fast, const char* format, ImGuiInputTextFlags extra_flags) { ImGuiWindow* window = GetCurrentWindow(); @@ -5723,3 +5723,878 @@ bool ImGui::MenuItem(const char* label, const char* shortcut, bool* p_selected, } return false; } + +//------------------------------------------------------------------------- +// [SECTION] Widgets: BeginTabBar, EndTabBar, etc. +//------------------------------------------------------------------------- +// - BeginTabBar() +// - BeginTabBarEx() [Internal] +// - EndTabBar() +// - TabBarLayout() [Internal] +// - TabBarCalcTabID() [Internal] +// - TabBarCalcMaxTabWidth() [Internal] +// - TabBarFindTabById() [Internal] +// - TabBarAddTab() [Internal] +// - TabBarRemoveTab() [Internal] +// - TabBarCloseTab() [Internal] +// - TabBarScrollClamp()v +// - TabBarScrollToTab() [Internal] +// - TabBarQueueChangeTabOrder() [Internal] +// - TabBarScrollingButtons() [Internal] +// - TabBarTabListPopupButton() [Internal] +//------------------------------------------------------------------------- + +namespace ImGui +{ + static void TabBarLayout(ImGuiTabBar* tab_bar); + static ImU32 TabBarCalcTabID(ImGuiTabBar* tab_bar, const char* label); + static float TabBarCalcMaxTabWidth(); + static float TabBarScrollClamp(ImGuiTabBar* tab_bar, float scrolling); + static void TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab); + static ImGuiTabItem* TabBarScrollingButtons(ImGuiTabBar* tab_bar); + static ImGuiTabItem* TabBarTabListPopupButton(ImGuiTabBar* tab_bar); +} + +ImGuiTabBar::ImGuiTabBar() +{ + ID = 0; + SelectedTabId = NextSelectedTabId = VisibleTabId = WantFocusTabId = 0; + CurrFrameVisible = PrevFrameVisible = -1; + OffsetMax = OffsetNextTab = 0.0f; + ScrollingAnim = ScrollingTarget = 0.0f; + Flags = ImGuiTabBarFlags_None; + ReorderRequestTabId = 0; + ReorderRequestDir = 0; + WantLayout = VisibleTabWasSubmitted = false; + LastTabItemIdx = -1; +} + +static int IMGUI_CDECL TabItemComparerByVisibleOffset(const void* lhs, const void* rhs) +{ + const ImGuiTabItem* a = (const ImGuiTabItem*)lhs; + const ImGuiTabItem* b = (const ImGuiTabItem*)rhs; + return (int)(a->Offset - b->Offset); +} + +static int IMGUI_CDECL TabBarSortItemComparer(const void* lhs, const void* rhs) +{ + const ImGuiTabBarSortItem* a = (const ImGuiTabBarSortItem*)lhs; + const ImGuiTabBarSortItem* b = (const ImGuiTabBarSortItem*)rhs; + if (int d = (int)(b->Width - a->Width)) + return d; + return (b->Index - a->Index); +} + +bool ImGui::BeginTabBar(const char* str_id, ImGuiTabBarFlags flags) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + if (window->SkipItems) + return false; + + ImGuiID id = window->GetID(str_id); + ImGuiTabBar* tab_bar = g.TabBars.GetOrAddByKey(id); + ImRect tab_bar_bb = ImRect(window->DC.CursorPos.x, window->DC.CursorPos.y, window->InnerClipRect.Max.x, window->DC.CursorPos.y + g.FontSize + g.Style.FramePadding.y * 2); + tab_bar->ID = id; + return BeginTabBarEx(tab_bar, tab_bar_bb, flags | ImGuiTabBarFlags_IsFocused); +} + +bool ImGui::BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& tab_bar_bb, ImGuiTabBarFlags flags) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + if (window->SkipItems) + return false; + + if ((flags & ImGuiTabBarFlags_DockNode) == 0) + window->IDStack.push_back(tab_bar->ID); + + g.CurrentTabBar.push_back(tab_bar); + if (tab_bar->CurrFrameVisible == g.FrameCount) + { + printf("[%05d] BeginTabBarEx already called this frame\n", g.FrameCount); + //IM_ASSERT(0); + return true; + } + + // When toggling back from ordered to manually-reorderable, shuffle tabs to enforce the last visible order. + // Otherwise, the most recently inserted tabs would move at the end of visible list which can be a little too confusing or magic for the user. + if ((flags & ImGuiTabBarFlags_Reorderable) && !(tab_bar->Flags & ImGuiTabBarFlags_Reorderable) && tab_bar->Tabs.Size > 1 && tab_bar->PrevFrameVisible != -1) + ImQsort(tab_bar->Tabs.Data, tab_bar->Tabs.Size, sizeof(ImGuiTabItem), TabItemComparerByVisibleOffset); + + // Flags + if ((flags & ImGuiTabBarFlags_FittingPolicyMask_) == 0) + flags |= ImGuiTabBarFlags_FittingPolicyDefault_; + + tab_bar->Flags = flags; + tab_bar->BarRect = tab_bar_bb; + tab_bar->WantLayout = true; // Layout will be done on the first call to ItemTab() + tab_bar->PrevFrameVisible = tab_bar->CurrFrameVisible; + tab_bar->CurrFrameVisible = g.FrameCount; + + // Layout + ItemSize(ImVec2(tab_bar->OffsetMax, tab_bar->BarRect.GetHeight())); + window->DC.CursorPos.x = tab_bar->BarRect.Min.x; + + // Draw separator + const ImU32 col = GetColorU32((flags & ImGuiTabBarFlags_IsFocused) ? ImGuiCol_TabActive : ImGuiCol_Tab); + const float y = tab_bar->BarRect.Max.y - 1.0f; + const float separator_min_x = tab_bar->BarRect.Min.x - ((flags & ImGuiTabBarFlags_DockNodeExplicitRoot) ? 0.0f : window->WindowPadding.x); + const float separator_max_x = tab_bar->BarRect.Max.x + ((flags & ImGuiTabBarFlags_DockNodeExplicitRoot) ? 0.0f : window->WindowPadding.x); + window->DrawList->AddLine(ImVec2(separator_min_x, y), ImVec2(separator_max_x, y), col, 1.0f); + + return true; +} + +void ImGui::EndTabBar() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + if (window->SkipItems) + return; + + IM_ASSERT(!g.CurrentTabBar.empty()); // Mismatched BeginTabBar/EndTabBar + ImGuiTabBar* tab_bar = g.CurrentTabBar.back(); + if (tab_bar->WantLayout) + TabBarLayout(tab_bar); + + // Restore the last visible height if no tab is visible, this reduce vertical flicker/movement when a tabs gets removed without calling SetTabItemClosed(). + const bool tab_bar_appearing = (tab_bar->PrevFrameVisible + 1 < g.FrameCount); + if (tab_bar->VisibleTabWasSubmitted || tab_bar->VisibleTabId == 0 || tab_bar_appearing) + tab_bar->ContentsHeight = ImMax(window->DC.CursorPos.y - tab_bar->BarRect.Max.y, 0.0f); + else + window->DC.CursorPos.y = tab_bar->BarRect.Max.y + tab_bar->ContentsHeight; + + if ((tab_bar->Flags & ImGuiTabBarFlags_DockNode) == 0) + PopID(); + g.CurrentTabBar.pop_back(); +} + +// This is called only once a frame before by the first call to ItemTab() +// The reason we're not calling it in BeginTabBar() is to leave a chance to the user to call the SetTabItemClosed() functions. +static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) +{ + ImGuiContext& g = *GImGui; + tab_bar->WantLayout = false; + + // Garbage collect + int tab_dst_n = 0; + for (int tab_src_n = 0; tab_src_n < tab_bar->Tabs.Size; tab_src_n++) + { + ImGuiTabItem* tab = &tab_bar->Tabs[tab_src_n]; + if (tab->LastFrameVisible < tab_bar->PrevFrameVisible) + { + if (tab->ID == tab_bar->SelectedTabId) + tab_bar->SelectedTabId = 0; + continue; + } + if (tab_dst_n != tab_src_n) + tab_bar->Tabs[tab_dst_n] = tab_bar->Tabs[tab_src_n]; + tab_dst_n++; + } + if (tab_bar->Tabs.Size != tab_dst_n) + tab_bar->Tabs.resize(tab_dst_n); + + // Setup next selected tab + ImGuiID scroll_track_selected_tab_id = 0; + tab_bar->WantFocusTabId = 0; + if (tab_bar->NextSelectedTabId) + { + tab_bar->SelectedTabId = tab_bar->WantFocusTabId = tab_bar->NextSelectedTabId; + tab_bar->NextSelectedTabId = 0; + scroll_track_selected_tab_id = tab_bar->SelectedTabId; + } + + // Process order change request (we could probably process it when requested but it's just saner to do it in a single spot). + if (tab_bar->ReorderRequestTabId != 0) + { + if (ImGuiTabItem* tab1 = TabBarFindTabByID(tab_bar, tab_bar->ReorderRequestTabId)) + { + IM_ASSERT(tab_bar->Flags & ImGuiTabBarFlags_Reorderable); + int tab2_order = tab_bar->GetTabOrder(tab1) + tab_bar->ReorderRequestDir; + if (tab2_order >= 0 && tab2_order < tab_bar->Tabs.Size) + { + ImGuiTabItem* tab2 = &tab_bar->Tabs[tab2_order]; + ImGuiTabItem item_tmp = *tab1; + *tab1 = *tab2; + *tab2 = item_tmp; + if (tab2->ID == tab_bar->SelectedTabId) + scroll_track_selected_tab_id = tab2->ID; + tab1 = tab2 = NULL; + } + if (tab_bar->Flags & ImGuiTabBarFlags_SaveSettings) + MarkIniSettingsDirty(); + } + tab_bar->ReorderRequestTabId = 0; + } + + ImVector& width_sort_buffer = g.TabSortByWidthBuffer; + width_sort_buffer.resize(tab_bar->Tabs.Size); + + // Compute ideal widths + float width_total_contents = 0.0f; + ImGuiTabItem* most_recently_selected_tab = NULL; + for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++) + { + ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; + IM_ASSERT(tab->LastFrameVisible >= tab_bar->PrevFrameVisible); + + if (most_recently_selected_tab == NULL || most_recently_selected_tab->LastFrameSelected < tab->LastFrameSelected) + most_recently_selected_tab = tab; + + // Refresh tab width immediately if we can (for manual tab bar, WidthContent will lag by one frame which is mostly noticeable when changing style.FramePadding.x) + // Additionally, when using TabBarAddTab() to manipulate tab bar order we occasionally insert new tabs that don't have a width yet, + // and we cannot wait for the next BeginTabItem() call. We cannot compute this width within TabBarAddTab() because font size depends on the active window. + if (tab->Window) + tab->WidthContents = TabItemCalcSize(tab->Window->Name, tab->Window->HasCloseButton).x; + width_total_contents += (tab_n > 0 ? g.Style.ItemInnerSpacing.x : 0.0f) + tab->WidthContents; + + // Store data so we can build an array sorted by width if we need to shrink tabs down + width_sort_buffer[tab_n].Index = tab_n; + width_sort_buffer[tab_n].Width = tab->WidthContents; + } + + // Compute width + const float width_avail = tab_bar->BarRect.GetWidth(); + float width_excess = (width_avail < width_total_contents) ? (width_total_contents - width_avail) : 0.0f; + if (width_excess > 0.0f && (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyResizeDown)) + { + // If we don't have enough room, resize down the largest tabs first + if (tab_bar->Tabs.Size > 1) + ImQsort(width_sort_buffer.Data, (size_t)width_sort_buffer.Size, sizeof(ImGuiTabBarSortItem), TabBarSortItemComparer); + int tab_count_same_width = 1; + while (width_excess > 0.0f && tab_count_same_width < tab_bar->Tabs.Size) + { + while (tab_count_same_width < tab_bar->Tabs.Size && width_sort_buffer[0].Width == width_sort_buffer[tab_count_same_width].Width) + tab_count_same_width++; + float width_to_remove_per_tab_max = (tab_count_same_width < tab_bar->Tabs.Size) ? (width_sort_buffer[0].Width - width_sort_buffer[tab_count_same_width].Width) : (width_sort_buffer[0].Width - 1.0f); + float width_to_remove_per_tab = ImMin(width_excess / tab_count_same_width, width_to_remove_per_tab_max); + for (int tab_n = 0; tab_n < tab_count_same_width; tab_n++) + width_sort_buffer[tab_n].Width -= width_to_remove_per_tab; + width_excess -= width_to_remove_per_tab * tab_count_same_width; + } + for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++) + tab_bar->Tabs[width_sort_buffer[tab_n].Index].Width = (float)(int)width_sort_buffer[tab_n].Width; + } + else + { + const float tab_max_width = TabBarCalcMaxTabWidth(); + for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++) + { + ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; + tab->Width = ImMin(tab->WidthContents, tab_max_width); + } + } + + // Layout all active tabs + float offset_x = 0.0f; + for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++) + { + ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; + tab->Offset = offset_x; + if (scroll_track_selected_tab_id == 0 && g.NavJustMovedToId == tab->ID) + scroll_track_selected_tab_id = tab->ID; + offset_x += tab->Width + g.Style.ItemInnerSpacing.x; + } + tab_bar->OffsetMax = ImMax(offset_x - g.Style.ItemInnerSpacing.x, 0.0f); + tab_bar->OffsetNextTab = 0.0f; + + // Tab List Popup + const bool tab_list_popup_button = (tab_bar->Flags & ImGuiTabBarFlags_DockNode) != 0 && (tab_bar->Flags & ImGuiTabBarFlags_NoTabListPopupButton) == 0; + if (tab_list_popup_button) + if (ImGuiTabItem* tab_to_select = TabBarTabListPopupButton(tab_bar)) // NB: Will alter BarRect.Max.x! + scroll_track_selected_tab_id = tab_bar->SelectedTabId = tab_to_select->ID; + + // Horizontal scrolling buttons + const bool scrolling_buttons = (tab_bar->OffsetMax > tab_bar->BarRect.GetWidth() && tab_bar->Tabs.Size > 1) && !(tab_bar->Flags & ImGuiTabBarFlags_NoTabListScrollingButtons) && (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyScroll); + if (scrolling_buttons) + if (ImGuiTabItem* tab_to_select = TabBarScrollingButtons(tab_bar)) // NB: Will alter BarRect.Max.x! + scroll_track_selected_tab_id = tab_bar->SelectedTabId = tab_to_select->ID; + + // If we have lost the selected tab, select the next most recently active one. + if (tab_bar->SelectedTabId == 0 && tab_bar->NextSelectedTabId == 0 && most_recently_selected_tab != NULL) + scroll_track_selected_tab_id = tab_bar->SelectedTabId = most_recently_selected_tab->ID; + + // Lock in visible tab + tab_bar->VisibleTabId = tab_bar->SelectedTabId; + tab_bar->VisibleTabWasSubmitted = false; + + // Update scrolling + if (scroll_track_selected_tab_id) + if (ImGuiTabItem* scroll_track_selected_tab = TabBarFindTabByID(tab_bar, scroll_track_selected_tab_id)) + TabBarScrollToTab(tab_bar, scroll_track_selected_tab); + tab_bar->ScrollingAnim = TabBarScrollClamp(tab_bar, tab_bar->ScrollingAnim); + tab_bar->ScrollingTarget = TabBarScrollClamp(tab_bar, tab_bar->ScrollingTarget); + const float scrolling_speed = (tab_bar->PrevFrameVisible + 1 < g.FrameCount) ? FLT_MAX : (g.IO.DeltaTime * g.FontSize * 70.0f); + if (tab_bar->ScrollingAnim != tab_bar->ScrollingTarget) + tab_bar->ScrollingAnim = ImLinearSweep(tab_bar->ScrollingAnim, tab_bar->ScrollingTarget, scrolling_speed); +} + +// Dockables uses Name/ID in the global namespace. Non-dockable items use the ID stack. +static ImU32 ImGui::TabBarCalcTabID(ImGuiTabBar* tab_bar, const char* label) +{ + if (tab_bar->Flags & ImGuiTabBarFlags_DockNode) + { + ImGuiID id = ImHash(label, 0); + KeepAliveID(id); + return id; + } + else + { + ImGuiWindow* window = GImGui->CurrentWindow; + return window->GetID(label); + } +} + +static float ImGui::TabBarCalcMaxTabWidth() +{ + ImGuiContext& g = *GImGui; + return g.FontSize * 20.0f; +} + +ImGuiTabItem* ImGui::TabBarFindTabByID(ImGuiTabBar* tab_bar, ImGuiID tab_id) +{ + if (tab_id != 0) + for (int n = 0; n < tab_bar->Tabs.Size; n++) + if (tab_bar->Tabs[n].ID == tab_id) + return &tab_bar->Tabs[n]; + return NULL; +} + +// The purpose of this call is to register tab in advance so we can control their order at the time they appear. +// Otherwise calling this is unnecessary as tabs are appending as needed by the BeginTabItem() function. +void ImGui::TabBarAddTab(ImGuiTabBar* tab_bar, ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(TabBarFindTabByID(tab_bar, window->ID) == NULL); + IM_ASSERT(g.CurrentTabBar.empty()); // Can't work while the tab bar is active as our tab doesn't have an X offset yet + + ImGuiTabItem new_tab; + new_tab.ID = window->ID; + new_tab.LastFrameVisible = tab_bar->CurrFrameVisible; // Required so BeginTabBar doesn't ditch the tab + if (new_tab.LastFrameVisible == -1) + new_tab.LastFrameVisible = g.FrameCount - 1; + new_tab.Window = window; // Required so tab bar layout can compute the tab width before tab submission + if (tab_bar->Tabs.empty()) + tab_bar->Tabs.push_back(new_tab); + else + tab_bar->Tabs.insert(tab_bar->Tabs.Data + tab_bar->Tabs.Size, new_tab); +} + +// The *TabId fields be already set by the docking system _before_ the actual TabItem was created, so we clear them regardless. +void ImGui::TabBarRemoveTab(ImGuiTabBar* tab_bar, ImGuiID tab_id) +{ + if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_id)) + tab_bar->Tabs.erase(tab); + if (tab_bar->VisibleTabId == tab_id) { tab_bar->VisibleTabId = 0; } + if (tab_bar->WantFocusTabId == tab_id) { tab_bar->WantFocusTabId = 0; } + if (tab_bar->SelectedTabId == tab_id) { tab_bar->SelectedTabId = 0; } + if (tab_bar->NextSelectedTabId == tab_id) { tab_bar->NextSelectedTabId = 0; } +} + +void ImGui::TabBarCloseTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab) +{ + if ((tab_bar->VisibleTabId == tab->ID) && !(tab->Flags & ImGuiTabItemFlags_UnsavedDocument)) + { + // This will remove a frame of lag for selecting another tab on closure. + // However we don't run it in the case where the 'Unsaved' flag is set, so user gets a chance to fully undo the closure + tab->LastFrameVisible = -1; + tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = 0; + } + else if ((tab_bar->VisibleTabId != tab->ID) && (tab->Flags & ImGuiTabItemFlags_UnsavedDocument)) + { + // Actually select before expecting closure + tab_bar->NextSelectedTabId = tab->ID; + } +} + +static float ImGui::TabBarScrollClamp(ImGuiTabBar* tab_bar, float scrolling) +{ + scrolling = ImMin(scrolling, tab_bar->OffsetMax - tab_bar->BarRect.GetWidth()); + return ImMax(scrolling, 0.0f); +} + +static void ImGui::TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab) +{ + ImGuiContext& g = *GImGui; + float margin = g.FontSize * 1.0f; // When to scroll to make Tab N+1 visible always make a bit of N visible to suggest more scrolling area (since we don't have a scrollbar) + int order = tab_bar->GetTabOrder(tab); + float tab_x1 = tab->Offset + (order > 0 ? -margin : 0.0f); + float tab_x2 = tab->Offset + tab->Width + (order + 1 < tab_bar->Tabs.Size ? margin : 1.0f); + if (tab_bar->ScrollingTarget > tab_x1) + tab_bar->ScrollingTarget = tab_x1; + if (tab_bar->ScrollingTarget + tab_bar->BarRect.GetWidth() < tab_x2) + tab_bar->ScrollingTarget = tab_x2 - tab_bar->BarRect.GetWidth(); +} + +void ImGui::TabBarQueueChangeTabOrder(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, int dir) +{ + IM_ASSERT(dir == -1 || dir == +1); + IM_ASSERT(tab_bar->ReorderRequestTabId == 0); + tab_bar->ReorderRequestTabId = tab->ID; + tab_bar->ReorderRequestDir = dir; +} + +static ImGuiTabItem* ImGui::TabBarScrollingButtons(ImGuiTabBar* tab_bar) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + + const ImVec2 arrow_button_size(g.FontSize - 2.0f, g.FontSize + g.Style.FramePadding.y * 2.0f); + const float scrolling_buttons_width = arrow_button_size.x * 2.0f; + + const ImVec2 backup_cursor_pos = window->DC.CursorPos; + //window->DrawList->AddRect(ImVec2(tab_bar->BarRect.Max.x - scrolling_buttons_width, tab_bar->BarRect.Min.y), ImVec2(tab_bar->BarRect.Max.x, tab_bar->BarRect.Max.y), IM_COL32(255,0,0,255)); + + const ImRect avail_bar_rect = tab_bar->BarRect; + bool want_clip_rect = !avail_bar_rect.Contains(ImRect(window->DC.CursorPos, window->DC.CursorPos + ImVec2(scrolling_buttons_width, 0.0f))); + if (want_clip_rect) + PushClipRect(tab_bar->BarRect.Min, tab_bar->BarRect.Max + ImVec2(g.Style.ItemInnerSpacing.x, 0.0f), true); + + ImGuiTabItem* tab_to_select = NULL; + + int select_dir = 0; + ImVec4 arrow_col = g.Style.Colors[ImGuiCol_Text]; + arrow_col.w *= 0.5f; + + PushStyleColor(ImGuiCol_Text, arrow_col); + PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0)); + const float backup_repeat_delay = g.IO.KeyRepeatDelay; + const float backup_repeat_rate = g.IO.KeyRepeatRate; + g.IO.KeyRepeatDelay = 0.250f; + g.IO.KeyRepeatRate = 0.200f; + window->DC.CursorPos = ImVec2(tab_bar->BarRect.Max.x - scrolling_buttons_width, tab_bar->BarRect.Min.y); + if (ArrowButtonEx("##<", ImGuiDir_Left, arrow_button_size, ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_Repeat)) + select_dir = -1; + window->DC.CursorPos = ImVec2(tab_bar->BarRect.Max.x - scrolling_buttons_width + arrow_button_size.x, tab_bar->BarRect.Min.y); + if (ArrowButtonEx("##>", ImGuiDir_Right, arrow_button_size, ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_Repeat)) + select_dir = +1; + PopStyleColor(2); + g.IO.KeyRepeatRate = backup_repeat_rate; + g.IO.KeyRepeatDelay = backup_repeat_delay; + + if (want_clip_rect) + PopClipRect(); + + if (select_dir != 0) + if (ImGuiTabItem* tab_item = TabBarFindTabByID(tab_bar, tab_bar->SelectedTabId)) + { + int selected_order = tab_bar->GetTabOrder(tab_item); + int target_order = selected_order + select_dir; + tab_to_select = &tab_bar->Tabs[(target_order >= 0 && target_order < tab_bar->Tabs.Size) ? target_order : selected_order]; // If we are at the end of the list, still scroll to make our tab visible + } + window->DC.CursorPos = backup_cursor_pos; + tab_bar->BarRect.Max.x -= scrolling_buttons_width + 1.0f; + + return tab_to_select; +} + +// FIXME-DOCK: Unused by Docking system +static ImGuiTabItem* ImGui::TabBarTabListPopupButton(ImGuiTabBar* tab_bar) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + + const float tab_list_popup_button_width = g.FontSize + g.Style.FramePadding.y * 2.0f; + const ImVec2 backup_cursor_pos = window->DC.CursorPos; + tab_bar->BarRect.Max.x -= tab_list_popup_button_width; + if (window->HasCloseButton) + tab_bar->BarRect.Max.x += g.Style.ItemInnerSpacing.x; + window->DC.CursorPos = ImVec2(tab_bar->BarRect.Max.x, tab_bar->BarRect.Min.y); + + ImVec4 arrow_col = g.Style.Colors[ImGuiCol_Text]; + arrow_col.w *= 0.5f; + PushStyleColor(ImGuiCol_Text, arrow_col); + PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0)); + bool open = BeginCombo("##v", NULL, ImGuiComboFlags_NoPreview | ImGuiComboFlags_PopupAlignLeft); + PopStyleColor(2); + + ImGuiTabItem* tab_to_select = NULL; + if (open) + { + for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++) + { + ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; + IM_ASSERT(tab->Window != NULL); + if (MenuItem(tab->Window->Name)) + tab_to_select = tab; + } + EndCombo(); + } + + window->DC.CursorPos = backup_cursor_pos; + return tab_to_select; +} + +//------------------------------------------------------------------------- +// [SECTION] Widgets: BeginTabItem, EndTabItem, etc. +//------------------------------------------------------------------------- +// - BeginTabItem() +// - EndTabItem() +// - TabItemEx() [Internal] +// - SetTabItemClosed() +// - TabItemCalcSize() [Internal] +// - TabItemRenderBackground() [Internal] +// - TabItemLabelAndCloseButton() [Internal] +//------------------------------------------------------------------------- + +bool ImGui::BeginTabItem(const char* label, bool* p_open, ImGuiTabItemFlags flags) +{ + ImGuiContext& g = *GImGui; + if (g.CurrentWindow->SkipItems) + return false; + + IM_ASSERT(g.CurrentTabBar.Size > 0 && "Needs to be called between BeginTabBar() and EndTabBar()!"); + ImGuiTabBar* tab_bar = g.CurrentTabBar.back(); + bool ret = TabItemEx(tab_bar, label, p_open, flags, NULL); + if (ret && !(flags & ImGuiTabItemFlags_NoPushId)) + PushID(tab_bar->Tabs[tab_bar->LastTabItemIdx].ID); + return ret; +} + +void ImGui::EndTabItem() +{ + ImGuiContext& g = *GImGui; + if (g.CurrentWindow->SkipItems) + return; + + IM_ASSERT(g.CurrentTabBar.Size > 0 && "Needs to be called between BeginTabBar() and EndTabBar()!"); + ImGuiTabBar* tab_bar = g.CurrentTabBar.back(); + ImGuiTabItem* tab = &tab_bar->Tabs[tab_bar->LastTabItemIdx]; + if (!(tab->Flags & ImGuiTabItemFlags_NoPushId)) + PopID(); +} + +bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags, ImGuiWindow* docked_window) +{ + // Layout whole tab bar if not already done + if (tab_bar->WantLayout) + TabBarLayout(tab_bar); + + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + if (window->SkipItems) + return false; + + const ImGuiStyle& style = g.Style; + const ImGuiID id = TabBarCalcTabID(tab_bar, label); + + // If the user called us with *p_open == false, we early out and don't render. We make a dummy call to ItemAdd() so that attempts to use a contextual popup menu with an implicit ID won't use an older ID. + if (p_open && !*p_open) + { + PushItemFlag(ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus, true); + ItemAdd(ImRect(), id); + PopItemFlag(); + return false; + } + + // Calculate tab contents size + ImVec2 size = TabItemCalcSize(label, p_open != NULL); + + // Acquire tab data + ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, id); + bool tab_is_new = false; + if (tab == NULL) + { + tab_bar->Tabs.push_back(ImGuiTabItem()); + tab = &tab_bar->Tabs.back(); + tab->ID = id; + tab->Width = size.x; + tab_is_new = true; + } + tab_bar->LastTabItemIdx = (short)tab_bar->Tabs.index_from_pointer(tab); + tab->WidthContents = size.x; + + const bool tab_bar_appearing = (tab_bar->PrevFrameVisible + 1 < g.FrameCount); + const bool tab_bar_focused = (tab_bar->Flags & ImGuiTabBarFlags_IsFocused) != 0; + const bool tab_appearing = (tab->LastFrameVisible + 1 < g.FrameCount); + tab->LastFrameVisible = g.FrameCount; + tab->Flags = flags; + tab->Window = docked_window; + + // If we are not reorderable, always reset offset based on submission order. + // (We already handled layout and sizing using the previous known order, but sizing is not affected by order!) + if (!tab_appearing && !(tab_bar->Flags & ImGuiTabBarFlags_Reorderable)) + { + tab->Offset = tab_bar->OffsetNextTab; + tab_bar->OffsetNextTab += tab->Width + g.Style.ItemInnerSpacing.x; + } + + // Update selected tab + if (tab_appearing && (tab_bar->Flags & ImGuiTabBarFlags_AutoSelectNewTabs) && tab_bar->NextSelectedTabId == 0) + if (!tab_bar_appearing || tab_bar->SelectedTabId == 0) + tab_bar->NextSelectedTabId = id; // New tabs gets activated + + // Lock visibility + bool tab_contents_visible = (tab_bar->VisibleTabId == id); + if (tab_contents_visible) + tab_bar->VisibleTabWasSubmitted = true; + + // On the very first frame of a tab bar we let first tab contents be visible to minimize appearing glitches + if (!tab_contents_visible && tab_bar->SelectedTabId == 0 && tab_bar_appearing && docked_window == NULL) + if (tab_bar->Tabs.Size == 1 && !(tab_bar->Flags & ImGuiTabBarFlags_AutoSelectNewTabs)) + tab_contents_visible = true; + + if (tab_appearing && !(tab_bar_appearing && !tab_is_new)) + { + PushItemFlag(ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus, true); + ItemAdd(ImRect(), id); + PopItemFlag(); + return tab_contents_visible; + } + + if (tab_bar->SelectedTabId == id) + tab->LastFrameSelected = g.FrameCount; + + // Backup current layout position + const ImVec2 backup_main_cursor_pos = window->DC.CursorPos; + + // Layout + size.x = tab->Width; + window->DC.CursorPos = tab_bar->BarRect.Min + ImVec2((float)(int)tab->Offset - tab_bar->ScrollingAnim, 0.0f); + ImVec2 pos = window->DC.CursorPos; + ImRect bb(pos, pos + size); + + // We don't have CPU clipping primitives to clip the CloseButton (until it becomes a texture), so need to add an extra draw call (temporary in the case of vertical animation) + bool want_clip_rect = (bb.Min.x < tab_bar->BarRect.Min.x) || (bb.Max.x >= tab_bar->BarRect.Max.x); + if (want_clip_rect) + PushClipRect(ImVec2(ImMax(bb.Min.x, tab_bar->BarRect.Min.x), bb.Min.y - 1), ImVec2(tab_bar->BarRect.Max.x, bb.Max.y), true); + + ItemSize(bb, style.FramePadding.y); + if (!ItemAdd(bb, id)) + { + if (want_clip_rect) + PopClipRect(); + window->DC.CursorPos = backup_main_cursor_pos; + return tab_contents_visible; + } + + // Click to Select a tab + ImGuiButtonFlags button_flags = (ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_AllowItemOverlap); + bool hovered, held; + bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags); + hovered |= (g.HoveredId == id); + if (pressed || ((flags & ImGuiTabItemFlags_SetSelected) && !tab_contents_visible)) // SetSelected can only be passed on explicit tab bar, so we don't need to set WantFocusTabId + tab_bar->NextSelectedTabId = id; + + // Allow the close button to overlap unless we are dragging (in which case we don't want any overlapping tabs to be hovered) + if (!held) + SetItemAllowOverlap(); + + // Drag and drop + if (held && !tab_appearing && IsMouseDragging()) + { + // Re-order local or dockable tabs + float drag_distance_from_edge_x = 0.0f; + if (!g.DragDropActive && ((tab_bar->Flags & ImGuiTabBarFlags_Reorderable) || (flags & ImGuiTabItemFlags_DockedWindow))) + { + // While moving a tab it will jump on the other side of the mouse, so we also test for MouseDelta.x + if (g.IO.MouseDelta.x < 0.0f && g.IO.MousePos.x < bb.Min.x) + { + drag_distance_from_edge_x = bb.Min.x - g.IO.MousePos.x; + if (tab_bar->Flags & ImGuiTabBarFlags_Reorderable) + TabBarQueueChangeTabOrder(tab_bar, tab, -1); + } + else if (g.IO.MouseDelta.x > 0.0f && g.IO.MousePos.x > bb.Max.x) + { + drag_distance_from_edge_x = g.IO.MousePos.x - bb.Max.x; + if (tab_bar->Flags & ImGuiTabBarFlags_Reorderable) + TabBarQueueChangeTabOrder(tab_bar, tab, +1); + } + } + + // Extract a Dockable window out of it's tab bar + if (flags & ImGuiTabItemFlags_DockedWindow) + { + // We use a variable threshold to distinguish dragging tabs within a tab bar and extracting them out of the tab bar + //ImVec2 drag_delta = GetMouseDragDelta(); + bool undocking_tab = (g.DragDropActive && g.DragDropPayload.SourceId == id); + if (!undocking_tab && held)// && (drag_delta.x != 0.0f || drag_delta.y != 0.0f)) + { + //if (!g.IO.ConfigDockingWithKeyMod || g.IO.KeyShift) + { + float threshold_base = g.FontSize; + //float threshold_base = g.IO.ConfigDockingWithKeyMod ? g.FontSize * 0.5f : g.FontSize; + float threshold_x = (threshold_base * 2.2f); + float threshold_y = (threshold_base * 1.5f) + ImClamp((ImFabs(g.IO.MouseDragMaxDistanceAbs[0].x) - threshold_base * 2.0f) * 0.20f, 0.0f, threshold_base * 4.0f); + //GetOverlayDrawList(window)->AddRect(ImVec2(bb.Min.x - threshold_x, bb.Min.y - threshold_y), ImVec2(bb.Max.x + threshold_x, bb.Max.y + threshold_y), IM_COL32_WHITE); // [DEBUG] + + float distance_from_edge_y = ImMax(bb.Min.y - g.IO.MousePos.y, g.IO.MousePos.y - bb.Max.y); + if (distance_from_edge_y >= threshold_y) + undocking_tab = true; + else if (drag_distance_from_edge_x > threshold_x) + if ((tab_bar->ReorderRequestDir < 0 && tab_bar->GetTabOrder(tab) == 0) || (tab_bar->ReorderRequestDir > 0 && tab_bar->GetTabOrder(tab) == tab_bar->Tabs.Size - 1)) + undocking_tab = true; + } + } + } + } + +#if 0 + if (hovered && g.HoveredIdTimer > 0.40f && bb.GetWidth() < tab->WidthContents) + { + // Enlarge tab display when hovering + bb.Max.x = bb.Min.x + (float)(int)ImLerp(bb.GetWidth(), tab->WidthContents, ImSaturate((g.HoveredIdTimer - 0.40f) * 6.0f)); + display_draw_list = GetOverlayDrawList(window); + TabItemRenderBackground(display_draw_list, bb, flags, GetColorU32(ImGuiCol_TitleBgActive)); + } +#endif + + // Render tab shape + ImDrawList* display_draw_list = window->DrawList; + const ImU32 tab_col = GetColorU32((held || hovered) ? ImGuiCol_TabHovered : tab_contents_visible ? (tab_bar_focused ? ImGuiCol_TabActive : ImGuiCol_TabUnfocusedActive) : (tab_bar_focused ? ImGuiCol_Tab : ImGuiCol_TabUnfocused)); + TabItemBackground(display_draw_list, bb, flags, tab_col); + RenderNavHighlight(bb, id); + + // Select with right mouse button. This is so the common idiom for context menu automatically highlight the current widget. + const bool hovered_unblocked = IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup); + if (hovered_unblocked && (IsMouseClicked(1) || IsMouseReleased(1))) + tab_bar->NextSelectedTabId = tab_bar->WantFocusTabId = id; + + if (tab_bar->Flags & ImGuiTabBarFlags_NoCloseWithMiddleMouseButton) + flags |= ImGuiTabItemFlags_NoCloseWithMiddleMouseButton; + + // Render tab label, process close button + const ImGuiID close_button_id = p_open ? window->GetID((void*)(intptr_t)(id + 1)) : 0; + bool just_closed = TabItemLabelAndCloseButton(display_draw_list, bb, flags, label, id, close_button_id); + if (just_closed) + { + *p_open = false; + TabBarCloseTab(tab_bar, tab); + } + + // Tooltip (FIXME: Won't work over the close button because ItemOverlap systems messes up with HoveredIdTimer) + if (g.HoveredId == id && !held && g.HoveredIdTimer > 0.50f) + SetTooltip("%.*s", (int)(FindRenderedTextEnd(label) - label), label); + + // Restore main window position so user can draw there + if (want_clip_rect) + PopClipRect(); + window->DC.CursorPos = backup_main_cursor_pos; + + return tab_contents_visible; +} + +// [Public] This is call is 100% optional but it allows to remove some one-frame glitches when a tab has been unexpectedly removed. +// To use it to need to call the function SetTabItemClosed() after BeginTabBar() and before any call to BeginTabItem() +void ImGui::SetTabItemClosed(const char* label) +{ + ImGuiContext& g = *GImGui; + bool is_within_manual_tab_bar = (g.CurrentTabBar.Size > 0) && !(g.CurrentTabBar.back()->Flags & ImGuiTabBarFlags_DockNode); + if (is_within_manual_tab_bar) + { + ImGuiTabBar* tab_bar = g.CurrentTabBar.back(); + IM_ASSERT(tab_bar->WantLayout); // Needs to be called AFTER BeginTabBar() and BEFORE the first call to BeginTabItem() + ImGuiID tab_id = TabBarCalcTabID(tab_bar, label); + TabBarRemoveTab(tab_bar, tab_id); + } +} + +ImVec2 ImGui::TabItemCalcSize(const char* label, bool has_close_button) +{ + ImGuiContext& g = *GImGui; + ImVec2 label_size = CalcTextSize(label, NULL, true); + ImVec2 size = ImVec2(label_size.x + g.Style.FramePadding.x, label_size.y + g.Style.FramePadding.y * 2.0f); + if (has_close_button) + size.x += g.Style.FramePadding.x + (g.Style.ItemInnerSpacing.x + g.FontSize); // We use Y intentionally to fit the close button circle. + else + size.x += g.Style.FramePadding.x + 1.0f; + return ImVec2(ImMin(size.x, TabBarCalcMaxTabWidth()), size.y); +} + +void ImGui::TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImU32 col) +{ + // While rendering tabs, we trim 1 pixel off the top of our bounding box so they can fit within a regular frame height while looking "detached" from it. + ImGuiContext& g = *GImGui; + const float width = bb.GetWidth(); + IM_ASSERT(width > 0.0f); + const float rounding = ImMax(0.0f, ImMin(g.FontSize * 0.35f, width * 0.5f - 1.0f)); // FIXME-DOCK: g.Style.TabRounding? + float y1 = bb.Min.y + 1.0f; + float y2 = bb.Max.y + ((flags & ImGuiTabItemFlags_Preview) ? 0.0f : -1.0f); + draw_list->PathLineTo(ImVec2(bb.Min.x, y2)); + draw_list->PathArcToFast(ImVec2(bb.Min.x + rounding, y1 + rounding), rounding, 6, 9); + draw_list->PathArcToFast(ImVec2(bb.Max.x - rounding, y1 + rounding), rounding, 9, 12); + draw_list->PathLineTo(ImVec2(bb.Max.x, y2)); + draw_list->AddConvexPolyFilled(draw_list->_Path.Data, draw_list->_Path.Size, col); + if (g.Style.TabBorderSize > 0.0f) + draw_list->AddPolyline(draw_list->_Path.Data, draw_list->_Path.Size, GetColorU32(ImGuiCol_Border), false, g.Style.TabBorderSize); + draw_list->PathClear(); +} + +// Render text label (with clipping + alpha gradient) + unsaved marker + Close Button logic +bool ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, const char* label, ImGuiID tab_id, ImGuiID close_button_id) +{ + ImGuiContext& g = *GImGui; + ImGuiStyle& style = g.Style; + ImVec2 label_size = CalcTextSize(label, NULL, true); + if (bb.GetWidth() <= 1.0f) + return false; + + // Render text label (with clipping + alpha gradient) + unsaved marker + const char* TAB_UNSAVED_MARKER = "*"; + ImRect text_pixel_clip_bb(bb.Min.x + style.FramePadding.x, bb.Min.y + style.FramePadding.y, bb.Max.x - style.FramePadding.x, bb.Max.y); + if (flags & ImGuiTabItemFlags_UnsavedDocument) + { + text_pixel_clip_bb.Max.x -= CalcTextSize(TAB_UNSAVED_MARKER, NULL, false).x; + ImVec2 unsaved_marker_pos(ImMin(bb.Min.x + style.FramePadding.x + label_size.x + 2, text_pixel_clip_bb.Max.x), bb.Min.y + style.FramePadding.y + (float)(int)(-g.FontSize * 0.25f)); + RenderTextClippedEx(draw_list, unsaved_marker_pos, bb.Max - style.FramePadding, TAB_UNSAVED_MARKER, NULL, NULL); + } + ImRect text_ellipsis_clip_bb = text_pixel_clip_bb; + + // Close Button + // We are relying on a subtle and confusing distinction between 'hovered' and 'g.HoveredId' which happens because we are using ImGuiButtonFlags_AllowOverlapMode + SetItemAllowOverlap() + // 'hovered' will be true when hovering the Tab but NOT when hovering the close button + // 'g.HoveredId==id' will be true when hovering the Tab including when hovering the close button + // 'g.ActiveId==close_button_id' will be true when we are holding on the close button, in which case both hovered booleans are false + bool close_button_pressed = false; + bool close_button_visible = false; + if (close_button_id != 0) + if (g.HoveredId == tab_id || g.HoveredId == close_button_id || g.ActiveId == close_button_id) + close_button_visible = true; + if (close_button_visible) + { + ImGuiItemHoveredDataBackup last_item_backup; + const float close_button_sz = g.FontSize * 0.5f; + if (CloseButton(close_button_id, ImVec2(bb.Max.x - style.FramePadding.x - close_button_sz, bb.Min.y + style.FramePadding.y + close_button_sz), close_button_sz)) + close_button_pressed = true; + last_item_backup.Restore(); + + // Close with middle mouse button + if (!(flags & ImGuiTabItemFlags_NoCloseWithMiddleMouseButton) && IsMouseClicked(2)) + close_button_pressed = true; + + text_pixel_clip_bb.Max.x -= close_button_sz * 2.0f; + } + + // Label with ellipsis + // FIXME: This could be extracted into a helper but or use of text_pixel_clip_bb and !close_button_visible makes it tricky to abstract at the moment + const char* label_display_end = FindRenderedTextEnd(label); + if (label_size.x > text_ellipsis_clip_bb.GetWidth()) + { + const int ellipsis_dot_count = 3; + const float ellipsis_width = (1.0f + 1.0f) * ellipsis_dot_count - 1.0f; + const char* label_end = NULL; + float label_size_clipped_x = g.Font->CalcTextSizeA(g.FontSize, text_ellipsis_clip_bb.GetWidth() - ellipsis_width + 1.0f, 0.0f, label, label_display_end, &label_end).x; + if (label_end == label && label_end < label_display_end) // Always display at least 1 character if there's no room for character + ellipsis + { + label_end = label + ImTextCountUtf8BytesFromChar(label, label_display_end); + label_size_clipped_x = g.Font->CalcTextSizeA(g.FontSize, FLT_MAX, 0.0f, label, label_end).x; + } + while (label_end > label && ImCharIsBlankA(label_end[-1])) // Trim trailing space + { + label_end--; + label_size_clipped_x -= g.Font->CalcTextSizeA(g.FontSize, FLT_MAX, 0.0f, label_end, label_end + 1).x; // Ascii blanks are always 1 byte + } + RenderTextClippedEx(draw_list, text_pixel_clip_bb.Min, text_pixel_clip_bb.Max, label, label_end, &label_size, ImVec2(0.0f, 0.0f)); + + const float ellipsis_x = text_pixel_clip_bb.Min.x + label_size_clipped_x + 1.0f; + if (!close_button_visible && ellipsis_x + ellipsis_width <= bb.Max.x) + RenderPixelEllipsis(draw_list, g.Font, ImVec2(ellipsis_x, text_pixel_clip_bb.Min.y), ellipsis_dot_count, GetColorU32(ImGuiCol_Text)); + } + else + { + RenderTextClippedEx(draw_list, text_pixel_clip_bb.Min, text_pixel_clip_bb.Max, label, label_display_end, &label_size, ImVec2(0.0f, 0.0f)); + } + + return close_button_pressed; +} From 2ec135c9f7514ed5dcd67325b665bf1a7a34bd4a Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 6 Sep 2018 20:50:01 +0200 Subject: [PATCH 232/828] Docking: Added ImVec2[] non const operator. Added ImStrSkipBlank. Reseting some values earlier in Begin. Added IMGUI_DEBUG_LOG() helper. Added docking source code section. --- imgui.cpp | 21 ++++++++++++++++----- imgui.h | 3 ++- imgui_internal.h | 34 +++++++++++++++++++++------------- 3 files changed, 39 insertions(+), 19 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 4721b232d329..165769f262b7 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1257,6 +1257,13 @@ void ImStrTrimBlanks(char* buf) buf[p - p_start] = 0; // Zero terminate } +const char* ImStrSkipBlank(const char* str) +{ + while (str[0] == ' ' || str[0] == '\t') + str++; + return str; +} + // A) MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size). // Ideally we would test for only one of those limits at runtime depending on the behavior the vsnprintf(), but trying to deduct it at compile time sounds like a pandora can of worm. // B) When buf==NULL vsnprintf() will return the output size. @@ -3234,6 +3241,7 @@ void ImGui::NewFrame() { ImGuiWindow* window = g.Windows[i]; window->WasActive = window->Active; + window->BeginCount = 0; window->Active = false; window->WriteAccessed = false; } @@ -3266,7 +3274,7 @@ void ImGui::Initialize(ImGuiContext* context) ini_handler.ReadOpenFn = SettingsHandlerWindow_ReadOpen; ini_handler.ReadLineFn = SettingsHandlerWindow_ReadLine; ini_handler.WriteAllFn = SettingsHandlerWindow_WriteAll; - g.SettingsHandlers.push_front(ini_handler); + g.SettingsHandlers.push_back(ini_handler); g.Initialized = true; } @@ -4527,9 +4535,15 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) const int current_frame = g.FrameCount; const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame); if (first_begin_of_the_frame) + { window->Flags = (ImGuiWindowFlags)flags; + window->BeginOrderWithinParent = 0; + window->BeginOrderWithinContext = g.WindowsActiveCount++; + } else + { flags = window->Flags; + } // Parent window is latched only on the first call to Begin() of the frame, so further append-calls can be done from a different window stack ImGuiWindow* parent_window_in_stack = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back(); @@ -4616,9 +4630,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) UpdateWindowParentAndRootLinks(window, flags, parent_window); window->Active = true; - window->BeginOrderWithinParent = 0; - window->BeginOrderWithinContext = g.WindowsActiveCount++; - window->BeginCount = 0; window->ClipRect = ImVec4(-FLT_MAX,-FLT_MAX,+FLT_MAX,+FLT_MAX); window->LastFrameActive = current_frame; window->IDStack.resize(1); @@ -8788,6 +8799,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::End(); return; } + static bool show_draw_cmd_clip_rects = true; static bool show_window_begin_order = false; ImGuiIO& io = ImGui::GetIO(); @@ -8798,7 +8810,6 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::Text("%d allocations", io.MetricsActiveAllocations); ImGui::Checkbox("Show clipping rectangles when hovering draw commands", &show_draw_cmd_clip_rects); ImGui::Checkbox("Ctrl shows window begin order", &show_window_begin_order); - ImGui::Separator(); struct Funcs diff --git a/imgui.h b/imgui.h index 62941edf12d6..65b17284ef6a 100644 --- a/imgui.h +++ b/imgui.h @@ -139,7 +139,8 @@ struct ImVec2 float x, y; ImVec2() { x = y = 0.0f; } ImVec2(float _x, float _y) { x = _x; y = _y; } - float operator[] (size_t i) const { IM_ASSERT(i <= 1); return (&x)[i]; } // We very rarely use this [] operator, the assert overhead is fine. + float operator[] (size_t idx) const { IM_ASSERT(idx <= 1); return (&x)[idx]; } // We very rarely use this [] operator, the assert overhead is fine. + float& operator[] (size_t idx) { IM_ASSERT(idx <= 1); return (&x)[idx]; } // We very rarely use this [] operator, the assert overhead is fine. #ifdef IM_VEC2_CLASS_EXTRA IM_VEC2_CLASS_EXTRA // Define additional constructors and implicit cast operators in imconfig.h to convert back and forth between your math types and ImVec2. #endif diff --git a/imgui_internal.h b/imgui_internal.h index fe84c35dcd9a..fa08754ad92e 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -100,6 +100,8 @@ extern IMGUI_API ImGuiContext* GImGui; // Current implicit ImGui context pointe #else #define IM_NEWLINE "\n" #endif + +#define IMGUI_DEBUG_LOG(FMT,...) printf("[%05d] " FMT, GImGui->FrameCount, __VA_ARGS__) #define IM_STATIC_ASSERT(_COND) typedef char static_assertion_##__line__[(_COND)?1:-1] #define IM_F32_TO_INT8_UNBOUND(_VAL) ((int)((_VAL) * 255.0f + ((_VAL)>=0 ? 0.5f : -0.5f))) // Unsaturated, for display purpose #define IM_F32_TO_INT8_SAT(_VAL) ((int)(ImSaturate(_VAL) * 255.0f + 0.5f)) // Saturated, always output 0..255 @@ -146,6 +148,7 @@ IMGUI_API int ImStrlenW(const ImWchar* str); IMGUI_API const ImWchar*ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin); // Find beginning-of-line IMGUI_API const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end); IMGUI_API void ImStrTrimBlanks(char* str); +IMGUI_API const char* ImStrSkipBlank(const char* str); IMGUI_API int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...) IM_FMTARGS(3); IMGUI_API int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args) IM_FMTLIST(3); IMGUI_API const char* ImParseFormatFindStart(const char* format); @@ -291,6 +294,19 @@ enum ImGuiSeparatorFlags_ ImGuiSeparatorFlags_Vertical = 1 << 1 }; +// Transient per-window ItemFlags, reset at the beginning of the frame. For child windows: inherited from parent on first Begin(). +// This is going to be exposed in imgui.h when stabilized enough. +enum ImGuiItemFlags_ +{ + ImGuiItemFlags_AllowKeyboardFocus = 1 << 0, // true + ImGuiItemFlags_ButtonRepeat = 1 << 1, // false // Button() will return true multiple times based on io.KeyRepeatDelay and io.KeyRepeatRate settings. + ImGuiItemFlags_Disabled = 1 << 2, // false // [BETA] Disable interactions but doesn't affect visuals yet. See github.com/ocornut/imgui/issues/211 + ImGuiItemFlags_NoNav = 1 << 3, // false + ImGuiItemFlags_NoNavDefaultFocus = 1 << 4, // false + ImGuiItemFlags_SelectableDontClosePopup = 1 << 5, // false // MenuItem/Selectable() automatically closes current Popup window + ImGuiItemFlags_Default_ = ImGuiItemFlags_AllowKeyboardFocus +}; + // Storage for LastItem data enum ImGuiItemStatusFlags_ { @@ -935,18 +951,9 @@ struct ImGuiContext } }; -// Transient per-window flags, reset at the beginning of the frame. For child window, inherited from parent on first Begin(). -// This is going to be exposed in imgui.h when stabilized enough. -enum ImGuiItemFlags_ -{ - ImGuiItemFlags_AllowKeyboardFocus = 1 << 0, // true - ImGuiItemFlags_ButtonRepeat = 1 << 1, // false // Button() will return true multiple times based on io.KeyRepeatDelay and io.KeyRepeatRate settings. - ImGuiItemFlags_Disabled = 1 << 2, // false // [BETA] Disable interactions but doesn't affect visuals yet. See github.com/ocornut/imgui/issues/211 - ImGuiItemFlags_NoNav = 1 << 3, // false - ImGuiItemFlags_NoNavDefaultFocus = 1 << 4, // false - ImGuiItemFlags_SelectableDontClosePopup = 1 << 5, // false // MenuItem/Selectable() automatically closes current Popup window - ImGuiItemFlags_Default_ = ImGuiItemFlags_AllowKeyboardFocus -}; +//----------------------------------------------------------------------------- +// ImGuiWindow +//----------------------------------------------------------------------------- // Transient per-window data, reset at the beginning of the frame. This used to be called ImGuiDrawContext, hence the DC variable name in ImGuiWindow. // FIXME: That's theory, in practice the delimitation between ImGuiWindow and ImGuiWindowTempData is quite tenuous and could be reconsidered. @@ -1215,7 +1222,7 @@ namespace ImGui inline ImGuiWindow* GetCurrentWindowRead() { ImGuiContext& g = *GImGui; return g.CurrentWindow; } inline ImGuiWindow* GetCurrentWindow() { ImGuiContext& g = *GImGui; g.CurrentWindow->WriteAccessed = true; return g.CurrentWindow; } IMGUI_API ImGuiWindow* FindWindowByName(const char* name); - IMGUI_API void FocusWindow(ImGuiWindow* window); + IMGUI_API void FocusWindow(ImGuiWindow* window); // FIXME: Rename to SetWindowFocus() IMGUI_API void FocusFrontMostActiveWindowIgnoringOne(ImGuiWindow* ignore_window); IMGUI_API void BringWindowToFront(ImGuiWindow* window); IMGUI_API void BringWindowToBack(ImGuiWindow* window); @@ -1226,6 +1233,7 @@ namespace ImGui IMGUI_API void SetWindowScrollX(ImGuiWindow* window, float new_scroll_x); IMGUI_API void SetWindowScrollY(ImGuiWindow* window, float new_scroll_y); IMGUI_API ImRect GetWindowAllowedExtentRect(ImGuiWindow* window); + IMGUI_API void SetCurrentFont(ImFont* font); inline ImFont* GetDefaultFont() { ImGuiContext& g = *GImGui; return g.IO.FontDefault ? g.IO.FontDefault : g.IO.Fonts->Fonts[0]; } From 5adcb9ce6dc127bd2cec622a72fbb5a7be419e4c Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 6 Sep 2018 20:51:27 +0200 Subject: [PATCH 233/828] Docking: Added empty skeleton/sections to facilitate diff/patch of incoming code (otherwise git gives us a messier patch). --- imgui.cpp | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/imgui.cpp b/imgui.cpp index 165769f262b7..93f3531e60bc 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -67,6 +67,7 @@ CODE // [SECTION] KEYBOARD/GAMEPAD NAVIGATION // [SECTION] COLUMNS // [SECTION] DRAG AND DROP +// [SECTION] DOCKING // [SECTION] LOGGING/CAPTURING // [SECTION] SETTINGS // [SECTION] PLATFORM DEPENDENT HELPERS @@ -8280,6 +8281,90 @@ void ImGui::EndDragDropTarget() g.DragDropWithinSourceOrTarget = false; } +//----------------------------------------------------------------------------- +// [SECTION] DOCKING +//----------------------------------------------------------------------------- +// Docking: Internal Types +// Docking: Forward Declarations +// Docking: ImGuiDockContext +// Docking: ImGuiDockContext Docking/Undocking functions +// Docking: ImGuiDockNode +// Docking: ImGuiDockNode Tree manipulation functions +// Docking: Public Functions (Dockspace, SetWindowDock) +// Docking: Begin/End Functions (called from Begin/End) +// Docking: Settings +//----------------------------------------------------------------------------- +// TODO: +// A~ document root node resizing behavior incorrect +// A~ document root node retrieval of ID ? +// B- resize sibling locking behavior may be less desirable if we merged same-axis sibling in a same node level? +// A- single visible node part of a hidden split hierarchy (OnlyNodeWithWindows) should show a normal tab bar +// B~ SetNextWindowDock() calls (with conditional) -> defer everything to DockContextUpdate (repro: Documents->[X]Windows->Dock 1 elsewhere->Click Redock All +// B- fix/disable auto-resize grip on split host nodes +// B~ tidy up tab list popup buttons (see old ImGuiTabBarFlags_NoTabListPopupButton code) +// B- DockSpace() border issues +// B- implicit per-viewport dockspace to dock to +// B- resizing a dock tree small currently has glitches (overlapping collapse and close button, etc.) +// B- tab bar: make selected tab always shows its full title? +// B- tab bar: the order/focus restoring code could be part of TabBar and not DockNode? +// B- nav: CTRL+TAB highlighting tabs shows the mismatch between focus-stack and tab-order (not visible in VS because it doesn't highlight the tabs) +// B- nav: design interactions so nav controls can dock/undock +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Docking: Internal Types +//----------------------------------------------------------------------------- + + + +//----------------------------------------------------------------------------- +// Docking: Forward Declarations +//----------------------------------------------------------------------------- + + + +//----------------------------------------------------------------------------- +// Docking: ImGuiDockContext +//----------------------------------------------------------------------------- + + + +//----------------------------------------------------------------------------- +// Docking: ImGuiDockContext Docking/Undocking functions +//----------------------------------------------------------------------------- + + + +//----------------------------------------------------------------------------- +// Docking: ImGuiDockNode +//----------------------------------------------------------------------------- + + + +//----------------------------------------------------------------------------- +// Docking: ImGuiDockNode Tree manipulation functions +//----------------------------------------------------------------------------- + + + +//----------------------------------------------------------------------------- +// Docking: Public Functions (SetWindowDock, Dockspace) +//----------------------------------------------------------------------------- + + + +//----------------------------------------------------------------------------- +// Docking: Begin/End Functions (called from Begin/End) +//----------------------------------------------------------------------------- + + + +//----------------------------------------------------------------------------- +// Docking: Settings +//----------------------------------------------------------------------------- + + + //----------------------------------------------------------------------------- // [SECTION] LOGGING/CAPTURING //----------------------------------------------------------------------------- From bd5b38e232dc86f08e704c2fc48c2272ff8506eb Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 6 Sep 2018 21:19:22 +0200 Subject: [PATCH 234/828] Docking: Added Docking system. Enable with io.ConfigFlags |= ImGuiConfigFlags_DockingEnable. (Part 1) (#351) --- docs/CHANGELOG.txt | 10 ++++ imgui.h | 24 +++++++- imgui_demo.cpp | 133 +++++++++++++++++++++++++++++++++++++++++++-- imgui_draw.cpp | 34 ++++++++++++ imgui_internal.h | 109 ++++++++++++++++++++++++++++++++++--- imgui_widgets.cpp | 58 +++++++++++++++++--- 6 files changed, 343 insertions(+), 25 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index dddf478955fd..e96403a48bb8 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -33,9 +33,19 @@ HOW TO UPDATE? DOCKING BRANCH (In Progress) ----------------------------------------------------------------------- +- Added ImGuiConfigFlags_DockingEnable flag to enable Docking. [BETA] + Set with `io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;`. - Added BeginTabBar(), EndTabBar(), BeginTabItem(), EndTabItem(), SetTabItemClosed() API. (#261, #351) +- Added DockSpace() API. (#351) +- Added SetNextWindowDock() API. (#351) +- Added IsWindowDocked() API. (#351) +- Added ImGuiWindowFlags_NoDocking window flag to disable the possibility for a window to be docked. + Popup, Menu and Child windows always have the ImGuiWindowFlags_NoDocking flag set. (#351) - Added ImGuiWindowFlags_UnsavedDocument window flag to append '*' to title without altering the ID, as a convenience to avoid using the ### operator. +- Added io.ConfigDockingWithKeyMod option to configure docking mode. +- Style: Added ImGuiCol_DockingPreview, ImGuiCol_DockingBg colors. (#351) +- Style: Added ImGuiCol_Tab, ImGuiCol_TabHovered, ImGuiCol_TabActive, ImGuiCol_TabUnfocused, ImGuiCol_TabUnfocusedActive colors. (#261, #351) - Demo: Added Layout->Tabs demo code. (#261, #351) - Demo: Added "Documents" example app showcasing possible use for tabs. (#261, #351) diff --git a/imgui.h b/imgui.h index 65b17284ef6a..a1caa8a097f9 100644 --- a/imgui.h +++ b/imgui.h @@ -26,6 +26,7 @@ #define IMGUI_VERSION "1.66 WIP" #define IMGUI_VERSION_NUM 16600 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert)) +#define IMGUI_HAS_DOCK 1 // Docking WIP branch #define IMGUI_HAS_TABS 1 // Docking WIP branch // Define attributes of all API symbols declarations (e.g. for DLL under Windows) @@ -209,7 +210,9 @@ namespace ImGui IMGUI_API bool IsWindowCollapsed(); IMGUI_API bool IsWindowFocused(ImGuiFocusedFlags flags=0); // is current window focused? or its root/child, depending on flags. see flags for options. IMGUI_API bool IsWindowHovered(ImGuiHoveredFlags flags=0); // is current window hovered (and typically: not blocked by a popup/modal)? see flags for options. NB: If you are trying to check whether your mouse should be dispatched to imgui or to your app, you should use the 'io.WantCaptureMouse' boolean for that! Please read the FAQ! - IMGUI_API ImDrawList* GetWindowDrawList(); // get draw list associated to the window, to append your own drawing primitives + IMGUI_API bool IsWindowDocked(); + IMGUI_API ImDrawList* GetWindowDrawList(); // get draw list associated to the current window, to append your own drawing primitives + IMGUI_API ImVec2 GetWindowPos(); // get current window position in screen space (useful if you want to do your own drawing via the DrawList API) IMGUI_API ImVec2 GetWindowSize(); // get current window size IMGUI_API float GetWindowWidth(); // get current window width (shortcut for GetWindowSize().x) @@ -228,6 +231,7 @@ namespace ImGui IMGUI_API void SetNextWindowCollapsed(bool collapsed, ImGuiCond cond = 0); // set next window collapsed state. call before Begin() IMGUI_API void SetNextWindowFocus(); // set next window to be focused / front-most. call before Begin() IMGUI_API void SetNextWindowBgAlpha(float alpha); // set next window background color alpha. helper to easily modify ImGuiCol_WindowBg/ChildBg/PopupBg. + IMGUI_API void SetNextWindowDock(ImGuiID dock_id, ImGuiCond cond = 0); // set next window dock id (FIXME-DOCK) IMGUI_API void SetWindowPos(const ImVec2& pos, ImGuiCond cond = 0); // (not recommended) set current window position - call within Begin()/End(). prefer using SetNextWindowPos(), as this may incur tearing and side-effects. IMGUI_API void SetWindowSize(const ImVec2& size, ImGuiCond cond = 0); // (not recommended) set current window size - call within Begin()/End(). set to ImVec2(0,0) to force an auto-fit. prefer using SetNextWindowSize(), as this may incur tearing and minor side-effects. IMGUI_API void SetWindowCollapsed(bool collapsed, ImGuiCond cond = 0); // (not recommended) set current window collapsed state. prefer using SetNextWindowCollapsed(). @@ -499,12 +503,18 @@ namespace ImGui // Tabs // [BETA API] API may evolve. + // Note: Tabs are automatically created by the docking system. Use this to create tab bars/tabs yourself without docking being involved. IMGUI_API bool BeginTabBar(const char* str_id, ImGuiTabBarFlags flags = 0); // create and append into a TabBar IMGUI_API void EndTabBar(); IMGUI_API bool BeginTabItem(const char* label, bool* p_open = NULL, ImGuiTabItemFlags = 0);// create a Tab. Returns true if the Tab is selected. IMGUI_API void EndTabItem(); // only call EndTabItem() if BeginTabItem() returns true! IMGUI_API void SetTabItemClosed(const char* tab_or_docked_window_label); // notify TabBar or Docking system of a closed tab/window ahead (useful to reduce visual flicker on reorderable tab bars). For tab-bar: call after BeginTabBar() and before Tab submissions. Otherwise call with a window name. + // Docking + // [BETA API] Enable with io.ConfigFlags |= ImGuiConfigFlags_DockingEnable. + // Note: you DO NOT need to call DockSpace() to use most Docking facilities! You can hold SHIFT anywhere while moving windows. Use DockSpace() if you need to create an explicit docking space _within_ an existing window. See Docking demo for details) + IMGUI_API void DockSpace(const char* str_id, const ImVec2& size = ImVec2(0, 0)); + // Logging/Capture: all text output from interface is captured to tty/file/clipboard. By default, tree nodes are automatically opened during logging. IMGUI_API void LogToTTY(int max_depth = -1); // start logging to tty IMGUI_API void LogToFile(int max_depth = -1, const char* filename = NULL); // start logging to file @@ -635,7 +645,8 @@ enum ImGuiWindowFlags_ ImGuiWindowFlags_NoNavInputs = 1 << 18, // No gamepad/keyboard navigation within the window ImGuiWindowFlags_NoNavFocus = 1 << 19, // No focusing toward this window with gamepad/keyboard navigation (e.g. skipped by CTRL+TAB) ImGuiWindowFlags_NoNav = ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus, - ImGuiWindowFlags_UnsavedDocument = 1 << 20, // Append '*' to title without altering the ID, as a convenience to avoid using the ### operator. When used in a tab/docking context, tab is selected on closure and closure is deferred by one frame to allow code to cancel the closure (with a confirmation popup, etc.) without flicker. + ImGuiWindowFlags_UnsavedDocument = 1 << 20, // Append '*' to title without affecting the ID, as a convenience to avoid using the ### operator. When used in a tab/docking context, tab is selected on closure and closure is deferred by one frame to allow code to cancel the closure (with a confirmation popup, etc.) without flicker. + ImGuiWindowFlags_NoDocking = 1 << 21, // Disable docking of this window // [Internal] ImGuiWindowFlags_NavFlattened = 1 << 23, // [BETA] Allow gamepad/keyboard navigation to cross over parent border to this child (only use on child that have no scrolling!) @@ -643,7 +654,8 @@ enum ImGuiWindowFlags_ ImGuiWindowFlags_Tooltip = 1 << 25, // Don't use! For internal use by BeginTooltip() ImGuiWindowFlags_Popup = 1 << 26, // Don't use! For internal use by BeginPopup() ImGuiWindowFlags_Modal = 1 << 27, // Don't use! For internal use by BeginPopupModal() - ImGuiWindowFlags_ChildMenu = 1 << 28 // Don't use! For internal use by BeginMenu() + ImGuiWindowFlags_ChildMenu = 1 << 28, // Don't use! For internal use by BeginMenu() + ImGuiWindowFlags_DockNodeHost = 1 << 29 // Don't use! For internal use by Begin()/NewFrame() // [Obsolete] //ImGuiWindowFlags_ShowBorders = 1 << 7, // --> Set style.FrameBorderSize=1.0f / style.WindowBorderSize=1.0f to enable borders around windows and items @@ -897,6 +909,9 @@ enum ImGuiConfigFlags_ ImGuiConfigFlags_NoMouse = 1 << 4, // Instruct imgui to clear mouse position/buttons in NewFrame(). This allows ignoring the mouse information set by the back-end. ImGuiConfigFlags_NoMouseCursorChange = 1 << 5, // Instruct back-end to not alter mouse cursor shape and visibility. Use if the back-end cursor changes are interfering with yours and you don't want to use SetMouseCursor() to change mouse cursor. You may want to honor requests from imgui by reading GetMouseCursor() yourself instead. + // [BETA] Docking + ImGuiConfigFlags_DockingEnable = 1 << 6, // Docking enable flags. Use SHIFT to dock window into another (or without SHIFT if io.ConfigDockingWithKeyMod = false). + // User storage (to allow your back-end/engine to communicate to code that may be shared between multiple projects. Those flags are not used by core ImGui) ImGuiConfigFlags_IsSRGB = 1 << 20, // Application is SRGB-aware. ImGuiConfigFlags_IsTouchScreen = 1 << 21 // Application is using a touch screen instead of a mouse. @@ -951,6 +966,8 @@ enum ImGuiCol_ ImGuiCol_TabActive, ImGuiCol_TabUnfocused, ImGuiCol_TabUnfocusedActive, + ImGuiCol_DockingPreview, + ImGuiCol_DockingBg, // Empty node ImGuiCol_PlotLines, ImGuiCol_PlotLinesHovered, ImGuiCol_PlotHistogram, @@ -1150,6 +1167,7 @@ struct ImGuiIO // Miscellaneous configuration options bool MouseDrawCursor; // = false // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). Cannot be easily renamed to 'io.ConfigXXX' because this is frequently used by back-end implementations. + bool ConfigDockingWithKeyMod; // = true // Enable docking with holding Shift key (reduce visual noise, allows dropping in wider space) bool ConfigMacOSXBehaviors; // = defined(__APPLE__) // OS X style: Text editing cursor movement using Alt instead of Ctrl, Shortcuts using Cmd/Super instead of Ctrl, Line/Text Start and End using Cmd+Arrows instead of Home/End, Double click selects by word instead of selecting whole text, Multi-selection in lists uses Cmd/Super instead of Ctrl (was called io.OptMacOSXBehaviors prior to 1.63) bool ConfigInputTextCursorBlink; // = true // Set to false to disable blinking cursor, for users who consider it distracting. (was called: io.OptCursorBlink prior to 1.63) bool ConfigResizeWindowsFromEdges; // = false // [BETA] Enable resizing of windows from their edges and from the lower-left corner. This requires (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) because it needs mouse cursor feedback. (This used to be the ImGuiWindowFlags_ResizeFromAnySide flag) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index b809b8e45034..6b47bf5b9535 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -36,6 +36,7 @@ Index of this file: // [SECTION] Example App: Simple Overlay / ShowExampleAppSimpleOverlay() // [SECTION] Example App: Manipulating Window Titles / ShowExampleAppWindowTitles() // [SECTION] Example App: Custom Rendering using ImDrawList API / ShowExampleAppCustomRendering() +// [SECTION] Example App: Docking, DockSpace / ShowExampleAppDockSpace() // [SECTION] Example App: Documents Handling / ShowExampleAppDocuments() */ @@ -99,6 +100,7 @@ Index of this file: #if !defined(IMGUI_DISABLE_DEMO_WINDOWS) // Forward Declarations +static void ShowExampleAppDockSpace(bool* p_open); static void ShowExampleAppDocuments(bool* p_open); static void ShowExampleAppMainMenuBar(); static void ShowExampleAppConsole(bool* p_open); @@ -159,6 +161,7 @@ void ImGui::ShowUserGuide() void ImGui::ShowDemoWindow(bool* p_open) { // Examples Apps (accessible from the "Examples" menu) + static bool show_app_dockspace = false; static bool show_app_documents = false; static bool show_app_main_menu_bar = false; static bool show_app_console = false; @@ -172,7 +175,8 @@ void ImGui::ShowDemoWindow(bool* p_open) static bool show_app_window_titles = false; static bool show_app_custom_rendering = false; - if (show_app_documents) ShowExampleAppDocuments(&show_app_documents); + if (show_app_dockspace) ShowExampleAppDockSpace(&show_app_dockspace); // Process the Docking app first, as explicit DockSpace() needs to be submitted early (read comments near the DockSpace function) + if (show_app_documents) ShowExampleAppDocuments(&show_app_documents); // Process the Document app next, as it may also use a DockSpace() if (show_app_main_menu_bar) ShowExampleAppMainMenuBar(); if (show_app_console) ShowExampleAppConsole(&show_app_console); if (show_app_log) ShowExampleAppLog(&show_app_log); @@ -211,6 +215,7 @@ void ImGui::ShowDemoWindow(bool* p_open) static bool no_collapse = false; static bool no_close = false; static bool no_nav = false; + static bool no_docking = false; ImGuiWindowFlags window_flags = 0; if (no_titlebar) window_flags |= ImGuiWindowFlags_NoTitleBar; @@ -220,6 +225,7 @@ void ImGui::ShowDemoWindow(bool* p_open) if (no_resize) window_flags |= ImGuiWindowFlags_NoResize; if (no_collapse) window_flags |= ImGuiWindowFlags_NoCollapse; if (no_nav) window_flags |= ImGuiWindowFlags_NoNav; + if (no_docking) window_flags |= ImGuiWindowFlags_NoDocking; if (no_close) p_open = NULL; // Don't pass our bool* to Begin // We specify a default position/size in case there's no data in the .ini file. Typically this isn't required! We only do it to make the Demo applications a little more welcoming. @@ -260,6 +266,7 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::MenuItem("Simple overlay", NULL, &show_app_simple_overlay); ImGui::MenuItem("Manipulating window titles", NULL, &show_app_window_titles); ImGui::MenuItem("Custom rendering", NULL, &show_app_custom_rendering); + ImGui::MenuItem("Docking", NULL, &show_app_dockspace); ImGui::MenuItem("Documents", NULL, &show_app_documents); ImGui::EndMenu(); } @@ -312,6 +319,12 @@ void ImGui::ShowDemoWindow(bool* p_open) } ImGui::CheckboxFlags("io.ConfigFlags: NoMouseCursorChange", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_NoMouseCursorChange); ImGui::SameLine(); ShowHelpMarker("Instruct back-end to not alter mouse cursor shape and visibility."); + + ImGui::CheckboxFlags("io.ConfigFlags: DockingEnable", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_DockingEnable); + ImGui::SameLine(); ShowHelpMarker("Use SHIFT to dock window into another (or without SHIFT if io.ConfigDockingWithKeyMod == false)"); + + ImGui::Checkbox("io.ConfigDockingWithKeyMod", &io.ConfigDockingWithKeyMod); + ImGui::SameLine(); ShowHelpMarker("Enable docking when holding Shift only (allows to drop in wider space, reduce visual noise)"); ImGui::Checkbox("io.ConfigInputTextCursorBlink", &io.ConfigInputTextCursorBlink); ImGui::SameLine(); ShowHelpMarker("Set to false to disable blinking cursor, for users who consider it distracting"); ImGui::Checkbox("io.ConfigResizeWindowsFromEdges [beta]", &io.ConfigResizeWindowsFromEdges); @@ -364,7 +377,8 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::Checkbox("No resize", &no_resize); ImGui::SameLine(300); ImGui::Checkbox("No collapse", &no_collapse); ImGui::Checkbox("No close", &no_close); ImGui::SameLine(150); - ImGui::Checkbox("No nav", &no_nav); + ImGui::Checkbox("No nav", &no_nav); ImGui::SameLine(300); + ImGui::Checkbox("No docking", &no_docking); } if (ImGui::CollapsingHeader("Widgets")) @@ -1463,6 +1477,7 @@ void ImGui::ShowDemoWindow(bool* p_open) // Calling IsItemHovered() after begin returns the hovered status of the title bar. // This is useful in particular if you want to create a context menu (with BeginPopupContextItem) associated to the title bar of a window. + // This will also work when docked into a Tab (the Tab replace the Title Bar and guarantee the same properties). static bool test_window = false; ImGui::Checkbox("Hovered/Active tests after Begin() for title bar testing", &test_window); if (test_window) @@ -1473,6 +1488,7 @@ void ImGui::ShowDemoWindow(bool* p_open) if (ImGui::MenuItem("Close")) { test_window = false; } ImGui::EndPopup(); } + //if (IsItemHovered() || IsItemActive()) { printf("[%05d] Hovered %d Active %d\n", GetFrameCount(), IsItemHovered(), IsItemActive()); } // [DEBUG] ImGui::Text( "IsItemHovered() after begin = %d (== is title bar hovered)\n" "IsItemActive() after begin = %d (== is window being clicked/moved)\n", @@ -3459,6 +3475,8 @@ static void ShowExampleAppConstrainedResize(bool* p_open) "Custom: Always Square", "Custom: Fixed Steps (100)", }; + if (ImGui::IsWindowDocked()) + ImGui::Text("Warning: Sizing Constraints won't work if the window is docked!"); if (ImGui::Button("200x200")) { ImGui::SetWindowSize(ImVec2(200, 200)); } ImGui::SameLine(); if (ImGui::Button("500x500")) { ImGui::SetWindowSize(ImVec2(500, 500)); } ImGui::SameLine(); if (ImGui::Button("800x200")) { ImGui::SetWindowSize(ImVec2(800, 200)); } @@ -3487,7 +3505,7 @@ static void ShowExampleAppSimpleOverlay(bool* p_open) if (corner != -1) ImGui::SetNextWindowPos(window_pos, ImGuiCond_Always, window_pos_pivot); ImGui::SetNextWindowBgAlpha(0.3f); // Transparent background - if (ImGui::Begin("Example: Simple Overlay", p_open, (corner != -1 ? ImGuiWindowFlags_NoMove : 0) | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav)) + if (ImGui::Begin("Example: Simple Overlay", p_open, (corner != -1 ? ImGuiWindowFlags_NoMove : 0) | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav)) { ImGui::Text("Simple overlay\n" "in the corner of the screen.\n" "(right-click to change position)"); ImGui::Separator(); @@ -3651,6 +3669,72 @@ static void ShowExampleAppCustomRendering(bool* p_open) ImGui::End(); } +//----------------------------------------------------------------------------- +// [SECTION] Example App: Docking, DockSpace / ShowExampleAppDockSpace() +//----------------------------------------------------------------------------- + +// Demonstrate using DockSpace() to create an explicit docking spacing within an existing window. +// Note that you already dock windows into each others _without_ a DockSpace() by just holding SHIFT when moving a window. +// DockSpace() is only useful to construct to a central location for your application. +void ShowExampleAppDockSpace(bool* p_open) +{ + static bool opt_fullscreen_persistant = true; + bool opt_fullscreen = opt_fullscreen_persistant; + + // We are using the ImGuiWindowFlags_NoDocking flag to make the parent window not dockable into. + // Because 1) it would be confusing to have two docking targets within each others. + // and 2) we want our main DockSpace to always be visible (never hidden within a tab bar): if the DockSpace disappear its child windows will be orphaned. + ImGuiWindowFlags flags = ImGuiWindowFlags_MenuBar; + flags |= ImGuiWindowFlags_NoDocking; + if (opt_fullscreen) + { + ImVec2 viewport_pos = ImVec2(0.0f, 0.0f); + ImVec2 viewport_size = ImGui::GetIO().DisplaySize; + ImGui::SetNextWindowPos(viewport_pos); + ImGui::SetNextWindowSize(viewport_size); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove; + flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus; + } + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); + ImGui::Begin("Docking Documents Demo", p_open, flags); + ImGui::PopStyleVar(); + + if (ImGui::BeginMenuBar()) + { + if (ImGui::BeginMenu("Docking")) + { + if (ImGui::MenuItem("Remove DockSpace", NULL, false, p_open != NULL)) + *p_open = false; + ImGui::EndMenu(); + } + // Disabling fullscreen would allow the window to be moved to the front of other windows, + // which we can't undo at the moment without finer window depth/z control. + /*if (ImGui::BeginMenu("Options")) + { + ImGui::MenuItem("Fullscreen", NULL, &opt_fullscreen_persistant); + ImGui::EndMenu(); + }*/ + ShowHelpMarker( + "You can _always_ dock _any_ window into another by holding the SHIFT key while moving a window. Try it now!" "\n" + "This demo app has nothing to do with it!" "\n\n" + "This demo app only demonstrate the use of ImGui::DockSpace() which allows you to specify a docking spot _within_ another window. This is useful so you can decorate your main application window (e.g. with a menu bar)." "\n\n" + "ImGui::DockSpace() comes with one hard constraint: it needs to be submitted _before_ any window which may be docked into it. Therefore, if you use a dock spot as the central point of your application, you'll probably want it to be part of the very first window you are submitting to imgui every frame." "\n\n" + "(NB: because of this constraint, the implicit \"Debug\" window can not be docked into an explicit DockSpace(), because that window is submitted as part of the NewFrame() call. An easy workaround is that you can create your own implicit \"Debug##2\" window after calling DockSpace() and leave it in the window stack for anyone to use.)" + ); + + ImGui::EndMenuBar(); + } + + //ImGui::PushStyleColor(ImGuiCol_DockingBg, ImVec4(0.2f, 0.2f, 0.2f, 1.0f)); + ImGui::DockSpace("##MyCentralDockSpace"); + //ImGui::PopStyleColor(); + + ImGui::End(); + if (opt_fullscreen) + ImGui::PopStyleVar(); +} + //----------------------------------------------------------------------------- // [SECTION] Example App: Documents Handling / ShowExampleAppDocuments() //----------------------------------------------------------------------------- @@ -3754,7 +3838,8 @@ void ShowExampleAppDocuments(bool* p_open) enum Target { Target_None, - Target_Tab // Create document as a local tab into a local tab bar + Target_Tab, // Create document as a local tab into a local tab bar + Target_Window // Create document as a regular window }; static Target opt_target = Target_Tab; static bool opt_reorderable = true; @@ -3802,9 +3887,11 @@ void ShowExampleAppDocuments(bool* p_open) ImGui::PopID(); } ImGui::PushItemWidth(ImGui::GetFontSize() * 12); - ImGui::Combo("Output", (int*)&opt_target, "None\0TabBar+Tabs\0"); + ImGui::Combo("Output", (int*)&opt_target, "None\0TabBar+Tabs\0DockSpace+Window\0"); ImGui::PopItemWidth(); + bool redock_all = false; if (opt_target == Target_Tab) { ImGui::SameLine(); ImGui::Checkbox("Reorderable Tabs", &opt_reorderable); } + if (opt_target == Target_Window) { ImGui::SameLine(); redock_all = ImGui::Button("Redock all"); } ImGui::Separator(); @@ -3848,6 +3935,42 @@ void ShowExampleAppDocuments(bool* p_open) ImGui::EndTabBar(); } + else if (opt_target == Target_Window) + { + NotifyOfDocumentsClosedElsewhere(app); + + // Create a DockSpace where any window can be docked + ImGui::DockSpace("##DockSpace"); + ImGuiID dockspace_id = ImGui::GetID("##DockSpace"); + + // Create Windows + for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++) + { + MyDocument* doc = &app.Documents[doc_n]; + if (!doc->Open) + continue; + + // FIXME-DOCK: SetNextWindowDock() + //ImGuiID default_dock_id = GetDockspaceRootDocumentDockID(); + //ImGuiID default_dock_id = GetDockspacePreferedDocumentDockID(); + ImGui::SetNextWindowDock(dockspace_id, redock_all ? ImGuiCond_Always : ImGuiCond_FirstUseEver); + ImGuiWindowFlags window_flags = (doc->Dirty ? ImGuiWindowFlags_UnsavedDocument : 0); + bool visible = ImGui::Begin(doc->Name, &doc->Open, window_flags); + + // Cancel attempt to close when unsaved add to save queue so we can display a popup. + if (!doc->Open && doc->Dirty) + { + doc->Open = true; + doc->DoQueueClose(); + } + + MyDocument::DisplayContextMenu(doc); + if (visible) + MyDocument::DisplayContents(doc); + + ImGui::End(); + } + } // Update closing queue static ImVector close_queue; diff --git a/imgui_draw.cpp b/imgui_draw.cpp index e7eff2e9bcfa..06a766710532 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -210,6 +210,8 @@ void ImGui::StyleColorsDark(ImGuiStyle* dst) colors[ImGuiCol_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f); colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f); colors[ImGuiCol_TabHovered] = colors[ImGuiCol_HeaderHovered]; + colors[ImGuiCol_DockingPreview] = colors[ImGuiCol_HeaderActive] * ImVec4(1.0f, 1.0f, 1.0f, 0.7f); + colors[ImGuiCol_DockingBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f);; colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f); colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f); colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); @@ -265,6 +267,8 @@ void ImGui::StyleColorsClassic(ImGuiStyle* dst) colors[ImGuiCol_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f); colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f); colors[ImGuiCol_TabHovered] = colors[ImGuiCol_HeaderHovered]; + colors[ImGuiCol_DockingPreview] = colors[ImGuiCol_Header] * ImVec4(1.0f, 1.0f, 1.0f, 0.7f); + colors[ImGuiCol_DockingBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f);; colors[ImGuiCol_PlotLines] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); colors[ImGuiCol_PlotLinesHovered] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); @@ -321,6 +325,8 @@ void ImGui::StyleColorsLight(ImGuiStyle* dst) colors[ImGuiCol_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f); colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f); colors[ImGuiCol_TabHovered] = colors[ImGuiCol_HeaderHovered]; + colors[ImGuiCol_DockingPreview] = colors[ImGuiCol_Header] * ImVec4(1.0f, 1.0f, 1.0f, 0.7f); + colors[ImGuiCol_DockingBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f);; colors[ImGuiCol_PlotLines] = ImVec4(0.39f, 0.39f, 0.39f, 1.00f); colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f); colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); @@ -2831,8 +2837,10 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col // (progressively moved from imgui.cpp to here when they are redesigned to stop accessing ImGui global state) //----------------------------------------------------------------------------- // - RenderMouseCursor() +// - RenderArrowDockMenu() // - RenderArrowPointingAt() // - RenderRectFilledRangeH() +// - RenderRectFilledWithHole() // - RenderPixelEllipsis() //----------------------------------------------------------------------------- @@ -2874,6 +2882,14 @@ void ImGui::RenderArrowPointingAt(ImDrawList* draw_list, ImVec2 pos, ImVec2 half } } +// This is less wide than RenderArrow() and we use in dock nodes instead of the regular RenderArrow() to denote a change of functionality, +// and because the saved space means that the left-most tab label can stay at exactly the same position as the label of a loose window. +void ImGui::RenderArrowDockMenu(ImDrawList* draw_list, ImVec2 p_min, float sz, ImU32 col) +{ + draw_list->AddRectFilled(p_min + ImVec2(sz * 0.10f, sz * 0.15f), p_min + ImVec2(sz * 0.70f, sz * 0.30f), col); + RenderArrowPointingAt(draw_list, p_min + ImVec2(sz * 0.40f, sz * 0.85f), ImVec2(sz * 0.30f, sz * 0.40f), ImGuiDir_Down, col); +} + static inline float ImAcos01(float x) { if (x <= 0.0f) return IM_PI * 0.5f; @@ -2942,6 +2958,24 @@ void ImGui::RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, Im draw_list->PathFillConvex(col); } +// For CTRL+TAB within a docking node we need to render the dimming background in 8 steps +// (Because the root node renders the background in one shot, in order to avoid flickering when a child dock node is not submitted) +void ImGui::RenderRectFilledWithHole(ImDrawList* draw_list, ImRect outer, ImRect inner, ImU32 col, float rounding) +{ + const bool fill_L = (inner.Min.x > outer.Min.x); + const bool fill_R = (inner.Max.x < outer.Max.x); + const bool fill_U = (inner.Min.y > outer.Min.y); + const bool fill_D = (inner.Max.y < outer.Max.y); + if (fill_L) draw_list->AddRectFilled(ImVec2(outer.Min.x, inner.Min.y), ImVec2(inner.Min.x, inner.Max.y), col, rounding, (fill_U ? 0 : ImDrawCornerFlags_TopLeft) | (fill_D ? 0 : ImDrawCornerFlags_BotLeft)); + if (fill_R) draw_list->AddRectFilled(ImVec2(inner.Max.x, inner.Min.y), ImVec2(outer.Max.x, inner.Max.y), col, rounding, (fill_U ? 0 : ImDrawCornerFlags_TopRight) | (fill_D ? 0 : ImDrawCornerFlags_BotRight)); + if (fill_U) draw_list->AddRectFilled(ImVec2(inner.Min.x, outer.Min.y), ImVec2(inner.Max.x, inner.Min.y), col, rounding, (fill_L ? 0 : ImDrawCornerFlags_TopLeft) | (fill_R ? 0 : ImDrawCornerFlags_TopRight)); + if (fill_D) draw_list->AddRectFilled(ImVec2(inner.Min.x, inner.Max.y), ImVec2(inner.Max.x, outer.Max.y), col, rounding, (fill_L ? 0 : ImDrawCornerFlags_BotLeft) | (fill_R ? 0 : ImDrawCornerFlags_BotRight)); + if (fill_L && fill_U) draw_list->AddRectFilled(ImVec2(outer.Min.x, outer.Min.y), ImVec2(inner.Min.x, inner.Min.y), col, rounding, ImDrawCornerFlags_TopLeft); + if (fill_R && fill_U) draw_list->AddRectFilled(ImVec2(inner.Max.x, outer.Min.y), ImVec2(outer.Max.x, inner.Min.y), col, rounding, ImDrawCornerFlags_TopRight); + if (fill_L && fill_D) draw_list->AddRectFilled(ImVec2(outer.Min.x, inner.Max.y), ImVec2(inner.Min.x, outer.Max.y), col, rounding, ImDrawCornerFlags_BotLeft); + if (fill_R && fill_D) draw_list->AddRectFilled(ImVec2(inner.Max.x, inner.Max.y), ImVec2(outer.Max.x, outer.Max.y), col, rounding, ImDrawCornerFlags_BotRight); +} + // FIXME: Rendering an ellipsis "..." is a surprisingly tricky problem... can't rely on font glyph having it, and regular dot are typically too wide. // If we render a dot/shape ourselves it comes with the risk that it wouldn't match the boldness or positioning of what the font uses... void ImGui::RenderPixelEllipsis(ImDrawList* draw_list, ImFont* font, ImVec2 pos, int count, ImU32 col) diff --git a/imgui_internal.h b/imgui_internal.h index fa08754ad92e..16c37d9dc118 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -40,6 +40,9 @@ struct ImGuiColorMod; // Stacked color modifier, backup of modifie struct ImGuiColumnData; // Storage data for a single column struct ImGuiColumnsSet; // Storage data for a columns set struct ImGuiContext; // Main imgui context +struct ImGuiDockContext; // Docking system context +struct ImGuiDockNode; // Docking system node (hold a list of Windows OR two child dock nodes) +struct ImGuiDockNodeSettings; // Storage for a dock node in .ini file (we preserve those even if the associated dock node isn't active during the session) struct ImGuiGroupData; // Stacked storage data for BeginGroup()/EndGroup() struct ImGuiInputTextState; // Internal state of the currently focused/edited text input box struct ImGuiItemHoveredDataBackup; // Backup and restore IsItemHovered() internal data @@ -90,6 +93,9 @@ namespace ImGuiStb extern IMGUI_API ImGuiContext* GImGui; // Current implicit ImGui context pointer #endif +// Internal Drag and Drop payload types. String starting with '_' are reserved for Dear ImGui. +#define IMGUI_PAYLOAD_TYPE_WINDOW "_IMWINDOW" // Payload == ImGuiWindow* + //----------------------------------------------------------------------------- // Helpers //----------------------------------------------------------------------------- @@ -406,6 +412,14 @@ struct ImVec1 ImVec1(float _x) { x = _x; } }; +// 2D vector (half-size integer) +struct ImVec2ih +{ + short x, y; + ImVec2ih() { x = y = 0; } + ImVec2ih(short _x, short _y) { x = _x; y = _y; } +}; + // 2D axis aligned bounding-box // NB: we can't rely on ImVec2 math operators being available here struct IMGUI_API ImRect @@ -524,15 +538,17 @@ struct ImGuiWindowSettings ImGuiID ID; ImVec2 Pos; ImVec2 Size; + ImGuiID DockId; // ID of last known DockNode (even if the DockNode is invisible because it has only 1 active window), or 0 if none. + short DockOrder; // Order of the last time the window was visible within its DockNode. This is used to reorder windows that are reappearing on the same frame. Same value between windows that were active and windows that were none are possible. bool Collapsed; - ImGuiWindowSettings() { Name = NULL; ID = 0; Pos = Size = ImVec2(0,0); Collapsed = false; } + ImGuiWindowSettings() { Name = NULL; ID = 0; Pos = Size = ImVec2(0, 0); DockId = 0; DockOrder = -1; Collapsed = false; } }; struct ImGuiSettingsHandler { - const char* TypeName; // Short description stored in .ini file. Disallowed characters: '[' ']' - ImGuiID TypeHash; // == ImHash(TypeName, 0, 0) + const char* TypeName; // Short description stored in .ini file. Disallowed characters: '[' ']' + ImGuiID TypeHash; // == ImHash(TypeName, 0, 0) void* (*ReadOpenFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, const char* name); // Read: Called when entering into a new ini entry e.g. "[Window][Name]" void (*ReadLineFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, void* entry, const char* line); // Read: Called for every line of text within an ini entry void (*WriteAllFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* out_buf); // Write: Output every entries into 'out_buf' @@ -642,33 +658,37 @@ struct ImGuiNextWindowData ImGuiCond SizeConstraintCond; ImGuiCond FocusCond; ImGuiCond BgAlphaCond; + ImGuiCond DockCond; ImVec2 PosVal; ImVec2 PosPivotVal; ImVec2 SizeVal; ImVec2 ContentSizeVal; + bool PosUndock; bool CollapsedVal; ImRect SizeConstraintRect; ImGuiSizeCallback SizeCallback; void* SizeCallbackUserData; float BgAlphaVal; + ImGuiID DockId; ImVec2 MenuBarOffsetMinVal; // This is not exposed publicly, so we don't clear it. ImGuiNextWindowData() { - PosCond = SizeCond = ContentSizeCond = CollapsedCond = SizeConstraintCond = FocusCond = BgAlphaCond = 0; + PosCond = SizeCond = ContentSizeCond = CollapsedCond = SizeConstraintCond = FocusCond = BgAlphaCond = DockCond = 0; PosVal = PosPivotVal = SizeVal = ImVec2(0.0f, 0.0f); ContentSizeVal = ImVec2(0.0f, 0.0f); - CollapsedVal = false; + PosUndock = CollapsedVal = false; SizeConstraintRect = ImRect(); SizeCallback = NULL; SizeCallbackUserData = NULL; BgAlphaVal = FLT_MAX; + DockId = 0; MenuBarOffsetMinVal = ImVec2(0.0f, 0.0f); } void Clear() { - PosCond = SizeCond = ContentSizeCond = CollapsedCond = SizeConstraintCond = FocusCond = BgAlphaCond = 0; + PosCond = SizeCond = ContentSizeCond = CollapsedCond = SizeConstraintCond = FocusCond = BgAlphaCond = DockCond = 0; } }; @@ -682,6 +702,43 @@ struct ImGuiTabBarSortItem float Width; }; +// sizeof() 88~124 +struct ImGuiDockNode +{ + ImGuiID ID; + ImGuiDockNode* ParentNode; + ImGuiDockNode* ChildNodes[2]; + ImVector Windows; // Note: unordered list! Iterate TabBar->Tabs for user-order. + ImGuiTabBar* TabBar; + ImVec2 Pos, Size; // Current position, size + ImVec2 LastExplicitSize; // Last explicit size (overridden when using a splitter affecting the node) + int SplitAxis; + float SplitRatio; + + ImGuiWindow* HostWindow; + ImGuiWindow* VisibleWindow; + ImGuiDockNode* OnlyNodeWithWindows; // Root node only, set when there is a single visible node within the hierarchy + ImGuiID SelectedTabID; + int LastFrameActive; + ImGuiID LastFocusedNodeID; + ImGuiID WantCloseOne; + bool InitFromFirstWindow :1; + bool IsVisible :1; // Set to false when the node is hidden (usually disabled as it has no active window) + bool IsExplicitRoot :1; // Mark root node as explicit when created from a DockSpace() + bool IsDocumentRoot :1; + bool HasCloseButton :1; + bool HasCollapseButton :1; + bool WantCloseAll :1; + bool WantLockSizeOnce :1; + + ImGuiDockNode(ImGuiID id); + ~ImGuiDockNode(); + bool IsRootNode() const { return ParentNode == NULL; } + bool IsParent() const { return ChildNodes[0] != NULL; } + bool IsEmpty() const { return ChildNodes[0] == NULL && Windows.Size == 0; } + ImRect Rect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } +}; + //----------------------------------------------------------------------------- // Main imgui context //----------------------------------------------------------------------------- @@ -710,6 +767,7 @@ struct ImGuiContext ImGuiWindow* CurrentWindow; // Being drawn into ImGuiWindow* HoveredWindow; // Will catch mouse inputs ImGuiWindow* HoveredRootWindow; // Will catch mouse inputs (for focus/move only) + ImGuiWindow* HoveredWindowUnderMovingWindow; ImGuiID HoveredId; // Hovered widget bool HoveredIdAllowOverlap; ImGuiID HoveredIdPreviousFrame; @@ -823,6 +881,10 @@ struct ImGuiContext ImVector PrivateClipboard; // If no custom clipboard handler is defined ImVec2 PlatformImePos, PlatformImeLastPos; // Cursor position request & last passed to the OS Input Method Editor + // Extensions + // FIXME: We could provide an API to register one slot in an array held in ImGuiContext? + ImGuiDockContext* DockContext; + // Settings bool SettingsLoaded; float SettingsDirtyTimer; // Save .ini Settings to memory when time reaches zero @@ -862,6 +924,7 @@ struct ImGuiContext CurrentWindow = NULL; HoveredWindow = NULL; HoveredRootWindow = NULL; + HoveredWindowUnderMovingWindow = NULL; HoveredId = 0; HoveredIdAllowOverlap = false; HoveredIdPreviousFrame = 0; @@ -934,6 +997,7 @@ struct ImGuiContext ScrollbarClickDeltaToGrabCenter = ImVec2(0.0f, 0.0f); TooltipOverrideCount = 0; PlatformImePos = PlatformImeLastPos = ImVec2(FLT_MAX, FLT_MAX); + DockContext = NULL; SettingsLoaded = false; SettingsDirtyTimer = 0.0f; @@ -1078,6 +1142,7 @@ struct IMGUI_API ImGuiWindow ImGuiCond SetWindowPosAllowFlags; // store acceptable condition flags for SetNextWindowPos() use. ImGuiCond SetWindowSizeAllowFlags; // store acceptable condition flags for SetNextWindowSize() use. ImGuiCond SetWindowCollapsedAllowFlags; // store acceptable condition flags for SetNextWindowCollapsed() use. + ImGuiCond SetWindowDockAllowFlags; // store acceptable condition flags for SetNextWindowDock() use. ImVec2 SetWindowPosVal; // store window position when using a non-zero Pivot (position set needs to be processed when we know the window size) ImVec2 SetWindowPosPivot; // store window pivot for positioning. ImVec2(0,0) when positioning from top-left corner; ImVec2(0.5f,0.5f) for centering; ImVec2(1,1) for bottom right. @@ -1099,6 +1164,7 @@ struct IMGUI_API ImGuiWindow ImDrawList DrawListInst; ImGuiWindow* ParentWindow; // If we are a child _or_ popup window, this is pointing to our parent. Otherwise NULL. ImGuiWindow* RootWindow; // Point to ourself or first ancestor that is not a child window. + ImGuiWindow* RootWindowDockStop; // Point to ourself or first ancestor that is not a child window. Doesn't cross through dock nodes. We use this so IsWindowFocused() can behave consistently regardless of docking state. ImGuiWindow* RootWindowForTitleBarHighlight; // Point to ourself or first ancestor which will display TitleBgActive color when this window is active. ImGuiWindow* RootWindowForNav; // Point to ourself or first ancestor which doesn't have the NavFlattened flag. @@ -1115,6 +1181,17 @@ struct IMGUI_API ImGuiWindow int FocusIdxAllRequestNext; // Item being requested for focus, for next update (relies on layout to be stable between the frame pressing TAB and the next frame) int FocusIdxTabRequestNext; // " + // Docking + ImGuiDockNode* DockNode; // Which node are we docked into + ImGuiDockNode* DockNodeAsHost; // Which node are we owning (for parent windows) + ImGuiID DockId; // Backup of last valid DockNode->Id, so single value remember their dock node id + ImGuiItemStatusFlags DockTabItemStatusFlags; + ImRect DockTabItemRect; + short DockOrder; // Order of the last time the window was visible within its DockNode. This is used to reorder windows that are reappearing on the same frame. Same value between windows that were active and windows that were none are possible. + bool DockIsActive; // =~ (DockNode != NULL) && (DockNode->Windows.Size > 1) + bool DockTabIsVisible; // Is the window visible this frame? =~ is the corresponding tab selected? + bool DockTabWantClose; + public: ImGuiWindow(ImGuiContext* context, const char* name); ~ImGuiWindow(); @@ -1221,6 +1298,7 @@ namespace ImGui // - You are calling ImGui functions after ImGui::EndFrame()/ImGui::Render() and before the next ImGui::NewFrame(), which is also illegal. inline ImGuiWindow* GetCurrentWindowRead() { ImGuiContext& g = *GImGui; return g.CurrentWindow; } inline ImGuiWindow* GetCurrentWindow() { ImGuiContext& g = *GImGui; g.CurrentWindow->WriteAccessed = true; return g.CurrentWindow; } + IMGUI_API ImGuiWindow* FindWindowByID(ImGuiID id); IMGUI_API ImGuiWindow* FindWindowByName(const char* name); IMGUI_API void FocusWindow(ImGuiWindow* window); // FIXME: Rename to SetWindowFocus() IMGUI_API void FocusFrontMostActiveWindowIgnoringOne(ImGuiWindow* ignore_window); @@ -1309,6 +1387,20 @@ namespace ImGui inline bool IsNavInputDown(ImGuiNavInput n) { return GImGui->IO.NavInputs[n] > 0.0f; } inline bool IsNavInputPressed(ImGuiNavInput n, ImGuiInputReadMode mode) { return GetNavInputAmount(n, mode) > 0.0f; } inline bool IsNavInputPressedAnyOfTwo(ImGuiNavInput n1, ImGuiNavInput n2, ImGuiInputReadMode mode) { return (GetNavInputAmount(n1, mode) + GetNavInputAmount(n2, mode)) > 0.0f; } + + // Docking + IMGUI_API void DockContextInitialize(ImGuiContext* imgui_context); + IMGUI_API void DockContextShutdown(ImGuiContext* imgui_context); + IMGUI_API void DockContextOnLoadSettings(); + IMGUI_API void DockContextRebuild(ImGuiDockContext* ctx); + IMGUI_API void DockContextUpdateUndocking(ImGuiDockContext* ctx); + IMGUI_API void DockContextUpdateDocking(ImGuiDockContext* ctx); + IMGUI_API void DockContextQueueUndock(ImGuiDockContext* ctx, ImGuiWindow* window); + IMGUI_API void BeginDocked(ImGuiWindow* window, bool* p_open); + IMGUI_API void BeginAsDockableDragDropSource(ImGuiWindow* window); + IMGUI_API void BeginAsDockableDragDropTarget(ImGuiWindow* window); + IMGUI_API void SetWindowDock(ImGuiWindow* window, ImGuiID dock_id, ImGuiCond cond); + IMGUI_API void ShowDockingDebug(); // Drag and Drop IMGUI_API bool BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id); @@ -1321,8 +1413,7 @@ namespace ImGui IMGUI_API void PushColumnClipRect(int column_index = -1); // Tab Bars - IMGUI_API void ShowDockingDebug(); - IMGUI_API bool BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& bb, ImGuiTabBarFlags flags); + IMGUI_API bool BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& bb, ImGuiTabBarFlags flags, ImGuiDockNode* dock_node); IMGUI_API ImGuiTabItem* TabBarFindTabByID(ImGuiTabBar* tab_bar, ImGuiID tab_id); IMGUI_API void TabBarAddTab(ImGuiTabBar* tab_bar, ImGuiWindow* window); IMGUI_API void TabBarRemoveTab(ImGuiTabBar* tab_bar, ImGuiID tab_id); @@ -1353,7 +1444,9 @@ namespace ImGui // Render helpers (those functions don't access any ImGui state!) IMGUI_API void RenderMouseCursor(ImDrawList* draw_list, ImVec2 pos, float scale, ImGuiMouseCursor mouse_cursor = ImGuiMouseCursor_Arrow); IMGUI_API void RenderArrowPointingAt(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, ImGuiDir direction, ImU32 col); + IMGUI_API void RenderArrowDockMenu(ImDrawList* draw_list, ImVec2 p_min, float sz, ImU32 col); IMGUI_API void RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, ImU32 col, float x_start_norm, float x_end_norm, float rounding); + IMGUI_API void RenderRectFilledWithHole(ImDrawList* draw_list, ImRect outer, ImRect inner, ImU32 col, float rounding); IMGUI_API void RenderPixelEllipsis(ImDrawList* draw_list, ImFont* font, ImVec2 pos, int count, ImU32 col); // Widgets diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 8be3daa1b70d..1cb04aca2acb 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -679,10 +679,16 @@ bool ImGui::CollapseButton(ImGuiID id, const ImVec2& pos) bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_None); + bool is_dock_menu = (window->DockNodeAsHost && !window->Collapsed); + ImVec2 off = is_dock_menu ? ImVec2((float)(int)(-g.Style.ItemInnerSpacing.x * 0.5f) + 0.5f, 0.0f) : ImVec2(0.0f, 0.0f); ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); if (hovered || held) - window->DrawList->AddCircleFilled(bb.GetCenter() + ImVec2(0.0f, -0.5f), g.FontSize * 0.5f + 1.0f, col, 9); - RenderArrow(bb.Min + g.Style.FramePadding, window->Collapsed ? ImGuiDir_Right : ImGuiDir_Down, 1.0f); + window->DrawList->AddCircleFilled(bb.GetCenter() + off + ImVec2(0,-0.5f), g.FontSize * 0.5f + 1.0f, col, 9); + + if (is_dock_menu) + RenderArrowDockMenu(window->DrawList, bb.Min + g.Style.FramePadding, g.FontSize, GetColorU32(ImGuiCol_Text)); + else + RenderArrow(bb.Min + g.Style.FramePadding, window->Collapsed ? ImGuiDir_Right : ImGuiDir_Down, 1.0f); // Switch to moving the window after mouse is moved beyond the initial drag threshold if (IsItemActive() && IsMouseDragging()) @@ -5429,7 +5435,7 @@ bool ImGui::BeginMainMenuBar() SetNextWindowSize(ImVec2(g.IO.DisplaySize.x, g.NextWindowData.MenuBarOffsetMinVal.y + g.FontBaseSize + g.Style.FramePadding.y)); PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0,0)); - ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_MenuBar; + ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_MenuBar; bool is_open = Begin("##MainMenuBar", NULL, window_flags) && BeginMenuBar(); PopStyleVar(2); g.NextWindowData.MenuBarOffsetMinVal = ImVec2(0.0f, 0.0f); @@ -5796,10 +5802,10 @@ bool ImGui::BeginTabBar(const char* str_id, ImGuiTabBarFlags flags) ImGuiTabBar* tab_bar = g.TabBars.GetOrAddByKey(id); ImRect tab_bar_bb = ImRect(window->DC.CursorPos.x, window->DC.CursorPos.y, window->InnerClipRect.Max.x, window->DC.CursorPos.y + g.FontSize + g.Style.FramePadding.y * 2); tab_bar->ID = id; - return BeginTabBarEx(tab_bar, tab_bar_bb, flags | ImGuiTabBarFlags_IsFocused); + return BeginTabBarEx(tab_bar, tab_bar_bb, flags | ImGuiTabBarFlags_IsFocused, NULL); } -bool ImGui::BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& tab_bar_bb, ImGuiTabBarFlags flags) +bool ImGui::BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& tab_bar_bb, ImGuiTabBarFlags flags, ImGuiDockNode* dock_node) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; @@ -5839,10 +5845,18 @@ bool ImGui::BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& tab_bar_bb, ImG // Draw separator const ImU32 col = GetColorU32((flags & ImGuiTabBarFlags_IsFocused) ? ImGuiCol_TabActive : ImGuiCol_Tab); const float y = tab_bar->BarRect.Max.y - 1.0f; - const float separator_min_x = tab_bar->BarRect.Min.x - ((flags & ImGuiTabBarFlags_DockNodeExplicitRoot) ? 0.0f : window->WindowPadding.x); - const float separator_max_x = tab_bar->BarRect.Max.x + ((flags & ImGuiTabBarFlags_DockNodeExplicitRoot) ? 0.0f : window->WindowPadding.x); - window->DrawList->AddLine(ImVec2(separator_min_x, y), ImVec2(separator_max_x, y), col, 1.0f); - + if (dock_node != NULL) + { + const float separator_min_x = dock_node->Pos.x; + const float separator_max_x = dock_node->Pos.x + dock_node->Size.x; + window->DrawList->AddLine(ImVec2(separator_min_x, y), ImVec2(separator_max_x, y), col, 1.0f); + } + else + { + const float separator_min_x = tab_bar->BarRect.Min.x - ((flags & ImGuiTabBarFlags_DockNodeExplicitRoot) ? 0.0f : window->WindowPadding.x); + const float separator_max_x = tab_bar->BarRect.Max.x + ((flags & ImGuiTabBarFlags_DockNodeExplicitRoot) ? 0.0f : window->WindowPadding.x); + window->DrawList->AddLine(ImVec2(separator_min_x, y), ImVec2(separator_max_x, y), col, 1.0f); + } return true; } @@ -6019,6 +6033,10 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) tab_bar->VisibleTabId = tab_bar->SelectedTabId; tab_bar->VisibleTabWasSubmitted = false; + // CTRL+TAB can override visible tab temporarily + if (g.NavWindowingTarget != NULL && g.NavWindowingTarget->DockNode && g.NavWindowingTarget->DockNode->TabBar == tab_bar) + tab_bar->VisibleTabId = scroll_track_selected_tab_id = g.NavWindowingTarget->ID; + // Update scrolling if (scroll_track_selected_tab_id) if (ImGuiTabItem* scroll_track_selected_tab = TabBarFindTabByID(tab_bar, scroll_track_selected_tab_id)) @@ -6371,6 +6389,8 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, // Click to Select a tab ImGuiButtonFlags button_flags = (ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_AllowItemOverlap); + if (g.DragDropActive && !g.DragDropPayload.IsDataType(IMGUI_PAYLOAD_TYPE_WINDOW)) + button_flags |= ImGuiButtonFlags_PressedOnDragDropHold; bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags); hovered |= (g.HoveredId == id); @@ -6427,6 +6447,16 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, undocking_tab = true; } } + + // Undock + if (undocking_tab && g.ActiveId == id && IsMouseDragging()) + { + ImGuiDockContext* ctx = g.DockContext; + DockContextQueueUndock(ctx, docked_window); + g.MovingWindow = docked_window; + g.ActiveId = g.MovingWindow->MoveId; + g.ActiveIdClickOffset -= g.MovingWindow->Pos - bb.Min; + } } } @@ -6488,6 +6518,16 @@ void ImGui::SetTabItemClosed(const char* label) ImGuiID tab_id = TabBarCalcTabID(tab_bar, label); TabBarRemoveTab(tab_bar, tab_id); } + else if (ImGuiWindow* window = FindWindowByName(label)) + { + if (window->DockIsActive) + if (ImGuiDockNode* node = window->DockNode) + { + ImGuiID tab_id = TabBarCalcTabID(node->TabBar, label); + TabBarRemoveTab(node->TabBar, tab_id); + window->DockTabWantClose = true; + } + } } ImVec2 ImGui::TabItemCalcSize(const char* label, bool has_close_button) From 9ac1e93aa16595a747583fc30adc5cbe5c8144cd Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 6 Sep 2018 21:23:15 +0200 Subject: [PATCH 235/828] Docking: Added Docking system. (Part 2) (#351) --- imgui.cpp | 465 ++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 386 insertions(+), 79 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 93f3531e60bc..08dc636c3f96 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1067,6 +1067,7 @@ ImGuiIO::ImGuiIO() DisplayVisibleMin = DisplayVisibleMax = ImVec2(0.0f, 0.0f); // Miscellaneous configuration options + ConfigDockingWithKeyMod = true; #ifdef __APPLE__ ConfigMacOSXBehaviors = true; // Set Mac OS X style defaults based on __APPLE__ compile time flag #else @@ -2345,7 +2346,7 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) AutoFitChildAxises = 0x00; AutoPosLastDirection = ImGuiDir_None; HiddenFramesRegular = HiddenFramesForResize = 0; - SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing; + SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = SetWindowDockAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing; SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX); LastFrameActive = -1; @@ -2367,6 +2368,12 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) FocusIdxAllCounter = FocusIdxTabCounter = -1; FocusIdxAllRequestCurrent = FocusIdxTabRequestCurrent = INT_MAX; FocusIdxAllRequestNext = FocusIdxTabRequestNext = INT_MAX; + + DockNode = DockNodeAsHost = NULL; + DockId = 0; + DockTabItemStatusFlags = 0; + DockOrder = -1; + DockIsActive = DockTabIsVisible = DockTabWantClose = false; } ImGuiWindow::~ImGuiWindow() @@ -2656,8 +2663,9 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) if ((window->DC.ItemFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled)) return false; - // Special handling for the 1st item after Begin() which represent the title bar. When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect tht case. - if (window->DC.LastItemId == window->MoveId && window->WriteAccessed) + // Special handling for the dummy item after Begin() which represent the title bar or tab. + // When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect the case. + if ((window->DC.LastItemId == window->ID || window->DC.LastItemId == window->MoveId) && window->WriteAccessed) return false; return true; } @@ -3042,16 +3050,17 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags() // - When moving a window we can skip the search, which also conveniently bypasses the fact that window->WindowRectClipped is lagging as this point of the frame. // - We also support the moved window toggling the NoInputs flag after moving has started in order to be able to detect windows below it, which is useful for e.g. docking mechanisms. FindHoveredWindow(); + //IM_ASSERT(g.HoveredWindow == NULL || g.HoveredWindow == g.MovingWindow || g.HoveredWindow->Viewport == g.MouseViewport); // Modal windows prevents cursor from hovering behind them. ImGuiWindow* modal_window = GetFrontMostPopupModal(); if (modal_window) if (g.HoveredRootWindow && !IsWindowChildOf(g.HoveredRootWindow, modal_window)) - g.HoveredRootWindow = g.HoveredWindow = NULL; + g.HoveredWindow = g.HoveredRootWindow = g.HoveredWindowUnderMovingWindow = NULL; // Disabled mouse? if (g.IO.ConfigFlags & ImGuiConfigFlags_NoMouse) - g.HoveredWindow = g.HoveredRootWindow = NULL; + g.HoveredWindow = g.HoveredRootWindow = g.HoveredWindowUnderMovingWindow = NULL; // We track click ownership. When clicked outside of a window the click is owned by the application and won't report hovering nor request capture even while dragging over our windows afterward. int mouse_earliest_button_down = -1; @@ -3071,7 +3080,7 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags() // FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02) const bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0; if (!mouse_avail_to_imgui && !mouse_dragging_extern_payload) - g.HoveredWindow = g.HoveredRootWindow = NULL; + g.HoveredWindow = g.HoveredRootWindow = g.HoveredWindowUnderMovingWindow = NULL; // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to imgui, false = dispatch mouse info to imgui + app) if (g.WantCaptureMouseNextFrame != -1) @@ -3210,9 +3219,16 @@ void ImGui::NewFrame() g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame); g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame))) : FLT_MAX; + // Undocking + // (needs to be before UpdateMovingWindow so the window is already offset and following the mouse on the detaching frame) + DockContextUpdateUndocking(g.DockContext); + + // Find hovered window + // (needs to be before UpdateMovingWindow so we fill HoveredWindowUnderMovingWindow on the mouse release frame) + UpdateHoveredWindowAndCaptureFlags(); + // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering) UpdateMouseMovingWindow(); - UpdateHoveredWindowAndCaptureFlags(); // Background darkening/whitening if (GetFrontMostPopupModal() != NULL || (g.NavWindowingTarget != NULL && g.NavWindowingHighlightAlpha > 0.0f)) @@ -3257,6 +3273,9 @@ void ImGui::NewFrame() g.CurrentPopupStack.resize(0); ClosePopupsOverWindow(g.NavWindow); + // Docking + DockContextUpdateDocking(g.DockContext); + // Create implicit window - we will only render it if the user has added something to it. // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags. SetNextWindowSize(ImVec2(400,400), ImGuiCond_FirstUseEver); @@ -3277,6 +3296,10 @@ void ImGui::Initialize(ImGuiContext* context) ini_handler.WriteAllFn = SettingsHandlerWindow_WriteAll; g.SettingsHandlers.push_back(ini_handler); + // Docking Extension + IM_ASSERT(g.DockContext == NULL); + DockContextInitialize(&g); + g.Initialized = true; } @@ -3297,6 +3320,10 @@ void ImGui::Shutdown(ImGuiContext* context) if (g.SettingsLoaded && g.IO.IniFilename != NULL) SaveIniSettingsToDisk(g.IO.IniFilename); + // Shutdown Docking extensions + IM_ASSERT(g.DockContext != NULL); + DockContextShutdown(&g); + // Clear everything else for (int i = 0; i < g.Windows.Size; i++) IM_DELETE(g.Windows[i]); @@ -3306,8 +3333,7 @@ void ImGui::Shutdown(ImGuiContext* context) g.CurrentWindowStack.clear(); g.WindowsById.Clear(); g.NavWindow = NULL; - g.HoveredWindow = NULL; - g.HoveredRootWindow = NULL; + g.HoveredWindow = g.HoveredRootWindow = g.HoveredWindowUnderMovingWindow = NULL; g.ActiveIdWindow = g.ActiveIdPreviousFrameWindow = NULL; g.MovingWindow = NULL; g.ColorModifiers.clear(); @@ -3470,6 +3496,14 @@ void ImGui::PopClipRect() window->ClipRect = window->DrawList->_ClipRectStack.back(); } +static ImGuiWindow* FindFromMostVisibleChildWindow(ImGuiWindow* window) +{ + for (int n = window->DC.ChildWindows.Size - 1; n >= 0; n--) + if (IsWindowActiveAndVisible(window->DC.ChildWindows[n])) + return FindFromMostVisibleChildWindow(window->DC.ChildWindows[n]); + return window; +} + // This is normally called by Render(). You may want to call it directly if you want to avoid calling Render() but the gain will be very minimal. void ImGui::EndFrame() { @@ -3492,6 +3526,33 @@ void ImGui::EndFrame() g.CurrentWindow->Active = false; End(); + // CTRL-TAB + const bool dim_bg_for_window_list = (g.NavWindowingTargetAnim != NULL); + if (dim_bg_for_window_list) + { + // Choose a draw list that will be front-most across all our children + ImGuiWindow* window = g.NavWindowingTargetAnim; + ImDrawList* draw_list = FindFromMostVisibleChildWindow(window->RootWindow)->DrawList; + draw_list->PushClipRectFullScreen(); + + // Docking: draw modal whitening background on other nodes of a same dock tree + if (window->RootWindowDockStop->DockIsActive) + if (window->RootWindow != window->RootWindowDockStop) + RenderRectFilledWithHole(draw_list, window->RootWindow->Rect(), window->RootWindowDockStop->Rect(), GetColorU32(ImGuiCol_NavWindowingDimBg, g.DimBgRatio), g.Style.WindowRounding); + + // Draw navigation selection/windowing rectangle border + float rounding = ImMax(window->WindowRounding, g.Style.WindowRounding); + ImRect bb = window->Rect(); + bb.Expand(g.FontSize); + if (bb.Contains(GetViewportRect())) // If a window fits the entire viewport, adjust its highlight inward + { + bb.Expand(-g.FontSize - 1.0f); + rounding = window->WindowRounding; + } + draw_list->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), rounding, ~0, 3.0f); + draw_list->PopClipRect(); + } + // Show CTRL+TAB list if (g.NavWindowingTarget) NavUpdateWindowingList(); @@ -3701,10 +3762,11 @@ static void FindHoveredWindow() ImGuiContext& g = *GImGui; ImGuiWindow* hovered_window = NULL; + ImGuiWindow* hovered_window_ignoring_moving_window = NULL; if (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoInputs)) hovered_window = g.MovingWindow; - for (int i = g.Windows.Size - 1; i >= 0 && hovered_window == NULL; i--) + for (int i = g.Windows.Size - 1; i >= 0; i--) { ImGuiWindow* window = g.Windows[i]; if (!window->Active || window->Hidden) @@ -3718,14 +3780,16 @@ static void FindHoveredWindow() { if (hovered_window == NULL) hovered_window = window; - if (hovered_window) + if (hovered_window_ignoring_moving_window == NULL && (!g.MovingWindow || window->RootWindow != g.MovingWindow->RootWindow)) + hovered_window_ignoring_moving_window = window; + if (hovered_window && hovered_window_ignoring_moving_window) break; } } g.HoveredWindow = hovered_window; g.HoveredRootWindow = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL; - + g.HoveredWindowUnderMovingWindow = hovered_window_ignoring_moving_window; } // Test if mouse cursor is hovering given rectangle @@ -3951,7 +4015,17 @@ bool ImGui::IsItemDeactivatedAfterEdit() bool ImGui::IsItemFocused() { ImGuiContext& g = *GImGui; - return g.NavId && !g.NavDisableHighlight && g.NavId == g.CurrentWindow->DC.LastItemId; + ImGuiWindow* window = g.CurrentWindow; + + if (g.NavId == 0 || g.NavDisableHighlight || g.NavId != window->DC.LastItemId) + return false; + + // Special handling for the dummy item after Begin() which represent the title bar or tab. + // When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect the case. + if (window->DC.LastItemId == window->ID && window->WriteAccessed) + return false; + + return true; } bool ImGui::IsItemClicked(int mouse_button) @@ -4030,7 +4104,7 @@ static bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size ImGuiContext& g = *GImGui; ImGuiWindow* parent_window = g.CurrentWindow; - flags |= ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_ChildWindow; + flags |= ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_ChildWindow|ImGuiWindowFlags_NoDocking; flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove); // Inherit the NoMove flag // Size @@ -4163,6 +4237,13 @@ static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, b window->SetWindowPosAllowFlags = enabled ? (window->SetWindowPosAllowFlags | flags) : (window->SetWindowPosAllowFlags & ~flags); window->SetWindowSizeAllowFlags = enabled ? (window->SetWindowSizeAllowFlags | flags) : (window->SetWindowSizeAllowFlags & ~flags); window->SetWindowCollapsedAllowFlags = enabled ? (window->SetWindowCollapsedAllowFlags | flags) : (window->SetWindowCollapsedAllowFlags & ~flags); + window->SetWindowDockAllowFlags = enabled ? (window->SetWindowDockAllowFlags | flags) : (window->SetWindowDockAllowFlags & ~flags); +} + +ImGuiWindow* ImGui::FindWindowByID(ImGuiID id) +{ + ImGuiContext& g = *GImGui; + return (ImGuiWindow*)g.WindowsById.GetVoidPtr(id); } ImGuiWindow* ImGui::FindWindowByName(const char* name) @@ -4195,6 +4276,8 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFl window->Collapsed = settings->Collapsed; if (ImLengthSqr(settings->Size) > 0.00001f) size = ImFloor(settings->Size); + window->DockId = settings->DockId; + window->DockOrder = settings->DockOrder; } window->Size = window->SizeFull = window->SizeFullAtLastBegin = size; window->DC.CursorMaxPos = window->Pos; // So first call to CalcSizeContents() doesn't return crazy values @@ -4220,6 +4303,11 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFl return window; } +static ImGuiWindow* GetWindowForTitleAndMenuHeight(ImGuiWindow* window) +{ + return (window->DockNodeAsHost && window->DockNodeAsHost->VisibleWindow) ? window->DockNodeAsHost->VisibleWindow : window; +} + static ImVec2 CalcSizeAfterConstraint(ImGuiWindow* window, ImVec2 new_size) { ImGuiContext& g = *GImGui; @@ -4244,8 +4332,9 @@ static ImVec2 CalcSizeAfterConstraint(ImGuiWindow* window, ImVec2 new_size) // Minimum size if (!(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize))) { + ImGuiWindow* window_for_height = GetWindowForTitleAndMenuHeight(window); new_size = ImMax(new_size, g.Style.WindowMinSize); - new_size.y = ImMax(new_size.y, window->TitleBarHeight() + window->MenuBarHeight() + ImMax(0.0f, g.Style.WindowRounding - 1.0f)); // Reduce artifacts with very small windows + new_size.y = ImMax(new_size.y, window_for_height->TitleBarHeight() + window_for_height->MenuBarHeight() + ImMax(0.0f, g.Style.WindowRounding - 1.0f)); // Reduce artifacts with very small windows } return new_size; } @@ -4476,7 +4565,7 @@ static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au } // Apply back modified position/size to window - if (size_target.x != FLT_MAX) + if (size_target.x != FLT_MAX && (size_target.x != window->SizeFull.x || size_target.y != window->SizeFull.y)) { window->SizeFull = size_target; MarkIniSettingsDirty(window); @@ -4493,9 +4582,11 @@ static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window) { window->ParentWindow = parent_window; - window->RootWindow = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window; + window->RootWindow = window->RootWindowDockStop = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window; if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip)) window->RootWindow = parent_window->RootWindow; + if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip) && !window->DockIsActive) + window->RootWindowDockStop = parent_window->RootWindowDockStop; if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup))) window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight; while (window->RootWindowForNav->Flags & ImGuiWindowFlags_NavFlattened) @@ -4526,6 +4617,15 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window = CreateNewWindow(name, size_on_first_use, flags); } + // Update stored window name when it changes (which can only happen with the "###" operator). + // Only if it is meant to be displayed to the end user in a different place than the title bar (which already always display the 'name' parameter) + bool window_title_visible_elsewhere = (window->DockIsActive); + if (!window_just_created && window_title_visible_elsewhere && strcmp(name, window->Name) != 0) + { + IM_DELETE(window->Name); + window->Name = ImStrdup(name); + } + // Automatically disable manual moving/resizing when NoInputs is set if (flags & ImGuiWindowFlags_NoInputs) flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize; @@ -4546,12 +4646,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) flags = window->Flags; } - // Parent window is latched only on the first call to Begin() of the frame, so further append-calls can be done from a different window stack - ImGuiWindow* parent_window_in_stack = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back(); - ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow; - IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow)); - window->HasCloseButton = (p_open != NULL); - // Update the Appearing flag bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesForResize > 0); @@ -4565,6 +4659,22 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if (window->Appearing) SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true); + // Docking + // (NB: during the frame dock nodes are created, it is possible that (window->DockIsActive == false) even though (window->DockNode->Windows.Size > 1) + IM_ASSERT(window->DockNode == NULL || window->DockNodeAsHost == NULL); // Cannot be both + if (g.NextWindowData.DockCond) + SetWindowDock(window, g.NextWindowData.DockId, g.NextWindowData.DockCond); + if (first_begin_of_the_frame && (window->DockId != 0 || window->DockNode != NULL)) + { + BeginDocked(window, p_open); + flags = window->Flags; + } + + // Parent window is latched only on the first call to Begin() of the frame, so further append-calls can be done from a different window stack + ImGuiWindow* parent_window_in_stack = window->DockIsActive ? window->DockNode->HostWindow : g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back(); + ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow; + IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow)); + // Add to stack g.CurrentWindowStack.push_back(window); SetCurrentWindow(window); @@ -4631,6 +4741,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) UpdateWindowParentAndRootLinks(window, flags, parent_window); window->Active = true; + window->HasCloseButton = (p_open != NULL); window->ClipRect = ImVec4(-FLT_MAX,-FLT_MAX,+FLT_MAX,+FLT_MAX); window->LastFrameActive = current_frame; window->IDStack.resize(1); @@ -4666,16 +4777,22 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) SetCurrentWindow(window); // Lock border size and padding for the frame (so that altering them doesn't cause inconsistencies) - window->WindowBorderSize = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildBorderSize : ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize; - window->WindowPadding = style.WindowPadding; - if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_Popup)) && window->WindowBorderSize == 0.0f) + if (window->DockIsActive) + window->WindowBorderSize = 0.0f; + else if (flags & ImGuiWindowFlags_ChildWindow) + window->WindowBorderSize = style.ChildBorderSize; + else + window->WindowBorderSize = ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize; + if (!window->DockIsActive && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_Popup)) && window->WindowBorderSize == 0.0f) window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f); + else + window->WindowPadding = style.WindowPadding; window->DC.MenuBarOffset.x = ImMax(ImMax(window->WindowPadding.x, style.ItemSpacing.x), g.NextWindowData.MenuBarOffsetMinVal.x); window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y; // Collapse window by double-clicking on title bar // At this point we don't have a clipping rectangle setup yet, so we can use the title bar area for hit detection and drawing - if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse)) + if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse) && !window->DockIsActive) { // We don't use a regular button+id to test for double-click on title bar (mostly due to legacy reason, could be fixed), so verify that we don't have items over the title bar. ImRect title_bar_rect = window->TitleBarRect(); @@ -4719,6 +4836,9 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) MarkIniSettingsDirty(window); } + //if (window->DockNode && window->DockIsActive) + // size_full_modified = window->SizeFull; + // Apply minimum/maximum window size constraints and final size window->SizeFull = CalcSizeAfterConstraint(window, window->SizeFull); window->Size = window->Collapsed && !(flags & ImGuiWindowFlags_ChildWindow) ? window->TitleBarRect().GetSize() : window->SizeFull; @@ -4768,14 +4888,12 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->Pos = FindBestWindowPosForPopup(window); // Clamp position so it stays visible - if (!(flags & ImGuiWindowFlags_ChildWindow)) + // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing. + if (!window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) { - if (!window_pos_set_by_api && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && g.IO.DisplaySize.x > 0.0f && g.IO.DisplaySize.y > 0.0f) // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing. - { - ImVec2 padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding); - window->Pos = ImMax(window->Pos + window->Size, padding) - window->Size; - window->Pos = ImMin(window->Pos, g.IO.DisplaySize - padding); - } + ImVec2 clamp_padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding); + window->Pos = ImMax(window->Pos + window->Size, clamp_padding) - window->Size; + window->Pos = ImMin(window->Pos, g.IO.DisplaySize - clamp_padding); } window->Pos = ImFloor(window->Pos); @@ -4795,8 +4913,12 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Apply window focus (new and reactivated windows are moved to front) bool want_focus = false; if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing)) - if (!(flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) || (flags & ImGuiWindowFlags_Popup)) + { + if (flags & ImGuiWindowFlags_Popup) want_focus = true; + else if ((window->DockIsActive || !(flags & ImGuiWindowFlags_ChildWindow)) && !(flags & ImGuiWindowFlags_Tooltip)) + want_focus = true; + } // Handle manual resize: Resize Grips, Borders, Gamepad int border_held = -1; @@ -4846,7 +4968,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) const float window_rounding = window->WindowRounding; const float window_border_size = window->WindowBorderSize; const ImGuiWindow* window_to_highlight = g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow; - const bool title_bar_is_highlight = want_focus || (window_to_highlight && window->RootWindowForTitleBarHighlight == window_to_highlight->RootWindowForTitleBarHighlight); + const bool title_bar_is_highlight = want_focus || (window_to_highlight && (window->RootWindowForTitleBarHighlight == window_to_highlight->RootWindowForTitleBarHighlight || (window->DockNode && window->DockNode == window_to_highlight->DockNode))); const ImRect title_bar_rect = window->TitleBarRect(); if (window->Collapsed) { @@ -4862,16 +4984,17 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Window background ImU32 bg_col = GetColorU32(GetWindowBgColorIdxFromFlags(flags)); if (g.NextWindowData.BgAlphaCond != 0) - { bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(g.NextWindowData.BgAlphaVal) << IM_COL32_A_SHIFT); - g.NextWindowData.BgAlphaCond = 0; - } window->DrawList->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight()), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Bot); // Title bar - ImU32 title_bar_col = GetColorU32(window->Collapsed ? ImGuiCol_TitleBgCollapsed : title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg); - if (!(flags & ImGuiWindowFlags_NoTitleBar)) + // (when docked, DockNode are drawing their own title bar. Individual windows however do NOT set the _NoTitleBar flag, + // in order for their pos/size to be matching their undocking state.) + if (!(flags & ImGuiWindowFlags_NoTitleBar) && !window->DockIsActive) + { + ImU32 title_bar_col = GetColorU32(window->Collapsed ? ImGuiCol_TitleBgCollapsed : title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg); window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawCornerFlags_Top); + } // Menu bar if (flags & ImGuiWindowFlags_MenuBar) @@ -4911,7 +5034,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) ImRect border = GetResizeBorderRect(window, border_held, grip_draw_size, 0.0f); window->DrawList->AddLine(border.Min, border.Max, GetColorU32(ImGuiCol_SeparatorActive), ImMax(1.0f, window_border_size)); } - if (style.FrameBorderSize > 0 && !(flags & ImGuiWindowFlags_NoTitleBar)) + if (style.FrameBorderSize > 0 && !(flags & ImGuiWindowFlags_NoTitleBar) && !window->DockIsActive) window->DrawList->AddLine(title_bar_rect.GetBL() + ImVec2(style.WindowBorderSize, -1), title_bar_rect.GetBR() + ImVec2(-style.WindowBorderSize, -1), GetColorU32(ImGuiCol_Border), style.FrameBorderSize); } @@ -4991,7 +5114,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) } // Title bar - if (!(flags & ImGuiWindowFlags_NoTitleBar)) + if (!(flags & ImGuiWindowFlags_NoTitleBar) && !window->DockIsActive) { // Close & collapse button are on layer 1 (same as menus) and don't default focus const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags; @@ -5007,9 +5130,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Close button if (p_open != NULL) { - const float pad = style.FramePadding.y; const float rad = g.FontSize * 0.5f; - if (CloseButton(window->GetID("#CLOSE"), window->Rect().GetTR() + ImVec2(-pad - rad, pad + rad), rad + 1)) + if (CloseButton(window->GetID("#CLOSE"), ImVec2(window->Pos.x + window->Size.x - style.FramePadding.x - rad, window->Pos.y + style.FramePadding.y + rad), rad + 1)) *p_open = false; } @@ -5042,6 +5164,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Save clipped aabb so we can access it in constant-time in FindHoveredWindow() window->OuterRectClipped = window->Rect(); + if (window->DockIsActive) + window->OuterRectClipped.Min.y += window->TitleBarHeight(); window->OuterRectClipped.ClipWith(window->ClipRect); // Pressing CTRL+C while holding on a window copy its content to the clipboard @@ -5069,13 +5193,39 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->InnerClipRect.Max.x = ImFloor(0.5f + window->InnerMainRect.Max.x - ImMax(0.0f, ImFloor(window->WindowPadding.x*0.5f - window->WindowBorderSize))); window->InnerClipRect.Max.y = ImFloor(0.5f + window->InnerMainRect.Max.y); - // After Begin() we fill the last item / hovered data based on title bar data. It is a standard behavior (to allow creation of context menus on title bar only, etc.). - window->DC.LastItemId = window->MoveId; - window->DC.LastItemStatusFlags = IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0; - window->DC.LastItemRect = title_bar_rect; + if (g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable) + { + // Docking: Dragging a dockable window (or any of its child) turns it into a drag and drop source. + // We need to do this _before_ we overwrite window->DC.LastItemId below because BeginAsDockableDragDropSource() also overwrites it. + if ((g.ActiveId == window->MoveId) && ((g.IO.ConfigDockingWithKeyMod && g.IO.KeyShift) || (!g.IO.ConfigDockingWithKeyMod))) + if ((window->RootWindow->Flags & (ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDocking)) == 0) + BeginAsDockableDragDropSource(window); + + // Docking: Any dockable window can act as a target. For dock node hosts we call BeginAsDockableDragDropTarget() in DockNodeUpdate() instead. + if (g.DragDropActive && !(flags & ImGuiWindowFlags_NoDocking)) + if (g.MovingWindow == NULL || g.MovingWindow->RootWindow != window) + if ((window == window->RootWindow) && !(window->Flags & ImGuiWindowFlags_DockNodeHost)) + BeginAsDockableDragDropTarget(window); + } + + // We fill last item data based on Title Bar or Tab, in order for IsItemHovered() and IsItemActive() to be usable after Begin(). + // This is useful to allow creating context menus on title bar only, etc. + if (window->DockIsActive) + { + window->DC.LastItemId = window->ID; + window->DC.LastItemStatusFlags = window->DockTabItemStatusFlags; + window->DC.LastItemRect = window->DockTabItemRect; + } + else + { + window->DC.LastItemId = window->MoveId; + window->DC.LastItemStatusFlags = IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0; + window->DC.LastItemRect = title_bar_rect; + } } - PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true); + if (!(flags & ImGuiWindowFlags_DockNodeHost)) + PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true); // Clear 'accessed' flag last thing (After PushClipRect which will set the flag. We want the flag to stay false when the default "Debug" window is unused) if (first_begin_of_the_frame) @@ -5084,12 +5234,14 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->BeginCount++; g.NextWindowData.Clear(); + if (window->DockIsActive && !window->DockTabIsVisible) + window->HiddenFramesRegular = 1; + if (flags & ImGuiWindowFlags_ChildWindow) { // Child window can be out of sight and have "negative" clip windows. // Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar). - IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0); - + IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0 || (window->DockIsActive)); if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y) window->HiddenFramesRegular = 1; @@ -5104,7 +5256,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->HiddenFramesRegular = 1; // Update the Hidden flag - window->Hidden = (window->HiddenFramesRegular > 0) || (window->HiddenFramesForResize); + window->Hidden = (window->HiddenFramesRegular > 0) || (window->HiddenFramesForResize > 0); // Return false if we don't intend to display anything to allow user to perform an early out optimization window->SkipItems = (window->Collapsed || !window->Active || window->Hidden) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && window->HiddenFramesForResize <= 0; @@ -5135,12 +5287,18 @@ void ImGui::End() if (window->DC.ColumnsSet != NULL) EndColumns(); - PopClipRect(); // Inner window clip rectangle + if (!(window->Flags & ImGuiWindowFlags_DockNodeHost)) // Pop inner window clip rectangle + PopClipRect(); // Stop logging if (!(window->Flags & ImGuiWindowFlags_ChildWindow)) // FIXME: add more options for scope of logging LogFinish(); + // Docking: report contents sizes to parent to allow for auto-resize + if (window->DockNode && window->DockTabIsVisible) + if (ImGuiWindow* host_window = window->DockNode->HostWindow) // FIXME-DOCKSPACE + host_window->DC.CursorMaxPos = window->DC.CursorMaxPos + window->WindowPadding - host_window->WindowPadding; + // Pop from window stack g.CurrentWindowStack.pop_back(); if (window->Flags & ImGuiWindowFlags_Popup) @@ -5192,7 +5350,7 @@ void ImGui::FocusWindow(ImGuiWindow* window) g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId g.NavIdIsAlive = false; g.NavLayer = 0; - //printf("[%05d] FocusWindow(\"%s\")\n", g.FrameCount, window ? window->Name : NULL); + //IMGUI_DEBUG_LOG("FocusWindow(\"%s\")\n", window ? window->Name : NULL); } // Passing NULL allow to disable keyboard focus @@ -5511,6 +5669,8 @@ const char* ImGui::GetStyleColorName(ImGuiCol idx) case ImGuiCol_TabActive: return "TabActive"; case ImGuiCol_TabUnfocused: return "TabUnfocused"; case ImGuiCol_TabUnfocusedActive: return "TabUnfocusedActive"; + case ImGuiCol_DockingPreview: return "DockingPreview"; + case ImGuiCol_DockingBg: return "DockingBg"; case ImGuiCol_PlotLines: return "PlotLines"; case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered"; case ImGuiCol_PlotHistogram: return "PlotHistogram"; @@ -5554,11 +5714,11 @@ bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags) switch (flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) { case ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows: - if (g.HoveredRootWindow != g.CurrentWindow->RootWindow) + if (g.HoveredWindow == NULL || g.HoveredWindow->RootWindowDockStop != g.CurrentWindow->RootWindowDockStop) return false; break; case ImGuiHoveredFlags_RootWindow: - if (g.HoveredWindow != g.CurrentWindow->RootWindow) + if (g.HoveredWindow != g.CurrentWindow->RootWindowDockStop) return false; break; case ImGuiHoveredFlags_ChildWindows: @@ -5591,9 +5751,9 @@ bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags) switch (flags & (ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows)) { case ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows: - return g.NavWindow && g.NavWindow->RootWindow == g.CurrentWindow->RootWindow; + return g.NavWindow && g.NavWindow->RootWindowDockStop == g.CurrentWindow->RootWindowDockStop; case ImGuiFocusedFlags_RootWindow: - return g.NavWindow == g.CurrentWindow->RootWindow; + return g.NavWindow == g.CurrentWindow->RootWindowDockStop; case ImGuiFocusedFlags_ChildWindows: return g.NavWindow && IsWindowChildOf(g.NavWindow, g.CurrentWindow); default: @@ -5601,10 +5761,16 @@ bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags) } } +bool ImGui::IsWindowDocked() +{ + ImGuiContext& g = *GImGui; + return (g.CurrentWindow->DockIsActive); +} + // Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext) bool ImGui::IsWindowNavFocusable(ImGuiWindow* window) { - return window->Active && window == window->RootWindow && !(window->Flags & ImGuiWindowFlags_NoNavFocus); + return window->Active && window == window->RootWindowDockStop && !(window->Flags & ImGuiWindowFlags_NoNavFocus); } float ImGui::GetWindowWidth() @@ -5777,6 +5943,7 @@ void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiCond cond, const ImVec2& pi g.NextWindowData.PosVal = pos; g.NextWindowData.PosPivotVal = pivot; g.NextWindowData.PosCond = cond ? cond : ImGuiCond_Always; + g.NextWindowData.PosUndock = true; } void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiCond cond) @@ -5824,6 +5991,13 @@ void ImGui::SetNextWindowBgAlpha(float alpha) g.NextWindowData.BgAlphaCond = ImGuiCond_Always; // Using a Cond member for consistency (may transition all of them to single flag set for fast Clear() op) } +void ImGui::SetNextWindowDock(ImGuiID id, ImGuiCond cond) +{ + ImGuiContext& g = *GImGui; + g.NextWindowData.DockCond = cond ? cond : ImGuiCond_Always; + g.NextWindowData.DockId = id; +} + // In window space (not screen space!) ImVec2 ImGui::GetContentRegionMax() { @@ -6280,7 +6454,7 @@ void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, bool override_previous_ window->HiddenFramesRegular = 1; ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", ++g.TooltipOverrideCount); } - ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip|ImGuiWindowFlags_NoInputs|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoNav; + ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip|ImGuiWindowFlags_NoInputs|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoNav|ImGuiWindowFlags_NoDocking; Begin(window_name, NULL, flags | extra_flags); } @@ -6494,7 +6668,7 @@ bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags) g.NextWindowData.Clear(); // We behave like Begin() and need to consume those values return false; } - return BeginPopupEx(g.CurrentWindow->GetID(str_id), flags|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings); + return BeginPopupEx(g.CurrentWindow->GetID(str_id), flags|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_NoDocking); } bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags) @@ -6992,8 +7166,8 @@ static void NavRestoreLayer(int layer) g.NavLayer = layer; if (layer == 0) g.NavWindow = ImGui::NavRestoreLastChildNavWindow(g.NavWindow); - if (layer == 0 && g.NavWindow->NavLastIds[0] != 0) - ImGui::SetNavIDWithRectRel(g.NavWindow->NavLastIds[0], layer, g.NavWindow->NavRectRel[0]); + if (g.NavWindow->NavLastIds[layer] != 0) + ImGui::SetNavIDWithRectRel(g.NavWindow->NavLastIds[layer], layer, g.NavWindow->NavRectRel[layer]); else ImGui::NavInitWindow(g.NavWindow, true); } @@ -7034,7 +7208,10 @@ static ImVec2 ImGui::NavCalcPreferredRefPos() { ImGuiContext& g = *GImGui; if (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow) + { + IM_ASSERT(ImGui::IsMousePosValid()); // This will probably trigger at some point, please share your repro! return ImFloor(g.IO.MousePos); + } // When navigation is active and mouse is disabled, decide on an arbitrary position around the bottom left of the currently navigated item const ImRect& rect_rel = g.NavWindow->NavRectRel[g.NavLayer]; @@ -7215,7 +7392,7 @@ static void ImGui::NavUpdate() { ClearActiveID(); } - else if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow) && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow) + else if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow) && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow && g.NavWindow != g.NavWindow->RootWindowDockStop) { // Exit child window ImGuiWindow* child_window = g.NavWindow; @@ -7596,7 +7773,7 @@ static void ImGui::NavUpdateWindowing() } // Apply final focus - if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindow)) + if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindowDockStop)) { g.NavDisableHighlight = false; g.NavDisableMouseHover = true; @@ -7618,7 +7795,10 @@ static void ImGui::NavUpdateWindowing() { // Move to parent menu if necessary ImGuiWindow* new_nav_window = g.NavWindow; - while ((new_nav_window->DC.NavLayerActiveMask & (1 << 1)) == 0 && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0) + while (new_nav_window->ParentWindow + && (new_nav_window->DC.NavLayerActiveMask & (1 << 1)) == 0 + && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 + && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0) new_nav_window = new_nav_window->ParentWindow; if (new_nav_window != g.NavWindow) { @@ -7628,7 +7808,13 @@ static void ImGui::NavUpdateWindowing() } g.NavDisableHighlight = false; g.NavDisableMouseHover = true; - NavRestoreLayer((g.NavWindow->DC.NavLayerActiveMask & (1 << 1)) ? (g.NavLayer ^ 1) : 0); + + // When entering a regular menu bar with the Alt key, we always reinitialize the navigation ID. It however persist on docking tab tabs. + const int new_nav_layer = (g.NavWindow->DC.NavLayerActiveMask & (1 << 1)) ? (g.NavLayer ^ 1) : 0; + const bool preserve_layer_1_nav_id = (new_nav_window->DockNodeAsHost != NULL); + if (new_nav_layer == 1 && !preserve_layer_1_nav_id) + g.NavWindow->NavLastIds[1] = 0; + NavRestoreLayer(new_nav_layer); } } @@ -7639,6 +7825,8 @@ static const char* GetFallbackWindowNameForWindowingList(ImGuiWindow* window) return "(Popup)"; if ((window->Flags & ImGuiWindowFlags_MenuBar) && strcmp(window->Name, "##MainMenuBar") == 0) return "(Main menu bar)"; + if (window->DockNodeAsHost) + return "(Dock node)"; return "(Untitled)"; } @@ -8178,7 +8366,7 @@ bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id) return false; ImGuiWindow* window = g.CurrentWindow; - if (g.HoveredWindow == NULL || window->RootWindow != g.HoveredWindow->RootWindow) + if (g.HoveredWindowUnderMovingWindow == NULL || window->RootWindow != g.HoveredWindowUnderMovingWindow->RootWindow) return false; IM_ASSERT(id != 0); if (!IsMouseHoveringRect(bb.Min, bb.Max) || (id == g.DragDropPayload.SourceId)) @@ -8206,7 +8394,7 @@ bool ImGui::BeginDragDropTarget() ImGuiWindow* window = g.CurrentWindow; if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect)) return false; - if (g.HoveredWindow == NULL || window->RootWindow != g.HoveredWindow->RootWindow) + if (g.HoveredWindowUnderMovingWindow == NULL || window->RootWindow != g.HoveredWindowUnderMovingWindow->RootWindow) return false; const ImRect& display_rect = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? window->DC.LastItemDisplayRect : window->DC.LastItemRect; @@ -8663,6 +8851,7 @@ void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size) } ImGui::MemFree(buf); g.SettingsLoaded = true; + DockContextOnLoadSettings(); } void ImGui::SaveIniSettingsToDisk(const char* ini_filename) @@ -8711,9 +8900,12 @@ static void SettingsHandlerWindow_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry; float x, y; int i; - if (sscanf(line, "Pos=%f,%f", &x, &y) == 2) settings->Pos = ImVec2(x, y); - else if (sscanf(line, "Size=%f,%f", &x, &y) == 2) settings->Size = ImMax(ImVec2(x, y), GImGui->Style.WindowMinSize); - else if (sscanf(line, "Collapsed=%d", &i) == 1) settings->Collapsed = (i != 0); + ImU32 u1; + if (sscanf(line, "Pos=%f,%f", &x, &y) == 2) { settings->Pos = ImVec2(x, y); } + else if (sscanf(line, "Size=%f,%f", &x, &y) == 2) { settings->Size = ImMax(ImVec2(x, y), GImGui->Style.WindowMinSize); } + else if (sscanf(line, "Collapsed=%d", &i) == 1) { settings->Collapsed = (i != 0); } + else if (sscanf(line, "DockId=0x%X,%d", &u1, &i) == 2) { settings->DockId = u1; settings->DockOrder = (short)i; } + else if (sscanf(line, "DockId=0x%X", &u1) == 1) { settings->DockId = u1; settings->DockOrder = -1; } } static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf) @@ -8721,6 +8913,8 @@ static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSetting // Gather data from windows that were active during this session // (if a window wasn't opened in this session we preserve its settings) ImGuiContext& g = *imgui_ctx; + if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable)) + return; for (int i = 0; i != g.Windows.Size; i++) { ImGuiWindow* window = g.Windows[i]; @@ -8736,6 +8930,9 @@ static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSetting IM_ASSERT(settings->ID == window->ID); settings->Pos = window->Pos; settings->Size = window->SizeFull; + IM_ASSERT(window->DockNode == NULL || window->DockNode->ID == window->DockId); + settings->DockId = window->DockId; + settings->DockOrder = window->DockOrder; settings->Collapsed = window->Collapsed; } @@ -8744,8 +8941,6 @@ static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSetting for (int i = 0; i != g.SettingsWindows.Size; i++) { const ImGuiWindowSettings* settings = &g.SettingsWindows[i]; - if (settings->Pos.x == FLT_MAX) - continue; const char* name = settings->Name; if (const char* p = strstr(name, "###")) // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID() name = p; @@ -8753,6 +8948,14 @@ static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSetting buf->appendf("Pos=%d,%d\n", (int)settings->Pos.x, (int)settings->Pos.y); buf->appendf("Size=%d,%d\n", (int)settings->Size.x, (int)settings->Size.y); buf->appendf("Collapsed=%d\n", settings->Collapsed); + if (settings->DockId != 0) + { + // Write DockId as 4 digits if possible. Automatic DockId are small numbers, but full explicit DockSpace() are full ImGuiID range. + if (settings->DockOrder == -1) + buf->appendf("DockId=0x%08X\n", settings->DockId); + else + buf->appendf("DockId=0x%08X,%d\n", settings->DockId, settings->DockOrder); + } buf->appendf("\n"); } } @@ -8899,6 +9102,32 @@ void ImGui::ShowMetricsWindow(bool* p_open) struct Funcs { + static void NodeDockNode(ImGuiDockNode* node, const char* label) + { + ImGui::SetNextTreeNodeOpen(true, ImGuiCond_Once); + bool open; + if (node->Windows.Size > 0) + open = ImGui::TreeNode((void*)(intptr_t)node->ID, "%s 0x%04X%s: %d windows (vis: '%s')", label, node->ID, node->IsVisible ? "" : " (hidden)", node->Windows.Size, node->VisibleWindow ? node->VisibleWindow->Name : "NULL"); + else + open = ImGui::TreeNode((void*)(intptr_t)node->ID, "%s 0x%04X%s: split %s (act: '%s')", label, node->ID, node->IsVisible ? "" : " (hidden)", (node->SplitAxis == ImGuiAxis_X) ? "horizontal" : (node->SplitAxis == ImGuiAxis_Y) ? "vertical" : "n/a", node->VisibleWindow ? node->VisibleWindow->Name : "NULL"); + if (open) + { + IM_ASSERT(node->ChildNodes[0] == NULL || node->ChildNodes[0]->ParentNode == node); + IM_ASSERT(node->ChildNodes[1] == NULL || node->ChildNodes[1]->ParentNode == node); + ImGui::BulletText("Pos (%.0f,%.0f), Size (%.0f, %.0f), LastExplicit (%.0f, %.0f)%s%s", + node->Pos.x, node->Pos.y, node->Size.x, node->Size.y, + node->LastExplicitSize.x, node->LastExplicitSize.y, + node->IsExplicitRoot ? ", IsExplicitRoot " : "", node->IsDocumentRoot ? ", IsDocumentRoot " : ""); + if (node->ChildNodes[0]) + NodeDockNode(node->ChildNodes[0], "Child[0]"); + if (node->ChildNodes[1]) + NodeDockNode(node->ChildNodes[1], "Child[1]"); + if (node->TabBar) + NodeTabBar(node->TabBar); + ImGui::TreePop(); + } + } + static void NodeTabBar(ImGuiTabBar* tab_bar) { // Previous window list @@ -9027,7 +9256,9 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::BulletText("NavRectRel[0]: (%.1f,%.1f)(%.1f,%.1f)", window->NavRectRel[0].Min.x, window->NavRectRel[0].Min.y, window->NavRectRel[0].Max.x, window->NavRectRel[0].Max.y); else ImGui::BulletText("NavRectRel[0]: "); + ImGui::BulletText("DockId: 0x%04X, DockOrder: %d, %s: 0x%p", window->DockId, window->DockOrder, window->DockNodeAsHost ? "DockNodeAsHost" : "DockNode", window->DockNodeAsHost ? window->DockNodeAsHost : window->DockNode); if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow"); + if (window->RootWindowDockStop != window->RootWindow) NodeWindow(window->RootWindowDockStop, "RootWindowDockStop"); if (window->ParentWindow != NULL) NodeWindow(window->ParentWindow, "ParentWindow"); if (window->DC.ChildWindows.Size > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows"); if (window->ColumnsStorage.Size > 0 && ImGui::TreeNode("Columns", "Columns sets (%d)", window->ColumnsStorage.Size)) @@ -9059,6 +9290,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) Funcs::NodeDrawList(NULL, g.DrawDataBuilder.Layers[0][i], "DrawList"); ImGui::TreePop(); } + if (ImGui::TreeNode("Popups", "Popups (%d)", g.OpenPopupStack.Size)) { for (int i = 0; i < g.OpenPopupStack.Size; i++) @@ -9068,17 +9300,93 @@ void ImGui::ShowMetricsWindow(bool* p_open) } ImGui::TreePop(); } - if (ImGui::TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.Data.Size)) + + if (ImGui::TreeNode("Docking")) { - for (int n = 0; n < g.TabBars.Data.Size; n++) - Funcs::NodeTabBar(g.TabBars.GetByIndex(n)); + ImGuiDockContext* ctx = g.DockContext; + static bool show_window_dock_info = false; + ImGui::Checkbox("Ctrl shows window dock info", &show_window_dock_info); + + if (ImGui::TreeNode("Dock nodes")) + { + if (ImGui::SmallButton("Clear settings")) { DockContextClearNodes(ctx, true); } + ImGui::SameLine(); + if (ImGui::SmallButton("Rebuild all")) { ctx->WantFullRebuild = true; } + for (int n = 0; n < ctx->Nodes.Data.Size; n++) + if (ImGuiDockNode* node = (ImGuiDockNode*)ctx->Nodes.Data[n].val_p) + if (node->IsRootNode()) + Funcs::NodeDockNode(node, "Node"); + ImGui::TreePop(); + } + + if (ImGui::TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.Data.Size)) + { + for (int n = 0; n < g.TabBars.Data.Size; n++) + Funcs::NodeTabBar(g.TabBars.GetByIndex(n)); + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Settings")) + { + if (ImGui::SmallButton("Refresh")) + SaveIniSettingsToMemory(); + ImGui::SameLine(); + if (ImGui::SmallButton("Save to disk")) + SaveIniSettingsToDisk(g.IO.IniFilename); + ImGui::Separator(); + ImGui::Text("Docked Windows:"); + for (int n = 0; n < g.SettingsWindows.Size; n++) + if (g.SettingsWindows[n].DockId != 0) + ImGui::BulletText("Window '%s' -> DockId %08X", g.SettingsWindows[n].Name, g.SettingsWindows[n].DockId); + ImGui::Separator(); + ImGui::Text("Dock Nodes:"); + for (int n = 0; n < ctx->SettingsNodes.Size; n++) + { + ImGuiDockNodeSettings* settings = &ctx->SettingsNodes[n]; + const char* selected_tab_name = NULL; + if (settings->SelectedTabID) + { + if (ImGuiWindow* window = FindWindowByID(settings->SelectedTabID)) + selected_tab_name = window->Name; + else if (ImGuiWindowSettings* window_settings = FindWindowSettings(settings->SelectedTabID)) + selected_tab_name = window_settings->Name; + } + ImGui::BulletText("Node %08X, Parent %08X, SelectedTab %08X ('%s')", settings->ID, settings->ParentID, settings->SelectedTabID, selected_tab_name ? selected_tab_name : settings->SelectedTabID ? "N/A" : ""); + } + ImGui::TreePop(); + } + + if (g.IO.KeyCtrl && show_window_dock_info) + { + for (int n = 0; n < ctx->Nodes.Data.Size; n++) + if (ImGuiDockNode* node = (ImGuiDockNode*)ctx->Nodes.Data[n].val_p) + { + ImGuiDockNode* root_node = DockNodeGetRootNode(node); + if (ImGuiDockNode* hovered_node = DockNodeTreeFindNodeByPos(root_node, g.IO.MousePos)) + if (hovered_node != node) + continue; + char buf[64] = ""; + char* p = buf; + ImDrawList* overlay_draw_list = GetOverlayDrawList(); + p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "DockId: %X%s\n", node->ID, node->IsDocumentRoot ? " *DocRoot*" : ""); + p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "Size: (%.0f, %.0f)\n", node->Size.x, node->Size.y); + p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "Explicit: (%.0f, %.0f)\n", node->LastExplicitSize.x, node->LastExplicitSize.y); + int depth = DockNodeGetDepth(node); + overlay_draw_list->AddRect(node->Pos + ImVec2(3,3) * (float)depth, node->Pos + node->Size - ImVec2(3,3) * (float)depth, IM_COL32(200, 100, 100, 255)); + ImVec2 pos = node->Pos + ImVec2(3,3) * (float)depth; + overlay_draw_list->AddRectFilled(pos - ImVec2(1, 1), pos + CalcTextSize(buf) + ImVec2(1, 1), IM_COL32(200, 100, 100, 255)); + overlay_draw_list->AddText(NULL, 0.0f, pos, IM_COL32(255, 255, 255, 255), buf); + } + } ImGui::TreePop(); } + if (ImGui::TreeNode("Internal state")) { const char* input_source_names[] = { "None", "Mouse", "Nav", "NavKeyboard", "NavGamepad" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT); ImGui::Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL"); ImGui::Text("HoveredRootWindow: '%s'", g.HoveredRootWindow ? g.HoveredRootWindow->Name : "NULL"); + ImGui::Text("HoveredWindowUnderMovingWindow: '%s'", g.HoveredWindowUnderMovingWindow ? g.HoveredWindowUnderMovingWindow->Name : "NULL"); ImGui::Text("HoveredId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d", g.HoveredId, g.HoveredIdPreviousFrame, g.HoveredIdTimer, g.HoveredIdAllowOverlap); // Data is "in-flight" so depending on when the Metrics window is called we may see current frame information or not ImGui::Text("ActiveId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d, Source: %s", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdTimer, g.ActiveIdAllowOverlap, input_source_names[g.ActiveIdSource]); ImGui::Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL"); @@ -9094,7 +9402,6 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::TreePop(); } - if (g.IO.KeyCtrl && show_window_begin_order) { for (int n = 0; n < g.Windows.Size; n++) From e381f22ac139815e253f351fdab5a066f224de8a Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 6 Sep 2018 21:38:43 +0200 Subject: [PATCH 236/828] Docking: Added Docking system. (Part 3) (#351) --- imgui.cpp | 2026 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 2014 insertions(+), 12 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 08dc636c3f96..457095441f96 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -8503,55 +8503,2057 @@ void ImGui::EndDragDropTarget() // Docking: Internal Types //----------------------------------------------------------------------------- +static float IMGUI_DOCK_SPLITTER_SIZE = 4.0f; +struct ImGuiDockRequest +{ + ImGuiWindow* WindowDockTarget; // Destination/Target window to dock into (may be a loose window or a DockNode) + ImGuiDockNode* WindowDockTargetNode; + ImGuiWindow* WindowDockPayload; // Source/Payload window to dock (may be a loose window or a DockNode) + ImGuiDir WindowDockSplitDir; + float WindowDockSplitRatio; + bool WindowDockSplitOuter; + ImGuiWindow* WindowUndock; + + ImGuiDockRequest() + { + WindowDockTarget = WindowDockPayload = WindowUndock = NULL; + WindowDockTargetNode = NULL; + WindowDockSplitDir = ImGuiDir_None; + WindowDockSplitRatio = 0.5f; + WindowDockSplitOuter = false; + } +}; + +struct ImGuiDockPreviewData +{ + ImGuiDockNode FutureNode; + bool IsDropAllowed; + bool IsCenterAvailable; + bool IsSidesAvailable; // Hold your breath, grammar freaks.. + bool IsSplitDirExplicit; // Set when hovered the drop rect (vs. implicit SplitDir==None when hovered the window) + ImGuiDockNode* SplitNode; + ImGuiDir SplitDir; + float SplitRatio; + ImRect DropRectsDraw[ImGuiDir_COUNT + 1]; // May be slightly different from hit-testing drop rects used in DockNodeCalcDropRects() + + ImGuiDockPreviewData() : FutureNode(0) { IsDropAllowed = IsCenterAvailable = IsSidesAvailable = IsSplitDirExplicit = false; SplitNode = NULL; SplitDir = ImGuiDir_None; SplitRatio = 0.f; } +}; + +// Persistent Settings data, stored contiguously in SettingsNodes (sizeof() ~32 bytes) +struct ImGuiDockNodeSettings +{ + ImGuiID ID; + ImGuiID ParentID; + ImGuiID SelectedTabID; + float SplitRatio; + char SplitAxis; + char Depth; + char IsExplicitRoot; + char IsDocumentRoot; + ImVec2ih Pos; + ImVec2ih Size; + ImVec2ih LastExplicitSize; + ImGuiDockNodeSettings() { ID = ParentID = SelectedTabID = 0; SplitRatio = 0.0f; SplitAxis = ImGuiAxis_None; Depth = 0; IsExplicitRoot = IsDocumentRoot = 0; } +}; + +struct ImGuiDockContext +{ + ImGuiStorage Nodes; // Map ID -> ImGuiDockNode*: Active nodes + ImVector Requests; + ImVector SettingsNodes; + int SettingsMaxDepth; + bool WantFullRebuild; + ImGuiDockContext() { SettingsMaxDepth = 0; WantFullRebuild = false; } +}; + +//----------------------------------------------------------------------------- +// Docking: Forward Declarations +//----------------------------------------------------------------------------- + +namespace ImGui +{ + // ImGuiDockContext + static ImGuiDockNode* DockContextFindNodeByID(ImGuiDockContext* ctx, ImGuiID id); + static ImGuiDockNode* DockContextAddNode(ImGuiDockContext* ctx, ImGuiID id); + static void DockContextRemoveNode(ImGuiDockContext* ctx, ImGuiDockNode* node); + static void DockContextQueueDock(ImGuiDockContext* ctx, ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, float split_ratio, bool split_outer); + static void DockContextProcessDock(ImGuiDockContext* ctx, ImGuiDockRequest* req); + static void DockContextProcessUndock(ImGuiDockContext* ctx, ImGuiWindow* window); + static void DockContextClearNodes(ImGuiDockContext* ctx, bool clear_references); + static void DockContextGcUnusedNodes(ImGuiDockContext* ctx); + static void DockContextBuildNodesFromSettings(ImGuiDockContext* ctx); + static void DockContextBuildAddWindowsToNodes(ImGuiDockContext* ctx); + + // ImGuiDockNode + static void DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window); + static void DockNodeMoveWindows(ImGuiDockNode* dst_node, ImGuiDockNode* src_node); + static void DockNodeMoveChilds(ImGuiDockNode* dst_node, ImGuiDockNode* src_node); + static void DockNodeApplyPosSizeToWindows(ImGuiDockNode* node); + static void DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window, ImGuiID save_dock_id); + static void DockNodeHideHostWindow(ImGuiDockNode* node); + static void DockNodeUpdate(ImGuiDockNode* node); + static ImGuiDockNode* DockNodeUpdateFindOnlyNodeWithWindows(ImGuiDockNode* node); + static void DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* node); + static void DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_window); + static void DockNodeUpdateVisibleFlag(ImGuiDockNode* node); + static bool DockNodeIsDropAllowed(ImGuiWindow* host_window, ImGuiWindow* payload_window); + static bool DockNodePreviewDockCalc(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* payload_window, ImGuiDockPreviewData* preview_data, bool is_explicit_target, bool is_outer_docking); + static void DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* payload_window, const ImGuiDockPreviewData* preview_data); + static ImRect DockNodeCalcTabBarRect(const ImGuiDockNode* node); + static void DockNodeCalcSplitRects(ImVec2& pos_old, ImVec2& size_old, ImVec2& pos_new, ImVec2& size_new, ImGuiDir dir, ImVec2 size_new_desired); + static bool DockNodeCalcDropRects(const ImRect& parent, ImGuiDir dir, ImRect& out_draw, bool outer_docking); + static ImGuiDockNode* DockNodeGetRootNode(ImGuiDockNode* node) { while (node->ParentNode) node = node->ParentNode; return node; } + static int DockNodeGetDepth(ImGuiDockNode* node) { int depth = 0; while (node->ParentNode) { node = node->ParentNode; depth++; } return depth; } + static int DockNodeGetTabOrder(ImGuiWindow* window); + + // ImGuiDockNode tree manipulations + static void DockNodeTreeSplit(ImGuiDockContext* ctx, ImGuiDockNode* parent_node, ImGuiAxis split_axis, int split_first_child, float split_ratio, ImGuiDockNode* new_node); + static void DockNodeTreeMerge(ImGuiDockContext* ctx, ImGuiDockNode* parent_node, ImGuiDockNode* merge_lead_child); + static void DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 size); + static void DockNodeTreeUpdateSplitter(ImGuiDockNode* node); + static ImGuiDockNode* DockNodeTreeFindNodeByPos(ImGuiDockNode* node, ImVec2 pos); + + // Settings + static void DockSettingsRemoveAllReferencesToNode(ImGuiID node_id); + static ImGuiDockNodeSettings* DockSettingsFindNodeSettings(ImGuiDockContext* ctx, ImGuiID node_id); + static void* DockSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name); + static void DockSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line); + static void DockSettingsHandler_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf); +} + +//----------------------------------------------------------------------------- +// Docking: ImGuiDockContext +//----------------------------------------------------------------------------- + +void ImGui::DockContextInitialize(ImGuiContext* imgui_context) +{ + ImGuiContext& g = *imgui_context; + IM_ASSERT(g.DockContext == NULL); + g.DockContext = IM_NEW(ImGuiDockContext)(); + + // Add .ini handle for persistent docking data + ImGuiSettingsHandler ini_handler; + ini_handler.TypeName = "Docking"; + ini_handler.TypeHash = ImHash("Docking", 0, 0); + ini_handler.ReadOpenFn = DockSettingsHandler_ReadOpen; + ini_handler.ReadLineFn = DockSettingsHandler_ReadLine; + ini_handler.WriteAllFn = DockSettingsHandler_WriteAll; + g.SettingsHandlers.push_back(ini_handler); +} + +void ImGui::DockContextShutdown(ImGuiContext* imgui_context) +{ + ImGuiContext& g = *imgui_context; + ImGuiDockContext* ctx = g.DockContext; + for (int n = 0; n < ctx->Nodes.Data.Size; n++) + if (ImGuiDockNode* node = (ImGuiDockNode*)ctx->Nodes.Data[n].val_p) + IM_DELETE(node); + IM_DELETE(g.DockContext); + g.DockContext = NULL; +} + +void ImGui::DockContextOnLoadSettings() +{ + ImGuiContext& g = *GImGui; + ImGuiDockContext* ctx = g.DockContext; + DockContextGcUnusedNodes(ctx); + DockContextBuildNodesFromSettings(ctx); +} + +void ImGui::DockContextRebuild(ImGuiDockContext* ctx) +{ + SaveIniSettingsToMemory(); + DockContextClearNodes(ctx, false); + DockContextBuildNodesFromSettings(ctx); + DockContextBuildAddWindowsToNodes(ctx); +} + +void ImGui::DockContextUpdateUndocking(ImGuiDockContext* ctx) +{ + ImGuiContext& g = *GImGui; + if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable)) + { + if (ctx->Nodes.Data.Size > 0 || ctx->Requests.Size > 0) + DockContextClearNodes(ctx, true); + return; + } + +#if 0 + if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_C))) + ctx->WantFullRebuild = true; +#endif + if (ctx->WantFullRebuild) + { + DockContextRebuild(ctx); + ctx->WantFullRebuild = false; + } + + // Process Undocking requests (called from NewFrame before UpdateMovingWindow) + for (int n = 0; n < ctx->Requests.Size; n++) + if (ctx->Requests[n].WindowUndock) + DockContextProcessUndock(ctx, ctx->Requests[n].WindowUndock); +} + +void ImGui::DockContextUpdateDocking(ImGuiDockContext* ctx) +{ + ImGuiContext& g = *GImGui; + if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable)) + return; + + // Process Docking requests + for (int n = 0; n < ctx->Requests.Size; n++) + if (ctx->Requests[n].WindowDockTarget) + DockContextProcessDock(ctx, &ctx->Requests[n]); + ctx->Requests.resize(0); + + // Create windows for each automatic docking nodes + // We can have NULL pointers when we delete nodes, but because ID are recycled this should amortize nicely (and our node count will never be very high) + for (int n = 0; n < ctx->Nodes.Data.Size; n++) + if (ImGuiDockNode* node = (ImGuiDockNode*)ctx->Nodes.Data[n].val_p) + if (!node->IsExplicitRoot && node->IsRootNode()) + DockNodeUpdate(node); +} + + +static ImGuiDockNode* ImGui::DockContextFindNodeByID(ImGuiDockContext* ctx, ImGuiID id) +{ + return (ImGuiDockNode*)ctx->Nodes.GetVoidPtr(id); +} + +static ImGuiDockNode* ImGui::DockContextAddNode(ImGuiDockContext* ctx, ImGuiID id) +{ + // Generate an ID for the new node (the exact ID value doesn't matter as long as it is not already used) and add the first window. + if (id == (ImGuiID)-1) + { + // FIXME-OPT: This is very suboptimal, however the node count is small enough not to be a worry. + id = 0x0001; + while (DockContextFindNodeByID(ctx, id) != NULL) + id++; + } + IM_ASSERT(DockContextFindNodeByID(ctx, id) == NULL); + ImGuiDockNode* node = IM_NEW(ImGuiDockNode)(id); + node->InitFromFirstWindow = true; + ctx->Nodes.SetVoidPtr(node->ID, node); + return node; +} + +static void ImGui::DockContextRemoveNode(ImGuiDockContext* ctx, ImGuiDockNode* node) +{ + //printf("[%05d] RemoveNode 0x%04X\n", node->ID); + IM_ASSERT(DockContextFindNodeByID(ctx, node->ID) == node); + IM_ASSERT(node->ChildNodes[0] == NULL && node->ChildNodes[1] == NULL); + IM_ASSERT(node->Windows.Size == 1 || node->HostWindow->DockNodeAsHost == node); + + if (node->HostWindow) + node->HostWindow->DockNodeAsHost = NULL; + + if (ImGuiDockNode* parent_node = node->ParentNode) + { + IM_ASSERT(parent_node->ChildNodes[0] == node || parent_node->ChildNodes[1] == node); + ImGuiDockNode* sibling_node = (parent_node->ChildNodes[0] == node ? parent_node->ChildNodes[1] : parent_node->ChildNodes[0]); + DockNodeTreeMerge(ctx, parent_node, sibling_node); + } + else + { + ctx->Nodes.SetVoidPtr(node->ID, NULL); + IM_DELETE(node); + } +} + +// Stress/functional test to make sure we can rebuild from scratch without a glitch +void ImGui::DockContextClearNodes(ImGuiDockContext* ctx, bool clear_references) +{ + SaveIniSettingsToMemory(NULL); + + ImGuiContext& g = *GImGui; + for (int n = 0; n < ctx->Nodes.Data.Size; n++) + if (ImGuiDockNode* node = (ImGuiDockNode*)ctx->Nodes.Data[n].val_p) + IM_DELETE(node); + ctx->Nodes.Clear(); + ctx->Requests.clear(); + for (int n = 0; n < g.Windows.Size; n++) + { + ImGuiWindow* window = g.Windows[n]; + window->DockNode = window->DockNodeAsHost = NULL; + window->DockIsActive = false; + if (clear_references) + window->DockId = 0; + } +} + +static void ImGui::DockContextGcUnusedNodes(ImGuiDockContext* ctx) +{ + ImGuiContext& g = *GImGui; + + // Count reference to dock ids from window settings + ImGuiStorage ref_count_map; // Map dock_id -> counter + ref_count_map.Data.reserve(ctx->SettingsNodes.Size); + for (int settings_n = 0; settings_n < g.SettingsWindows.Size; settings_n++) + if (g.SettingsWindows[settings_n].DockId != 0) + (*ref_count_map.GetIntRef(g.SettingsWindows[settings_n].DockId, 0))++; + + // Mark nodes that are parents + ImGuiStorage is_parent_map; + is_parent_map.Data.reserve(ctx->SettingsNodes.Size); + for (int settings_n = 0; settings_n < ctx->SettingsNodes.Size; settings_n++) + if (ctx->SettingsNodes[settings_n].ParentID) + is_parent_map.SetInt(ctx->SettingsNodes[settings_n].ParentID, 1); + + // If a root node has only 1 reference in window settings we clear it + for (int settings_n = 0; settings_n < ctx->SettingsNodes.Size; settings_n++) + { + ImGuiDockNodeSettings* settings = &ctx->SettingsNodes[settings_n]; + int ref_count = ref_count_map.GetInt(settings->ID, 0); + if (ref_count <= 1) + { + bool remove = false; + remove |= (ref_count == 1 && settings->ParentID == 0 && !settings->IsDocumentRoot); // Root node with only 1 window + remove |= (ref_count == 0 && settings->ParentID == 0 && is_parent_map.GetInt(settings->ID, 0) == 0); // Leaf nodes with 0 window + if (remove) + { + DockSettingsRemoveAllReferencesToNode(settings->ID); + settings->ID = 0; + } + } + } +} + +static void ImGui::DockContextBuildNodesFromSettings(ImGuiDockContext* ctx) +{ + // Build nodes + for (int node_n = 0; node_n < ctx->SettingsNodes.Size; node_n++) + { + ImGuiDockNodeSettings* node_settings = &ctx->SettingsNodes[node_n]; + if (node_settings->ID == 0) + continue; + ImGuiDockNode* node = DockContextAddNode(ctx, node_settings->ID); + node->ParentNode = node_settings->ParentID ? DockContextFindNodeByID(ctx, node_settings->ParentID) : NULL; + node->Pos = ImVec2(node_settings->Pos.x, node_settings->Pos.y); + node->Size = ImVec2(node_settings->Size.x, node_settings->Size.y); + node->LastExplicitSize = ImVec2(node_settings->LastExplicitSize.x, node_settings->LastExplicitSize.y); + if (node->ParentNode && node->ParentNode->ChildNodes[0] == NULL) + node->ParentNode->ChildNodes[0] = node; + else if (node->ParentNode && node->ParentNode->ChildNodes[1] == NULL) + node->ParentNode->ChildNodes[1] = node; + node->SelectedTabID = node_settings->SelectedTabID; + node->SplitAxis = node_settings->SplitAxis; + node->SplitRatio = node_settings->SplitRatio; + node->IsExplicitRoot = node_settings->IsExplicitRoot != 0; + node->IsDocumentRoot = node_settings->IsDocumentRoot != 0; + } +} + +void ImGui::DockContextBuildAddWindowsToNodes(ImGuiDockContext* ctx) +{ + // Rebuild nodes (they can also lazily rebuild but we'll have a visible glitch during the first frame) + ImGuiContext& g = *GImGui; + for (int n = 0; n < g.Windows.Size; n++) + { + ImGuiWindow* window = g.Windows[n]; + if (window->DockId == 0 || window->LastFrameActive < g.FrameCount - 1) + continue; + + ImGuiDockNode* dock_node = DockContextFindNodeByID(ctx, window->DockId); + if (dock_node == NULL) + dock_node = DockContextAddNode(ctx, window->DockId); + DockNodeAddWindow(dock_node, window); + } +} + +//----------------------------------------------------------------------------- +// Docking: ImGuiDockContext Docking/Undocking functions +//----------------------------------------------------------------------------- + +void ImGui::DockContextQueueDock(ImGuiDockContext* ctx, ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, float split_ratio, bool split_outer) +{ + ImGuiDockRequest req; + req.WindowDockTarget = target; + req.WindowDockTargetNode = target_node; + req.WindowDockPayload = payload; + req.WindowDockSplitDir = split_dir; + req.WindowDockSplitRatio = split_ratio; + req.WindowDockSplitOuter = split_outer; + ctx->Requests.push_back(req); +} + +void ImGui::DockContextQueueUndock(ImGuiDockContext* ctx, ImGuiWindow* window) +{ + ImGuiDockRequest req; + req.WindowUndock = window; + ctx->Requests.push_back(req); +} + +void ImGui::DockContextProcessDock(ImGuiDockContext* ctx, ImGuiDockRequest* req) +{ + ImGuiWindow* target_window = req->WindowDockTarget; + ImGuiWindow* payload_window = req->WindowDockPayload; + ImGuiDockNode* target_node = req->WindowDockTargetNode; + + // Decide which Tab will be selected at the end of the operation (do it before the target/payload swap) + ImGuiID next_selected_id = 0; + ImGuiDockNode* payload_node = payload_window->DockNodeAsHost; + if (payload_node && !payload_node->IsParent()) + next_selected_id = payload_node->TabBar->NextSelectedTabId ? payload_node->TabBar->NextSelectedTabId : payload_node->TabBar->SelectedTabId; + if (payload_node == NULL) + next_selected_id = payload_window->ID; + + // FIXME-DOCK: When we are trying to dock an existing single-window node into a loose window, transfer Node ID as well + + if (target_node && target_node == target_window->DockNodeAsHost) + IM_ASSERT(target_node->Windows.Size > 1 || target_node->IsParent() || target_node->IsDocumentRoot); + + if (target_node == NULL) + { + // Create new node and add existing window to it + target_node = DockContextAddNode(ctx, (ImGuiID)-1); + target_node->Pos = target_window->Pos; + target_node->Size = target_window->Size; + if (target_window->DockNodeAsHost == NULL) + { + DockNodeAddWindow(target_node, target_window); + target_window->DockIsActive = true; + } + } + + ImGuiDir split_dir = req->WindowDockSplitDir; + if (split_dir == ImGuiDir_None) + { + target_node->LastFocusedNodeID = target_node ? target_node->ID : 0; + } + else + { + // Split into one, one side will be our payload node unless we are dropping a loose window + const ImGuiAxis split_axis = (split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y; + const int split_inheritor_child_idx = (split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Up) ? 1 : 0; + const float split_ratio = req->WindowDockSplitRatio; + if (payload_node) + DockNodeTreeSplit(ctx, target_node, split_axis, split_inheritor_child_idx, split_ratio, payload_node); + else + DockNodeTreeSplit(ctx, target_node, split_axis, split_inheritor_child_idx, split_ratio, NULL); + ImGuiDockNode* inheritor_node = target_node->ChildNodes[split_inheritor_child_idx]; + ImGuiDockNode* new_node = target_node->ChildNodes[split_inheritor_child_idx ^ 1]; + target_node->LastFocusedNodeID = new_node->ID; + new_node->HostWindow = target_node->HostWindow; + if (target_node) + { + inheritor_node->IsDocumentRoot = target_node->IsDocumentRoot; + target_node->IsDocumentRoot = false; + } + target_node = new_node; + } + + if (target_node != payload_node) + { + // Create tab bar before we call DockNoveMoveWindows (which would attempt to move the old tab-bar, not holding the tab order we expect) + if (target_node->Windows.Size > 0 && target_node->TabBar == NULL) + { + target_node->TabBar = IM_NEW(ImGuiTabBar)(); + for (int n = 0; n < target_node->Windows.Size; n++) + TabBarAddTab(target_node->TabBar, target_node->Windows[n]); + } + + if (payload_node != NULL) + { + // Transfer full payload node (with 1+ child windows or child nodes) + // FIXME-DOCKING: Transition persistent DockId for all non-active windows? + if (payload_node->IsParent()) + { + if (target_node->Windows.Size > 0) + { + // When can dock into a node that already has windows only if our payload is a node tree + // with a single visible node. In this situation, we move the windows of the target node + // into the visible node of the payload. + IM_ASSERT(payload_node->OnlyNodeWithWindows != NULL); + ImGuiDockNode* visible_node = payload_node->OnlyNodeWithWindows; + if (visible_node->TabBar) + IM_ASSERT(visible_node->TabBar->Tabs.Size > 0); + for (int n = 0; n < visible_node->Windows.Size; n++) + TabBarAddTab(target_node->TabBar, visible_node->Windows[n]); + DockNodeMoveWindows(target_node, visible_node); + DockNodeMoveWindows(visible_node, target_node); + } + DockNodeMoveChilds(target_node, payload_node); + } + else + { + DockNodeMoveWindows(target_node, payload_node); + } + DockContextRemoveNode(ctx, payload_node); + } + else + { + // Transfer single window + target_node->VisibleWindow = payload_window; + DockNodeAddWindow(target_node, payload_window); + } + } + + // Update selection immediately + if (target_node->TabBar) + target_node->TabBar->NextSelectedTabId = next_selected_id; +} + +void ImGui::DockContextProcessUndock(ImGuiDockContext* ctx, ImGuiWindow* window) +{ + (void)ctx; + if (window->DockNode) + DockNodeRemoveWindow(window->DockNode, window, 0); + else + window->DockId = 0; + window->Collapsed = false; + window->DockIsActive = false; + window->DockTabIsVisible = false; +} + +//----------------------------------------------------------------------------- +// Docking: ImGuiDockNode +//----------------------------------------------------------------------------- + +ImGuiDockNode::ImGuiDockNode(ImGuiID id) +{ + ID = id; + ParentNode = ChildNodes[0] = ChildNodes[1] = NULL; + TabBar = NULL; + SplitAxis = ImGuiAxis_None; + SplitRatio = 0.5f; + HostWindow = VisibleWindow = NULL; + OnlyNodeWithWindows = NULL; + SelectedTabID = 0; + LastFocusedNodeID = 0; + LastFrameActive = -1; + WantCloseOne = 0; + IsVisible = true; + InitFromFirstWindow = IsExplicitRoot = IsDocumentRoot = HasCloseButton = HasCollapseButton = WantCloseAll = WantLockSizeOnce = false; +} + +ImGuiDockNode::~ImGuiDockNode() +{ + IM_DELETE(TabBar); + TabBar = NULL; +} + +int ImGui::DockNodeGetTabOrder(ImGuiWindow* window) +{ + ImGuiTabBar* tab_bar = window->DockNode->TabBar; + if (tab_bar == NULL) + return -1; + ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, window->ID); + return tab ? tab_bar->GetTabOrder(tab) : -1; +} + +static void ImGui::DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; (void)g; + if (window->DockNode) + { + // Can overwrite an existing window->DockNode (e.g. pointing to a disabled DockSpace) + IM_ASSERT(window->DockNode->ID != node->ID); + DockNodeRemoveWindow(window->DockNode, window, 0); + } + IM_ASSERT(window->DockNode == NULL || window->DockNodeAsHost == NULL); + + node->Windows.push_back(window); + window->DockNode = node; + window->DockId = node->ID; + window->DockIsActive = (node->Windows.Size > 1); + window->DockTabWantClose = false; + MarkIniSettingsDirty(); + + // If 2+ windows appeared on the same frame, creating a new DockNode+TabBar from the second window, + // then we need to hide the first one after the fact otherwise it would be visible as a standalone window for one frame. + if (node->Windows.Size == 2 && node->HostWindow == NULL && node->Windows[0]->WasActive == false) + { + IM_ASSERT(node->Windows[0]->DockIsActive == false); + node->Windows[0]->Hidden = true; + node->Windows[0]->HiddenFramesRegular = 1; + } + + // When reactivating a node from two loose window, the target window pos/size are authoritative + if (node->Windows.Size == 2 && !node->IsExplicitRoot) + node->InitFromFirstWindow = true; + + if (node->TabBar) + node->TabBar->NextSelectedTabId = window->ID; + + DockNodeUpdateVisibleFlag(node); + + // Update this without waiting for the next time we Begin() in the window, so our host window will have the proper title bar color on its first frame. + if (node->HostWindow) + UpdateWindowParentAndRootLinks(window, window->Flags | ImGuiWindowFlags_ChildWindow, node->HostWindow); +} + +static void ImGui::DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window, ImGuiID save_dock_id) +{ + ImGuiContext& g = *GImGui; + ImGuiDockContext* ctx = g.DockContext; + IM_ASSERT(window->DockNode == node); + //IM_ASSERT(window->RootWindow == node->HostWindow); + IM_ASSERT(window->LastFrameActive < g.FrameCount); + IM_ASSERT(save_dock_id == 0 || save_dock_id == node->ID); + + window->DockNode = NULL; + window->DockIsActive = window->DockTabWantClose = false; + window->DockId = save_dock_id; + UpdateWindowParentAndRootLinks(window, window->Flags & ~ImGuiWindowFlags_ChildWindow, NULL); // Update immediately + MarkIniSettingsDirty(); + + if (node->Windows.Size == 1 && !node->IsDocumentRoot && window->DockId != node->ID) + { + // Automatic dock node delete themselves if they are not holding at least one tab + IM_ASSERT(node->Windows[0] == window); + DockContextRemoveNode(ctx, node); + return; + } + + bool erased = false; + for (int n = 0; n < node->Windows.Size; n++) + if (node->Windows[n] == window) + { + node->Windows.erase(node->Windows.Data + n); + erased = true; + break; + } + IM_ASSERT(erased); + + if (node->TabBar) + TabBarRemoveTab(node->TabBar, window->ID); + + if (node->Windows.Size == 1 && !node->IsDocumentRoot && node->HostWindow) + { + ImGuiWindow* remaining_window = node->Windows[0]; + remaining_window->Collapsed = node->HostWindow->Collapsed; + } + + int tab_count_threshold_for_tab_bar = node->IsDocumentRoot ? 1 : 2; + if (node->Windows.Size < tab_count_threshold_for_tab_bar && node->TabBar) + { + IM_DELETE(node->TabBar); + node->TabBar = NULL; + } + + // Update visibility immediately is required so the DockNodeUpdateRemoveInactiveChilds() processing can reflect changes up the tree + DockNodeUpdateVisibleFlag(node); +} + +static void ImGui::DockNodeMoveChilds(ImGuiDockNode* dst_node, ImGuiDockNode* src_node) +{ + IM_ASSERT(dst_node->Windows.Size == 0); + dst_node->ChildNodes[0] = src_node->ChildNodes[0]; + dst_node->ChildNodes[1] = src_node->ChildNodes[1]; + if (dst_node->ChildNodes[0]) + dst_node->ChildNodes[0]->ParentNode = dst_node; + if (dst_node->ChildNodes[1]) + dst_node->ChildNodes[1]->ParentNode = dst_node; + dst_node->SplitAxis = src_node->SplitAxis; + dst_node->SplitRatio = src_node->SplitRatio; + src_node->ChildNodes[0] = src_node->ChildNodes[1] = NULL; +} + +static void ImGui::DockNodeMoveWindows(ImGuiDockNode* dst_node, ImGuiDockNode* src_node) +{ + // Insert tabs in the same orders as currently ordered (node->Windows isn't ordered) + IM_ASSERT(dst_node != src_node); + ImGuiTabBar* src_tab_bar = src_node->TabBar; + if (src_tab_bar != NULL) + IM_ASSERT(src_node->Windows.Size == src_node->TabBar->Tabs.Size); + for (int n = 0; n < src_node->Windows.Size; n++) + { + ImGuiWindow* window = src_tab_bar ? src_tab_bar->Tabs[n].Window : src_node->Windows[n]; + window->DockNode = NULL; + window->DockIsActive = false; + DockNodeAddWindow(dst_node, window); + } + if (dst_node->TabBar == NULL) + dst_node->TabBar = src_node->TabBar; + else + IM_DELETE(src_node->TabBar); + src_node->TabBar = NULL; + src_node->Windows.clear(); +} + +static void ImGui::DockNodeApplyPosSizeToWindows(ImGuiDockNode* node) +{ + for (int n = 0; n < node->Windows.Size; n++) + { + node->Windows[n]->Pos = node->Pos; + node->Windows[n]->SizeFull = node->Size; + } +} + +static void ImGui::DockNodeHideHostWindow(ImGuiDockNode* node) +{ + if (node->HostWindow) + { + if (node->HostWindow->DockNodeAsHost == node) + node->HostWindow->DockNodeAsHost = NULL; + node->HostWindow = NULL; + } + + if (node->Windows.Size == 1) + { + node->VisibleWindow = node->Windows[0]; + node->Windows[0]->DockIsActive = false; + } + + if (node->TabBar) + { + IM_DELETE(node->TabBar); + node->TabBar = NULL; + } +} + +static void DockNodeUpdateFindOnlyNodeWithWindowsRec(ImGuiDockNode* node, int* p_count, ImGuiDockNode** p_only_node_with_windows) +{ + if (node->Windows.Size > 0) + { + if (*p_only_node_with_windows == NULL) + *p_only_node_with_windows = node; + (*p_count)++; + } + if (*p_count > 1) + return; + if (node->ChildNodes[0]) + DockNodeUpdateFindOnlyNodeWithWindowsRec(node->ChildNodes[0], p_count, p_only_node_with_windows); + if (node->ChildNodes[1]) + DockNodeUpdateFindOnlyNodeWithWindowsRec(node->ChildNodes[1], p_count, p_only_node_with_windows); +} + +static ImGuiDockNode* ImGui::DockNodeUpdateFindOnlyNodeWithWindows(ImGuiDockNode* node) +{ + int count = 0; + ImGuiDockNode* only_node_with_windows = NULL; + DockNodeUpdateFindOnlyNodeWithWindowsRec(node, &count, &only_node_with_windows); + return (count == 1 ? only_node_with_windows : NULL); +} + +static void ImGui::DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* node) +{ + ImGuiContext& g = *GImGui; + + IM_ASSERT(node->ParentNode == NULL || node->ParentNode->ChildNodes[0] == node || node->ParentNode->ChildNodes[1] == node); + + // Recurse into children + // There is the possibility that one of our child becoming empty will delete itself and moving its sibling contents into 'node'. + // If 'node->ChildNode[0]' delete itself, then 'node->ChildNode[1]->Windows' will be moved into 'node' + // If 'node->ChildNode[1]' delete itself, then 'node->ChildNode[0]->Windows' will be moved into 'node' and the "remove inactive windows" loop will have run twice on those windows (harmless) + if (node->ChildNodes[0]) + DockNodeUpdateVisibleFlagAndInactiveChilds(node->ChildNodes[0]); + if (node->ChildNodes[1]) + DockNodeUpdateVisibleFlagAndInactiveChilds(node->ChildNodes[1]); + + // Remove inactive windows + for (int window_n = 0; window_n < node->Windows.Size; window_n++) + { + ImGuiWindow* window = node->Windows[window_n]; + IM_ASSERT(window->DockNode == node); + + bool node_was_active = (node->LastFrameActive + 1 == g.FrameCount); + bool remove = false; + remove |= node_was_active && (window->LastFrameActive + 1 < g.FrameCount); + remove |= node_was_active && (node->WantCloseAll || node->WantCloseOne == window->ID) && window->HasCloseButton && !(window->Flags & ImGuiWindowFlags_UnsavedDocument); // Submit all _expected_ closure from last frame + remove |= (window->DockTabWantClose); + if (!remove) + continue; + window->DockTabWantClose = false; + if (node->Windows.Size == 1 && !node->IsDocumentRoot) + { + DockNodeHideHostWindow(node); + DockNodeRemoveWindow(node, window, node->ID); // Will delete the node so it'll be invalid on return + return; + } + DockNodeRemoveWindow(node, window, node->ID); + window_n--; + } + + DockNodeUpdateVisibleFlag(node); +} + +static void ImGui::DockNodeUpdateVisibleFlag(ImGuiDockNode* node) +{ + // Update visibility flag + bool is_visible = (node->ParentNode == 0) ? node->IsExplicitRoot : node->IsDocumentRoot; + is_visible |= (node->Windows.Size > 0); + is_visible |= (node->ChildNodes[0] && node->ChildNodes[0]->IsVisible); + is_visible |= (node->ChildNodes[1] && node->ChildNodes[1]->IsVisible); + node->IsVisible = is_visible; +} + +static void ImGui::DockNodeUpdate(ImGuiDockNode* node) +{ + ImGuiContext& g = *GImGui; + + if (node->IsRootNode()) + { + DockNodeUpdateVisibleFlagAndInactiveChilds(node); + ImGuiDockNode* only_node_with_windows = node->IsExplicitRoot ? NULL : DockNodeUpdateFindOnlyNodeWithWindows(node); + node->OnlyNodeWithWindows = only_node_with_windows; + } + + // Early out for standalone floating window that are holding on a DockId (with an invisible dock node) + if (node->IsRootNode() && node->Windows.Size == 1 && !node->IsExplicitRoot) + { + // Floating window pos/size is authoritative + node->Pos = node->Windows[0]->Pos; + node->Size = node->Windows[0]->SizeFull; + + // Transfer focus immediately so when we revert to a regular window it is immediately selected + if (node->HostWindow && g.NavWindow == node->HostWindow) + FocusWindow(node->Windows[0]); + + DockNodeHideHostWindow(node); + node->InitFromFirstWindow = false; + node->WantCloseAll = false; + node->WantCloseOne = 0; + node->HasCloseButton = node->HasCollapseButton = false; + node->LastFrameActive = g.FrameCount; + return; + } + + ImGuiWindow* host_window = NULL; + bool beginned_into_host_window = false; + if (node->IsExplicitRoot) + { + // [Explicit root node] + IM_ASSERT(node->HostWindow); + node->HasCloseButton = false; + node->HasCollapseButton = true; + host_window = node->HostWindow; + } + else + { + // [Automatic root or child nodes] + node->HasCloseButton = false; + node->HasCollapseButton = (node->Windows.Size > 0); + for (int window_n = 0; window_n < node->Windows.Size; window_n++) + { + ImGuiWindow* window = node->Windows[window_n]; + window->DockIsActive = (node->Windows.Size > 1); + node->HasCloseButton |= window->HasCloseButton; + } + + if (node->IsRootNode() && node->IsVisible) + { + if (node->InitFromFirstWindow && node->Windows.Size > 0) + { + ImGuiWindow* init_window = node->Windows[0]; + SetNextWindowPos(init_window->Pos); + SetNextWindowSize(init_window->SizeFull); + SetNextWindowCollapsed(init_window->Collapsed); + } + else if (node->HostWindow == NULL) + { + // Leaf + SetNextWindowPos(node->Pos); + SetNextWindowSize(node->Size); + } + + // Begin into the host window + char window_label[20]; + ImFormatString(window_label, IM_ARRAYSIZE(window_label), "##DockNode_%02X", node->ID); + ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_DockNodeHost; + window_flags |= ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus; + window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse; + + PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); + Begin(window_label, NULL, window_flags); + PopStyleVar(); + beginned_into_host_window = true; + + node->HostWindow = host_window = g.CurrentWindow; + host_window->DockNodeAsHost = node; + host_window->DC.CursorPos = host_window->Pos; + node->Pos = host_window->Pos; + node->Size = host_window->Size; + } + else if (node->ParentNode) + { + node->HostWindow = host_window = node->ParentNode->HostWindow; + } + node->InitFromFirstWindow = false; + } + + // Update active node (the one whose title bar is highlight) within a node tree + if (!node->IsParent()) + node->LastFocusedNodeID = node->ID; // This also ensure on our creation frame we will receive the title screen highlight + else if (g.NavWindow && g.NavWindow->RootWindowDockStop->DockNode && g.NavWindow->RootWindowDockStop->ParentWindow == host_window) + node->LastFocusedNodeID = g.NavWindow->RootWindowDockStop->DockNode->ID; + + // Draw and populate Tab Bar + if (host_window && node->Windows.Size > 0) + { + DockNodeUpdateTabBar(node, host_window); + if (node->TabBar->SelectedTabId) + node->SelectedTabID = node->TabBar->SelectedTabId; + } + else + { + node->WantCloseAll = false; + node->WantCloseOne = 0; + if (node->Windows.Size > 0) + node->SelectedTabID = node->Windows[0]->ID; + } + if (host_window && node->IsVisible) + { + // Background for empty nodes + if (node->Windows.Size == 0 && !node->IsParent()) + host_window->DrawList->AddRectFilled(node->Pos, node->Pos + node->Size, GetColorU32(ImGuiCol_DockingBg)); + + // Drop target + if (node->IsRootNode() && (g.MovingWindow == NULL || g.MovingWindow->RootWindow != host_window)) + BeginAsDockableDragDropTarget(host_window); + } + node->LastFrameActive = g.FrameCount; + + // Update pos/size and recurse into children + if (host_window) + { + if (node->IsRootNode()) + { + DockNodeTreeUpdatePosSize(node, host_window->Pos, host_window->Size); + DockNodeTreeUpdateSplitter(node); + } + if (node->ChildNodes[0]) + DockNodeUpdate(node->ChildNodes[0]); + if (node->ChildNodes[1]) + DockNodeUpdate(node->ChildNodes[1]); + + // End host window + if (beginned_into_host_window) + End(); + } +} + +// Compare TabItem nodes given the last known DockOrder (will persist in .ini file as hint), used to sort tabs when multiple tabs are added on the same frame. +static int IMGUI_CDECL TabItemComparerByDockOrder(const void* lhs, const void* rhs) +{ + ImGuiWindow* a = ((const ImGuiTabItem*)lhs)->Window; + ImGuiWindow* b = ((const ImGuiTabItem*)rhs)->Window; + if (int d = ((a->DockOrder == -1) ? INT_MAX : a->DockOrder) - ((b->DockOrder == -1) ? INT_MAX : b->DockOrder)) + return d; + return (a->BeginOrderWithinContext - b->BeginOrderWithinContext); +} + +static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_window) +{ + ImGuiContext& g = *GImGui; + ImGuiStyle& style = g.Style; + + const bool node_was_active = (node->LastFrameActive + 1 == g.FrameCount); + const bool closed_all = node->WantCloseAll && node_was_active; + const ImGuiID closed_one = node->WantCloseOne && node_was_active; + node->WantCloseAll = false; + node->WantCloseOne = 0; + + // Move ourselves to the Menu layer + Undo SkipItems flag in order to draw over the title bar (even if the window is collapsed) + bool backup_skip_item = host_window->SkipItems; + if (!node->IsExplicitRoot) + { + host_window->SkipItems = false; + host_window->DC.NavLayerCurrent++; + host_window->DC.NavLayerCurrentMask <<= 1; + } + + PushID(node->ID); + ImGuiTabBar* tab_bar = node->TabBar; + bool tab_bar_is_new = (tab_bar == NULL); + if (tab_bar == NULL) + tab_bar = node->TabBar = IM_NEW(ImGuiTabBar)(); + + // Decide if we should use focused color + bool is_focused = false; + ImGuiDockNode* root_node = DockNodeGetRootNode(node); + if (g.NavWindowingTarget) + is_focused = (g.NavWindowingTarget->DockNode == node); + else if (g.NavWindow && g.NavWindow->RootWindowForTitleBarHighlight == host_window->RootWindow && root_node->LastFocusedNodeID == node->ID) + is_focused = true; + //else if (tab_bar->SelectedTabId && tab_bar->NextSelectedTabId == tab_bar->SelectedTabId) // Handle the clicking frame + // is_focused = true; + + // Collapse button changes shape and display a list + if (IsPopupOpen("#TabListMenu")) + { + SetNextWindowPos(ImVec2(node->Pos.x - style.FramePadding.x, node->Pos.y + GetFrameHeight())); + if (BeginPopup("#TabListMenu")) + { + is_focused = true; + for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++) + { + ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; + IM_ASSERT(tab->Window != NULL); + if (Selectable(tab->Window->Name, tab->ID == tab_bar->SelectedTabId)) + tab_bar->NextSelectedTabId = tab->ID; + SameLine(); + Text(" "); + } + EndPopup(); + } + } + + ImGuiID focus_tab_id = 0; + + // Title bar + ImRect title_bar_rect = ImRect(node->Pos, node->Pos + ImVec2(node->Size.x, g.FontSize + style.FramePadding.y * 2.0f)); + ImU32 title_bar_col = GetColorU32(host_window->Collapsed ? ImGuiCol_TitleBgCollapsed : is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg); + host_window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, host_window->WindowRounding, ImDrawCornerFlags_Top); + host_window->DrawList->AddLine(title_bar_rect.GetBL(), title_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border), style.WindowBorderSize); + + // Collapse button + if (CollapseButton(host_window->GetID("#COLLAPSE"), title_bar_rect.Min)) + OpenPopup("#TabListMenu"); + if (IsItemActive()) + focus_tab_id = tab_bar->SelectedTabId; + + // Create tab bar and setup initial selection + ImRect tab_bar_rect = DockNodeCalcTabBarRect(node); + if (g.NavWindow && g.NavWindow->RootWindowDockStop->DockNode == node) + { + //printf("[%05d] tab bar create focus '%s'\n", g.FrameCount, window_focused->Name); + ImGuiID focused_id = g.NavWindow->RootWindowDockStop->ID; + if (focused_id != tab_bar->SelectedTabId) + if (focused_id != tab_bar->NextSelectedTabId || (tab_bar->NextSelectedTabId == 0 && focused_id != tab_bar->SelectedTabId)) + tab_bar->SelectedTabId = focused_id; + } + + // Submit tabs order + // If multiple tabs are appearing on the same frame, we add them ahead sorted based on their persistent DockOrder value + int tabs_count_old = tab_bar->Tabs.Size; + for (int window_n = 0; window_n < node->Windows.Size; window_n++) + if (TabBarFindTabByID(tab_bar, node->Windows[window_n]->ID) == NULL) + TabBarAddTab(tab_bar, node->Windows[window_n]); + //printf("[%05d] Sorting %d new appearing tabs\n", g.FrameCount, tab_bar->Tabs.Size - tabs_count_old); + if (tab_bar->Tabs.Size > tabs_count_old + 1) + ImQsort(tab_bar->Tabs.Data + tabs_count_old, tab_bar->Tabs.Size - tabs_count_old, sizeof(ImGuiTabItem), TabItemComparerByDockOrder); + + // Selected newly added tabs, or persistent tab ID if the tab bar was just recreated + if (tab_bar_is_new && TabBarFindTabByID(tab_bar, node->SelectedTabID) != NULL) + tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = node->SelectedTabID; + else if (tab_bar->Tabs.Size > tabs_count_old) + tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = tab_bar->Tabs.back().Window->ID; + + // Begin tab bar + ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_Reorderable | ImGuiTabBarFlags_AutoSelectNewTabs | ImGuiTabBarFlags_NoTabListPopupButton;// | ImGuiTabBarFlags_NoTabListScrollingButtons); + tab_bar_flags |= ImGuiTabBarFlags_SaveSettings; + tab_bar_flags |= ImGuiTabBarFlags_DockNode | (node->IsExplicitRoot ? ImGuiTabBarFlags_DockNodeExplicitRoot : 0); + if (!host_window->Collapsed && is_focused) + tab_bar_flags |= ImGuiTabBarFlags_IsFocused; + BeginTabBarEx(node->TabBar, tab_bar_rect, tab_bar_flags, node); + //host_window->DrawList->AddRect(tab_bar_rect.Min, tab_bar_rect.Max, IM_COL32(255,0,255,255)); + + // Submit actual tabs + node->VisibleWindow = NULL; + for (int window_n = 0; window_n < node->Windows.Size; window_n++) + { + ImGuiWindow* window = node->Windows[window_n]; + if ((closed_all || closed_one == window->ID) && window->HasCloseButton && !(window->Flags & ImGuiWindowFlags_UnsavedDocument)) + continue; + if (window->LastFrameActive + 1 >= g.FrameCount || !node_was_active) + { + ImGuiTabItemFlags tab_item_flags = ImGuiTabItemFlags_DockedWindow; + if (window->Flags & ImGuiWindowFlags_UnsavedDocument) + tab_item_flags |= ImGuiTabItemFlags_UnsavedDocument; + if (tab_bar->Flags & ImGuiTabBarFlags_NoCloseWithMiddleMouseButton) + tab_item_flags |= ImGuiTabItemFlags_NoCloseWithMiddleMouseButton; + + bool tab_open = true; + TabItemEx(tab_bar, window->Name, window->HasCloseButton ? &tab_open : NULL, tab_item_flags, window); + if (!tab_open) + node->WantCloseOne = window->ID; + if (tab_bar->VisibleTabId == window->ID) + node->VisibleWindow = window; + + window->DockTabItemStatusFlags = host_window->DC.LastItemStatusFlags; + window->DockTabItemRect = host_window->DC.LastItemRect; + + // Update navigation ID on menu layer + if (g.NavWindow && g.NavWindow->RootWindowDockStop == window && (window->DC.NavLayerActiveMask & (1 << 1)) == 0) + host_window->NavLastIds[1] = window->ID; + } + } + + // Notify root of visible window (used to display title in OS task bar) + if (node->VisibleWindow) + if (is_focused || root_node->VisibleWindow == NULL) + root_node->VisibleWindow = node->VisibleWindow; + + // Close button (after VisibleWindow was updated) + // Note that VisibleWindow may have been overrided by CTRL+Tabbing, so VisibleWindow->ID may be != from node->TabBar->SelectedTabId + if (node->VisibleWindow) + { + if (!node->VisibleWindow->HasCloseButton) + { + PushItemFlag(ImGuiItemFlags_Disabled, true); + PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_Text] * ImVec4(1.0f,1.0f,1.0f,0.5f)); + } + const float rad = g.FontSize * 0.5f; + if (CloseButton(host_window->GetID("#CLOSE"), title_bar_rect.GetTR() + ImVec2(-style.FramePadding.x - rad, style.FramePadding.y + rad), rad + 1)) + if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_bar->VisibleTabId)) + { + node->WantCloseOne = tab->ID; + TabBarCloseTab(tab_bar, tab); + } + //if (IsItemActive()) + // focus_tab_id = tab_bar->SelectedTabId; + if (!node->VisibleWindow->HasCloseButton) + { + PopStyleColor(); + PopItemFlag(); + } + } + + // When clicked on a tab we requested focus to the docked child + // Since we are processing this on the frame after the click, make sure focus hasn't already been taken by someone else. + if (node->TabBar->WantFocusTabId && g.NavWindow && g.NavWindow->RootWindow == host_window) + focus_tab_id = tab_bar->WantFocusTabId; + + // When clicking on the title bar outside of tabs, we still focus the selected tab for that node + if (g.HoveredWindow == host_window && g.HoveredId == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && IsMouseClicked(0)) + { + focus_tab_id = tab_bar->SelectedTabId; + if (ImGuiTabItem* tab = TabBarFindTabByID(node->TabBar, focus_tab_id)) + StartMouseMovingWindow(tab->Window); + } + + // Apply navigation focus + if (focus_tab_id != 0) + if (ImGuiTabItem* tab = TabBarFindTabByID(node->TabBar, focus_tab_id)) + { + FocusWindow(tab->Window); + NavInitWindow(tab->Window, false); + } + + EndTabBar(); + PopID(); + + // Restore SkipItems flag + if (!node->IsExplicitRoot) + { + host_window->DC.NavLayerCurrent--; + host_window->DC.NavLayerCurrentMask >>= 1; + host_window->SkipItems = backup_skip_item; + } +} + +static bool ImGui::DockNodeIsDropAllowed(ImGuiWindow* host_window, ImGuiWindow* root_payload) +{ + if (root_payload->DockNodeAsHost && root_payload->DockNodeAsHost->IsParent()) + return true; + + const int payload_count = root_payload->DockNodeAsHost ? root_payload->DockNodeAsHost->Windows.Size : 1; + for (int payload_n = 0; payload_n < payload_count; payload_n++) + { + ImGuiWindow* payload = root_payload->DockNodeAsHost ? root_payload->DockNodeAsHost->Windows[payload_n] : root_payload; + if ((host_window->Flags & ImGuiWindowFlags_DockNodeHost) && payload->BeginOrderWithinContext < host_window->BeginOrderWithinContext) + continue; + return true; + } + return false; +} + +static ImRect ImGui::DockNodeCalcTabBarRect(const ImGuiDockNode* node) +{ + ImGuiContext& g = *GImGui; + ImRect r = ImRect(node->Pos.x, node->Pos.y, node->Pos.x + node->Size.x, node->Pos.y + (g.FontSize + g.Style.FramePadding.y * 2.0f)); + if (node->HasCollapseButton) + r.Min.x += g.Style.FramePadding.x + g.FontSize; // + g.Style.ItemInnerSpacing.x; // <-- Adding ItemInnerSpacing makes the title text moves slightly when in a tab bar. Instead we adjusted RenderArrowDockMenu() + if (node->HasCloseButton) + r.Max.x -= g.Style.FramePadding.x + g.FontSize + 1.0f; + return r; +} + +void ImGui::DockNodeCalcSplitRects(ImVec2& pos_old, ImVec2& size_old, ImVec2& pos_new, ImVec2& size_new, ImGuiDir dir, ImVec2 size_new_desired) +{ + ImGuiContext& g = *GImGui; + const float dock_spacing = g.Style.ItemInnerSpacing.x; + const ImGuiAxis axis = (dir == ImGuiDir_Left || dir == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y; + pos_new[axis ^ 1] = pos_old[axis ^ 1]; + size_new[axis ^ 1] = size_old[axis ^ 1]; + + // Distribute size on given axis (with a desired size or equally) + const float w_avail = size_old[axis] - dock_spacing; + if (size_new_desired[axis] > 0.0f && size_new_desired[axis] <= w_avail * 0.5f) + { + size_new[axis] = size_new_desired[axis]; + size_old[axis] = (float)(int)(w_avail - size_new[axis]); + } + else + { + size_new[axis] = (float)(int)(w_avail * 0.5f); + size_old[axis] = (float)(int)(w_avail - size_new[axis]); + } + + // Position each node + if (dir == ImGuiDir_Right || dir == ImGuiDir_Down) + { + pos_new[axis] = pos_old[axis] + size_old[axis] + dock_spacing; + } + else if (dir == ImGuiDir_Left || dir == ImGuiDir_Up) + { + pos_new[axis] = pos_old[axis]; + pos_old[axis] = pos_new[axis] + size_new[axis] + dock_spacing; + } +} + +// Retrieve the drop rectangles for a given direction or for the center + perform hit testing. +bool ImGui::DockNodeCalcDropRects(const ImRect& parent, ImGuiDir dir, ImRect& out_r, bool outer_docking) +{ + ImGuiContext& g = *GImGui; + + const float parent_smaller_axis = ImMin(parent.GetWidth(), parent.GetHeight()); + const float hs_for_central_nodes = ImMin(g.FontSize * 1.5f, ImMax(g.FontSize * 0.5f, parent_smaller_axis / 8.0f)); + float hs_w; // Half-size, longer axis + float hs_h; // Half-size, smaller axis + ImVec2 off; // Distance from edge or center + if (outer_docking) + { + //hs_w = ImFloor(ImClamp(parent_smaller_axis - hs_for_central_nodes * 4.0f, g.FontSize * 0.5f, g.FontSize * 8.0f)); + //hs_h = ImFloor(hs_w * 0.15f); + //off = ImVec2(ImFloor(parent.GetWidth() * 0.5f - GetFrameHeightWithSpacing() * 1.4f - hs_h), ImFloor(parent.GetHeight() * 0.5f - GetFrameHeightWithSpacing() * 1.4f - hs_h)); + hs_w = ImFloor(hs_for_central_nodes * 1.50f); + hs_h = ImFloor(hs_for_central_nodes * 0.80f); + off = ImVec2(ImFloor(parent.GetWidth() * 0.5f - GetFrameHeightWithSpacing() * 0.0f - hs_h), ImFloor(parent.GetHeight() * 0.5f - GetFrameHeightWithSpacing() * 0.0f - hs_h)); + } + else + { + hs_w = ImFloor(hs_for_central_nodes); + hs_h = ImFloor(hs_for_central_nodes * 0.90f); + off = ImVec2(ImFloor(hs_w * 2.40f), ImFloor(hs_w * 2.40f)); + } + + ImVec2 c = ImFloor(parent.GetCenter()); + if (dir == ImGuiDir_None) { out_r = ImRect(c.x - hs_w, c.y - hs_w, c.x + hs_w, c.y + hs_w); } + else if (dir == ImGuiDir_Up) { out_r = ImRect(c.x - hs_w, c.y - off.y - hs_h, c.x + hs_w, c.y - off.y + hs_h); } + else if (dir == ImGuiDir_Down) { out_r = ImRect(c.x - hs_w, c.y + off.y - hs_h, c.x + hs_w, c.y + off.y + hs_h); } + else if (dir == ImGuiDir_Left) { out_r = ImRect(c.x - off.x - hs_h, c.y - hs_w, c.x - off.x + hs_h, c.y + hs_w); } + else if (dir == ImGuiDir_Right) { out_r = ImRect(c.x + off.x - hs_h, c.y - hs_w, c.x + off.x + hs_h, c.y + hs_w); } + + ImRect hit_r = out_r; + if (!outer_docking) + { + // Custom hit testing for the 5-way selection, designed to reduce flickering when moving diagonally between sides + hit_r.Expand(ImFloor(hs_w * 0.30f)); + ImVec2 mouse_delta = (g.IO.MousePos - c); + float mouse_delta_len2 = ImLengthSqr(mouse_delta); + float r_threshold_center = hs_w * 1.4f; + float r_threshold_sides = hs_w * (1.4f + 1.2f); + if (mouse_delta_len2 < r_threshold_center * r_threshold_center) + return (dir == ImGuiDir_None); + if (mouse_delta_len2 < r_threshold_sides * r_threshold_sides) + return (dir == ImGetDirQuadrantFromDelta(mouse_delta.x, mouse_delta.y)); + } + return hit_r.Contains(g.IO.MousePos); +} -//----------------------------------------------------------------------------- -// Docking: Forward Declarations -//----------------------------------------------------------------------------- +// host_node may be NULL if the window doesn't have a DockNode already. +static bool ImGui::DockNodePreviewDockCalc(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* root_payload, ImGuiDockPreviewData* data, bool is_explicit_target, bool is_outer_docking) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(g.CurrentWindow == host_window); // Because we rely on font size to calculate tab sizes + data->FutureNode.HasCloseButton = (host_node ? host_node->HasCloseButton : host_window->HasCloseButton) || (root_payload->HasCloseButton); + data->FutureNode.HasCollapseButton = host_node ? true : ((host_window->Flags & ImGuiWindowFlags_NoCollapse) == 0); + data->FutureNode.Pos = host_node ? host_node->Pos : host_window->Pos; + data->FutureNode.Size = host_node ? host_node->Size : host_window->Size; + const bool src_is_visibly_splitted = root_payload->DockNodeAsHost && root_payload->DockNodeAsHost->IsParent() && (root_payload->DockNodeAsHost->OnlyNodeWithWindows == NULL); + data->IsCenterAvailable = !is_outer_docking; + if (src_is_visibly_splitted && (!host_node || !host_node->IsEmpty())) + data->IsCenterAvailable = false; -//----------------------------------------------------------------------------- -// Docking: ImGuiDockContext -//----------------------------------------------------------------------------- + data->IsSidesAvailable = true; + if (!is_outer_docking && host_node && host_node->ParentNode == NULL && host_node->IsDocumentRoot) + data->IsSidesAvailable = false; + + // Calculate drop shapes geometry for allowed splitting directions + IM_ASSERT(ImGuiDir_None == -1); + data->SplitNode = host_node; + data->SplitDir = ImGuiDir_None; + data->IsSplitDirExplicit = false; + if (!host_window->Collapsed) + for (int dir = ImGuiDir_None; dir < ImGuiDir_COUNT; dir++) + { + if (dir == ImGuiDir_None && !data->IsCenterAvailable) + continue; + if (dir != ImGuiDir_None && !data->IsSidesAvailable) + continue; + if (DockNodeCalcDropRects(data->FutureNode.Rect(), (ImGuiDir)dir, data->DropRectsDraw[dir+1], is_outer_docking)) + { + data->SplitDir = (ImGuiDir)dir; + data->IsSplitDirExplicit = true; + } + } + // When docking without holding Shift, we only allow and preview docking when hovering over a drop rect or over the title bar + data->IsDropAllowed = (data->SplitDir != ImGuiDir_None) || (data->IsCenterAvailable); + if (!is_explicit_target && !data->IsSplitDirExplicit && !g.IO.ConfigDockingWithKeyMod) + data->IsDropAllowed = false; + // Calculate split area + data->SplitRatio = 0.0f; + if (data->SplitDir != ImGuiDir_None) + { + ImGuiDir split_dir = data->SplitDir; + ImGuiAxis split_axis = (split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y; + ImVec2 pos_old = data->FutureNode.Pos, pos_new; + ImVec2 size_old = data->FutureNode.Size, size_new; + DockNodeCalcSplitRects(pos_old, size_old, pos_new, size_new, split_dir, root_payload->Size); -//----------------------------------------------------------------------------- -// Docking: ImGuiDockContext Docking/Undocking functions -//----------------------------------------------------------------------------- + // Calculate split ratio so we can pass it down the docking request + float split_ratio = ImSaturate(size_new[split_axis] / data->FutureNode.Size[split_axis]); + data->FutureNode.Pos = pos_new; + data->FutureNode.Size = size_new; + data->SplitRatio = (split_dir == ImGuiDir_Right || split_dir == ImGuiDir_Down) ? (1.0f - split_ratio) : (split_ratio); + } + + return data->IsSplitDirExplicit; +} +static void ImGui::DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* root_payload, const ImGuiDockPreviewData* data) +{ + ImGuiContext& g = *GImGui; + // In case the two windows involved are on different viewports, we will draw the overlay on each of them. + int overlay_draw_lists_count = 0; + ImDrawList* overlay_draw_lists[1]; + overlay_draw_lists[overlay_draw_lists_count++] = GetOverlayDrawList(); -//----------------------------------------------------------------------------- -// Docking: ImGuiDockNode -//----------------------------------------------------------------------------- + // Draw main preview rectangle + const ImU32 overlay_col_tabs = GetColorU32(ImGuiCol_TabActive); + const ImU32 overlay_col_main = GetColorU32(ImGuiCol_DockingPreview, 0.40f); + const ImU32 overlay_col_drop = GetColorU32(ImGuiCol_DockingPreview, 0.70f); + const ImU32 overlay_col_drop_hovered = GetColorU32(ImGuiCol_DockingPreview); + const ImU32 overlay_col_lines = GetColorU32(ImGuiCol_NavWindowingHighlight, 0.60f); + + // Display area preview + const bool can_preview_tabs = (root_payload->DockNodeAsHost == NULL || root_payload->DockNodeAsHost->Windows.Size > 0); + if (data->IsDropAllowed) + { + ImRect overlay_rect = data->FutureNode.Rect(); + if (data->SplitDir == ImGuiDir_None && can_preview_tabs) + overlay_rect.Min.y += GetFrameHeight(); + if (data->SplitDir != ImGuiDir_None || data->IsCenterAvailable) + for (int overlay_n = 0; overlay_n < overlay_draw_lists_count; overlay_n++) + overlay_draw_lists[overlay_n]->AddRectFilled(overlay_rect.Min, overlay_rect.Max, overlay_col_main, host_window->WindowRounding); + } + + // Display tab shape/label preview unless we are splitting node (it generally makes the situation harder to read) + if (data->IsDropAllowed && can_preview_tabs && data->SplitDir == ImGuiDir_None && data->IsCenterAvailable) + { + // Compute target tab bar geometry so we can locate our preview tabs + ImRect tab_bar_rect = DockNodeCalcTabBarRect(&data->FutureNode); + ImVec2 tab_pos = tab_bar_rect.Min; + if (host_node && host_node->TabBar) + tab_pos.x += host_node->TabBar->OffsetMax + g.Style.ItemInnerSpacing.x; // We don't use OffsetNewTab because when using non-persistent-order tab bar it is incremented with each Tab submission. + else if (!(host_window->Flags & ImGuiWindowFlags_DockNodeHost)) + tab_pos.x += g.Style.ItemInnerSpacing.x + TabItemCalcSize(host_window->Name, host_window->HasCloseButton).x; // Account for slight offset which will be added when changing from title bar to tab bar + + // Draw tab shape/label preview (payload may be a loose window or a host window carrying multiple tabbed windows) + if (root_payload->DockNodeAsHost) + IM_ASSERT(root_payload->DockNodeAsHost->Windows.Size == root_payload->DockNodeAsHost->TabBar->Tabs.Size); + const int payload_count = root_payload->DockNodeAsHost ? root_payload->DockNodeAsHost->TabBar->Tabs.Size : 1; + for (int payload_n = 0; payload_n < payload_count; payload_n++) + { + // Calculate the tab bounding box for each payload window + ImGuiWindow* payload = root_payload->DockNodeAsHost ? root_payload->DockNodeAsHost->TabBar->Tabs[payload_n].Window : root_payload; + if ((host_window->Flags & ImGuiWindowFlags_DockNodeHost) && payload->BeginOrderWithinContext < host_window->BeginOrderWithinContext) + continue; + ImVec2 tab_size = TabItemCalcSize(payload->Name, payload->HasCloseButton); + ImRect tab_bb(tab_pos.x, tab_pos.y, tab_pos.x + tab_size.x, tab_pos.y + tab_size.y); + tab_pos.x += tab_size.x + g.Style.ItemInnerSpacing.x; + for (int overlay_n = 0; overlay_n < overlay_draw_lists_count; overlay_n++) + { + ImGuiTabItemFlags tab_flags = ImGuiTabItemFlags_Preview | ((payload->Flags & ImGuiWindowFlags_UnsavedDocument) ? ImGuiTabItemFlags_UnsavedDocument : 0); + if (!tab_bar_rect.Contains(tab_bb)) + overlay_draw_lists[overlay_n]->PushClipRect(tab_bar_rect.Min, tab_bar_rect.Max); + TabItemBackground(overlay_draw_lists[overlay_n], tab_bb, tab_flags, overlay_col_tabs); + TabItemLabelAndCloseButton(overlay_draw_lists[overlay_n], tab_bb, tab_flags, payload->Name, 0, 0); + if (!tab_bar_rect.Contains(tab_bb)) + overlay_draw_lists[overlay_n]->PopClipRect(); + } + } + } + // Display drop boxes + const float overlay_rounding = ImMax(3.0f, g.Style.FrameRounding); + for (int dir = ImGuiDir_None; dir < ImGuiDir_COUNT; dir++) + if (!data->DropRectsDraw[dir + 1].IsInverted()) + { + ImRect draw_r = data->DropRectsDraw[dir + 1]; + ImRect draw_r_in = draw_r; + draw_r_in.Expand(-2.0f); + ImU32 overlay_col = (data->SplitDir == (ImGuiDir)dir && data->IsSplitDirExplicit) ? overlay_col_drop_hovered : overlay_col_drop; + for (int overlay_n = 0; overlay_n < overlay_draw_lists_count; overlay_n++) + { + ImVec2 center = ImFloor(draw_r_in.GetCenter()); + overlay_draw_lists[overlay_n]->AddRectFilled(draw_r.Min, draw_r.Max, overlay_col, overlay_rounding); + overlay_draw_lists[overlay_n]->AddRect(draw_r_in.Min, draw_r_in.Max, overlay_col_lines, overlay_rounding); + if (dir == ImGuiDir_Left || dir == ImGuiDir_Right) + overlay_draw_lists[overlay_n]->AddLine(ImVec2(center.x, draw_r_in.Min.y), ImVec2(center.x, draw_r_in.Max.y), overlay_col_lines); + if (dir == ImGuiDir_Up || dir == ImGuiDir_Down) + overlay_draw_lists[overlay_n]->AddLine(ImVec2(draw_r_in.Min.x, center.y), ImVec2(draw_r_in.Max.x, center.y), overlay_col_lines); + } + } +} //----------------------------------------------------------------------------- // Docking: ImGuiDockNode Tree manipulation functions //----------------------------------------------------------------------------- +void ImGui::DockNodeTreeSplit(ImGuiDockContext* ctx, ImGuiDockNode* parent_node, ImGuiAxis split_axis, int split_inheritor_child_idx, float split_ratio, ImGuiDockNode* new_node) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(split_axis != ImGuiAxis_None); + + ImGuiDockNode* child_0 = (new_node && split_inheritor_child_idx != 0) ? new_node : DockContextAddNode(ctx, (ImGuiID)-1); + child_0->ParentNode = parent_node; + + ImGuiDockNode* child_1 = (new_node && split_inheritor_child_idx != 1) ? new_node : DockContextAddNode(ctx, (ImGuiID)-1); + child_1->ParentNode = parent_node; + + ImGuiDockNode* child_inheritor = (split_inheritor_child_idx == 0) ? child_0 : child_1; + DockNodeMoveChilds(child_inheritor, parent_node); + parent_node->ChildNodes[0] = child_0; + parent_node->ChildNodes[1] = child_1; + parent_node->ChildNodes[split_inheritor_child_idx]->VisibleWindow = parent_node->VisibleWindow; + parent_node->SplitAxis = split_axis; + parent_node->SplitRatio = split_ratio; + parent_node->VisibleWindow = NULL; + + float size_avail = (parent_node->Size[split_axis] - IMGUI_DOCK_SPLITTER_SIZE); + child_0->LastExplicitSize = child_1->LastExplicitSize = parent_node->Size; + child_0->LastExplicitSize[split_axis] = ImMax(g.Style.WindowMinSize[split_axis], ImFloor(size_avail * split_ratio)); + child_1->LastExplicitSize[split_axis] = ImMax(g.Style.WindowMinSize[split_axis], ImFloor(size_avail - child_0->LastExplicitSize[split_axis])); + + DockNodeMoveWindows(parent_node->ChildNodes[split_inheritor_child_idx], parent_node); + DockNodeTreeUpdatePosSize(parent_node, parent_node->Pos, parent_node->Size); +} + +void ImGui::DockNodeTreeMerge(ImGuiDockContext* ctx, ImGuiDockNode* parent_node, ImGuiDockNode* merge_lead_child) +{ + ImGuiDockNode* child_0 = parent_node->ChildNodes[0]; + ImGuiDockNode* child_1 = parent_node->ChildNodes[1]; + IM_ASSERT(child_0 && child_1); + IM_ASSERT(merge_lead_child == child_0 || merge_lead_child == child_1); + IM_ASSERT(parent_node->TabBar == NULL); + IM_ASSERT(parent_node->Windows.Size == 0); + + DockNodeMoveChilds(parent_node, merge_lead_child); + DockNodeMoveWindows(parent_node, merge_lead_child); + DockNodeApplyPosSizeToWindows(parent_node); + parent_node->InitFromFirstWindow = false; + parent_node->VisibleWindow = merge_lead_child->VisibleWindow; + parent_node->IsDocumentRoot = child_0->IsDocumentRoot || child_1->IsDocumentRoot; + + ctx->Nodes.SetVoidPtr(child_0->ID, NULL); + ctx->Nodes.SetVoidPtr(child_1->ID, NULL); + IM_DELETE(child_0); + IM_DELETE(child_1); +} + +// Update Pos/Size for a node hierarchy (don't affect child Windows yet) +void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 size) +{ + node->Pos = pos; + node->Size = size; + if (!node->IsParent()) + return; + + ImGuiDockNode* child_0 = node->ChildNodes[0]; + ImGuiDockNode* child_1 = node->ChildNodes[1]; + ImVec2 child_0_pos = pos, child_1_pos = pos; + ImVec2 child_0_size = size, child_1_size = size; + if (child_0->IsVisible && child_1->IsVisible) + { + const float spacing = IMGUI_DOCK_SPLITTER_SIZE; + const ImGuiAxis axis = (ImGuiAxis)node->SplitAxis; + const float size_avail = ImMax(size[axis] - spacing, 0.0f); + +#if 0 + child_0_size[axis] = ImFloor(size_avail * node->SplitRatio); + child_1_size[axis] = size_avail - child_0_size[axis]; + child_1_pos[axis] += spacing + child_0_size[axis]; +#else + // Size allocation policy + // 1) The first 0..WindowMinSize[axis]*2 are allocated evenly to both windows. + ImGuiContext& g = *GImGui; + const float size_min_each = ImFloor(ImMin(size_avail, g.Style.WindowMinSize[axis] * 2.0f) * 0.5f); + + // 2) Process locked absolute size (during a splitter resize we preserve the child of nodes not touching the splitter edge) + IM_ASSERT(!(child_0->WantLockSizeOnce && child_1->WantLockSizeOnce)); + if (child_0->WantLockSizeOnce) + { + child_0->WantLockSizeOnce = false; + child_0_size[axis] = child_0->LastExplicitSize[axis] = child_0->Size[axis]; + child_1_size[axis] = child_1->LastExplicitSize[axis] = (size_avail - child_0_size[axis]); + + } + else if (child_1->WantLockSizeOnce) + { + child_1->WantLockSizeOnce = false; + child_1_size[axis] = child_1->LastExplicitSize[axis] = child_1->Size[axis]; + child_0_size[axis] = child_0->LastExplicitSize[axis] = (size_avail - child_1_size[axis]); + } + + // 3) If one window is the document root (~ use remaining space, should be made explicit!), use explicit size from the other, and remainder for the document root + else if (child_1->IsDocumentRoot && child_0->LastExplicitSize[axis] != 0.0f) + { + child_0_size[axis] = ImMin(size_avail - size_min_each, child_0->LastExplicitSize[axis]); + child_1_size[axis] = (size_avail - child_0_size[axis]); + } + else if (child_0->IsDocumentRoot && child_1->LastExplicitSize[axis] != 0.0f) + { + child_1_size[axis] = ImMin(size_avail - size_min_each, child_1->LastExplicitSize[axis]); + child_0_size[axis] = (size_avail - child_1_size[axis]); + } + else + { + // 4) Otherwise distribute according to SplitRatio + //float split_ratio = node->SplitRatio; + float split_ratio = child_0->LastExplicitSize[axis] / (child_0->LastExplicitSize[axis] + child_1->LastExplicitSize[axis]); + child_0_size[axis] = ImMax(size_min_each, ImFloor(size_avail * split_ratio + 0.5F)); + child_1_size[axis] = (size_avail - child_0_size[axis]); + } + child_1_pos[axis] += spacing + child_0_size[axis]; +#endif + } + if (child_0->IsVisible) + DockNodeTreeUpdatePosSize(child_0, child_0_pos, child_0_size); + if (child_1->IsVisible) + DockNodeTreeUpdatePosSize(child_1, child_1_pos, child_1_size); +} + +static void DockNodeTreeUpdateSplitterFindTouchingNode(ImGuiDockNode* node, ImGuiAxis axis, int side, ImVector* touching_nodes) +{ + if (!node->IsParent()) + { + touching_nodes->push_back(node); + return; + } + if (node->ChildNodes[0]->IsVisible) + if (node->SplitAxis != axis || side == 0 || !node->ChildNodes[1]->IsVisible) + DockNodeTreeUpdateSplitterFindTouchingNode(node->ChildNodes[0], axis, side, touching_nodes); + if (node->ChildNodes[1]->IsVisible) + if (node->SplitAxis != axis || side == 1 || !node->ChildNodes[0]->IsVisible) + DockNodeTreeUpdateSplitterFindTouchingNode(node->ChildNodes[1], axis, side, touching_nodes); +} + +void ImGui::DockNodeTreeUpdateSplitter(ImGuiDockNode* node) +{ + if (!node->IsParent()) + return; + + ImGuiContext& g = *GImGui; + + ImGuiDockNode* child_0 = node->ChildNodes[0]; + ImGuiDockNode* child_1 = node->ChildNodes[1]; + if (child_0->IsVisible && child_1->IsVisible) + { + // Extend hovering range past the displayed border + float HOVER_EXTEND = 4.0f; + + // Use a short delay before highlighting the splitter (and changing the mouse cursor) in order for regular mouse movement to not highlight many splitters + float HOVER_VISIBILITY_DELAY = 0.040f; + + // Bounding box of the splitter cover the space between both nodes (w = Spacing, h = Size[xy^1] for when splitting horizontally) + const ImGuiAxis axis = (ImGuiAxis)node->SplitAxis; + IM_ASSERT(axis != ImGuiAxis_None); + ImRect bb; + bb.Min = child_0->Pos; + bb.Max = child_1->Pos; + bb.Min[axis] += child_0->Size[axis]; + bb.Max[axis ^ 1] += child_1->Size[axis ^ 1]; + //GetOverlayDrawList(g.CurrentWindow->Viewport)->AddRect(bb.Min, bb.Max, IM_COL32(255,0,255,255)); + + float w1 = child_0->Size[axis]; + float w2 = child_1->Size[axis]; + bb.Min[axis] += 1; // Display a little inward so highlight doesn't connect with nearby tabs on the neighbor node. + bb.Max[axis] -= 1; + PushID(node->ID); + + // Gather list of nodes that are touching the splitter line. Find resizing limits based on those nodes. + ImVector touching_nodes[2]; + float min_size = g.Style.WindowMinSize[axis]; + float resize_limits[2]; + resize_limits[0] = node->ChildNodes[0]->Pos[axis] + min_size; + resize_limits[1] = node->ChildNodes[1]->Pos[axis] + node->ChildNodes[1]->Size[axis] - min_size; + + ImGuiID splitter_id = GetID("##Splitter"); + if (g.ActiveId == splitter_id) + { + // Only process when splitter is active + DockNodeTreeUpdateSplitterFindTouchingNode(child_0, axis, 1, &touching_nodes[0]); + DockNodeTreeUpdateSplitterFindTouchingNode(child_1, axis, 0, &touching_nodes[1]); + for (int touching_node_n = 0; touching_node_n < touching_nodes[0].Size; touching_node_n++) + resize_limits[0] = ImMax(resize_limits[0], touching_nodes[0][touching_node_n]->Rect().Min[axis] + min_size); + for (int touching_node_n = 0; touching_node_n < touching_nodes[1].Size; touching_node_n++) + resize_limits[1] = ImMin(resize_limits[1], touching_nodes[1][touching_node_n]->Rect().Max[axis] - min_size); + + /* + // [DEBUG] Render limits + ImDrawList* draw_list = node->HostWindow ? GetOverlayDrawList(node->HostWindow) : GetOverlayDrawList((ImGuiViewportP*)GetMainViewport()); + for (int n = 0; n < 2; n++) + if (axis == ImGuiAxis_X) + draw_list->AddLine(ImVec2(resize_limits[n], node->ChildNodes[n]->Pos.y), ImVec2(resize_limits[n], node->ChildNodes[n]->Pos.y + node->ChildNodes[n]->Size.y), IM_COL32(255, 0, 255, 255), 3.0f); + else + draw_list->AddLine(ImVec2(node->ChildNodes[n]->Pos.x, resize_limits[n]), ImVec2(node->ChildNodes[n]->Pos.x + node->ChildNodes[n]->Size.x, resize_limits[n]), IM_COL32(255, 0, 255, 255), 3.0f); + */ + } + + float min_size_0 = resize_limits[0] - child_0->Pos[axis]; + float min_size_1 = child_1->Pos[axis] + child_1->Size[axis] - resize_limits[1]; + if (SplitterBehavior(bb, GetID("##Splitter"), axis, &w1, &w2, min_size_0, min_size_1, HOVER_EXTEND, HOVER_VISIBILITY_DELAY)) + { + if (touching_nodes[0].Size > 0 && touching_nodes[1].Size > 0) + { + node->SplitRatio = w1 / (w1 + w2); + IM_ASSERT(node->SplitRatio > 0.0f && node->SplitRatio < 1.0f); + child_0->Size[axis] = child_0->LastExplicitSize[axis] = w1; + child_1->Pos[axis] -= w2 - child_1->Size[axis]; + child_1->Size[axis] = child_1->LastExplicitSize[axis] = w2; + + // Lock the size of every node that is a sibling of the node we are touching + // This might be less desirable if we can merge sibling of a same axis into the same parental level. +#if 1 + for (int side_n = 0; side_n < 2; side_n++) + for (int touching_node_n = 0; touching_node_n < touching_nodes[side_n].Size; touching_node_n++) + { + ImGuiDockNode* touching_node = touching_nodes[side_n][touching_node_n]; + //ImDrawList* draw_list = node->HostWindow ? GetOverlayDrawList(node->HostWindow) : GetOverlayDrawList((ImGuiViewportP*)GetMainViewport()); + //draw_list->AddRect(touching_node->Pos, touching_node->Pos + touching_node->Size, IM_COL32(255, 128, 0, 255)); + while (touching_node->ParentNode != node) + { + if (touching_node->ParentNode->SplitAxis == axis) + { + // Mark other node so its size will be preserved during the upcoming call to DockNodeTreeUpdatePosSize(). + ImGuiDockNode* node_to_preserve = touching_node->ParentNode->ChildNodes[side_n]; + node_to_preserve->WantLockSizeOnce = true; + //draw_list->AddRect(touching_node->Pos, touching_node->Rect().Max, IM_COL32(255, 0, 0, 255)); + //draw_list->AddRectFilled(node_to_preserve->Pos, node_to_preserve->Rect().Max, IM_COL32(0, 255, 0, 100)); + } + touching_node = touching_node->ParentNode; + } + } +#endif + + DockNodeTreeUpdatePosSize(child_0, child_0->Pos, child_0->Size); + DockNodeTreeUpdatePosSize(child_1, child_1->Pos, child_1->Size); + MarkIniSettingsDirty(); + } + } + PopID(); + } + + if (child_0->IsVisible) + DockNodeTreeUpdateSplitter(child_0); + if (child_1->IsVisible) + DockNodeTreeUpdateSplitter(child_1); +} +ImGuiDockNode* ImGui::DockNodeTreeFindNodeByPos(ImGuiDockNode* node, ImVec2 pos) +{ + if (!node->IsVisible) + return NULL; + if (node->IsParent()) + { + if (ImGuiDockNode* hovered_node = DockNodeTreeFindNodeByPos(node->ChildNodes[0], pos)) + return hovered_node; + if (ImGuiDockNode* hovered_node = DockNodeTreeFindNodeByPos(node->ChildNodes[1], pos)) + return hovered_node; + } + else + { + ImGuiContext& g = *GImGui; + const float dock_spacing = g.Style.ItemInnerSpacing.x; + ImRect r(node->Pos, node->Pos + node->Size); + r.Expand(dock_spacing * 0.5f); + if (r.Contains(pos)) + return node; + } + return NULL; +} //----------------------------------------------------------------------------- // Docking: Public Functions (SetWindowDock, Dockspace) //----------------------------------------------------------------------------- +void ImGui::SetWindowDock(ImGuiWindow* window, ImGuiID dock_id, ImGuiCond cond) +{ + // Test condition (NB: bit 0 is always true) and clear flags for next time + if (cond && (window->SetWindowDockAllowFlags & cond) == 0) + return; + window->SetWindowDockAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing); + + // Set + if (window->DockId == dock_id) + return; + if (window->DockNode) + DockNodeRemoveWindow(window->DockNode, window, 0); + window->DockId = dock_id; +} + +void ImGui::DockSpace(const char* str_id, const ImVec2& size_arg) +{ + ImGuiContext& g = *GImGui; + ImGuiDockContext* ctx = g.DockContext; + + ImGuiWindow* window = GetCurrentWindow(); + ImGuiID id = window->GetID(str_id); + + // It is possible that the node has already been claimed by a docked window which appeared before the DockSpace(), so we overwrite IsExplicit again. + ImGuiDockNode* node = DockContextFindNodeByID(ctx, id); + if (!node) + { + node = DockContextAddNode(ctx, id); + node->IsDocumentRoot = true; + } + node->IsExplicitRoot = true; + + const ImVec2 content_avail = GetContentRegionAvail(); + ImVec2 size = ImFloor(size_arg); + if (size.x <= 0.0f) + size.x = ImMax(content_avail.x + size.x, 4.0f); // Arbitrary minimum child size (0.0f causing too much issues) + if (size.y <= 0.0f) + size.y = ImMax(content_avail.y + size.y, 4.0f); + + node->Pos = window->DC.CursorPos; + node->Size = node->LastExplicitSize = size; + SetNextWindowPos(node->Pos); + SetNextWindowSize(node->Size); + g.NextWindowData.PosUndock = false; + + ImGuiWindowFlags window_flags = ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_DockNodeHost; + window_flags |= ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar; + + char title[256]; + ImFormatString(title, IM_ARRAYSIZE(title), "%s/%s", window->Name, str_id); + + if (node->Windows.Size > 0 || node->IsParent()) + PushStyleColor(ImGuiCol_ChildBg, IM_COL32(0, 0, 0, 0)); + Begin(title, NULL, window_flags); + if (node->Windows.Size > 0 || node->IsParent()) + PopStyleColor(); + + ImGuiWindow* host_window = g.CurrentWindow; + host_window->DockNodeAsHost = node; + host_window->ChildId = window->GetID(title); + node->HostWindow = host_window; + + IM_ASSERT(node->IsRootNode()); + DockNodeUpdate(node); + End(); +} //----------------------------------------------------------------------------- // Docking: Begin/End Functions (called from Begin/End) //----------------------------------------------------------------------------- +void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) +{ + ImGuiContext& g = *GImGui; + + // Calling SetNextWindowPos() undock windows by default (by setting PosUndock) + bool want_undock = false; + want_undock |= (window->Flags & ImGuiWindowFlags_NoDocking) != 0; + want_undock |= (g.NextWindowData.PosCond && (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) && g.NextWindowData.PosUndock); + g.NextWindowData.PosUndock = false; + if (want_undock) + { + DockContextProcessUndock(g.DockContext, window); + return; + } + + // Bind to our dock node + ImGuiDockNode* dock_node = window->DockNode; + if (window->DockId != 0 && dock_node == NULL) + { + dock_node = DockContextFindNodeByID(g.DockContext, window->DockId); + if (dock_node == NULL) + dock_node = DockContextAddNode(g.DockContext, window->DockId); + + if (dock_node->IsParent()) + { + DockContextProcessUndock(g.DockContext, window); + return; + } + + DockNodeAddWindow(dock_node, window); + IM_ASSERT(dock_node == window->DockNode); + + // Fix an edge case with auto-resizing windows: if they are created on the same frame they are creating their dock node, + // we don't want their initial zero-size to spread to the DockNode. We preserve their size. + SetNextWindowPos(window->Pos); + SetNextWindowSize(window->SizeFull); + g.NextWindowData.PosUndock = false; + } + + // Undock if our dockspace disappeared + if (dock_node->LastFrameActive < g.FrameCount) + { + // If the window has been orphaned (lost its dockspace), transition the docknode to an implicit node processed in DockContextUpdateDocking() + ImGuiDockNode* root_node = DockNodeGetRootNode(dock_node); + if (root_node->IsExplicitRoot && root_node->LastFrameActive < g.FrameCount) + root_node->IsExplicitRoot = false; + window->DockIsActive = false; + return; + } + + // Undock if we are submitted earlier than the host window + if (dock_node->HostWindow && window->BeginOrderWithinContext < dock_node->HostWindow->BeginOrderWithinContext) + { + DockContextProcessUndock(g.DockContext, window); + return; + } + + // FIXME-DOCKSPACE: replace ->HostWindow NULL compare with something more explicit (~was initially intended as a first frame test) + if (dock_node->HostWindow == NULL) + { + window->DockTabIsVisible = false; + return; + } + + IM_ASSERT(dock_node->HostWindow); + IM_ASSERT(!dock_node->IsParent()); + window->DockIsActive = true; + window->DockTabIsVisible = (dock_node->TabBar && dock_node->TabBar->VisibleTabId == window->ID); + + // Update window flag + IM_ASSERT((window->Flags & ImGuiWindowFlags_ChildWindow) == 0); + window->Flags |= ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_NoResize; + window->Flags &= ~ImGuiWindowFlags_NoTitleBar; // Clear the NoTitleBar flag in case the user set it: confusingly enough we need a title bar height so we are correctly offset, but it won't be displayed! + + // Position window + SetNextWindowPos(dock_node->Pos); + SetNextWindowSize(dock_node->Size); + g.NextWindowData.PosUndock = false; + // Save new dock order only if the tab bar is active + if (dock_node->TabBar) + window->DockOrder = (short)DockNodeGetTabOrder(window); + + if ((dock_node->WantCloseAll || dock_node->WantCloseOne == window->ID) && p_open != NULL) + *p_open = false; + + // Update ChildId to allow returning from Child to Parent with Escape + ImGuiWindow* parent_window = window->DockNode->HostWindow; + window->ChildId = parent_window->GetID(window->Name); +} + +void ImGui::BeginAsDockableDragDropSource(ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(g.ActiveId == window->MoveId); + + window->DC.LastItemId = window->MoveId; + window = window->RootWindow; + IM_ASSERT((window->Flags & ImGuiWindowFlags_NoDocking) == 0); + bool is_drag_docking = (g.IO.ConfigDockingWithKeyMod) || (window->Flags & ImGuiWindowFlags_NoTitleBar) || ImRect(0, 0, window->SizeFull.x, window->TitleBarHeight()).Contains(g.ActiveIdClickOffset); + if (is_drag_docking && BeginDragDropSource(ImGuiDragDropFlags_SourceNoPreviewTooltip | ImGuiDragDropFlags_SourceNoHoldToOpenOthers | ImGuiDragDropFlags_SourceAutoExpirePayload)) + { + SetDragDropPayload(IMGUI_PAYLOAD_TYPE_WINDOW, &window, sizeof(window)); + EndDragDropSource(); + } +} + +void ImGui::BeginAsDockableDragDropTarget(ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT((window->Flags & ImGuiWindowFlags_NoDocking) == 0); + if (!g.DragDropActive) + return; + + if (!BeginDragDropTargetCustom(window->Rect(), window->ID)) + return; + + // Peek into the payload before calling AcceptDragDropPayload() so we can handle overlapping dock nodes with filtering + // (this is a little unusual pattern, normally most code would call AcceptDragDropPayload directly) + const ImGuiPayload* payload = &g.DragDropPayload; + if (!payload->IsDataType(IMGUI_PAYLOAD_TYPE_WINDOW) || !DockNodeIsDropAllowed(window, *(ImGuiWindow**)payload->Data)) + { + EndDragDropTarget(); + return; + } + + ImGuiWindow* payload_window = *(ImGuiWindow**)payload->Data; + if (AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_WINDOW, ImGuiDragDropFlags_AcceptBeforeDelivery | ImGuiDragDropFlags_AcceptNoDrawDefaultRect)) + { + bool allow_null_target_node = false; + ImGuiDockNode* target_node = NULL; + if (window->DockNodeAsHost) + target_node = DockNodeTreeFindNodeByPos(window->DockNodeAsHost, g.IO.MousePos); + else if (window->DockNode && window->DockIsActive) + target_node = window->DockNode; + else + allow_null_target_node = true; // Dock into a regular window + + const ImRect explicit_target_rect = (target_node && target_node->TabBar) ? target_node->TabBar->BarRect : window->TitleBarRect(); + const bool is_explicit_target = g.IO.ConfigDockingWithKeyMod || IsMouseHoveringRect(explicit_target_rect.Min, explicit_target_rect.Max); + + // Preview docking request and find out split direction/ratio + //const bool do_preview = true; // Ignore testing for payload->IsPreview() which removes one frame of delay, but breaks overlapping drop targets within the same window. + const bool do_preview = payload->IsPreview() || payload->IsDelivery(); + if (do_preview && (target_node != NULL || allow_null_target_node)) + { + ImGuiDockPreviewData split_inner, split_outer; + ImGuiDockPreviewData* split_data = &split_inner; + if (target_node && (target_node->ParentNode || target_node->IsDocumentRoot)) + if (ImGuiDockNode* root_node = DockNodeGetRootNode(target_node)) + if (DockNodePreviewDockCalc(window, root_node, payload_window, &split_outer, is_explicit_target, true)) + split_data = &split_outer; + DockNodePreviewDockCalc(window, target_node, payload_window, &split_inner, is_explicit_target, false); + + // Draw inner then outer, so that previewed tab (in inner data) will be behind the outer drop boxes + DockNodePreviewDockRender(window, target_node, payload_window, &split_inner); + DockNodePreviewDockRender(window, target_node, payload_window, &split_outer); + + // Queue docking request + if (split_data && split_data->IsDropAllowed && payload->IsDelivery()) + DockContextQueueDock(g.DockContext, window, split_data->SplitNode, payload_window, split_data->SplitDir, split_data->SplitRatio, split_data == &split_outer); + } + } + EndDragDropTarget(); +} //----------------------------------------------------------------------------- // Docking: Settings //----------------------------------------------------------------------------- +static void ImGui::DockSettingsRemoveAllReferencesToNode(ImGuiID node_id) +{ + ImGuiContext& g = *GImGui; + for (int settings_n = 0; settings_n < g.SettingsWindows.Size; settings_n++) // FIXME-OPT: We could remove this loop by storing the index in the map + { + ImGuiWindowSettings* window_settings = &g.SettingsWindows[settings_n]; + if (window_settings->DockId == node_id) + { + window_settings->DockId = 0; + window_settings->DockOrder = -1; + break; + } + } +} + +static ImGuiDockNodeSettings* ImGui::DockSettingsFindNodeSettings(ImGuiDockContext* ctx, ImGuiID id) +{ + for (int n = 0; n < ctx->SettingsNodes.Size; n++) + if (ctx->SettingsNodes[n].ID == id) + return &ctx->SettingsNodes[n]; + return NULL; +} + +static void* ImGui::DockSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name) +{ + if (strcmp(name, "Data") != 0) + return NULL; + return (void*)1; +} + +static void ImGui::DockSettingsHandler_ReadLine(ImGuiContext* imgui_ctx, ImGuiSettingsHandler*, void*, const char* line) +{ + char c = 0; + float f = 0.0f; + int x = 0, y = 0; + int r = 0; + + // Parsing, e.g. + // " DockNode ID=0x00000001 Pos=383,193 Size=201,322 Split=Y,0.506 " + // " DockNode ID=0x00000002 Parent=0x00000001 " + ImGuiDockNodeSettings node; + line = ImStrSkipBlank(line); + if (strncmp(line, "DockNode", 8) != 0) return; + line = ImStrSkipBlank(line + strlen("DockNode")); + if (sscanf(line, "ID=0x%08X%n", &node.ID, &r) == 1) { line += r; } else return; + if (sscanf(line, " Parent=0x%08X%n", &node.ParentID, &r) == 1) { line += r; if (node.ParentID == 0) return; } + if (node.ParentID == 0) + { + if (sscanf(line, " Pos=%i,%i%n", &x, &y, &r) == 2) { line += r; node.Pos = ImVec2ih((short)x, (short)y); } else return; + if (sscanf(line, " Size=%i,%i%n", &x, &y, &r) == 2) { line += r; node.Size = ImVec2ih((short)x, (short)y); } else return; + } + else + { + if (sscanf(line, " LastExplicitSize=%i,%i%n", &x, &y, &r) == 2) { line += r; node.LastExplicitSize = ImVec2ih((short)x, (short)y); } + } + if (sscanf(line, " Split=%c,%f%n", &c, &f, &r) == 2) { line += r; if (c == 'X') node.SplitAxis = ImGuiAxis_X; else if (c == 'Y') node.SplitAxis = ImGuiAxis_Y; node.SplitRatio = f; } + if (sscanf(line, " ExplicitRoot=%d%n", &x, &r) == 1) { line += r; node.IsExplicitRoot = (x != 0); } + if (sscanf(line, " DocumentRoot=%d%n", &x, &r) == 1) { line += r; node.IsDocumentRoot = (x != 0); } + if (sscanf(line, " SelectedTab=0x%08X%n", &node.SelectedTabID,&r) == 1) { line += r; } + //if (node.ParentID == 0 && node.SplitAxis == ImGuiAxis_None) + // return; + ImGuiDockContext* ctx = imgui_ctx->DockContext; + if (node.ParentID != 0) + if (ImGuiDockNodeSettings* parent_settings = DockSettingsFindNodeSettings(ctx, node.ParentID)) + node.Depth = parent_settings->Depth + 1; + ctx->SettingsNodes.push_back(node); +} + +static void DockSettingsHandler_DockNodeToSettings(ImGuiDockContext* ctx, ImGuiDockNode* node, int depth) +{ + ImGuiDockNodeSettings node_settings; + node_settings.ID = node->ID; + node_settings.ParentID = node->ParentNode ? node->ParentNode->ID : 0; + node_settings.SelectedTabID = node->SelectedTabID; + node_settings.SplitRatio = node->SplitRatio; + node_settings.SplitAxis = (char)node->SplitAxis; + node_settings.Depth = (char)depth; + node_settings.IsExplicitRoot = (char)node->IsExplicitRoot; + node_settings.IsDocumentRoot = (char)node->IsDocumentRoot; + node_settings.Pos = ImVec2ih((short)node->Pos.x, (short)node->Pos.y); + node_settings.Size = ImVec2ih((short)node->Size.x, (short)node->Size.y); + node_settings.LastExplicitSize = ImVec2ih((short)node->LastExplicitSize.x, (short)node->LastExplicitSize.y); + ctx->SettingsNodes.push_back(node_settings); + ctx->SettingsMaxDepth = ImMax(ctx->SettingsMaxDepth, depth); + if (node->ChildNodes[0]) + DockSettingsHandler_DockNodeToSettings(ctx, node->ChildNodes[0], depth + 1); + if (node->ChildNodes[1]) + DockSettingsHandler_DockNodeToSettings(ctx, node->ChildNodes[1], depth + 1); +} + +static void ImGui::DockSettingsHandler_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf) +{ + // Gather settings data + ImGuiDockContext* ctx = imgui_ctx->DockContext; + ctx->SettingsNodes.resize(0); + ctx->SettingsNodes.reserve(ctx->Nodes.Data.Size); + ctx->SettingsMaxDepth = 0; + for (int n = 0; n < ctx->Nodes.Data.Size; n++) + if (ImGuiDockNode* node = (ImGuiDockNode*)ctx->Nodes.Data[n].val_p) + if (node->IsRootNode()) + DockSettingsHandler_DockNodeToSettings(ctx, node, 0); + // Write to text buffer + buf->appendf("[%s][Data]\n", handler->TypeName); + for (int node_n = 0; node_n < ctx->SettingsNodes.Size; node_n++) + { + const ImGuiDockNodeSettings* node_settings = &ctx->SettingsNodes[node_n]; + buf->appendf("%*sDockNode%*s", node_settings->Depth * 2, "", (ImMax(ctx->SettingsMaxDepth,0) - node_settings->Depth) * 2, ""); // Text align nodes to facilitate looking at .ini file + buf->appendf(" ID=0x%08X", node_settings->ID); + if (node_settings->ParentID) + buf->appendf(" Parent=0x%08X LastExplicitSize=%d,%d", node_settings->ParentID, node_settings->LastExplicitSize.x, node_settings->LastExplicitSize.y); + else + buf->appendf(" Pos=%d,%d Size=%d,%d", node_settings->Pos.x, node_settings->Pos.y, node_settings->Size.x, node_settings->Size.y); + if (node_settings->SplitAxis != ImGuiAxis_None) + buf->appendf(" Split=%c,%.3f", (node_settings->SplitAxis == ImGuiAxis_X) ? 'X' : 'Y', node_settings->SplitRatio); + if (node_settings->IsExplicitRoot) + buf->appendf(" ExplicitRoot=%d", node_settings->IsExplicitRoot); + if (node_settings->IsDocumentRoot) + buf->appendf(" DocumentRoot=%d", node_settings->IsDocumentRoot); + if (node_settings->SelectedTabID) + buf->appendf(" SelectedTab=0x%08X", node_settings->SelectedTabID); + +#if 0 // [DEBUG] Include windows names in the .ini file + if (ImGuiDockNode* node = DockContextFindNodeByID(ctx, node_settings->ID)) + if (node->Windows.Size > 0) + { + buf->appendf("%*s; recently: %d %s (", 15, "", node->Windows.Size, node->Windows.Size == 1 ? "window " : "windows"); + for (int window_n = 0; window_n < node->Windows.Size; window_n++) + buf->appendf("\"%s\"%s", node->Windows[window_n]->Name, (window_n + 1 < node->Windows.Size) ? ", " : ")"); + } +#endif + buf->appendf("\n"); + } + buf->appendf("\n"); +} //----------------------------------------------------------------------------- // [SECTION] LOGGING/CAPTURING From 49533bc86eced2a48094b9fd7eb1e1dd72090b37 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 6 Sep 2018 21:53:35 +0200 Subject: [PATCH 237/828] Examples: Enabled Docking + Nav by default in the branch. (#351) --- examples/example_allegro5/main.cpp | 4 +++- examples/example_apple_opengl2/main.mm | 3 ++- examples/example_freeglut_opengl2/main.cpp | 3 ++- examples/example_glfw_opengl2/main.cpp | 5 +++-- examples/example_glfw_opengl3/main.cpp | 5 +++-- examples/example_glfw_vulkan/main.cpp | 5 +++-- examples/example_marmalade/main.cpp | 4 +++- examples/example_sdl_opengl2/main.cpp | 3 ++- examples/example_sdl_opengl3/main.cpp | 3 ++- examples/example_sdl_vulkan/main.cpp | 3 ++- examples/example_win32_directx10/main.cpp | 3 ++- examples/example_win32_directx11/main.cpp | 3 ++- examples/example_win32_directx12/main.cpp | 3 ++- examples/example_win32_directx9/main.cpp | 4 +++- 14 files changed, 34 insertions(+), 17 deletions(-) diff --git a/examples/example_allegro5/main.cpp b/examples/example_allegro5/main.cpp index 18a429f6d37e..2c8fc5e02f14 100644 --- a/examples/example_allegro5/main.cpp +++ b/examples/example_allegro5/main.cpp @@ -26,7 +26,9 @@ int main(int, char**) IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking + ImGui_ImplAllegro5_Init(display); // Setup style diff --git a/examples/example_apple_opengl2/main.mm b/examples/example_apple_opengl2/main.mm index cd5dd9418a6d..e33248729dd1 100644 --- a/examples/example_apple_opengl2/main.mm +++ b/examples/example_apple_opengl2/main.mm @@ -241,7 +241,8 @@ -(void)applicationDidFinishLaunching:(NSNotification *)aNotification IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking ImGui_ImplOSX_Init(); ImGui_ImplOpenGL2_Init(); diff --git a/examples/example_freeglut_opengl2/main.cpp b/examples/example_freeglut_opengl2/main.cpp index 8b489dd615ef..832fd36c9fcb 100644 --- a/examples/example_freeglut_opengl2/main.cpp +++ b/examples/example_freeglut_opengl2/main.cpp @@ -98,7 +98,8 @@ int main(int argc, char** argv) // Setup ImGui binding ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking ImGui_ImplFreeGLUT_Init(); ImGui_ImplFreeGLUT_InstallFuncs(); diff --git a/examples/example_glfw_opengl2/main.cpp b/examples/example_glfw_opengl2/main.cpp index db99c8d00356..f69c954df7ac 100644 --- a/examples/example_glfw_opengl2/main.cpp +++ b/examples/example_glfw_opengl2/main.cpp @@ -33,8 +33,9 @@ int main(int, char**) IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls - //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking ImGui_ImplGlfw_InitForOpenGL(window, true); ImGui_ImplOpenGL2_Init(); diff --git a/examples/example_glfw_opengl3/main.cpp b/examples/example_glfw_opengl3/main.cpp index 2795ff49d3b6..cd03a770bddc 100644 --- a/examples/example_glfw_opengl3/main.cpp +++ b/examples/example_glfw_opengl3/main.cpp @@ -76,8 +76,9 @@ int main(int, char**) IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls - //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking ImGui_ImplGlfw_InitForOpenGL(window, true); ImGui_ImplOpenGL3_Init(glsl_version); diff --git a/examples/example_glfw_vulkan/main.cpp b/examples/example_glfw_vulkan/main.cpp index 5ca9e9c99208..d6f87aa78949 100644 --- a/examples/example_glfw_vulkan/main.cpp +++ b/examples/example_glfw_vulkan/main.cpp @@ -347,8 +347,9 @@ int main(int, char**) IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls - //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking // Setup GLFW binding ImGui_ImplGlfw_InitForVulkan(window, true); diff --git a/examples/example_marmalade/main.cpp b/examples/example_marmalade/main.cpp index 41c65953113f..71a9d9249ae7 100644 --- a/examples/example_marmalade/main.cpp +++ b/examples/example_marmalade/main.cpp @@ -20,7 +20,9 @@ int main(int, char**) IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking + ImGui_Marmalade_Init(true); // Setup style diff --git a/examples/example_sdl_opengl2/main.cpp b/examples/example_sdl_opengl2/main.cpp index 1a1079b7f706..034618bfda6e 100644 --- a/examples/example_sdl_opengl2/main.cpp +++ b/examples/example_sdl_opengl2/main.cpp @@ -38,7 +38,8 @@ int main(int, char**) IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking ImGui_ImplSDL2_InitForOpenGL(window, gl_context); ImGui_ImplOpenGL2_Init(); diff --git a/examples/example_sdl_opengl3/main.cpp b/examples/example_sdl_opengl3/main.cpp index 156520f7277b..75523ade88b4 100644 --- a/examples/example_sdl_opengl3/main.cpp +++ b/examples/example_sdl_opengl3/main.cpp @@ -76,7 +76,8 @@ int main(int, char**) IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking ImGui_ImplSDL2_InitForOpenGL(window, gl_context); ImGui_ImplOpenGL3_Init(glsl_version); diff --git a/examples/example_sdl_vulkan/main.cpp b/examples/example_sdl_vulkan/main.cpp index 29ad434eff3d..63a749213eb5 100644 --- a/examples/example_sdl_vulkan/main.cpp +++ b/examples/example_sdl_vulkan/main.cpp @@ -335,7 +335,8 @@ int main(int, char**) // Setup ImGui binding ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking // Setup SDL binding ImGui_ImplSDL2_InitForVulkan(window); diff --git a/examples/example_win32_directx10/main.cpp b/examples/example_win32_directx10/main.cpp index 29d25fcabe9c..6620dda4bc1f 100644 --- a/examples/example_win32_directx10/main.cpp +++ b/examples/example_win32_directx10/main.cpp @@ -116,7 +116,8 @@ int main(int, char**) IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking ImGui_ImplWin32_Init(hwnd); ImGui_ImplDX10_Init(g_pd3dDevice); diff --git a/examples/example_win32_directx11/main.cpp b/examples/example_win32_directx11/main.cpp index c98e6392cd4b..a923d50fa571 100644 --- a/examples/example_win32_directx11/main.cpp +++ b/examples/example_win32_directx11/main.cpp @@ -119,7 +119,8 @@ int main(int, char**) IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking ImGui_ImplWin32_Init(hwnd); ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext); diff --git a/examples/example_win32_directx12/main.cpp b/examples/example_win32_directx12/main.cpp index a4ce237ef2af..257024911bc6 100644 --- a/examples/example_win32_directx12/main.cpp +++ b/examples/example_win32_directx12/main.cpp @@ -290,7 +290,8 @@ int main(int, char**) IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking ImGui_ImplWin32_Init(hwnd); ImGui_ImplDX12_Init(g_pd3dDevice, NUM_FRAMES_IN_FLIGHT, diff --git a/examples/example_win32_directx9/main.cpp b/examples/example_win32_directx9/main.cpp index 6731b5711566..6d0bf7f2fc9c 100644 --- a/examples/example_win32_directx9/main.cpp +++ b/examples/example_win32_directx9/main.cpp @@ -79,7 +79,9 @@ int main(int, char**) IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking + ImGui_ImplWin32_Init(hwnd); ImGui_ImplDX9_Init(g_pd3dDevice); From b872aa5c8e6cb954226c3939482334fb428c15ac Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 7 Sep 2018 19:17:30 +0200 Subject: [PATCH 238/828] Docking: io.ConfigResizeWindowsFromEdges default to true in Docking branch. Moved code in BeginTabItem(). --- examples/example_win32_directx11/main.cpp | 2 ++ imgui.cpp | 8 ++++++-- imgui.h | 2 +- imgui_demo.cpp | 4 ++-- imgui_widgets.cpp | 8 ++++---- 5 files changed, 15 insertions(+), 9 deletions(-) diff --git a/examples/example_win32_directx11/main.cpp b/examples/example_win32_directx11/main.cpp index 6461571f0635..6026ce186fee 100644 --- a/examples/example_win32_directx11/main.cpp +++ b/examples/example_win32_directx11/main.cpp @@ -141,6 +141,8 @@ int main(int, char**) //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoMerge; io.ConfigFlags |= ImGuiConfigFlags_DpiEnableScaleFonts; // FIXME-DPI: THIS CURRENTLY DOESN'T WORK AS EXPECTED. DON'T USE IN USER APP! io.ConfigFlags |= ImGuiConfigFlags_DpiEnableScaleViewports; // FIXME-DPI + io.ConfigResizeWindowsFromEdges = true; + io.ConfigDockingWithKeyMod = true; ImGui_ImplWin32_Init(hwnd); ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext); diff --git a/imgui.cpp b/imgui.cpp index a823422d4930..4327ecea0e8f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1093,7 +1093,7 @@ ImGuiIO::ImGuiIO() ConfigMacOSXBehaviors = false; #endif ConfigInputTextCursorBlink = true; - ConfigResizeWindowsFromEdges = false; + ConfigResizeWindowsFromEdges = true; // Settings (User Functions) GetClipboardTextFn = GetClipboardTextFn_DefaultImpl; // Platform dependent default implementations @@ -9468,16 +9468,20 @@ void ImGui::EndDragDropTarget() // TODO: // A~ document root node resizing behavior incorrect // A~ document root node retrieval of ID ? +// A~ Unreal style document system (requires low-level controls of dockspace serialization fork/copy/delete) // B- resize sibling locking behavior may be less desirable if we merged same-axis sibling in a same node level? // A- single visible node part of a hidden split hierarchy (OnlyNodeWithWindows) should show a normal tab bar // B~ SetNextWindowDock() calls (with conditional) -> defer everything to DockContextUpdate (repro: Documents->[X]Windows->Dock 1 elsewhere->Click Redock All // B- fix/disable auto-resize grip on split host nodes // B~ tidy up tab list popup buttons (see old ImGuiTabBarFlags_NoTabListPopupButton code) // B- DockSpace() border issues +// B- inconsistent clipping/border 1-pixel issue (#2) +// B- SetNextWindowFocus() doesn't seem to apply if the window is hidden this frame, need repro (#4) // B- implicit per-viewport dockspace to dock to // B- resizing a dock tree small currently has glitches (overlapping collapse and close button, etc.) +// B- tab bar: appearing on first frame with a dumb layout would do less harm that not appearing? (when behind dynamic branch) or store titles + render in EndTabBar() // B- tab bar: make selected tab always shows its full title? -// B- tab bar: the order/focus restoring code could be part of TabBar and not DockNode? +// B- tab bar: the order/focus restoring code could be part of TabBar and not DockNode? (#8) // B- nav: CTRL+TAB highlighting tabs shows the mismatch between focus-stack and tab-order (not visible in VS because it doesn't highlight the tabs) // B- nav: design interactions so nav controls can dock/undock //----------------------------------------------------------------------------- diff --git a/imgui.h b/imgui.h index ecbc3aaeab37..53f177952ed2 100644 --- a/imgui.h +++ b/imgui.h @@ -1195,7 +1195,7 @@ struct ImGuiIO bool ConfigDockingWithKeyMod; // = true // Enable docking with holding Shift key (reduce visual noise, allows dropping in wider space) bool ConfigMacOSXBehaviors; // = defined(__APPLE__) // OS X style: Text editing cursor movement using Alt instead of Ctrl, Shortcuts using Cmd/Super instead of Ctrl, Line/Text Start and End using Cmd+Arrows instead of Home/End, Double click selects by word instead of selecting whole text, Multi-selection in lists uses Cmd/Super instead of Ctrl (was called io.OptMacOSXBehaviors prior to 1.63) bool ConfigInputTextCursorBlink; // = true // Set to false to disable blinking cursor, for users who consider it distracting. (was called: io.OptCursorBlink prior to 1.63) - bool ConfigResizeWindowsFromEdges; // = false // [BETA] Enable resizing of windows from their edges and from the lower-left corner. This requires (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) because it needs mouse cursor feedback. (This used to be the ImGuiWindowFlags_ResizeFromAnySide flag) + bool ConfigResizeWindowsFromEdges; // = true // [BETA] Enable resizing of windows from their edges and from the lower-left corner. This requires (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) because it needs mouse cursor feedback. (This used to be the ImGuiWindowFlags_ResizeFromAnySide flag) //------------------------------------------------------------------ // Settings (User Functions) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 052c267718ee..da89d978cfaf 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -267,7 +267,7 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::MenuItem("Simple overlay", NULL, &show_app_simple_overlay); ImGui::MenuItem("Manipulating window titles", NULL, &show_app_window_titles); ImGui::MenuItem("Custom rendering", NULL, &show_app_custom_rendering); - ImGui::MenuItem("Docking", NULL, &show_app_dockspace); + ImGui::MenuItem("Dockspace", NULL, &show_app_dockspace); ImGui::MenuItem("Documents", NULL, &show_app_documents); ImGui::EndMenu(); } @@ -3713,7 +3713,7 @@ void ShowExampleAppDockSpace(bool* p_open) flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus; } ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); - ImGui::Begin("Docking Documents Demo", p_open, flags); + ImGui::Begin("DockSpace Demo", p_open, flags); ImGui::PopStyleVar(); if (ImGui::BeginMenuBar()) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 67d2686f4e9d..a622a034bd5e 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6496,15 +6496,15 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, TabBarCloseTab(tab_bar, tab); } - // Tooltip (FIXME: Won't work over the close button because ItemOverlap systems messes up with HoveredIdTimer) - if (g.HoveredId == id && !held && g.HoveredIdTimer > 0.50f) - SetTooltip("%.*s", (int)(FindRenderedTextEnd(label) - label), label); - // Restore main window position so user can draw there if (want_clip_rect) PopClipRect(); window->DC.CursorPos = backup_main_cursor_pos; + // Tooltip (FIXME: Won't work over the close button because ItemOverlap systems messes up with HoveredIdTimer) + if (g.HoveredId == id && !held && g.HoveredIdTimer > 0.50f) + SetTooltip("%.*s", (int)(FindRenderedTextEnd(label) - label), label); + return tab_contents_visible; } From a68c98bb67e07b82b18934a51bc398b23820d23d Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 10 Sep 2018 12:07:29 +0200 Subject: [PATCH 239/828] Docking: Added SetNextWindowUserType() + user type filtering in dockspace and window merging. Added DockSpace() flag ImGuiDockFlags_NoSplit. Renaming. Reworked the DockNodeUpdateFindOnlyNodeWithWindows code so DockNodeUpdate can access the first window. --- imgui.cpp | 69 +++++++++++++++++++++++++++++++++--------------- imgui.h | 11 +++++++- imgui_internal.h | 11 +++++--- 3 files changed, 65 insertions(+), 26 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 4327ecea0e8f..39b41b662337 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2371,6 +2371,7 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) HiddenFramesRegular = HiddenFramesForResize = 0; SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = SetWindowDockAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing; SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX); + UserTypeId = 0; LastFrameActive = -1; ItemWidthDefault = 0.0f; @@ -3350,7 +3351,7 @@ void ImGui::NewFrame() // Undocking // (needs to be before UpdateMovingWindow so the window is already offset and following the mouse on the detaching frame) - DockContextUpdateUndocking(g.DockContext); + DockContextNewFrameUpdateUndocking(g.DockContext); // Find hovered window // (needs to be before UpdateMovingWindow so we fill HoveredWindowUnderMovingWindow on the mouse release frame) @@ -3404,7 +3405,7 @@ void ImGui::NewFrame() ClosePopupsOverWindow(g.NavWindow); // Docking - DockContextUpdateDocking(g.DockContext); + DockContextNewFrameUpdateDocking(g.DockContext); // Create implicit window - we will only render it if the user has added something to it. // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags. @@ -4954,6 +4955,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) { window->SizeContentsExplicit = ImVec2(0.0f, 0.0f); } + window->UserTypeId = g.NextWindowData.UserTypeId; if (g.NextWindowData.CollapsedCond) SetWindowCollapsed(window, g.NextWindowData.CollapsedVal, g.NextWindowData.CollapsedCond); if (g.NextWindowData.FocusCond) @@ -6320,6 +6322,12 @@ void ImGui::SetNextWindowDock(ImGuiID id, ImGuiCond cond) g.NextWindowData.DockId = id; } +void ImGui::SetNextWindowUserType(ImGuiID user_type) +{ + ImGuiContext& g = *GImGui; + g.NextWindowData.UserTypeId = user_type; +} + // In window space (not screen space!) ImVec2 ImGui::GetContentRegionMax() { @@ -9580,7 +9588,6 @@ namespace ImGui static void DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window, ImGuiID save_dock_id); static void DockNodeHideHostWindow(ImGuiDockNode* node); static void DockNodeUpdate(ImGuiDockNode* node); - static ImGuiDockNode* DockNodeUpdateFindOnlyNodeWithWindows(ImGuiDockNode* node); static void DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* node); static void DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_window); static void DockNodeUpdateVisibleFlag(ImGuiDockNode* node); @@ -9656,7 +9663,7 @@ void ImGui::DockContextRebuild(ImGuiDockContext* ctx) DockContextBuildAddWindowsToNodes(ctx); } -void ImGui::DockContextUpdateUndocking(ImGuiDockContext* ctx) +void ImGui::DockContextNewFrameUpdateUndocking(ImGuiDockContext* ctx) { ImGuiContext& g = *GImGui; if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable)) @@ -9682,7 +9689,7 @@ void ImGui::DockContextUpdateUndocking(ImGuiDockContext* ctx) DockContextProcessUndock(ctx, ctx->Requests[n].WindowUndock); } -void ImGui::DockContextUpdateDocking(ImGuiDockContext* ctx) +void ImGui::DockContextNewFrameUpdateDocking(ImGuiDockContext* ctx) { ImGuiContext& g = *GImGui; if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable)) @@ -10000,6 +10007,8 @@ void ImGui::DockContextProcessUndock(ImGuiDockContext* ctx, ImGuiWindow* window) ImGuiDockNode::ImGuiDockNode(ImGuiID id) { ID = id; + UserTypeIdFilter = 0; + Flags = 0; ParentNode = ChildNodes[0] = ChildNodes[1] = NULL; TabBar = NULL; SplitAxis = ImGuiAxis_None; @@ -10197,28 +10206,20 @@ static void ImGui::DockNodeHideHostWindow(ImGuiDockNode* node) } } -static void DockNodeUpdateFindOnlyNodeWithWindowsRec(ImGuiDockNode* node, int* p_count, ImGuiDockNode** p_only_node_with_windows) +static void DockNodeUpdateFindOnlyNodeWithWindowsRec(ImGuiDockNode* node, int* p_count, ImGuiDockNode** p_first_node_with_windows) { if (node->Windows.Size > 0) { - if (*p_only_node_with_windows == NULL) - *p_only_node_with_windows = node; + if (*p_first_node_with_windows == NULL) + *p_first_node_with_windows = node; (*p_count)++; } if (*p_count > 1) return; if (node->ChildNodes[0]) - DockNodeUpdateFindOnlyNodeWithWindowsRec(node->ChildNodes[0], p_count, p_only_node_with_windows); + DockNodeUpdateFindOnlyNodeWithWindowsRec(node->ChildNodes[0], p_count, p_first_node_with_windows); if (node->ChildNodes[1]) - DockNodeUpdateFindOnlyNodeWithWindowsRec(node->ChildNodes[1], p_count, p_only_node_with_windows); -} - -static ImGuiDockNode* ImGui::DockNodeUpdateFindOnlyNodeWithWindows(ImGuiDockNode* node) -{ - int count = 0; - ImGuiDockNode* only_node_with_windows = NULL; - DockNodeUpdateFindOnlyNodeWithWindowsRec(node, &count, &only_node_with_windows); - return (count == 1 ? only_node_with_windows : NULL); + DockNodeUpdateFindOnlyNodeWithWindowsRec(node->ChildNodes[1], p_count, p_first_node_with_windows); } static void ImGui::DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* node) @@ -10280,8 +10281,19 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) if (node->IsRootNode()) { DockNodeUpdateVisibleFlagAndInactiveChilds(node); - ImGuiDockNode* only_node_with_windows = node->IsExplicitRoot ? NULL : DockNodeUpdateFindOnlyNodeWithWindows(node); - node->OnlyNodeWithWindows = only_node_with_windows; + + // Find if there's only a single visible window in the hierarchy (in which case we need to display a regular title bar, FIXME-DOCK: Not done yet!) + if (!node->IsExplicitRoot) + { + int count = 0; + ImGuiDockNode* first_node_with_windows = NULL; + DockNodeUpdateFindOnlyNodeWithWindowsRec(node, &count, &first_node_with_windows); + node->OnlyNodeWithWindows = (count == 1 ? first_node_with_windows : NULL); + + // Copy the user type from _any_ of our window so it can be used for proper dock filtering. + if (first_node_with_windows) + node->UserTypeIdFilter = first_node_with_windows->Windows[0]->UserTypeId; + } } // Early out for standalone floating window that are holding on a DockId (with an invisible dock node) @@ -10640,6 +10652,11 @@ static bool ImGui::DockNodeIsDropAllowed(ImGuiWindow* host_window, ImGuiWindow* ImGuiWindow* payload = root_payload->DockNodeAsHost ? root_payload->DockNodeAsHost->Windows[payload_n] : root_payload; if ((host_window->Flags & ImGuiWindowFlags_DockNodeHost) && payload->BeginOrderWithinContext < host_window->BeginOrderWithinContext) continue; + + ImGuiID host_user_type_id = host_window->DockNodeAsHost ? host_window->DockNodeAsHost->UserTypeIdFilter : host_window->UserTypeId; + if (payload->UserTypeId != host_user_type_id) + return false; + return true; } return false; @@ -10756,6 +10773,8 @@ static bool ImGui::DockNodePreviewDockCalc(ImGuiWindow* host_window, ImGuiDockNo data->IsCenterAvailable = false; data->IsSidesAvailable = true; + if (host_node && (host_node->Flags & ImGuiDockFlags_NoSplit)) + data->IsSidesAvailable = false; if (!is_outer_docking && host_node && host_node->ParentNode == NULL && host_node->IsDocumentRoot) data->IsSidesAvailable = false; @@ -10871,6 +10890,9 @@ static void ImGui::DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDock } } + if (host_node && (host_node->Flags & ImGuiDockFlags_NoSplit)) + return; + // Display drop boxes const float overlay_rounding = ImMax(3.0f, g.Style.FrameRounding); for (int dir = ImGuiDir_None; dir < ImGuiDir_COUNT; dir++) @@ -11189,7 +11211,7 @@ void ImGui::SetWindowDock(ImGuiWindow* window, ImGuiID dock_id, ImGuiCond cond) window->DockId = dock_id; } -void ImGui::DockSpace(const char* str_id, const ImVec2& size_arg) +void ImGui::DockSpace(const char* str_id, const ImVec2& size_arg, ImGuiDockFlags dock_flags, ImGuiID user_type_filter) { ImGuiContext& g = *GImGui; ImGuiDockContext* ctx = g.DockContext; @@ -11204,6 +11226,8 @@ void ImGui::DockSpace(const char* str_id, const ImVec2& size_arg) node = DockContextAddNode(ctx, id); node->IsDocumentRoot = true; } + node->Flags = dock_flags; + node->UserTypeIdFilter = user_type_filter; node->IsExplicitRoot = true; const ImVec2 content_avail = GetContentRegionAvail(); @@ -11235,6 +11259,7 @@ void ImGui::DockSpace(const char* str_id, const ImVec2& size_arg) host_window->DockNodeAsHost = node; host_window->ChildId = window->GetID(title); node->HostWindow = host_window; + node->OnlyNodeWithWindows = NULL; IM_ASSERT(node->IsRootNode()); DockNodeUpdate(node); @@ -11320,7 +11345,7 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) window->Flags |= ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_NoResize; window->Flags &= ~ImGuiWindowFlags_NoTitleBar; // Clear the NoTitleBar flag in case the user set it: confusingly enough we need a title bar height so we are correctly offset, but it won't be displayed! - // Position window + // Position window SetNextWindowPos(dock_node->Pos); SetNextWindowSize(dock_node->Size); g.NextWindowData.PosUndock = false; diff --git a/imgui.h b/imgui.h index 53f177952ed2..528d6a5ee9ed 100644 --- a/imgui.h +++ b/imgui.h @@ -111,6 +111,7 @@ typedef int ImGuiColorEditFlags; // -> enum ImGuiColorEditFlags_ // Flags: f typedef int ImGuiColumnsFlags; // -> enum ImGuiColumnsFlags_ // Flags: for Columns(), BeginColumns() typedef int ImGuiConfigFlags; // -> enum ImGuiConfigFlags_ // Flags: for io.ConfigFlags typedef int ImGuiComboFlags; // -> enum ImGuiComboFlags_ // Flags: for BeginCombo() +typedef int ImGuiDockFlags; // -> enum ImGuiDockFlags_ // Flags: for DockSpace() typedef int ImGuiDragDropFlags; // -> enum ImGuiDragDropFlags_ // Flags: for *DragDrop*() typedef int ImGuiFocusedFlags; // -> enum ImGuiFocusedFlags_ // Flags: for IsWindowFocused() typedef int ImGuiHoveredFlags; // -> enum ImGuiHoveredFlags_ // Flags: for IsItemHovered(), IsWindowHovered() etc. @@ -519,7 +520,8 @@ namespace ImGui // Docking // [BETA API] Enable with io.ConfigFlags |= ImGuiConfigFlags_DockingEnable. // Note: you DO NOT need to call DockSpace() to use most Docking facilities! You can hold SHIFT anywhere while moving windows. Use DockSpace() if you need to create an explicit docking space _within_ an existing window. See Docking demo for details) - IMGUI_API void DockSpace(const char* str_id, const ImVec2& size = ImVec2(0, 0)); + IMGUI_API void DockSpace(const char* str_id, const ImVec2& size = ImVec2(0, 0), ImGuiDockFlags flags = 0, ImGuiID user_type_filter = 0); + IMGUI_API void SetNextWindowUserType(ImGuiID user_type); // FIXME-DOCK: set next window user type (docking filters by same user_type) // Logging/Capture: all text output from interface is captured to tty/file/clipboard. By default, tree nodes are automatically opened during logging. IMGUI_API void LogToTTY(int max_depth = -1); // start logging to tty @@ -779,6 +781,13 @@ enum ImGuiTabItemFlags_ ImGuiTabItemFlags_NoPushId = 1 << 3 // Don't call PushID(tab->ID)/PopID() on BeginTabItem()/EndTabItem() }; +// Flags for ImGui::DockSpace() +enum ImGuiDockFlags_ +{ + ImGuiDockFlags_None = 0, + ImGuiDockFlags_NoSplit = 1 << 0 +}; + // Flags for ImGui::IsWindowFocused() enum ImGuiFocusedFlags_ { diff --git a/imgui_internal.h b/imgui_internal.h index 19640bce9edf..0d8fe5648147 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -704,6 +704,7 @@ struct ImGuiNextWindowData float BgAlphaVal; ImGuiID ViewportId; ImGuiID DockId; + ImGuiID UserTypeId; ImVec2 MenuBarOffsetMinVal; // This is not exposed publicly, so we don't clear it. ImGuiNextWindowData() @@ -716,13 +717,14 @@ struct ImGuiNextWindowData SizeCallback = NULL; SizeCallbackUserData = NULL; BgAlphaVal = FLT_MAX; - ViewportId = DockId = 0; + ViewportId = DockId = UserTypeId = 0; MenuBarOffsetMinVal = ImVec2(0.0f, 0.0f); } void Clear() { PosCond = SizeCond = ContentSizeCond = CollapsedCond = SizeConstraintCond = FocusCond = BgAlphaCond = ViewportCond = DockCond = 0; + UserTypeId = 0; } }; @@ -740,6 +742,8 @@ struct ImGuiTabBarSortItem struct ImGuiDockNode { ImGuiID ID; + ImGuiID UserTypeIdFilter; + ImGuiDockFlags Flags; ImGuiDockNode* ParentNode; ImGuiDockNode* ChildNodes[2]; ImVector Windows; // Note: unordered list! Iterate TabBar->Tabs for user-order. @@ -1200,6 +1204,7 @@ struct IMGUI_API ImGuiWindow ImGuiCond SetWindowDockAllowFlags; // store acceptable condition flags for SetNextWindowDock() use. ImVec2 SetWindowPosVal; // store window position when using a non-zero Pivot (position set needs to be processed when we know the window size) ImVec2 SetWindowPosPivot; // store window pivot for positioning. ImVec2(0,0) when positioning from top-left corner; ImVec2(0.5f,0.5f) for centering; ImVec2(1,1) for bottom right. + ImGuiID UserTypeId; // user value set with SetNextWindowUserType(const char*) ImGuiWindowTempData DC; // Temporary per-window data, reset at the beginning of the frame. This used to be called ImGuiDrawContext, hence the "DC" variable name. ImVector IDStack; // ID stack. ID are hashes seeded with the value at the top of the stack @@ -1457,8 +1462,8 @@ namespace ImGui IMGUI_API void DockContextShutdown(ImGuiContext* imgui_context); IMGUI_API void DockContextOnLoadSettings(); IMGUI_API void DockContextRebuild(ImGuiDockContext* ctx); - IMGUI_API void DockContextUpdateUndocking(ImGuiDockContext* ctx); - IMGUI_API void DockContextUpdateDocking(ImGuiDockContext* ctx); + IMGUI_API void DockContextNewFrameUpdateUndocking(ImGuiDockContext* ctx); + IMGUI_API void DockContextNewFrameUpdateDocking(ImGuiDockContext* ctx); IMGUI_API void DockContextQueueUndock(ImGuiDockContext* ctx, ImGuiWindow* window); IMGUI_API void BeginDocked(ImGuiWindow* window, bool* p_open); IMGUI_API void BeginAsDockableDragDropSource(ImGuiWindow* window); From dcef0c023753f964799c0b3b26a2f4ac3e6babf3 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 10 Sep 2018 15:40:05 +0200 Subject: [PATCH 240/828] Docking: Updating LastFrameActive earlier in Begin() because BeginDocked() will need to use it. Extracted some code into a DockNodeIsDropAllowedOne() function. Comments. --- imgui.cpp | 62 +++++++++++++++++++++++++++++++----------------- imgui_internal.h | 1 + 2 files changed, 41 insertions(+), 22 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 39b41b662337..47662c0bea07 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3395,6 +3395,7 @@ void ImGui::NewFrame() } // Closing the focused window restore focus to the first active root window in descending z-order + // FIXME: Because z-order and nav-order are correlated, this will focus the wrong window if we are part of a hierarchy with NoBringToFrontOnFocus flag. if (g.NavWindow && !g.NavWindow->WasActive) FocusFrontMostActiveWindowIgnoringOne(NULL); @@ -3674,6 +3675,9 @@ void ImGui::EndFrame() g.CurrentWindow->Active = false; End(); + // Docking + DockContextEndFrame(g.DockContext); + // Draw modal whitening background on _other_ viewports than the one the modal or target are on ImGuiWindow* modal_window = GetFrontMostPopupModal(); const bool dim_bg_for_modal = (modal_window != NULL); @@ -4862,17 +4866,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) const int current_frame = g.FrameCount; const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame); - if (first_begin_of_the_frame) - { - window->FlagsPreviousFrame = window->Flags; - window->Flags = (ImGuiWindowFlags)flags; - window->BeginOrderWithinParent = 0; - window->BeginOrderWithinContext = g.WindowsActiveCount++; - } - else - { - flags = window->Flags; - } // Update the Appearing flag bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on @@ -4887,6 +4880,20 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if (window->Appearing) SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true); + // Update Flags, LastFrameActive, BeginOrderXXX fields + if (first_begin_of_the_frame) + { + window->FlagsPreviousFrame = window->Flags; + window->Flags = (ImGuiWindowFlags)flags; + window->LastFrameActive = current_frame; + window->BeginOrderWithinParent = 0; + window->BeginOrderWithinContext = g.WindowsActiveCount++; + } + else + { + flags = window->Flags; + } + // Docking // (NB: during the frame dock nodes are created, it is possible that (window->DockIsActive == false) even though (window->DockNode->Windows.Size > 1) IM_ASSERT(window->DockNode == NULL || window->DockNodeAsHost == NULL); // Cannot be both @@ -4973,7 +4980,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->Active = true; window->HasCloseButton = (p_open != NULL); window->ClipRect = ImVec4(-FLT_MAX,-FLT_MAX,+FLT_MAX,+FLT_MAX); - window->LastFrameActive = current_frame; window->IDStack.resize(1); // UPDATE CONTENTS SIZE, UPDATE HIDDEN STATUS @@ -7611,6 +7617,7 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) } else if ((flags & ImGuiWindowFlags_DockNodeHost) && (window->Appearing)) { + // Mark so the dock host can be on its own viewport window->ViewportAllowPlatformMonitorExtend = FindPlatformMonitorForRect(window->Rect()); } if (window->ViewportTrySplit && window->ViewportAllowPlatformMonitorExtend < 0) @@ -9709,6 +9716,10 @@ void ImGui::DockContextNewFrameUpdateDocking(ImGuiDockContext* ctx) DockNodeUpdate(node); } +void ImGui::DockContextEndFrame(ImGuiDockContext* ctx) +{ + (void)ctx; +} static ImGuiDockNode* ImGui::DockContextFindNodeByID(ImGuiDockContext* ctx, ImGuiID id) { @@ -9950,7 +9961,7 @@ void ImGui::DockContextProcessDock(ImGuiDockContext* ctx, ImGuiDockRequest* req) if (payload_node != NULL) { // Transfer full payload node (with 1+ child windows or child nodes) - // FIXME-DOCKING: Transition persistent DockId for all non-active windows? + // FIXME-DOCK: Transition persistent DockId for all non-active windows? if (payload_node->IsParent()) { if (target_node->Windows.Size > 0) @@ -10277,6 +10288,7 @@ static void ImGui::DockNodeUpdateVisibleFlag(ImGuiDockNode* node) static void ImGui::DockNodeUpdate(ImGuiDockNode* node) { ImGuiContext& g = *GImGui; + IM_ASSERT(node->LastFrameActive != g.FrameCount); if (node->IsRootNode()) { @@ -10641,6 +10653,18 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w } } +static bool DockNodeIsDropAllowedOne(ImGuiWindow* payload, ImGuiWindow* host_window) +{ + if ((host_window->Flags & ImGuiWindowFlags_DockNodeHost) && host_window->DockNodeAsHost->IsExplicitRoot && payload->BeginOrderWithinContext < host_window->BeginOrderWithinContext) + return false; + + ImGuiID host_user_type_id = host_window->DockNodeAsHost ? host_window->DockNodeAsHost->UserTypeIdFilter : host_window->UserTypeId; + if (payload->UserTypeId != host_user_type_id) + return false; + + return true; +} + static bool ImGui::DockNodeIsDropAllowed(ImGuiWindow* host_window, ImGuiWindow* root_payload) { if (root_payload->DockNodeAsHost && root_payload->DockNodeAsHost->IsParent()) @@ -10650,14 +10674,8 @@ static bool ImGui::DockNodeIsDropAllowed(ImGuiWindow* host_window, ImGuiWindow* for (int payload_n = 0; payload_n < payload_count; payload_n++) { ImGuiWindow* payload = root_payload->DockNodeAsHost ? root_payload->DockNodeAsHost->Windows[payload_n] : root_payload; - if ((host_window->Flags & ImGuiWindowFlags_DockNodeHost) && payload->BeginOrderWithinContext < host_window->BeginOrderWithinContext) - continue; - - ImGuiID host_user_type_id = host_window->DockNodeAsHost ? host_window->DockNodeAsHost->UserTypeIdFilter : host_window->UserTypeId; - if (payload->UserTypeId != host_user_type_id) - return false; - - return true; + if (DockNodeIsDropAllowedOne(payload, host_window)) + return true; } return false; } @@ -10871,7 +10889,7 @@ static void ImGui::DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDock { // Calculate the tab bounding box for each payload window ImGuiWindow* payload = root_payload->DockNodeAsHost ? root_payload->DockNodeAsHost->TabBar->Tabs[payload_n].Window : root_payload; - if ((host_window->Flags & ImGuiWindowFlags_DockNodeHost) && payload->BeginOrderWithinContext < host_window->BeginOrderWithinContext) + if (!DockNodeIsDropAllowedOne(payload, host_window)) continue; ImVec2 tab_size = TabItemCalcSize(payload->Name, payload->HasCloseButton); diff --git a/imgui_internal.h b/imgui_internal.h index 0d8fe5648147..84fcb7526dc9 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1464,6 +1464,7 @@ namespace ImGui IMGUI_API void DockContextRebuild(ImGuiDockContext* ctx); IMGUI_API void DockContextNewFrameUpdateUndocking(ImGuiDockContext* ctx); IMGUI_API void DockContextNewFrameUpdateDocking(ImGuiDockContext* ctx); + IMGUI_API void DockContextEndFrame(ImGuiDockContext* ctx); IMGUI_API void DockContextQueueUndock(ImGuiDockContext* ctx, ImGuiWindow* window); IMGUI_API void BeginDocked(ImGuiWindow* window, bool* p_open); IMGUI_API void BeginAsDockableDragDropSource(ImGuiWindow* window); From d5692bff00a4643f91794fe8b6056aa3b210f9aa Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 10 Sep 2018 20:30:14 +0200 Subject: [PATCH 241/828] Nav, Focus: Fixed ImGuiWindowFlags_NoBringToFrontOnFocus windows not being restoring focus properly after the main menu bar or last focused window is deactivated. --- docs/CHANGELOG.txt | 3 ++ imgui.cpp | 74 +++++++++++++++++++++++++++++----------------- imgui_demo.cpp | 21 +++++++------ imgui_internal.h | 10 ++++--- imgui_widgets.cpp | 2 +- 5 files changed, 69 insertions(+), 41 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index b59a1bc45c82..f0c5d6498b07 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -54,6 +54,9 @@ HOW TO UPDATE? VERSION 1.66 (In Progress) ----------------------------------------------------------------------- +- Nav, Focus: Fixed ImGuiWindowFlags_NoBringToFrontOnFocus windows not being restoring focus + properly after the main menu bar or last focused window is deactivated. + ----------------------------------------------------------------------- VERSION 1.65 (Released 2018-09-06) diff --git a/imgui.cpp b/imgui.cpp index 47662c0bea07..bf933a1dfc1d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -918,7 +918,7 @@ static void CheckStacksSize(ImGuiWindow* window, bool write); static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window, bool snap_on_edges); static void AddDrawListToDrawData(ImVector* out_list, ImDrawList* draw_list); -static void AddWindowToSortedBuffer(ImVector* out_sorted_windows, ImGuiWindow* window); +static void AddWindowToSortBuffer(ImVector* out_sorted_windows, ImGuiWindow* window); // Settings static void* SettingsHandlerWindow_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name); @@ -3385,6 +3385,7 @@ void ImGui::NewFrame() g.NavIdTabCounter = INT_MAX; // Mark all windows as not visible + IM_ASSERT(g.WindowsFocusOrder.Size == g.Windows.Size); for (int i = 0; i != g.Windows.Size; i++) { ImGuiWindow* window = g.Windows[i]; @@ -3395,9 +3396,8 @@ void ImGui::NewFrame() } // Closing the focused window restore focus to the first active root window in descending z-order - // FIXME: Because z-order and nav-order are correlated, this will focus the wrong window if we are part of a hierarchy with NoBringToFrontOnFocus flag. if (g.NavWindow && !g.NavWindow->WasActive) - FocusFrontMostActiveWindowIgnoringOne(NULL); + FocusPreviousWindowIgnoringOne(NULL); // No window should be open at the beginning of the frame. // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear. @@ -3475,6 +3475,7 @@ void ImGui::Shutdown(ImGuiContext* context) for (int i = 0; i < g.Windows.Size; i++) IM_DELETE(g.Windows[i]); g.Windows.clear(); + g.WindowsFocusOrder.clear(); g.WindowsSortBuffer.clear(); g.CurrentWindow = NULL; g.CurrentWindowStack.clear(); @@ -3524,7 +3525,7 @@ static int IMGUI_CDECL ChildWindowComparer(const void* lhs, const void* rhs) return (a->BeginOrderWithinParent - b->BeginOrderWithinParent); } -static void AddWindowToSortedBuffer(ImVector* out_sorted_windows, ImGuiWindow* window) +static void AddWindowToSortBuffer(ImVector* out_sorted_windows, ImGuiWindow* window) { out_sorted_windows->push_back(window); if (window->Active) @@ -3536,7 +3537,7 @@ static void AddWindowToSortedBuffer(ImVector* out_sorted_windows, { ImGuiWindow* child = window->DC.ChildWindows[i]; if (child->Active) - AddWindowToSortedBuffer(out_sorted_windows, child); + AddWindowToSortBuffer(out_sorted_windows, child); } } } @@ -3807,7 +3808,7 @@ void ImGui::EndFrame() ImGuiWindow* window = g.Windows[i]; if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow)) // if a child is active its parent will add it continue; - AddWindowToSortedBuffer(&g.WindowsSortBuffer, window); + AddWindowToSortBuffer(&g.WindowsSortBuffer, window); } IM_ASSERT(g.Windows.Size == g.WindowsSortBuffer.Size); // we done something wrong @@ -4503,8 +4504,9 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFl window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0); } + g.WindowsFocusOrder.push_back(window); if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus) - g.Windows.insert(g.Windows.begin(), window); // Quite slow but rare and only once + g.Windows.push_front(window); // Quite slow but rare and only once else g.Windows.push_back(window); return window; @@ -5621,7 +5623,21 @@ void ImGui::End() SetCurrentViewport(g.CurrentWindow, g.CurrentWindow->Viewport); } -void ImGui::BringWindowToFront(ImGuiWindow* window) +void ImGui::BringWindowToFocusFront(ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; + if (g.WindowsFocusOrder.back() == window) + return; + for (int i = g.WindowsFocusOrder.Size - 2; i >= 0; i--) // We can ignore the front most window + if (g.WindowsFocusOrder[i] == window) + { + memmove(&g.WindowsFocusOrder[i], &g.WindowsFocusOrder[i + 1], (size_t)(g.WindowsFocusOrder.Size - i - 1) * sizeof(ImGuiWindow*)); + g.WindowsFocusOrder[g.WindowsFocusOrder.Size - 1] = window; + break; + } +} + +void ImGui::BringWindowToDisplayFront(ImGuiWindow* window) { ImGuiContext& g = *GImGui; ImGuiWindow* current_front_window = g.Windows.back(); @@ -5630,13 +5646,13 @@ void ImGui::BringWindowToFront(ImGuiWindow* window) for (int i = g.Windows.Size - 2; i >= 0; i--) // We can ignore the front most window if (g.Windows[i] == window) { - g.Windows.erase(g.Windows.Data + i); - g.Windows.push_back(window); + memmove(&g.Windows[i], &g.Windows[i + 1], (size_t)(g.Windows.Size - i - 1) * sizeof(ImGuiWindow*)); + g.Windows[g.Windows.Size - 1] = window; break; } } -void ImGui::BringWindowToBack(ImGuiWindow* window) +void ImGui::BringWindowToDisplayBack(ImGuiWindow* window) { ImGuiContext& g = *GImGui; if (g.Windows[0] == window) @@ -5681,20 +5697,24 @@ void ImGui::FocusWindow(ImGuiWindow* window) ClearActiveID(); // Bring to front + BringWindowToFocusFront(window); if (!(window->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) - BringWindowToFront(window); + BringWindowToDisplayFront(window); } -void ImGui::FocusFrontMostActiveWindowIgnoringOne(ImGuiWindow* ignore_window) +void ImGui::FocusPreviousWindowIgnoringOne(ImGuiWindow* ignore_window) { ImGuiContext& g = *GImGui; - for (int i = g.Windows.Size - 1; i >= 0; i--) - if (g.Windows[i] != ignore_window && g.Windows[i]->WasActive && !(g.Windows[i]->Flags & ImGuiWindowFlags_ChildWindow)) + for (int i = g.WindowsFocusOrder.Size - 1; i >= 0; i--) + { + ImGuiWindow* window = g.WindowsFocusOrder[i]; + if (window != ignore_window && window->WasActive && !(window->Flags & ImGuiWindowFlags_ChildWindow)) { - ImGuiWindow* focus_window = NavRestoreLastChildNavWindow(g.Windows[i]); + ImGuiWindow* focus_window = NavRestoreLastChildNavWindow(window); FocusWindow(focus_window); return; } + } } void ImGui::PushItemWidth(float item_width) @@ -8639,11 +8659,11 @@ static float ImGui::NavUpdatePageUpPageDown(int allowed_dir_flags) return 0.0f; } -static int FindWindowIndex(ImGuiWindow* window) // FIXME-OPT O(N) +static int FindWindowFocusIndex(ImGuiWindow* window) // FIXME-OPT O(N) { ImGuiContext& g = *GImGui; - for (int i = g.Windows.Size-1; i >= 0; i--) - if (g.Windows[i] == window) + for (int i = g.WindowsFocusOrder.Size-1; i >= 0; i--) + if (g.WindowsFocusOrder[i] == window) return i; return -1; } @@ -8651,9 +8671,9 @@ static int FindWindowIndex(ImGuiWindow* window) // FIXME-OPT O(N) static ImGuiWindow* FindWindowNavFocusable(int i_start, int i_stop, int dir) // FIXME-OPT O(N) { ImGuiContext& g = *GImGui; - for (int i = i_start; i >= 0 && i < g.Windows.Size && i != i_stop; i += dir) - if (ImGui::IsWindowNavFocusable(g.Windows[i])) - return g.Windows[i]; + for (int i = i_start; i >= 0 && i < g.WindowsFocusOrder.Size && i != i_stop; i += dir) + if (ImGui::IsWindowNavFocusable(g.WindowsFocusOrder[i])) + return g.WindowsFocusOrder[i]; return NULL; } @@ -8664,10 +8684,10 @@ static void NavUpdateWindowingHighlightWindow(int focus_change_dir) if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal) return; - const int i_current = FindWindowIndex(g.NavWindowingTarget); + const int i_current = FindWindowFocusIndex(g.NavWindowingTarget); ImGuiWindow* window_target = FindWindowNavFocusable(i_current + focus_change_dir, -INT_MAX, focus_change_dir); if (!window_target) - window_target = FindWindowNavFocusable((focus_change_dir < 0) ? (g.Windows.Size - 1) : 0, i_current, focus_change_dir); + window_target = FindWindowNavFocusable((focus_change_dir < 0) ? (g.WindowsFocusOrder.Size - 1) : 0, i_current, focus_change_dir); if (window_target) // Don't reset windowing target if there's a single window in the list g.NavWindowingTarget = g.NavWindowingTargetAnim = window_target; g.NavWindowingToggleLayer = false; @@ -8699,7 +8719,7 @@ static void ImGui::NavUpdateWindowing() bool start_windowing_with_gamepad = !g.NavWindowingTarget && IsNavInputPressed(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed); bool start_windowing_with_keyboard = !g.NavWindowingTarget && g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard); if (start_windowing_with_gamepad || start_windowing_with_keyboard) - if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.Windows.Size - 1, -INT_MAX, -1)) + if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1)) { g.NavWindowingTarget = g.NavWindowingTargetAnim = window; g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f; @@ -8843,9 +8863,9 @@ void ImGui::NavUpdateWindowingList() SetNextWindowPos(viewport->Pos + viewport->Size * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f)); PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f); Begin("###NavWindowingList", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings); - for (int n = g.Windows.Size - 1; n >= 0; n--) + for (int n = g.WindowsFocusOrder.Size - 1; n >= 0; n--) { - ImGuiWindow* window = g.Windows[n]; + ImGuiWindow* window = g.WindowsFocusOrder[n]; if (!IsWindowNavFocusable(window)) continue; const char* label = window->Name; diff --git a/imgui_demo.cpp b/imgui_demo.cpp index da89d978cfaf..04884436cf6c 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -215,18 +215,20 @@ void ImGui::ShowDemoWindow(bool* p_open) static bool no_collapse = false; static bool no_close = false; static bool no_nav = false; + static bool no_bring_to_front = false; static bool no_docking = false; ImGuiWindowFlags window_flags = 0; - if (no_titlebar) window_flags |= ImGuiWindowFlags_NoTitleBar; - if (no_scrollbar) window_flags |= ImGuiWindowFlags_NoScrollbar; - if (!no_menu) window_flags |= ImGuiWindowFlags_MenuBar; - if (no_move) window_flags |= ImGuiWindowFlags_NoMove; - if (no_resize) window_flags |= ImGuiWindowFlags_NoResize; - if (no_collapse) window_flags |= ImGuiWindowFlags_NoCollapse; - if (no_nav) window_flags |= ImGuiWindowFlags_NoNav; - if (no_docking) window_flags |= ImGuiWindowFlags_NoDocking; - if (no_close) p_open = NULL; // Don't pass our bool* to Begin + if (no_titlebar) window_flags |= ImGuiWindowFlags_NoTitleBar; + if (no_scrollbar) window_flags |= ImGuiWindowFlags_NoScrollbar; + if (!no_menu) window_flags |= ImGuiWindowFlags_MenuBar; + if (no_move) window_flags |= ImGuiWindowFlags_NoMove; + if (no_resize) window_flags |= ImGuiWindowFlags_NoResize; + if (no_collapse) window_flags |= ImGuiWindowFlags_NoCollapse; + if (no_nav) window_flags |= ImGuiWindowFlags_NoNav; + if (no_bring_to_front) window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus; + if (no_docking) window_flags |= ImGuiWindowFlags_NoDocking; + if (no_close) p_open = NULL; // Don't pass our bool* to Begin // We specify a default position/size in case there's no data in the .ini file. Typically this isn't required! We only do it to make the Demo applications a little more welcoming. ImVec2 main_viewport_pos = ImGui::GetMainViewport()->Pos; @@ -389,6 +391,7 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::Checkbox("No collapse", &no_collapse); ImGui::Checkbox("No close", &no_close); ImGui::SameLine(150); ImGui::Checkbox("No nav", &no_nav); ImGui::SameLine(300); + ImGui::Checkbox("No bring to front", &no_bring_to_front); ImGui::Checkbox("No docking", &no_docking); } diff --git a/imgui_internal.h b/imgui_internal.h index 84fcb7526dc9..d0505ffdc993 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -799,7 +799,8 @@ struct ImGuiContext int FrameCountEnded; int FrameCountPlatformEnded; int FrameCountRendered; - ImVector Windows; + ImVector Windows; // Windows, sorted in display order, back to front + ImVector WindowsFocusOrder; // Windows, sorted in focus order, back to front ImVector WindowsSortBuffer; ImVector CurrentWindowStack; ImGuiStorage WindowsById; @@ -1362,9 +1363,10 @@ namespace ImGui IMGUI_API ImGuiWindow* FindWindowByID(ImGuiID id); IMGUI_API ImGuiWindow* FindWindowByName(const char* name); IMGUI_API void FocusWindow(ImGuiWindow* window); // FIXME: Rename to SetWindowFocus() - IMGUI_API void FocusFrontMostActiveWindowIgnoringOne(ImGuiWindow* ignore_window); - IMGUI_API void BringWindowToFront(ImGuiWindow* window); - IMGUI_API void BringWindowToBack(ImGuiWindow* window); + IMGUI_API void FocusPreviousWindowIgnoringOne(ImGuiWindow* ignore_window); + IMGUI_API void BringWindowToFocusFront(ImGuiWindow* window); + IMGUI_API void BringWindowToDisplayFront(ImGuiWindow* window); + IMGUI_API void BringWindowToDisplayBack(ImGuiWindow* window); IMGUI_API void UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window); IMGUI_API ImVec2 CalcWindowExpectedSize(ImGuiWindow* window); IMGUI_API bool IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index a622a034bd5e..c949e45266fe 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -5457,7 +5457,7 @@ void ImGui::EndMainMenuBar() // When the user has left the menu layer (typically: closed menus through activation of an item), we restore focus to the previous window ImGuiContext& g = *GImGui; if (g.CurrentWindow == g.NavWindow && g.NavLayer == 0) - FocusFrontMostActiveWindowIgnoringOne(g.NavWindow); + FocusPreviousWindowIgnoringOne(g.NavWindow); End(); } From 416918429d42ded3c36426b5defcdcfd291fe45a Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 10 Sep 2018 22:08:33 +0200 Subject: [PATCH 242/828] Docking: Added Type enum in ImGuiDockRequest. Renamed fields. DockSpace() skips node update if already submitted (when transitioning from implicit -> explicit DockSpace). --- imgui.cpp | 72 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 45 insertions(+), 27 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index bf933a1dfc1d..cf0f797f37e4 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -9527,23 +9527,32 @@ void ImGui::EndDragDropTarget() static float IMGUI_DOCK_SPLITTER_SIZE = 4.0f; +enum ImGuiDockRequestType +{ + ImGuiDockRequestType_None = 0, + ImGuiDockRequestType_Dock, + ImGuiDockRequestType_Undock +}; + struct ImGuiDockRequest { - ImGuiWindow* WindowDockTarget; // Destination/Target window to dock into (may be a loose window or a DockNode) - ImGuiDockNode* WindowDockTargetNode; - ImGuiWindow* WindowDockPayload; // Source/Payload window to dock (may be a loose window or a DockNode) - ImGuiDir WindowDockSplitDir; - float WindowDockSplitRatio; - bool WindowDockSplitOuter; - ImGuiWindow* WindowUndock; + ImGuiDockRequestType Type; + ImGuiWindow* DockTarget; // Destination/Target window to dock into (may be a loose window or a DockNode) + ImGuiDockNode* DockTargetNode; + ImGuiWindow* DockPayload; // Source/Payload window to dock (may be a loose window or a DockNode) + ImGuiDir DockSplitDir; + float DockSplitRatio; + bool DockSplitOuter; + ImGuiWindow* UndockTarget; ImGuiDockRequest() { - WindowDockTarget = WindowDockPayload = WindowUndock = NULL; - WindowDockTargetNode = NULL; - WindowDockSplitDir = ImGuiDir_None; - WindowDockSplitRatio = 0.5f; - WindowDockSplitOuter = false; + Type = ImGuiDockRequestType_None; + DockTarget = DockPayload = UndockTarget = NULL; + DockTargetNode = NULL; + DockSplitDir = ImGuiDir_None; + DockSplitRatio = 0.5f; + DockSplitOuter = false; } }; @@ -9669,7 +9678,10 @@ void ImGui::DockContextShutdown(ImGuiContext* imgui_context) ImGuiDockContext* ctx = g.DockContext; for (int n = 0; n < ctx->Nodes.Data.Size; n++) if (ImGuiDockNode* node = (ImGuiDockNode*)ctx->Nodes.Data[n].val_p) + { + node->ChildNodes[0] = node->ChildNodes[1] = NULL; IM_DELETE(node); + } IM_DELETE(g.DockContext); g.DockContext = NULL; } @@ -9712,8 +9724,8 @@ void ImGui::DockContextNewFrameUpdateUndocking(ImGuiDockContext* ctx) // Process Undocking requests (called from NewFrame before UpdateMovingWindow) for (int n = 0; n < ctx->Requests.Size; n++) - if (ctx->Requests[n].WindowUndock) - DockContextProcessUndock(ctx, ctx->Requests[n].WindowUndock); + if (ctx->Requests[n].Type == ImGuiDockRequestType_Undock) + DockContextProcessUndock(ctx, ctx->Requests[n].UndockTarget); } void ImGui::DockContextNewFrameUpdateDocking(ImGuiDockContext* ctx) @@ -9724,7 +9736,7 @@ void ImGui::DockContextNewFrameUpdateDocking(ImGuiDockContext* ctx) // Process Docking requests for (int n = 0; n < ctx->Requests.Size; n++) - if (ctx->Requests[n].WindowDockTarget) + if (ctx->Requests[n].Type == ImGuiDockRequestType_Dock) DockContextProcessDock(ctx, &ctx->Requests[n]); ctx->Requests.resize(0); @@ -9893,27 +9905,29 @@ void ImGui::DockContextBuildAddWindowsToNodes(ImGuiDockContext* ctx) void ImGui::DockContextQueueDock(ImGuiDockContext* ctx, ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, float split_ratio, bool split_outer) { ImGuiDockRequest req; - req.WindowDockTarget = target; - req.WindowDockTargetNode = target_node; - req.WindowDockPayload = payload; - req.WindowDockSplitDir = split_dir; - req.WindowDockSplitRatio = split_ratio; - req.WindowDockSplitOuter = split_outer; + req.Type = ImGuiDockRequestType_Dock; + req.DockTarget = target; + req.DockTargetNode = target_node; + req.DockPayload = payload; + req.DockSplitDir = split_dir; + req.DockSplitRatio = split_ratio; + req.DockSplitOuter = split_outer; ctx->Requests.push_back(req); } void ImGui::DockContextQueueUndock(ImGuiDockContext* ctx, ImGuiWindow* window) { ImGuiDockRequest req; - req.WindowUndock = window; + req.Type = ImGuiDockRequestType_Undock; + req.UndockTarget = window; ctx->Requests.push_back(req); } void ImGui::DockContextProcessDock(ImGuiDockContext* ctx, ImGuiDockRequest* req) { - ImGuiWindow* target_window = req->WindowDockTarget; - ImGuiWindow* payload_window = req->WindowDockPayload; - ImGuiDockNode* target_node = req->WindowDockTargetNode; + ImGuiWindow* target_window = req->DockTarget; + ImGuiWindow* payload_window = req->DockPayload; + ImGuiDockNode* target_node = req->DockTargetNode; // Decide which Tab will be selected at the end of the operation (do it before the target/payload swap) ImGuiID next_selected_id = 0; @@ -9941,7 +9955,7 @@ void ImGui::DockContextProcessDock(ImGuiDockContext* ctx, ImGuiDockRequest* req) } } - ImGuiDir split_dir = req->WindowDockSplitDir; + ImGuiDir split_dir = req->DockSplitDir; if (split_dir == ImGuiDir_None) { target_node->LastFocusedNodeID = target_node ? target_node->ID : 0; @@ -9951,7 +9965,7 @@ void ImGui::DockContextProcessDock(ImGuiDockContext* ctx, ImGuiDockRequest* req) // Split into one, one side will be our payload node unless we are dropping a loose window const ImGuiAxis split_axis = (split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y; const int split_inheritor_child_idx = (split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Up) ? 1 : 0; - const float split_ratio = req->WindowDockSplitRatio; + const float split_ratio = req->DockSplitRatio; if (payload_node) DockNodeTreeSplit(ctx, target_node, split_axis, split_inheritor_child_idx, split_ratio, payload_node); else @@ -10056,6 +10070,7 @@ ImGuiDockNode::ImGuiDockNode(ImGuiID id) ImGuiDockNode::~ImGuiDockNode() { + IM_ASSERT(ChildNodes[0] == NULL && ChildNodes[1] == NULL); IM_DELETE(TabBar); TabBar = NULL; } @@ -11268,6 +11283,9 @@ void ImGui::DockSpace(const char* str_id, const ImVec2& size_arg, ImGuiDockFlags node->UserTypeIdFilter = user_type_filter; node->IsExplicitRoot = true; + if (node->LastFrameActive == g.FrameCount) + return; + const ImVec2 content_avail = GetContentRegionAvail(); ImVec2 size = ImFloor(size_arg); if (size.x <= 0.0f) From c4e26f4b929dd7ca5c8f7e82a1c3eb4ba9592c11 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 10 Sep 2018 22:19:54 +0200 Subject: [PATCH 243/828] Nav: Added a way for code to cancel Alt for menu toggle (ImGuiNavInput_KeyMenu_). Cancelling out on platform window. close request. (#1542, #787) --- imgui.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index cf0f797f37e4..28f74c662be5 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5418,6 +5418,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if (p_open != NULL && window->Viewport->PlatformRequestClose && window->Viewport != GetMainViewport()) { window->Viewport->PlatformRequestClose = false; + g.NavWindowingToggleLayer = false; // Assume user mapped PlatformRequestClose on ALT-F4 so we disable ALT for menu toggle. False positive not an issue. *p_open = false; } @@ -8767,7 +8768,9 @@ static void ImGui::NavUpdateWindowing() // Keyboard: Press and Release ALT to toggle menu layer // FIXME: We lack an explicit IO variable for "is the imgui window focused", so compare mouse validity to detect the common case of back-end clearing releases all keys on ALT-TAB - if ((g.ActiveId == 0 || g.ActiveIdAllowOverlap) && IsNavInputPressed(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Released)) + if (IsNavInputPressed(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Pressed)) + g.NavWindowingToggleLayer = true; + if ((g.ActiveId == 0 || g.ActiveIdAllowOverlap) && g.NavWindowingToggleLayer && IsNavInputPressed(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Released)) if (IsMousePosValid(&g.IO.MousePos) == IsMousePosValid(&g.IO.MousePosPrev)) apply_toggle_layer = true; From f29b3b40339d7143f97bd3f7753a6eecb2f4786c Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 10 Sep 2018 22:19:54 +0200 Subject: [PATCH 244/828] Nav: Added a way for code to cancel Alt for menu toggle (ImGuiNavInput_KeyMenu_). Cancelling out on platform window. close request. (#1542, #787) --- imgui.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index aaa7eccbfb5f..d727048a28f8 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5296,6 +5296,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if (p_open != NULL && window->Viewport->PlatformRequestClose && window->Viewport != GetMainViewport()) { window->Viewport->PlatformRequestClose = false; + g.NavWindowingToggleLayer = false; // Assume user mapped PlatformRequestClose on ALT-F4 so we disable ALT for menu toggle. False positive not an issue. *p_open = false; } @@ -8572,7 +8573,9 @@ static void ImGui::NavUpdateWindowing() // Keyboard: Press and Release ALT to toggle menu layer // FIXME: We lack an explicit IO variable for "is the imgui window focused", so compare mouse validity to detect the common case of back-end clearing releases all keys on ALT-TAB - if ((g.ActiveId == 0 || g.ActiveIdAllowOverlap) && IsNavInputPressed(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Released)) + if (IsNavInputPressed(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Pressed)) + g.NavWindowingToggleLayer = true; + if ((g.ActiveId == 0 || g.ActiveIdAllowOverlap) && g.NavWindowingToggleLayer && IsNavInputPressed(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Released)) if (IsMousePosValid(&g.IO.MousePos) == IsMousePosValid(&g.IO.MousePosPrev)) apply_toggle_layer = true; From 211a9c8fd2ac59af21c6ca06fdaa28b071349604 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 11 Sep 2018 16:04:02 +0200 Subject: [PATCH 245/828] Docking: Added ImGuiDockSpaceFlags_KeepAliveOnly, important for multiple level of tabs. (also renamed ImGuiDockFlags to ImGuiDockSpaceFlags.) --- imgui.cpp | 45 ++++++++++++++++++++++++++++++++------------- imgui.h | 11 ++++++----- imgui_internal.h | 5 +++-- 3 files changed, 41 insertions(+), 20 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 28f74c662be5..f4f56a8c184e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10065,7 +10065,7 @@ ImGuiDockNode::ImGuiDockNode(ImGuiID id) OnlyNodeWithWindows = NULL; SelectedTabID = 0; LastFocusedNodeID = 0; - LastFrameActive = -1; + LastFrameAlive = LastFrameActive = -1; WantCloseOne = 0; IsVisible = true; InitFromFirstWindow = IsExplicitRoot = IsDocumentRoot = HasCloseButton = HasCollapseButton = WantCloseAll = WantLockSizeOnce = false; @@ -10327,6 +10327,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) { ImGuiContext& g = *GImGui; IM_ASSERT(node->LastFrameActive != g.FrameCount); + node->LastFrameAlive = g.FrameCount; if (node->IsRootNode()) { @@ -10829,7 +10830,7 @@ static bool ImGui::DockNodePreviewDockCalc(ImGuiWindow* host_window, ImGuiDockNo data->IsCenterAvailable = false; data->IsSidesAvailable = true; - if (host_node && (host_node->Flags & ImGuiDockFlags_NoSplit)) + if (host_node && (host_node->Flags & ImGuiDockSpaceFlags_NoSplit)) data->IsSidesAvailable = false; if (!is_outer_docking && host_node && host_node->ParentNode == NULL && host_node->IsDocumentRoot) data->IsSidesAvailable = false; @@ -10946,7 +10947,7 @@ static void ImGui::DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDock } } - if (host_node && (host_node->Flags & ImGuiDockFlags_NoSplit)) + if (host_node && (host_node->Flags & ImGuiDockSpaceFlags_NoSplit)) return; // Display drop boxes @@ -11267,7 +11268,7 @@ void ImGui::SetWindowDock(ImGuiWindow* window, ImGuiID dock_id, ImGuiCond cond) window->DockId = dock_id; } -void ImGui::DockSpace(const char* str_id, const ImVec2& size_arg, ImGuiDockFlags dock_flags, ImGuiID user_type_filter) +void ImGui::DockSpace(const char* str_id, const ImVec2& size_arg, ImGuiDockSpaceFlags dock_space_flags, ImGuiID user_type_filter) { ImGuiContext& g = *GImGui; ImGuiDockContext* ctx = g.DockContext; @@ -11282,13 +11283,21 @@ void ImGui::DockSpace(const char* str_id, const ImVec2& size_arg, ImGuiDockFlags node = DockContextAddNode(ctx, id); node->IsDocumentRoot = true; } - node->Flags = dock_flags; + node->Flags = dock_space_flags; node->UserTypeIdFilter = user_type_filter; node->IsExplicitRoot = true; + // When a Dockspace transitioned form implicit to explicit this may be called a second time if (node->LastFrameActive == g.FrameCount) return; + // Keep alive mode, this is allow windows docked into this node so stay docked even if they are not visible + if (dock_space_flags & ImGuiDockSpaceFlags_KeepAliveOnly) + { + node->LastFrameAlive = g.FrameCount; + return; + } + const ImVec2 content_avail = GetContentRegionAvail(); ImVec2 size = ImFloor(size_arg); if (size.x <= 0.0f) @@ -11370,13 +11379,16 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) } // Undock if our dockspace disappeared - if (dock_node->LastFrameActive < g.FrameCount) + // Note how we are testing for LastFrameAlive and NOT LastFrameActive. A DockSpace can be maintained alive while being inactive with ImGuiDockSpaceFlags_KeepAliveOnly. + if (dock_node->LastFrameAlive < g.FrameCount) { // If the window has been orphaned (lost its dockspace), transition the docknode to an implicit node processed in DockContextUpdateDocking() ImGuiDockNode* root_node = DockNodeGetRootNode(dock_node); - if (root_node->IsExplicitRoot && root_node->LastFrameActive < g.FrameCount) + if (root_node->LastFrameAlive < g.FrameCount) + { root_node->IsExplicitRoot = false; - window->DockIsActive = false; + window->DockIsActive = false; + } return; } @@ -11396,7 +11408,19 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) IM_ASSERT(dock_node->HostWindow); IM_ASSERT(!dock_node->IsParent()); + + // Position window + SetNextWindowPos(dock_node->Pos); + SetNextWindowSize(dock_node->Size); + g.NextWindowData.PosUndock = false; // Cancel implicit undocking of SetNextWindowPos() + window->DockIsActive = true; + if (dock_node->Flags & ImGuiDockSpaceFlags_KeepAliveOnly) + { + window->DockTabIsVisible = false; + return; + } + window->DockTabIsVisible = (dock_node->TabBar && dock_node->TabBar->VisibleTabId == window->ID); // Update window flag @@ -11404,11 +11428,6 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) window->Flags |= ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_NoResize; window->Flags &= ~ImGuiWindowFlags_NoTitleBar; // Clear the NoTitleBar flag in case the user set it: confusingly enough we need a title bar height so we are correctly offset, but it won't be displayed! - // Position window - SetNextWindowPos(dock_node->Pos); - SetNextWindowSize(dock_node->Size); - g.NextWindowData.PosUndock = false; - // Save new dock order only if the tab bar is active if (dock_node->TabBar) window->DockOrder = (short)DockNodeGetTabOrder(window); diff --git a/imgui.h b/imgui.h index 528d6a5ee9ed..47df5b260f7b 100644 --- a/imgui.h +++ b/imgui.h @@ -111,7 +111,7 @@ typedef int ImGuiColorEditFlags; // -> enum ImGuiColorEditFlags_ // Flags: f typedef int ImGuiColumnsFlags; // -> enum ImGuiColumnsFlags_ // Flags: for Columns(), BeginColumns() typedef int ImGuiConfigFlags; // -> enum ImGuiConfigFlags_ // Flags: for io.ConfigFlags typedef int ImGuiComboFlags; // -> enum ImGuiComboFlags_ // Flags: for BeginCombo() -typedef int ImGuiDockFlags; // -> enum ImGuiDockFlags_ // Flags: for DockSpace() +typedef int ImGuiDockSpaceFlags; // -> enum ImGuiDockSpaceFlags_ // Flags: for DockSpace() typedef int ImGuiDragDropFlags; // -> enum ImGuiDragDropFlags_ // Flags: for *DragDrop*() typedef int ImGuiFocusedFlags; // -> enum ImGuiFocusedFlags_ // Flags: for IsWindowFocused() typedef int ImGuiHoveredFlags; // -> enum ImGuiHoveredFlags_ // Flags: for IsItemHovered(), IsWindowHovered() etc. @@ -520,7 +520,7 @@ namespace ImGui // Docking // [BETA API] Enable with io.ConfigFlags |= ImGuiConfigFlags_DockingEnable. // Note: you DO NOT need to call DockSpace() to use most Docking facilities! You can hold SHIFT anywhere while moving windows. Use DockSpace() if you need to create an explicit docking space _within_ an existing window. See Docking demo for details) - IMGUI_API void DockSpace(const char* str_id, const ImVec2& size = ImVec2(0, 0), ImGuiDockFlags flags = 0, ImGuiID user_type_filter = 0); + IMGUI_API void DockSpace(const char* str_id, const ImVec2& size = ImVec2(0, 0), ImGuiDockSpaceFlags flags = 0, ImGuiID user_type_filter = 0); IMGUI_API void SetNextWindowUserType(ImGuiID user_type); // FIXME-DOCK: set next window user type (docking filters by same user_type) // Logging/Capture: all text output from interface is captured to tty/file/clipboard. By default, tree nodes are automatically opened during logging. @@ -782,10 +782,11 @@ enum ImGuiTabItemFlags_ }; // Flags for ImGui::DockSpace() -enum ImGuiDockFlags_ +enum ImGuiDockSpaceFlags_ { - ImGuiDockFlags_None = 0, - ImGuiDockFlags_NoSplit = 1 << 0 + ImGuiDockSpaceFlags_None = 0, + ImGuiDockSpaceFlags_KeepAliveOnly = 1 << 0, // Don't create/display the dockspace but keep it alive. Windows docked into this dockspace won't be undocked. + ImGuiDockSpaceFlags_NoSplit = 1 << 1 // Disable splitting the dockspace into smaller nodes. Useful e.g. when embedding dockspaces into a main root one. }; // Flags for ImGui::IsWindowFocused() diff --git a/imgui_internal.h b/imgui_internal.h index d0505ffdc993..2180935c4cb7 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -738,12 +738,12 @@ struct ImGuiTabBarSortItem float Width; }; -// sizeof() 88~124 +// sizeof() 92~128 struct ImGuiDockNode { ImGuiID ID; ImGuiID UserTypeIdFilter; - ImGuiDockFlags Flags; + ImGuiDockSpaceFlags Flags; ImGuiDockNode* ParentNode; ImGuiDockNode* ChildNodes[2]; ImVector Windows; // Note: unordered list! Iterate TabBar->Tabs for user-order. @@ -757,6 +757,7 @@ struct ImGuiDockNode ImGuiWindow* VisibleWindow; ImGuiDockNode* OnlyNodeWithWindows; // Root node only, set when there is a single visible node within the hierarchy ImGuiID SelectedTabID; + int LastFrameAlive; int LastFrameActive; ImGuiID LastFocusedNodeID; ImGuiID WantCloseOne; From 1cefc48f1381beac2083ba9d13be498267419da9 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 11 Sep 2018 18:00:01 +0200 Subject: [PATCH 246/828] Docking: Internal renaming and comments. + Fixed DockNodeMoveChildNodes() to transfer the LastExplicitSize value (was inconsequential afaik). --- imgui.cpp | 87 ++++++++++++++++++++++++------------------------ imgui_internal.h | 28 ++++++++-------- 2 files changed, 57 insertions(+), 58 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index f4f56a8c184e..f731a11af257 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -9622,7 +9622,7 @@ namespace ImGui // ImGuiDockNode static void DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window); static void DockNodeMoveWindows(ImGuiDockNode* dst_node, ImGuiDockNode* src_node); - static void DockNodeMoveChilds(ImGuiDockNode* dst_node, ImGuiDockNode* src_node); + static void DockNodeMoveChildNodes(ImGuiDockNode* dst_node, ImGuiDockNode* src_node); static void DockNodeApplyPosSizeToWindows(ImGuiDockNode* node); static void DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window, ImGuiID save_dock_id); static void DockNodeHideHostWindow(ImGuiDockNode* node); @@ -9928,14 +9928,14 @@ void ImGui::DockContextQueueUndock(ImGuiDockContext* ctx, ImGuiWindow* window) void ImGui::DockContextProcessDock(ImGuiDockContext* ctx, ImGuiDockRequest* req) { - ImGuiWindow* target_window = req->DockTarget; ImGuiWindow* payload_window = req->DockPayload; + ImGuiWindow* target_window = req->DockTarget; ImGuiDockNode* target_node = req->DockTargetNode; - // Decide which Tab will be selected at the end of the operation (do it before the target/payload swap) + // Decide which Tab will be selected at the end of the operation ImGuiID next_selected_id = 0; ImGuiDockNode* payload_node = payload_window->DockNodeAsHost; - if (payload_node && !payload_node->IsParent()) + if (payload_node && !payload_node->IsSplitNode()) next_selected_id = payload_node->TabBar->NextSelectedTabId ? payload_node->TabBar->NextSelectedTabId : payload_node->TabBar->SelectedTabId; if (payload_node == NULL) next_selected_id = payload_window->ID; @@ -9943,11 +9943,11 @@ void ImGui::DockContextProcessDock(ImGuiDockContext* ctx, ImGuiDockRequest* req) // FIXME-DOCK: When we are trying to dock an existing single-window node into a loose window, transfer Node ID as well if (target_node && target_node == target_window->DockNodeAsHost) - IM_ASSERT(target_node->Windows.Size > 1 || target_node->IsParent() || target_node->IsDocumentRoot); + IM_ASSERT(target_node->Windows.Size > 1 || target_node->IsSplitNode() || target_node->IsDocumentRoot); + // Create new node and add existing window to it if (target_node == NULL) { - // Create new node and add existing window to it target_node = DockContextAddNode(ctx, (ImGuiID)-1); target_node->Pos = target_window->Pos; target_node->Size = target_window->Size; @@ -9969,10 +9969,7 @@ void ImGui::DockContextProcessDock(ImGuiDockContext* ctx, ImGuiDockRequest* req) const ImGuiAxis split_axis = (split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y; const int split_inheritor_child_idx = (split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Up) ? 1 : 0; const float split_ratio = req->DockSplitRatio; - if (payload_node) - DockNodeTreeSplit(ctx, target_node, split_axis, split_inheritor_child_idx, split_ratio, payload_node); - else - DockNodeTreeSplit(ctx, target_node, split_axis, split_inheritor_child_idx, split_ratio, NULL); + DockNodeTreeSplit(ctx, target_node, split_axis, split_inheritor_child_idx, split_ratio, payload_node); // payload_node may be NULL here! ImGuiDockNode* inheritor_node = target_node->ChildNodes[split_inheritor_child_idx]; ImGuiDockNode* new_node = target_node->ChildNodes[split_inheritor_child_idx ^ 1]; target_node->LastFocusedNodeID = new_node->ID; @@ -9987,7 +9984,7 @@ void ImGui::DockContextProcessDock(ImGuiDockContext* ctx, ImGuiDockRequest* req) if (target_node != payload_node) { - // Create tab bar before we call DockNoveMoveWindows (which would attempt to move the old tab-bar, not holding the tab order we expect) + // Create tab bar before we call DockNoveMoveWindows (which would attempt to move the old tab-bar, which would lead us to payload tabs wrongly appearing before target tabs!) if (target_node->Windows.Size > 0 && target_node->TabBar == NULL) { target_node->TabBar = IM_NEW(ImGuiTabBar)(); @@ -9999,13 +9996,13 @@ void ImGui::DockContextProcessDock(ImGuiDockContext* ctx, ImGuiDockRequest* req) { // Transfer full payload node (with 1+ child windows or child nodes) // FIXME-DOCK: Transition persistent DockId for all non-active windows? - if (payload_node->IsParent()) + if (payload_node->IsSplitNode()) { if (target_node->Windows.Size > 0) { - // When can dock into a node that already has windows only if our payload is a node tree - // with a single visible node. In this situation, we move the windows of the target node - // into the visible node of the payload. + // We can dock into a node that already has windows _only_ if our payload is a node tree with a single visible node. + // In this situation, we move the windows of the target node into the currently visible node of the payload. + // This allows us to preserve some of the underlying settings nicely. IM_ASSERT(payload_node->OnlyNodeWithWindows != NULL); ImGuiDockNode* visible_node = payload_node->OnlyNodeWithWindows; if (visible_node->TabBar) @@ -10015,7 +10012,8 @@ void ImGui::DockContextProcessDock(ImGuiDockContext* ctx, ImGuiDockRequest* req) DockNodeMoveWindows(target_node, visible_node); DockNodeMoveWindows(visible_node, target_node); } - DockNodeMoveChilds(target_node, payload_node); + IM_ASSERT(target_node->Windows.Size == 0); + DockNodeMoveChildNodes(target_node, payload_node); } else { @@ -10063,10 +10061,10 @@ ImGuiDockNode::ImGuiDockNode(ImGuiID id) SplitRatio = 0.5f; HostWindow = VisibleWindow = NULL; OnlyNodeWithWindows = NULL; - SelectedTabID = 0; - LastFocusedNodeID = 0; LastFrameAlive = LastFrameActive = -1; - WantCloseOne = 0; + LastFocusedNodeID = 0; + SelectedTabID = 0; + WantCloseTabID = 0; IsVisible = true; InitFromFirstWindow = IsExplicitRoot = IsDocumentRoot = HasCloseButton = HasCollapseButton = WantCloseAll = WantLockSizeOnce = false; } @@ -10188,7 +10186,7 @@ static void ImGui::DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window DockNodeUpdateVisibleFlag(node); } -static void ImGui::DockNodeMoveChilds(ImGuiDockNode* dst_node, ImGuiDockNode* src_node) +static void ImGui::DockNodeMoveChildNodes(ImGuiDockNode* dst_node, ImGuiDockNode* src_node) { IM_ASSERT(dst_node->Windows.Size == 0); dst_node->ChildNodes[0] = src_node->ChildNodes[0]; @@ -10199,6 +10197,7 @@ static void ImGui::DockNodeMoveChilds(ImGuiDockNode* dst_node, ImGuiDockNode* sr dst_node->ChildNodes[1]->ParentNode = dst_node; dst_node->SplitAxis = src_node->SplitAxis; dst_node->SplitRatio = src_node->SplitRatio; + dst_node->LastExplicitSize = src_node->LastExplicitSize; src_node->ChildNodes[0] = src_node->ChildNodes[1] = NULL; } @@ -10295,7 +10294,7 @@ static void ImGui::DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* nod bool node_was_active = (node->LastFrameActive + 1 == g.FrameCount); bool remove = false; remove |= node_was_active && (window->LastFrameActive + 1 < g.FrameCount); - remove |= node_was_active && (node->WantCloseAll || node->WantCloseOne == window->ID) && window->HasCloseButton && !(window->Flags & ImGuiWindowFlags_UnsavedDocument); // Submit all _expected_ closure from last frame + remove |= node_was_active && (node->WantCloseAll || node->WantCloseTabID == window->ID) && window->HasCloseButton && !(window->Flags & ImGuiWindowFlags_UnsavedDocument); // Submit all _expected_ closure from last frame remove |= (window->DockTabWantClose); if (!remove) continue; @@ -10361,7 +10360,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) DockNodeHideHostWindow(node); node->InitFromFirstWindow = false; node->WantCloseAll = false; - node->WantCloseOne = 0; + node->WantCloseTabID = 0; node->HasCloseButton = node->HasCollapseButton = false; node->LastFrameActive = g.FrameCount; return; @@ -10432,7 +10431,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) } // Update active node (the one whose title bar is highlight) within a node tree - if (!node->IsParent()) + if (!node->IsSplitNode()) node->LastFocusedNodeID = node->ID; // This also ensure on our creation frame we will receive the title screen highlight else if (g.NavWindow && g.NavWindow->RootWindowDockStop->DockNode && g.NavWindow->RootWindowDockStop->ParentWindow == host_window) node->LastFocusedNodeID = g.NavWindow->RootWindowDockStop->DockNode->ID; @@ -10447,14 +10446,14 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) else { node->WantCloseAll = false; - node->WantCloseOne = 0; + node->WantCloseTabID = 0; if (node->Windows.Size > 0) node->SelectedTabID = node->Windows[0]->ID; } if (host_window && node->IsVisible) { // Background for empty nodes - if (node->Windows.Size == 0 && !node->IsParent()) + if (node->Windows.Size == 0 && !node->IsSplitNode()) host_window->DrawList->AddRectFilled(node->Pos, node->Pos + node->Size, GetColorU32(ImGuiCol_DockingBg)); // Drop target @@ -10499,9 +10498,9 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w const bool node_was_active = (node->LastFrameActive + 1 == g.FrameCount); const bool closed_all = node->WantCloseAll && node_was_active; - const ImGuiID closed_one = node->WantCloseOne && node_was_active; + const ImGuiID closed_one = node->WantCloseTabID && node_was_active; node->WantCloseAll = false; - node->WantCloseOne = 0; + node->WantCloseTabID = 0; // Move ourselves to the Menu layer + Undo SkipItems flag in order to draw over the title bar (even if the window is collapsed) bool backup_skip_item = host_window->SkipItems; @@ -10525,8 +10524,6 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w is_focused = (g.NavWindowingTarget->DockNode == node); else if (g.NavWindow && g.NavWindow->RootWindowForTitleBarHighlight == host_window->RootWindow && root_node->LastFocusedNodeID == node->ID) is_focused = true; - //else if (tab_bar->SelectedTabId && tab_bar->NextSelectedTabId == tab_bar->SelectedTabId) // Handle the clicking frame - // is_focused = true; // Collapse button changes shape and display a list if (IsPopupOpen("#TabListMenu")) @@ -10616,7 +10613,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w bool tab_open = true; TabItemEx(tab_bar, window->Name, window->HasCloseButton ? &tab_open : NULL, tab_item_flags, window); if (!tab_open) - node->WantCloseOne = window->ID; + node->WantCloseTabID = window->ID; if (tab_bar->VisibleTabId == window->ID) node->VisibleWindow = window; @@ -10647,7 +10644,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w if (CloseButton(host_window->GetID("#CLOSE"), title_bar_rect.GetTR() + ImVec2(-style.FramePadding.x - rad, style.FramePadding.y + rad), rad + 1)) if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_bar->VisibleTabId)) { - node->WantCloseOne = tab->ID; + node->WantCloseTabID = tab->ID; TabBarCloseTab(tab_bar, tab); } //if (IsItemActive()) @@ -10706,7 +10703,7 @@ static bool DockNodeIsDropAllowedOne(ImGuiWindow* payload, ImGuiWindow* host_win static bool ImGui::DockNodeIsDropAllowed(ImGuiWindow* host_window, ImGuiWindow* root_payload) { - if (root_payload->DockNodeAsHost && root_payload->DockNodeAsHost->IsParent()) + if (root_payload->DockNodeAsHost && root_payload->DockNodeAsHost->IsSplitNode()) return true; const int payload_count = root_payload->DockNodeAsHost ? root_payload->DockNodeAsHost->Windows.Size : 1; @@ -10824,7 +10821,7 @@ static bool ImGui::DockNodePreviewDockCalc(ImGuiWindow* host_window, ImGuiDockNo data->FutureNode.Pos = host_node ? host_node->Pos : host_window->Pos; data->FutureNode.Size = host_node ? host_node->Size : host_window->Size; - const bool src_is_visibly_splitted = root_payload->DockNodeAsHost && root_payload->DockNodeAsHost->IsParent() && (root_payload->DockNodeAsHost->OnlyNodeWithWindows == NULL); + const bool src_is_visibly_splitted = root_payload->DockNodeAsHost && root_payload->DockNodeAsHost->IsSplitNode() && (root_payload->DockNodeAsHost->OnlyNodeWithWindows == NULL); data->IsCenterAvailable = !is_outer_docking; if (src_is_visibly_splitted && (!host_node || !host_node->IsEmpty())) data->IsCenterAvailable = false; @@ -10988,7 +10985,7 @@ void ImGui::DockNodeTreeSplit(ImGuiDockContext* ctx, ImGuiDockNode* parent_node, child_1->ParentNode = parent_node; ImGuiDockNode* child_inheritor = (split_inheritor_child_idx == 0) ? child_0 : child_1; - DockNodeMoveChilds(child_inheritor, parent_node); + DockNodeMoveChildNodes(child_inheritor, parent_node); parent_node->ChildNodes[0] = child_0; parent_node->ChildNodes[1] = child_1; parent_node->ChildNodes[split_inheritor_child_idx]->VisibleWindow = parent_node->VisibleWindow; @@ -11014,12 +11011,14 @@ void ImGui::DockNodeTreeMerge(ImGuiDockContext* ctx, ImGuiDockNode* parent_node, IM_ASSERT(parent_node->TabBar == NULL); IM_ASSERT(parent_node->Windows.Size == 0); - DockNodeMoveChilds(parent_node, merge_lead_child); + ImVec2 backup_last_explicit_size = parent_node->LastExplicitSize; + DockNodeMoveChildNodes(parent_node, merge_lead_child); DockNodeMoveWindows(parent_node, merge_lead_child); DockNodeApplyPosSizeToWindows(parent_node); parent_node->InitFromFirstWindow = false; parent_node->VisibleWindow = merge_lead_child->VisibleWindow; parent_node->IsDocumentRoot = child_0->IsDocumentRoot || child_1->IsDocumentRoot; + parent_node->LastExplicitSize = backup_last_explicit_size; ctx->Nodes.SetVoidPtr(child_0->ID, NULL); ctx->Nodes.SetVoidPtr(child_1->ID, NULL); @@ -11032,7 +11031,7 @@ void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 si { node->Pos = pos; node->Size = size; - if (!node->IsParent()) + if (!node->IsSplitNode()) return; ImGuiDockNode* child_0 = node->ChildNodes[0]; @@ -11101,7 +11100,7 @@ void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 si static void DockNodeTreeUpdateSplitterFindTouchingNode(ImGuiDockNode* node, ImGuiAxis axis, int side, ImVector* touching_nodes) { - if (!node->IsParent()) + if (!node->IsSplitNode()) { touching_nodes->push_back(node); return; @@ -11116,7 +11115,7 @@ static void DockNodeTreeUpdateSplitterFindTouchingNode(ImGuiDockNode* node, ImGu void ImGui::DockNodeTreeUpdateSplitter(ImGuiDockNode* node) { - if (!node->IsParent()) + if (!node->IsSplitNode()) return; ImGuiContext& g = *GImGui; @@ -11230,7 +11229,7 @@ ImGuiDockNode* ImGui::DockNodeTreeFindNodeByPos(ImGuiDockNode* node, ImVec2 pos) { if (!node->IsVisible) return NULL; - if (node->IsParent()) + if (node->IsSplitNode()) { if (ImGuiDockNode* hovered_node = DockNodeTreeFindNodeByPos(node->ChildNodes[0], pos)) return hovered_node; @@ -11317,10 +11316,10 @@ void ImGui::DockSpace(const char* str_id, const ImVec2& size_arg, ImGuiDockSpace char title[256]; ImFormatString(title, IM_ARRAYSIZE(title), "%s/%s", window->Name, str_id); - if (node->Windows.Size > 0 || node->IsParent()) + if (node->Windows.Size > 0 || node->IsSplitNode()) PushStyleColor(ImGuiCol_ChildBg, IM_COL32(0, 0, 0, 0)); Begin(title, NULL, window_flags); - if (node->Windows.Size > 0 || node->IsParent()) + if (node->Windows.Size > 0 || node->IsSplitNode()) PopStyleColor(); ImGuiWindow* host_window = g.CurrentWindow; @@ -11362,7 +11361,7 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) if (dock_node == NULL) dock_node = DockContextAddNode(g.DockContext, window->DockId); - if (dock_node->IsParent()) + if (dock_node->IsSplitNode()) { DockContextProcessUndock(g.DockContext, window); return; @@ -11407,7 +11406,7 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) } IM_ASSERT(dock_node->HostWindow); - IM_ASSERT(!dock_node->IsParent()); + IM_ASSERT(!dock_node->IsSplitNode()); // Position window SetNextWindowPos(dock_node->Pos); @@ -11432,7 +11431,7 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) if (dock_node->TabBar) window->DockOrder = (short)DockNodeGetTabOrder(window); - if ((dock_node->WantCloseAll || dock_node->WantCloseOne == window->ID) && p_open != NULL) + if ((dock_node->WantCloseAll || dock_node->WantCloseTabID == window->ID) && p_open != NULL) *p_open = false; // Update ChildId to allow returning from Child to Parent with Escape diff --git a/imgui_internal.h b/imgui_internal.h index 2180935c4cb7..6df4c4cec79f 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -738,42 +738,42 @@ struct ImGuiTabBarSortItem float Width; }; -// sizeof() 92~128 +// sizeof() 108~144 struct ImGuiDockNode { ImGuiID ID; ImGuiID UserTypeIdFilter; ImGuiDockSpaceFlags Flags; ImGuiDockNode* ParentNode; - ImGuiDockNode* ChildNodes[2]; + ImGuiDockNode* ChildNodes[2]; // [Split node only] Child nodes (left/right or top/bottom). Consider switching to an array. ImVector Windows; // Note: unordered list! Iterate TabBar->Tabs for user-order. ImGuiTabBar* TabBar; - ImVec2 Pos, Size; // Current position, size - ImVec2 LastExplicitSize; // Last explicit size (overridden when using a splitter affecting the node) - int SplitAxis; - float SplitRatio; + ImVec2 Pos, Size; // Current position, size. + ImVec2 LastExplicitSize; // [Split node only] Last explicit size (overridden when using a splitter affecting the node) + int SplitAxis; // [Split node only] Split axis (X or Y) + float SplitRatio; // [Split node only] Split ratio FIXME-DOCK: This can be obsoleted in favor of LastExplicitSize. ImGuiWindow* HostWindow; ImGuiWindow* VisibleWindow; - ImGuiDockNode* OnlyNodeWithWindows; // Root node only, set when there is a single visible node within the hierarchy - ImGuiID SelectedTabID; - int LastFrameAlive; - int LastFrameActive; - ImGuiID LastFocusedNodeID; - ImGuiID WantCloseOne; + ImGuiDockNode* OnlyNodeWithWindows; // [Root node only] Set when there is a single visible node within the hierarchy + int LastFrameAlive; // Last frame number the node was updated or kept alive explicitly with DockSpace() + mGuiDockSpaceFlags_KeepAliveOnly + int LastFrameActive; // Last frame number the node was updated. + ImGuiID LastFocusedNodeID; // [Root node only] Which of our child node (any ancestor in the hierarchy) was last focused. + ImGuiID SelectedTabID; // [Tab node only] Which of our tab is selected. + ImGuiID WantCloseTabID; // [Tab node only] Set when closing a specific tab. bool InitFromFirstWindow :1; bool IsVisible :1; // Set to false when the node is hidden (usually disabled as it has no active window) bool IsExplicitRoot :1; // Mark root node as explicit when created from a DockSpace() bool IsDocumentRoot :1; bool HasCloseButton :1; bool HasCollapseButton :1; - bool WantCloseAll :1; + bool WantCloseAll :1; // Set when closing all tabs at once. bool WantLockSizeOnce :1; ImGuiDockNode(ImGuiID id); ~ImGuiDockNode(); bool IsRootNode() const { return ParentNode == NULL; } - bool IsParent() const { return ChildNodes[0] != NULL; } + bool IsSplitNode() const { return ChildNodes[0] != NULL; } bool IsEmpty() const { return ChildNodes[0] == NULL && Windows.Size == 0; } ImRect Rect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } }; From c355ed126775f36dfb19202af8526ba2ce183877 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 13 Sep 2018 11:46:59 +0200 Subject: [PATCH 247/828] Docking: Flicker fix when clicking on a Tab leading of a new window, in particular would be noticeable when using nested tab bars. --- imgui.cpp | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index f731a11af257..18dd2ecfd4d4 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11419,9 +11419,16 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) window->DockTabIsVisible = false; return; } - window->DockTabIsVisible = (dock_node->TabBar && dock_node->TabBar->VisibleTabId == window->ID); + // When we are about to select this tab (which will only be visible on the _next frame_), flag it with a non-zero HiddenFramesForResize. + // This will have the important effect of actually returning true in Begin() and not setting SkipItems, allowing an earlier submission of the window contents. + // This is analogous to regular windows being hidden from one frame. It is especially important as nested TabBars would otherwise generate flicker in the form + // of one empty frame. + // Note that we set HiddenFramesForResize=2 because BeginDocked() is called just before Begin() has a chance to decrement the value. Effectively it'll be a 1 frame thing. + if (!window->DockTabIsVisible && dock_node->TabBar && dock_node->TabBar->NextSelectedTabId == window->ID) + window->HiddenFramesForResize = 2; + // Update window flag IM_ASSERT((window->Flags & ImGuiWindowFlags_ChildWindow) == 0); window->Flags |= ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_NoResize; @@ -12482,10 +12489,12 @@ void ImGui::ShowDockingDebug() { IM_ASSERT(node->ChildNodes[0] == NULL || node->ChildNodes[0]->ParentNode == node); IM_ASSERT(node->ChildNodes[1] == NULL || node->ChildNodes[1]->ParentNode == node); - ImGui::BulletText("Pos (%.0f,%.0f), Size (%.0f, %.0f), LastExplicit (%.0f, %.0f)%s%s", - node->Pos.x, node->Pos.y, node->Size.x, node->Size.y, - node->LastExplicitSize.x, node->LastExplicitSize.y, - node->IsExplicitRoot ? ", IsExplicitRoot " : "", node->IsDocumentRoot ? ", IsDocumentRoot " : ""); + ImGui::BulletText("Pos (%.0f,%.0f), Size (%.0f, %.0f), LastExplicit (%.0f, %.0f)", + node->Pos.x, node->Pos.y, node->Size.x, node->Size.y, + node->LastExplicitSize.x, node->LastExplicitSize.y); + ImGui::BulletText("Flags %02X%s%s%s%s", + node->Flags, node->IsExplicitRoot ? ", IsExplicitRoot" : "", node->IsDocumentRoot ? ", IsDocumentRoot" : "", + (GImGui->FrameCount - node->LastFrameAlive < 2) ? ", IsAlive" : "", (GImGui->FrameCount - node->LastFrameActive < 2) ? ", IsActive" : ""); if (node->ChildNodes[0]) NodeDockNode(node->ChildNodes[0], "Child[0]"); if (node->ChildNodes[1]) From 136fc56af03ed7976e425e89e58de2c3d047b79d Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 13 Sep 2018 14:38:37 +0200 Subject: [PATCH 248/828] Docking: Better handling of window losing its dock node or having its dock node not active. --- imgui.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 18dd2ecfd4d4..c2da9f53299e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10132,7 +10132,7 @@ static void ImGui::DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window ImGuiDockContext* ctx = g.DockContext; IM_ASSERT(window->DockNode == node); //IM_ASSERT(window->RootWindow == node->HostWindow); - IM_ASSERT(window->LastFrameActive < g.FrameCount); + //IM_ASSERT(window->LastFrameActive < g.FrameCount); // We may call this from Begin() IM_ASSERT(save_dock_id == 0 || save_dock_id == node->ID); window->DockNode = NULL; @@ -11385,8 +11385,12 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) ImGuiDockNode* root_node = DockNodeGetRootNode(dock_node); if (root_node->LastFrameAlive < g.FrameCount) { - root_node->IsExplicitRoot = false; - window->DockIsActive = false; + DockContextProcessUndock(g.DockContext, window); + } + else + { + window->DockIsActive = true; + window->DockTabIsVisible = false; } return; } @@ -11414,12 +11418,12 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) g.NextWindowData.PosUndock = false; // Cancel implicit undocking of SetNextWindowPos() window->DockIsActive = true; + window->DockTabIsVisible = false; if (dock_node->Flags & ImGuiDockSpaceFlags_KeepAliveOnly) - { - window->DockTabIsVisible = false; return; - } - window->DockTabIsVisible = (dock_node->TabBar && dock_node->TabBar->VisibleTabId == window->ID); + + if (dock_node->TabBar && dock_node->TabBar->VisibleTabId == window->ID) + window->DockTabIsVisible = true; // When we are about to select this tab (which will only be visible on the _next frame_), flag it with a non-zero HiddenFramesForResize. // This will have the important effect of actually returning true in Begin() and not setting SkipItems, allowing an earlier submission of the window contents. From 6d91055462d044ed5ff74d0cb5221aa2695f3449 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 13 Sep 2018 15:06:27 +0200 Subject: [PATCH 249/828] Docking: Fixed floating->docking transition on a platform window mistakenly destroying the platform window because the upcoming tab isn't visible and triggers viewport GC. Fixed missing title bar data on recreate window. --- imgui.cpp | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index c2da9f53299e..8ef8252072dd 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7274,9 +7274,18 @@ void ImGui::SetCurrentViewport(ImGuiWindow* current_window, ImGuiViewportP* view if (viewport && current_window && viewport->Window && (current_window->DockNode || (current_window->Flags & ImGuiWindowFlags_DockNodeHost))) if (viewport->LastFrameActive < g.FrameCount && viewport->Window != current_window) { - //printf("[%05d] Window '%s' steal Viewport %08X from Window '%s'\n", g.FrameCount, current_window->Name, viewport->ID, viewport->Window->Name); - viewport->Window = current_window; - viewport->ID = current_window->ID; + // When called from Begin() we don't have access to a proper version of the Hidden flag yet. + bool will_be_visible = true; + if (current_window->DockIsActive && !current_window->DockTabIsVisible) + will_be_visible = false; + + if (will_be_visible) + { + //printf("[%05d] Window '%s' steal Viewport %08X from Window '%s'\n", g.FrameCount, current_window->Name, viewport->ID, viewport->Window->Name); + viewport->Window = current_window; + viewport->ID = current_window->ID; + viewport->LastNameHash = 0; + } } if (viewport) @@ -7710,6 +7719,7 @@ void ImGui::UpdatePlatformWindows() g.PlatformIO.Platform_CreateWindow(viewport); if (g.PlatformIO.Renderer_CreateWindow != NULL) g.PlatformIO.Renderer_CreateWindow(viewport); + viewport->LastNameHash = 0; viewport->RendererLastSize = viewport->Size; viewport->CreatedPlatformWindow = true; } @@ -10107,7 +10117,6 @@ static void ImGui::DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window) // then we need to hide the first one after the fact otherwise it would be visible as a standalone window for one frame. if (node->Windows.Size == 2 && node->HostWindow == NULL && node->Windows[0]->WasActive == false) { - IM_ASSERT(node->Windows[0]->DockIsActive == false); node->Windows[0]->Hidden = true; node->Windows[0]->HiddenFramesRegular = 1; } From 85a3fb3bef7261fd5ccd247c36c0edeb4a1005ec Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 13 Sep 2018 17:11:06 +0200 Subject: [PATCH 250/828] Viewport: Fix an issue introduced on ~August 16, which would assert when viewport are disabled. Also made the DestroyPlatformWindow process more sturdy. (#1542) --- imgui.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index d727048a28f8..86f411895722 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7656,17 +7656,20 @@ void ImGui::DestroyPlatformWindow(ImGuiViewportP* viewport) g.PlatformIO.Renderer_DestroyWindow(viewport); if (viewport->CreatedPlatformWindow && g.PlatformIO.Platform_DestroyWindow) g.PlatformIO.Platform_DestroyWindow(viewport); - viewport->CreatedPlatformWindow = false; IM_ASSERT(viewport->RendererUserData == NULL); - IM_ASSERT(viewport->PlatformUserData == NULL && viewport->PlatformHandle == NULL); + IM_ASSERT(viewport->PlatformUserData == NULL); + viewport->PlatformHandle = NULL; + viewport->RendererUserData = viewport->PlatformHandle = NULL; + viewport->CreatedPlatformWindow = false; } void ImGui::DestroyPlatformWindows() { // We call the destroy window on the main viewport (index 0) to give a chance to the back-end to clear any data - // have stored in e.g. PlatformHandle. - // It is expected that the back-end stored a flag to remember that it doesn't own the window created for the - // main viewport, and won't destroy the underlying platform/renderer data. + // have stored in e.g. PlatformUserData, RendererUserData. It can be convenient for the platform back-end code to + // store something in the main viewport, in order for e.g. the mouse handling code to work in a more generic manner. + // It is expected that the back-end can handle calls to Renderer_DestroyWindow/Platform_DestroyWindow without + // crashing if it doesn't have data stored. ImGuiContext& g = *GImGui; for (int i = 0; i < g.Viewports.Size; i++) if (g.Viewports[i]->CreatedPlatformWindow) From 132d8c5a9943cb9d4b92860b4dca1c7540c4b156 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 14 Sep 2018 11:37:17 +0200 Subject: [PATCH 251/828] Viewport: Increased threshold for setting ImGuiViewportFlags_NoFocusOnAppearing. (#1542), 2 doesn't seem enough with some docking setup. --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 86f411895722..31bd521c1aa2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7552,7 +7552,7 @@ void ImGui::UpdatePlatformWindows() // Show window. On startup ensure platform window don't get focus if (is_new_window) { - if (g.FrameCount < 2) + if (g.FrameCount < 3) // Give a few frames for the application to stabilize (nested contents may lead to viewport being created a few frames late) viewport->Flags |= ImGuiViewportFlags_NoFocusOnAppearing; g.PlatformIO.Platform_ShowWindow(viewport); } From ba7b68798d7fd0d45986dd0dd8297a5fac858d28 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 14 Sep 2018 14:56:36 +0200 Subject: [PATCH 252/828] Docking: Misc rework/rename toward being able to rebuild a branch selectively, so we can honor settings changes on a per Dockspace basis. + Comments --- imgui.cpp | 140 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 88 insertions(+), 52 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 7652962f62fe..2fd80c8937ac 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3350,7 +3350,7 @@ void ImGui::NewFrame() g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame))) : FLT_MAX; // Undocking - // (needs to be before UpdateMovingWindow so the window is already offset and following the mouse on the detaching frame) + // (needs to be before UpdateMouseMovingWindow so the window is already offset and following the mouse on the detaching frame) DockContextNewFrameUpdateUndocking(g.DockContext); // Find hovered window @@ -9614,9 +9614,8 @@ struct ImGuiDockContext ImGuiStorage Nodes; // Map ID -> ImGuiDockNode*: Active nodes ImVector Requests; ImVector SettingsNodes; - int SettingsMaxDepth; bool WantFullRebuild; - ImGuiDockContext() { SettingsMaxDepth = 0; WantFullRebuild = false; } + ImGuiDockContext() { WantFullRebuild = false; } }; //----------------------------------------------------------------------------- @@ -9632,10 +9631,10 @@ namespace ImGui static void DockContextQueueDock(ImGuiDockContext* ctx, ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, float split_ratio, bool split_outer); static void DockContextProcessDock(ImGuiDockContext* ctx, ImGuiDockRequest* req); static void DockContextProcessUndock(ImGuiDockContext* ctx, ImGuiWindow* window); - static void DockContextClearNodes(ImGuiDockContext* ctx, bool clear_references); - static void DockContextGcUnusedNodes(ImGuiDockContext* ctx); - static void DockContextBuildNodesFromSettings(ImGuiDockContext* ctx); - static void DockContextBuildAddWindowsToNodes(ImGuiDockContext* ctx); + static void DockContextGcUnusedSettingsNodes(ImGuiDockContext* ctx); + static void DockContextClearNodes(ImGuiDockContext* ctx, ImGuiID root_id, bool clear_persistent_docking_references); // Set root_id==0 to clear all + static void DockContextBuildNodesFromSettings(ImGuiDockContext* ctx, ImGuiDockNodeSettings* node_settings_array, int node_settings_count); + static void DockContextBuildAddWindowsToNodes(ImGuiDockContext* ctx, ImGuiID root_id); // Use root_id==0 to add all // ImGuiDockNode static void DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window); @@ -9666,7 +9665,7 @@ namespace ImGui static ImGuiDockNode* DockNodeTreeFindNodeByPos(ImGuiDockNode* node, ImVec2 pos); // Settings - static void DockSettingsRemoveAllReferencesToNode(ImGuiID node_id); + static void DockSettingsRemoveReferencesToNodes(ImGuiID* node_ids, int node_ids_count); static ImGuiDockNodeSettings* DockSettingsFindNodeSettings(ImGuiDockContext* ctx, ImGuiID node_id); static void* DockSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name); static void DockSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line); @@ -9676,6 +9675,13 @@ namespace ImGui //----------------------------------------------------------------------------- // Docking: ImGuiDockContext //----------------------------------------------------------------------------- +// The lifetime model is different from the one of regular windows: we always create a ImGuiDockNode for each ImGuiDockNodeSettings, +// or we always hold the entire docking node tree. Nodes are frequently hidden, e.g. if the window(s) or child nodes they host are not active. +// At boot time only, we run a simple GC to remove nodes that have no references. +// Because dock node settings (which are small, contiguous structures) are always mirrored by their corresponding dock nodes (more complete structures), +// we can also very easily recreate the nodes from scratch given the settings data (this is what DockContextRebuild() does). +// This is convenient as docking reconfiguration can be implemented by mostly poking at the simpler setttings data. +//----------------------------------------------------------------------------- void ImGui::DockContextInitialize(ImGuiContext* imgui_context) { @@ -9699,10 +9705,7 @@ void ImGui::DockContextShutdown(ImGuiContext* imgui_context) ImGuiDockContext* ctx = g.DockContext; for (int n = 0; n < ctx->Nodes.Data.Size; n++) if (ImGuiDockNode* node = (ImGuiDockNode*)ctx->Nodes.Data[n].val_p) - { - node->ChildNodes[0] = node->ChildNodes[1] = NULL; IM_DELETE(node); - } IM_DELETE(g.DockContext); g.DockContext = NULL; } @@ -9711,25 +9714,29 @@ void ImGui::DockContextOnLoadSettings() { ImGuiContext& g = *GImGui; ImGuiDockContext* ctx = g.DockContext; - DockContextGcUnusedNodes(ctx); - DockContextBuildNodesFromSettings(ctx); + DockContextGcUnusedSettingsNodes(ctx); + DockContextBuildNodesFromSettings(ctx, ctx->SettingsNodes.Data, ctx->SettingsNodes.Size); } +// This function also acts as a defacto test to make sure we can rebuild from scratch without a glitch void ImGui::DockContextRebuild(ImGuiDockContext* ctx) { + //IMGUI_DEBUG_LOG("[docking] full rebuild\n"); SaveIniSettingsToMemory(); - DockContextClearNodes(ctx, false); - DockContextBuildNodesFromSettings(ctx); - DockContextBuildAddWindowsToNodes(ctx); + ImGuiID root_id = 0; // Rebuild all + DockContextClearNodes(ctx, root_id, false); + DockContextBuildNodesFromSettings(ctx, ctx->SettingsNodes.Data, ctx->SettingsNodes.Size); + DockContextBuildAddWindowsToNodes(ctx, root_id); } +// Docking context update function, called by NewFrame() void ImGui::DockContextNewFrameUpdateUndocking(ImGuiDockContext* ctx) { ImGuiContext& g = *GImGui; if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable)) { if (ctx->Nodes.Data.Size > 0 || ctx->Requests.Size > 0) - DockContextClearNodes(ctx, true); + DockContextClearNodes(ctx, 0, true); return; } @@ -9743,12 +9750,13 @@ void ImGui::DockContextNewFrameUpdateUndocking(ImGuiDockContext* ctx) ctx->WantFullRebuild = false; } - // Process Undocking requests (called from NewFrame before UpdateMovingWindow) + // Process Undocking requests (we need to process them _before_ the UpdateMouseMovingWindow call in NewFrame) for (int n = 0; n < ctx->Requests.Size; n++) if (ctx->Requests[n].Type == ImGuiDockRequestType_Undock) DockContextProcessUndock(ctx, ctx->Requests[n].UndockTarget); } +// Docking context update function, called by NewFrame() void ImGui::DockContextNewFrameUpdateDocking(ImGuiDockContext* ctx) { ImGuiContext& g = *GImGui; @@ -9769,6 +9777,7 @@ void ImGui::DockContextNewFrameUpdateDocking(ImGuiDockContext* ctx) DockNodeUpdate(node); } +// Docking context update function, called by EndFrame() void ImGui::DockContextEndFrame(ImGuiDockContext* ctx) { (void)ctx; @@ -9784,7 +9793,7 @@ static ImGuiDockNode* ImGui::DockContextAddNode(ImGuiDockContext* ctx, ImGuiID i // Generate an ID for the new node (the exact ID value doesn't matter as long as it is not already used) and add the first window. if (id == (ImGuiID)-1) { - // FIXME-OPT: This is very suboptimal, however the node count is small enough not to be a worry. + // FIXME-OPT: This is suboptimal, even if the node count is small enough not to be a worry. We could poke in ctx->Nodes to find a suitable ID faster. id = 0x0001; while (DockContextFindNodeByID(ctx, id) != NULL) id++; @@ -9819,30 +9828,46 @@ static void ImGui::DockContextRemoveNode(ImGuiDockContext* ctx, ImGuiDockNode* n } } -// Stress/functional test to make sure we can rebuild from scratch without a glitch -void ImGui::DockContextClearNodes(ImGuiDockContext* ctx, bool clear_references) +void ImGui::DockContextClearNodes(ImGuiDockContext* ctx, ImGuiID root_id, bool clear_persistent_docking_references) { SaveIniSettingsToMemory(NULL); ImGuiContext& g = *GImGui; - for (int n = 0; n < ctx->Nodes.Data.Size; n++) - if (ImGuiDockNode* node = (ImGuiDockNode*)ctx->Nodes.Data[n].val_p) - IM_DELETE(node); - ctx->Nodes.Clear(); - ctx->Requests.clear(); + + // Clear references in windows for (int n = 0; n < g.Windows.Size; n++) { ImGuiWindow* window = g.Windows[n]; - window->DockNode = window->DockNodeAsHost = NULL; - window->DockIsActive = false; - if (clear_references) - window->DockId = 0; + bool want_removal = root_id == 0 || (window->DockNode && DockNodeGetRootNode(window->DockNode)->ID == root_id) || (window->DockNodeAsHost && window->DockNodeAsHost->ID == root_id); + if (want_removal) + { + window->DockNode = window->DockNodeAsHost = NULL; + window->DockIsActive = false; + if (clear_persistent_docking_references) + window->DockId = 0; + } } + + // Clear nodes + for (int n = 0; n < ctx->Nodes.Data.Size; n++) + if (ImGuiDockNode* node = (ImGuiDockNode*)ctx->Nodes.Data[n].val_p) + { + bool want_removal = (root_id == 0 || DockNodeGetRootNode(node)->ID == root_id); + if (want_removal) + { + IM_DELETE(node); + ctx->Nodes.Data[n].val_p = NULL; + } + } + if (root_id == 0) + ctx->Nodes.Clear(); + ctx->Requests.clear(); } -static void ImGui::DockContextGcUnusedNodes(ImGuiDockContext* ctx) +static void ImGui::DockContextGcUnusedSettingsNodes(ImGuiDockContext* ctx) { ImGuiContext& g = *GImGui; + IM_ASSERT(g.Windows.Size == 0); // Count reference to dock ids from window settings ImGuiStorage ref_count_map; // Map dock_id -> counter @@ -9870,19 +9895,19 @@ static void ImGui::DockContextGcUnusedNodes(ImGuiDockContext* ctx) remove |= (ref_count == 0 && settings->ParentID == 0 && is_parent_map.GetInt(settings->ID, 0) == 0); // Leaf nodes with 0 window if (remove) { - DockSettingsRemoveAllReferencesToNode(settings->ID); + DockSettingsRemoveReferencesToNodes(&settings->ID, 1); settings->ID = 0; } } } } -static void ImGui::DockContextBuildNodesFromSettings(ImGuiDockContext* ctx) +static void ImGui::DockContextBuildNodesFromSettings(ImGuiDockContext* ctx, ImGuiDockNodeSettings* node_settings_array, int node_settings_count) { // Build nodes - for (int node_n = 0; node_n < ctx->SettingsNodes.Size; node_n++) + for (int node_n = 0; node_n < node_settings_count; node_n++) { - ImGuiDockNodeSettings* node_settings = &ctx->SettingsNodes[node_n]; + ImGuiDockNodeSettings* node_settings = &node_settings_array[node_n]; if (node_settings->ID == 0) continue; ImGuiDockNode* node = DockContextAddNode(ctx, node_settings->ID); @@ -9902,20 +9927,22 @@ static void ImGui::DockContextBuildNodesFromSettings(ImGuiDockContext* ctx) } } -void ImGui::DockContextBuildAddWindowsToNodes(ImGuiDockContext* ctx) +void ImGui::DockContextBuildAddWindowsToNodes(ImGuiDockContext* ctx, ImGuiID root_id) { - // Rebuild nodes (they can also lazily rebuild but we'll have a visible glitch during the first frame) + // Rebind all windows to nodes (they can also lazily rebind but we'll have a visible glitch during the first frame) ImGuiContext& g = *GImGui; for (int n = 0; n < g.Windows.Size; n++) { ImGuiWindow* window = g.Windows[n]; if (window->DockId == 0 || window->LastFrameActive < g.FrameCount - 1) continue; + if (window->DockNode != NULL) + continue; ImGuiDockNode* dock_node = DockContextFindNodeByID(ctx, window->DockId); - if (dock_node == NULL) - dock_node = DockContextAddNode(ctx, window->DockId); - DockNodeAddWindow(dock_node, window); + IM_ASSERT(dock_node != NULL); // This should have been called after DockContextBuildNodesFromSettings() + if (root_id == 0 || DockNodeGetRootNode(dock_node)->ID == root_id) + DockNodeAddWindow(dock_node, window); } } @@ -10089,9 +10116,9 @@ ImGuiDockNode::ImGuiDockNode(ImGuiID id) ImGuiDockNode::~ImGuiDockNode() { - IM_ASSERT(ChildNodes[0] == NULL && ChildNodes[1] == NULL); IM_DELETE(TabBar); TabBar = NULL; + ChildNodes[0] = ChildNodes[1] = NULL; } int ImGui::DockNodeGetTabOrder(ImGuiWindow* window) @@ -11546,18 +11573,23 @@ void ImGui::BeginAsDockableDragDropTarget(ImGuiWindow* window) // Docking: Settings //----------------------------------------------------------------------------- -static void ImGui::DockSettingsRemoveAllReferencesToNode(ImGuiID node_id) +// Remove references stored in ImGuiWindowSettings to the given ImGuiDockNodeSettings +static void ImGui::DockSettingsRemoveReferencesToNodes(ImGuiID* node_ids, int node_ids_count) { ImGuiContext& g = *GImGui; + int found = 0; for (int settings_n = 0; settings_n < g.SettingsWindows.Size; settings_n++) // FIXME-OPT: We could remove this loop by storing the index in the map { ImGuiWindowSettings* window_settings = &g.SettingsWindows[settings_n]; - if (window_settings->DockId == node_id) - { - window_settings->DockId = 0; - window_settings->DockOrder = -1; - break; - } + for (int node_n = 0; node_n < node_ids_count; node_n++) + if (window_settings->DockId == node_ids[node_n]) + { + window_settings->DockId = 0; + window_settings->DockOrder = -1; + if (++found < node_ids_count) + break; + return; + } } } @@ -11617,6 +11649,7 @@ static void ImGui::DockSettingsHandler_ReadLine(ImGuiContext* imgui_ctx, ImGuiSe static void DockSettingsHandler_DockNodeToSettings(ImGuiDockContext* ctx, ImGuiDockNode* node, int depth) { ImGuiDockNodeSettings node_settings; + IM_ASSERT(depth < (1 << (sizeof(node_settings.Depth) << 3))); node_settings.ID = node->ID; node_settings.ParentID = node->ParentNode ? node->ParentNode->ID : 0; node_settings.SelectedTabID = node->SelectedTabID; @@ -11629,7 +11662,6 @@ static void DockSettingsHandler_DockNodeToSettings(ImGuiDockContext* ctx, ImGuiD node_settings.Size = ImVec2ih((short)node->Size.x, (short)node->Size.y); node_settings.LastExplicitSize = ImVec2ih((short)node->LastExplicitSize.x, (short)node->LastExplicitSize.y); ctx->SettingsNodes.push_back(node_settings); - ctx->SettingsMaxDepth = ImMax(ctx->SettingsMaxDepth, depth); if (node->ChildNodes[0]) DockSettingsHandler_DockNodeToSettings(ctx, node->ChildNodes[0], depth + 1); if (node->ChildNodes[1]) @@ -11639,21 +11671,25 @@ static void DockSettingsHandler_DockNodeToSettings(ImGuiDockContext* ctx, ImGuiD static void ImGui::DockSettingsHandler_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf) { // Gather settings data + // (unlike our windows settings, because nodes are always built we can do a full rewrite of the SettingsNode buffer) ImGuiDockContext* ctx = imgui_ctx->DockContext; ctx->SettingsNodes.resize(0); ctx->SettingsNodes.reserve(ctx->Nodes.Data.Size); - ctx->SettingsMaxDepth = 0; for (int n = 0; n < ctx->Nodes.Data.Size; n++) if (ImGuiDockNode* node = (ImGuiDockNode*)ctx->Nodes.Data[n].val_p) if (node->IsRootNode()) DockSettingsHandler_DockNodeToSettings(ctx, node, 0); + int max_depth = 0; + for (int node_n = 0; node_n < ctx->SettingsNodes.Size; node_n++) + max_depth = ImMax((int)ctx->SettingsNodes[node_n].Depth, max_depth); + // Write to text buffer buf->appendf("[%s][Data]\n", handler->TypeName); for (int node_n = 0; node_n < ctx->SettingsNodes.Size; node_n++) { const ImGuiDockNodeSettings* node_settings = &ctx->SettingsNodes[node_n]; - buf->appendf("%*sDockNode%*s", node_settings->Depth * 2, "", (ImMax(ctx->SettingsMaxDepth,0) - node_settings->Depth) * 2, ""); // Text align nodes to facilitate looking at .ini file + buf->appendf("%*sDockNode%*s", node_settings->Depth * 2, "", (max_depth - node_settings->Depth) * 2, ""); // Text align nodes to facilitate looking at .ini file buf->appendf(" ID=0x%08X", node_settings->ID); if (node_settings->ParentID) buf->appendf(" Parent=0x%08X LastExplicitSize=%d,%d", node_settings->ParentID, node_settings->LastExplicitSize.x, node_settings->LastExplicitSize.y); @@ -12561,7 +12597,7 @@ void ImGui::ShowDockingDebug() if (ImGui::TreeNode("Dock nodes")) { - if (ImGui::SmallButton("Clear settings")) { DockContextClearNodes(ctx, true); } + if (ImGui::SmallButton("Clear settings")) { DockContextClearNodes(ctx, 0, true); } ImGui::SameLine(); if (ImGui::SmallButton("Rebuild all")) { ctx->WantFullRebuild = true; } for (int n = 0; n < ctx->Nodes.Data.Size; n++) From 175bab4f5f7f08cbb933f2b0f3c4a51bb44d7f7d Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 14 Sep 2018 17:51:30 +0200 Subject: [PATCH 253/828] Docking: Removed SplitRatio from ImGuiDockNode and ImGuiDockNodeSettings, which we don't need anymore. --- imgui.cpp | 24 ++++-------------------- imgui_internal.h | 1 - 2 files changed, 4 insertions(+), 21 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 2fd80c8937ac..8bb581cd87c2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -9598,7 +9598,6 @@ struct ImGuiDockNodeSettings ImGuiID ID; ImGuiID ParentID; ImGuiID SelectedTabID; - float SplitRatio; char SplitAxis; char Depth; char IsExplicitRoot; @@ -9606,7 +9605,7 @@ struct ImGuiDockNodeSettings ImVec2ih Pos; ImVec2ih Size; ImVec2ih LastExplicitSize; - ImGuiDockNodeSettings() { ID = ParentID = SelectedTabID = 0; SplitRatio = 0.0f; SplitAxis = ImGuiAxis_None; Depth = 0; IsExplicitRoot = IsDocumentRoot = 0; } + ImGuiDockNodeSettings() { ID = ParentID = SelectedTabID = 0; SplitAxis = ImGuiAxis_None; Depth = 0; IsExplicitRoot = IsDocumentRoot = 0; } }; struct ImGuiDockContext @@ -9921,7 +9920,6 @@ static void ImGui::DockContextBuildNodesFromSettings(ImGuiDockContext* ctx, ImGu node->ParentNode->ChildNodes[1] = node; node->SelectedTabID = node_settings->SelectedTabID; node->SplitAxis = node_settings->SplitAxis; - node->SplitRatio = node_settings->SplitRatio; node->IsExplicitRoot = node_settings->IsExplicitRoot != 0; node->IsDocumentRoot = node_settings->IsDocumentRoot != 0; } @@ -10103,7 +10101,6 @@ ImGuiDockNode::ImGuiDockNode(ImGuiID id) ParentNode = ChildNodes[0] = ChildNodes[1] = NULL; TabBar = NULL; SplitAxis = ImGuiAxis_None; - SplitRatio = 0.5f; HostWindow = VisibleWindow = NULL; OnlyNodeWithWindows = NULL; LastFrameAlive = LastFrameActive = -1; @@ -10240,7 +10237,6 @@ static void ImGui::DockNodeMoveChildNodes(ImGuiDockNode* dst_node, ImGuiDockNode if (dst_node->ChildNodes[1]) dst_node->ChildNodes[1]->ParentNode = dst_node; dst_node->SplitAxis = src_node->SplitAxis; - dst_node->SplitRatio = src_node->SplitRatio; dst_node->LastExplicitSize = src_node->LastExplicitSize; src_node->ChildNodes[0] = src_node->ChildNodes[1] = NULL; } @@ -11034,7 +11030,6 @@ void ImGui::DockNodeTreeSplit(ImGuiDockContext* ctx, ImGuiDockNode* parent_node, parent_node->ChildNodes[1] = child_1; parent_node->ChildNodes[split_inheritor_child_idx]->VisibleWindow = parent_node->VisibleWindow; parent_node->SplitAxis = split_axis; - parent_node->SplitRatio = split_ratio; parent_node->VisibleWindow = NULL; float size_avail = (parent_node->Size[split_axis] - IMGUI_DOCK_SPLITTER_SIZE); @@ -11088,11 +11083,6 @@ void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 si const ImGuiAxis axis = (ImGuiAxis)node->SplitAxis; const float size_avail = ImMax(size[axis] - spacing, 0.0f); -#if 0 - child_0_size[axis] = ImFloor(size_avail * node->SplitRatio); - child_1_size[axis] = size_avail - child_0_size[axis]; - child_1_pos[axis] += spacing + child_0_size[axis]; -#else // Size allocation policy // 1) The first 0..WindowMinSize[axis]*2 are allocated evenly to both windows. ImGuiContext& g = *GImGui; @@ -11127,14 +11117,12 @@ void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 si } else { - // 4) Otherwise distribute according to SplitRatio - //float split_ratio = node->SplitRatio; + // 4) Otherwise distribute according to the relative ratio of each LastExplicitSize value float split_ratio = child_0->LastExplicitSize[axis] / (child_0->LastExplicitSize[axis] + child_1->LastExplicitSize[axis]); child_0_size[axis] = ImMax(size_min_each, ImFloor(size_avail * split_ratio + 0.5F)); child_1_size[axis] = (size_avail - child_0_size[axis]); } child_1_pos[axis] += spacing + child_0_size[axis]; -#endif } if (child_0->IsVisible) DockNodeTreeUpdatePosSize(child_0, child_0_pos, child_0_size); @@ -11225,8 +11213,6 @@ void ImGui::DockNodeTreeUpdateSplitter(ImGuiDockNode* node) { if (touching_nodes[0].Size > 0 && touching_nodes[1].Size > 0) { - node->SplitRatio = w1 / (w1 + w2); - IM_ASSERT(node->SplitRatio > 0.0f && node->SplitRatio < 1.0f); child_0->Size[axis] = child_0->LastExplicitSize[axis] = w1; child_1->Pos[axis] -= w2 - child_1->Size[axis]; child_1->Size[axis] = child_1->LastExplicitSize[axis] = w2; @@ -11611,7 +11597,6 @@ static void* ImGui::DockSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHan static void ImGui::DockSettingsHandler_ReadLine(ImGuiContext* imgui_ctx, ImGuiSettingsHandler*, void*, const char* line) { char c = 0; - float f = 0.0f; int x = 0, y = 0; int r = 0; @@ -11633,7 +11618,7 @@ static void ImGui::DockSettingsHandler_ReadLine(ImGuiContext* imgui_ctx, ImGuiSe { if (sscanf(line, " LastExplicitSize=%i,%i%n", &x, &y, &r) == 2) { line += r; node.LastExplicitSize = ImVec2ih((short)x, (short)y); } } - if (sscanf(line, " Split=%c,%f%n", &c, &f, &r) == 2) { line += r; if (c == 'X') node.SplitAxis = ImGuiAxis_X; else if (c == 'Y') node.SplitAxis = ImGuiAxis_Y; node.SplitRatio = f; } + if (sscanf(line, " Split=%c%n", &c, &r) == 1) { line += r; if (c == 'X') node.SplitAxis = ImGuiAxis_X; else if (c == 'Y') node.SplitAxis = ImGuiAxis_Y; } if (sscanf(line, " ExplicitRoot=%d%n", &x, &r) == 1) { line += r; node.IsExplicitRoot = (x != 0); } if (sscanf(line, " DocumentRoot=%d%n", &x, &r) == 1) { line += r; node.IsDocumentRoot = (x != 0); } if (sscanf(line, " SelectedTab=0x%08X%n", &node.SelectedTabID,&r) == 1) { line += r; } @@ -11653,7 +11638,6 @@ static void DockSettingsHandler_DockNodeToSettings(ImGuiDockContext* ctx, ImGuiD node_settings.ID = node->ID; node_settings.ParentID = node->ParentNode ? node->ParentNode->ID : 0; node_settings.SelectedTabID = node->SelectedTabID; - node_settings.SplitRatio = node->SplitRatio; node_settings.SplitAxis = (char)node->SplitAxis; node_settings.Depth = (char)depth; node_settings.IsExplicitRoot = (char)node->IsExplicitRoot; @@ -11696,7 +11680,7 @@ static void ImGui::DockSettingsHandler_WriteAll(ImGuiContext* imgui_ctx, ImGuiSe else buf->appendf(" Pos=%d,%d Size=%d,%d", node_settings->Pos.x, node_settings->Pos.y, node_settings->Size.x, node_settings->Size.y); if (node_settings->SplitAxis != ImGuiAxis_None) - buf->appendf(" Split=%c,%.3f", (node_settings->SplitAxis == ImGuiAxis_X) ? 'X' : 'Y', node_settings->SplitRatio); + buf->appendf(" Split=%c", (node_settings->SplitAxis == ImGuiAxis_X) ? 'X' : 'Y'); if (node_settings->IsExplicitRoot) buf->appendf(" ExplicitRoot=%d", node_settings->IsExplicitRoot); if (node_settings->IsDocumentRoot) diff --git a/imgui_internal.h b/imgui_internal.h index 1d90f6e322c3..daf2097eb4af 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -751,7 +751,6 @@ struct ImGuiDockNode ImVec2 Pos, Size; // Current position, size. ImVec2 LastExplicitSize; // [Split node only] Last explicit size (overridden when using a splitter affecting the node) int SplitAxis; // [Split node only] Split axis (X or Y) - float SplitRatio; // [Split node only] Split ratio FIXME-DOCK: This can be obsoleted in favor of LastExplicitSize. ImGuiWindow* HostWindow; ImGuiWindow* VisibleWindow; From 0f1c21a6c99f94128f0e5550174be48a19539aa3 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 14 Sep 2018 18:02:54 +0200 Subject: [PATCH 254/828] Docking: Changed DockSpace() signature to take an ID, as ID will frequently be used by other API it makes more sense to let the user compute it. --- imgui.cpp | 7 +++---- imgui.h | 2 +- imgui_demo.cpp | 7 ++++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 8bb581cd87c2..2191a4d57856 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -9717,7 +9717,7 @@ void ImGui::DockContextOnLoadSettings() DockContextBuildNodesFromSettings(ctx, ctx->SettingsNodes.Data, ctx->SettingsNodes.Size); } -// This function also acts as a defacto test to make sure we can rebuild from scratch without a glitch +// This function also acts as a de-facto test to make sure we can rebuild from scratch without a glitch void ImGui::DockContextRebuild(ImGuiDockContext* ctx) { //IMGUI_DEBUG_LOG("[docking] full rebuild\n"); @@ -11297,13 +11297,12 @@ void ImGui::SetWindowDock(ImGuiWindow* window, ImGuiID dock_id, ImGuiCond cond) window->DockId = dock_id; } -void ImGui::DockSpace(const char* str_id, const ImVec2& size_arg, ImGuiDockSpaceFlags dock_space_flags, ImGuiID user_type_filter) +void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockSpaceFlags dock_space_flags, ImGuiID user_type_filter) { ImGuiContext& g = *GImGui; ImGuiDockContext* ctx = g.DockContext; ImGuiWindow* window = GetCurrentWindow(); - ImGuiID id = window->GetID(str_id); // It is possible that the node has already been claimed by a docked window which appeared before the DockSpace(), so we overwrite IsExplicit again. ImGuiDockNode* node = DockContextFindNodeByID(ctx, id); @@ -11344,7 +11343,7 @@ void ImGui::DockSpace(const char* str_id, const ImVec2& size_arg, ImGuiDockSpace window_flags |= ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar; char title[256]; - ImFormatString(title, IM_ARRAYSIZE(title), "%s/%s", window->Name, str_id); + ImFormatString(title, IM_ARRAYSIZE(title), "%s/DockSpace_%08X", window->Name, id); if (node->Windows.Size > 0 || node->IsSplitNode()) PushStyleColor(ImGuiCol_ChildBg, IM_COL32(0, 0, 0, 0)); diff --git a/imgui.h b/imgui.h index 2d0bf3f88411..7815de3963dd 100644 --- a/imgui.h +++ b/imgui.h @@ -520,7 +520,7 @@ namespace ImGui // Docking // [BETA API] Enable with io.ConfigFlags |= ImGuiConfigFlags_DockingEnable. // Note: you DO NOT need to call DockSpace() to use most Docking facilities! You can hold SHIFT anywhere while moving windows. Use DockSpace() if you need to create an explicit docking space _within_ an existing window. See Docking demo for details) - IMGUI_API void DockSpace(const char* str_id, const ImVec2& size = ImVec2(0, 0), ImGuiDockSpaceFlags flags = 0, ImGuiID user_type_filter = 0); + IMGUI_API void DockSpace(ImGuiID id, const ImVec2& size = ImVec2(0, 0), ImGuiDockSpaceFlags flags = 0, ImGuiID user_type_filter = 0); IMGUI_API void SetNextWindowUserType(ImGuiID user_type); // FIXME-DOCK: set next window user type (docking filters by same user_type) // Logging/Capture: all text output from interface is captured to tty/file/clipboard. By default, tree nodes are automatically opened during logging. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 9d06f7aaf117..514e58758644 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3746,7 +3746,8 @@ void ShowExampleAppDockSpace(bool* p_open) } //ImGui::PushStyleColor(ImGuiCol_DockingBg, ImVec4(0.2f, 0.2f, 0.2f, 1.0f)); - ImGui::DockSpace("##MyCentralDockSpace"); + ImGuiID dockspace_id = ImGui::GetID("MyDockspace"); + ImGui::DockSpace(dockspace_id); //ImGui::PopStyleColor(); ImGui::End(); @@ -3959,8 +3960,8 @@ void ShowExampleAppDocuments(bool* p_open) NotifyOfDocumentsClosedElsewhere(app); // Create a DockSpace where any window can be docked - ImGui::DockSpace("##DockSpace"); - ImGuiID dockspace_id = ImGui::GetID("##DockSpace"); + ImGuiID dockspace_id = ImGui::GetID("MyDockSpace"); + ImGui::DockSpace(dockspace_id); // Create Windows for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++) From b55b9aee9b929b451503609a9216012ac07cac97 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 14 Sep 2018 18:09:46 +0200 Subject: [PATCH 255/828] Fixed bad merge (a82be53) where we lost the version number in Docking branch + moved Docking related API together for now. --- imgui.cpp | 2 +- imgui.h | 10 +++++----- imgui_demo.cpp | 2 +- imgui_draw.cpp | 2 +- imgui_internal.h | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 2191a4d57856..dd8081966ed5 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.65 +// dear imgui, v1.66 WIP // (main code and documentation) // Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp for demo code. diff --git a/imgui.h b/imgui.h index 7815de3963dd..cdb2b3bec4c1 100644 --- a/imgui.h +++ b/imgui.h @@ -1,4 +1,4 @@ -// dear imgui, v1.65 +// dear imgui, v1.66 WIP // (headers) // See imgui.cpp file for documentation. @@ -23,8 +23,8 @@ // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY00 then bounced up to XYY01 when release tagging happens) -#define IMGUI_VERSION "1.65" -#define IMGUI_VERSION_NUM 16501 +#define IMGUI_VERSION "1.66 WIP" +#define IMGUI_VERSION_NUM 16600 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert)) #define IMGUI_HAS_VIEWPORT 1 // Viewport WIP branch #define IMGUI_HAS_DOCK 1 // Docking WIP branch @@ -216,7 +216,6 @@ namespace ImGui IMGUI_API bool IsWindowCollapsed(); IMGUI_API bool IsWindowFocused(ImGuiFocusedFlags flags=0); // is current window focused? or its root/child, depending on flags. see flags for options. IMGUI_API bool IsWindowHovered(ImGuiHoveredFlags flags=0); // is current window hovered (and typically: not blocked by a popup/modal)? see flags for options. NB: If you are trying to check whether your mouse should be dispatched to imgui or to your app, you should use the 'io.WantCaptureMouse' boolean for that! Please read the FAQ! - IMGUI_API bool IsWindowDocked(); IMGUI_API ImDrawList* GetWindowDrawList(); // get draw list associated to the current window, to append your own drawing primitives IMGUI_API float GetWindowDpiScale(); // get DPI scale currently associated to the current window's viewport. IMGUI_API ImGuiViewport*GetWindowViewport(); // get viewport currently associated to the current window. @@ -239,7 +238,6 @@ namespace ImGui IMGUI_API void SetNextWindowFocus(); // set next window to be focused / front-most. call before Begin() IMGUI_API void SetNextWindowBgAlpha(float alpha); // set next window background color alpha. helper to easily modify ImGuiCol_WindowBg/ChildBg/PopupBg. IMGUI_API void SetNextWindowViewport(ImGuiID viewport_id); // set next window viewport - IMGUI_API void SetNextWindowDock(ImGuiID dock_id, ImGuiCond cond = 0); // set next window dock id (FIXME-DOCK) IMGUI_API void SetWindowPos(const ImVec2& pos, ImGuiCond cond = 0); // (not recommended) set current window position - call within Begin()/End(). prefer using SetNextWindowPos(), as this may incur tearing and side-effects. IMGUI_API void SetWindowSize(const ImVec2& size, ImGuiCond cond = 0); // (not recommended) set current window size - call within Begin()/End(). set to ImVec2(0,0) to force an auto-fit. prefer using SetNextWindowSize(), as this may incur tearing and minor side-effects. IMGUI_API void SetWindowCollapsed(bool collapsed, ImGuiCond cond = 0); // (not recommended) set current window collapsed state. prefer using SetNextWindowCollapsed(). @@ -521,7 +519,9 @@ namespace ImGui // [BETA API] Enable with io.ConfigFlags |= ImGuiConfigFlags_DockingEnable. // Note: you DO NOT need to call DockSpace() to use most Docking facilities! You can hold SHIFT anywhere while moving windows. Use DockSpace() if you need to create an explicit docking space _within_ an existing window. See Docking demo for details) IMGUI_API void DockSpace(ImGuiID id, const ImVec2& size = ImVec2(0, 0), ImGuiDockSpaceFlags flags = 0, ImGuiID user_type_filter = 0); + IMGUI_API void SetNextWindowDock(ImGuiID dock_id, ImGuiCond cond = 0); // set next window dock id (FIXME-DOCK) IMGUI_API void SetNextWindowUserType(ImGuiID user_type); // FIXME-DOCK: set next window user type (docking filters by same user_type) + IMGUI_API bool IsWindowDocked(); // is current window docked into another window? // Logging/Capture: all text output from interface is captured to tty/file/clipboard. By default, tree nodes are automatically opened during logging. IMGUI_API void LogToTTY(int max_depth = -1); // start logging to tty diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 514e58758644..d49bf797e15c 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.65 +// dear imgui, v1.66 WIP // (demo code) // Message to the person tempted to delete this file when integrating ImGui into their code base: diff --git a/imgui_draw.cpp b/imgui_draw.cpp index d6a6e434539f..89da302a54e2 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.65 +// dear imgui, v1.66 WIP // (drawing and font code) /* diff --git a/imgui_internal.h b/imgui_internal.h index daf2097eb4af..79cea7b9643c 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1,4 +1,4 @@ -// dear imgui, v1.65 +// dear imgui, v1.66 WIP // (internal structures/api) // You may use this file to debug, understand or extend ImGui features but we don't provide any guarantee of forward compatibility! From 28094b7cea7b9f66ee7a4ab06dc2f3d24345d8ba Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 17 Sep 2018 15:12:38 +0200 Subject: [PATCH 256/828] Docking: Fixed central docking display with ImGuiDockSpaceFlags_NoSplit, affecting in particular with io.ConfigDockingWithKeyMod = false. + comments, asserts --- imgui.cpp | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index dd8081966ed5..56baa7365d29 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -9526,13 +9526,14 @@ void ImGui::EndDragDropTarget() // A~ document root node retrieval of ID ? // A~ Unreal style document system (requires low-level controls of dockspace serialization fork/copy/delete) // B- resize sibling locking behavior may be less desirable if we merged same-axis sibling in a same node level? -// A- single visible node part of a hidden split hierarchy (OnlyNodeWithWindows) should show a normal tab bar +// A- single visible node part of a hidden split hierarchy (OnlyNodeWithWindows) should show a normal title bar (not a tab bar) // B~ SetNextWindowDock() calls (with conditional) -> defer everything to DockContextUpdate (repro: Documents->[X]Windows->Dock 1 elsewhere->Click Redock All -// B- fix/disable auto-resize grip on split host nodes // B~ tidy up tab list popup buttons (see old ImGuiTabBarFlags_NoTabListPopupButton code) // B- DockSpace() border issues // B- inconsistent clipping/border 1-pixel issue (#2) +// B- fix/disable auto-resize grip on split host nodes (~#2) // B- SetNextWindowFocus() doesn't seem to apply if the window is hidden this frame, need repro (#4) +// B- drag from collapse button should drag entire dock node // B- implicit per-viewport dockspace to dock to // B- resizing a dock tree small currently has glitches (overlapping collapse and close button, etc.) // B- tab bar: appearing on first frame with a dumb layout would do less harm that not appearing? (when behind dynamic branch) or store titles + render in EndTabBar() @@ -9971,6 +9972,7 @@ void ImGui::DockContextQueueUndock(ImGuiDockContext* ctx, ImGuiWindow* window) void ImGui::DockContextProcessDock(ImGuiDockContext* ctx, ImGuiDockRequest* req) { + ImGuiContext& g = *GImGui; ImGuiWindow* payload_window = req->DockPayload; ImGuiWindow* target_window = req->DockTarget; ImGuiDockNode* target_node = req->DockTargetNode; @@ -9985,6 +9987,8 @@ void ImGui::DockContextProcessDock(ImGuiDockContext* ctx, ImGuiDockRequest* req) // FIXME-DOCK: When we are trying to dock an existing single-window node into a loose window, transfer Node ID as well + if (target_node) + IM_ASSERT(target_node->LastFrameAlive < g.FrameCount); if (target_node && target_node == target_window->DockNodeAsHost) IM_ASSERT(target_node->Windows.Size > 1 || target_node->IsSplitNode() || target_node->IsDocumentRoot); @@ -10080,6 +10084,8 @@ void ImGui::DockContextProcessDock(ImGuiDockContext* ctx, ImGuiDockRequest* req) void ImGui::DockContextProcessUndock(ImGuiDockContext* ctx, ImGuiWindow* window) { (void)ctx; + ImGuiContext& g = *GImGui; + IM_ASSERT(window->LastFrameActive < g.FrameCount); if (window->DockNode) DockNodeRemoveWindow(window->DockNode, window, 0); else @@ -10984,12 +10990,10 @@ static void ImGui::DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDock } } - if (host_node && (host_node->Flags & ImGuiDockSpaceFlags_NoSplit)) - return; - // Display drop boxes const float overlay_rounding = ImMax(3.0f, g.Style.FrameRounding); for (int dir = ImGuiDir_None; dir < ImGuiDir_COUNT; dir++) + { if (!data->DropRectsDraw[dir + 1].IsInverted()) { ImRect draw_r = data->DropRectsDraw[dir + 1]; @@ -11007,6 +11011,11 @@ static void ImGui::DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDock overlay_draw_lists[overlay_n]->AddLine(ImVec2(draw_r_in.Min.x, center.y), ImVec2(draw_r_in.Max.x, center.y), overlay_col_lines); } } + + // Stop after ImGuiDir_None + if (host_node && (host_node->Flags & ImGuiDockSpaceFlags_NoSplit)) + return; + } } //----------------------------------------------------------------------------- @@ -11526,7 +11535,7 @@ void ImGui::BeginAsDockableDragDropTarget(ImGuiWindow* window) else allow_null_target_node = true; // Dock into a regular window - const ImRect explicit_target_rect = (target_node && target_node->TabBar) ? target_node->TabBar->BarRect : window->TitleBarRect(); + const ImRect explicit_target_rect = (target_node && target_node->TabBar) ? target_node->TabBar->BarRect : ImRect(window->Pos, window->Pos + ImVec2(window->Size.x, GetFrameHeight())); const bool is_explicit_target = g.IO.ConfigDockingWithKeyMod || IsMouseHoveringRect(explicit_target_rect.Min, explicit_target_rect.Max); // Preview docking request and find out split direction/ratio From 35032d41fa4cd42d5e9da57b7c7582af8668e07e Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 17 Sep 2018 17:06:52 +0200 Subject: [PATCH 257/828] Docking: Internals: Transitioning some code toward consistently using ImGuiContext* ctx parameter instead of ImGuiDockContext --- imgui.cpp | 275 ++++++++++++++++++++++++---------------------- imgui_internal.h | 16 +-- imgui_widgets.cpp | 3 +- 3 files changed, 151 insertions(+), 143 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 56baa7365d29..04dd1b249ff2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3351,7 +3351,7 @@ void ImGui::NewFrame() // Undocking // (needs to be before UpdateMouseMovingWindow so the window is already offset and following the mouse on the detaching frame) - DockContextNewFrameUpdateUndocking(g.DockContext); + DockContextNewFrameUpdateUndocking(&g); // Find hovered window // (needs to be before UpdateMovingWindow so we fill HoveredWindowUnderMovingWindow on the mouse release frame) @@ -3406,7 +3406,7 @@ void ImGui::NewFrame() ClosePopupsOverWindow(g.NavWindow); // Docking - DockContextNewFrameUpdateDocking(g.DockContext); + DockContextNewFrameUpdateDocking(&g); // Create implicit window - we will only render it if the user has added something to it. // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags. @@ -3682,7 +3682,7 @@ void ImGui::EndFrame() End(); // Docking - DockContextEndFrame(g.DockContext); + DockContextEndFrame(&g); // Draw modal whitening background on _other_ viewports than the one the modal or target are on ImGuiWindow* modal_window = GetFrontMostPopupModal(); @@ -9625,16 +9625,16 @@ struct ImGuiDockContext namespace ImGui { // ImGuiDockContext - static ImGuiDockNode* DockContextFindNodeByID(ImGuiDockContext* ctx, ImGuiID id); - static ImGuiDockNode* DockContextAddNode(ImGuiDockContext* ctx, ImGuiID id); - static void DockContextRemoveNode(ImGuiDockContext* ctx, ImGuiDockNode* node); - static void DockContextQueueDock(ImGuiDockContext* ctx, ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, float split_ratio, bool split_outer); - static void DockContextProcessDock(ImGuiDockContext* ctx, ImGuiDockRequest* req); - static void DockContextProcessUndock(ImGuiDockContext* ctx, ImGuiWindow* window); - static void DockContextGcUnusedSettingsNodes(ImGuiDockContext* ctx); - static void DockContextClearNodes(ImGuiDockContext* ctx, ImGuiID root_id, bool clear_persistent_docking_references); // Set root_id==0 to clear all - static void DockContextBuildNodesFromSettings(ImGuiDockContext* ctx, ImGuiDockNodeSettings* node_settings_array, int node_settings_count); - static void DockContextBuildAddWindowsToNodes(ImGuiDockContext* ctx, ImGuiID root_id); // Use root_id==0 to add all + static ImGuiDockNode* DockContextFindNodeByID(ImGuiContext* ctx, ImGuiID id); + static ImGuiDockNode* DockContextAddNode(ImGuiContext* ctx, ImGuiID id); + static void DockContextRemoveNode(ImGuiContext* ctx, ImGuiDockNode* node); + static void DockContextQueueDock(ImGuiContext* ctx, ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, float split_ratio, bool split_outer); + static void DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req); + static void DockContextProcessUndock(ImGuiContext* ctx, ImGuiWindow* window); + static void DockContextGcUnusedSettingsNodes(ImGuiContext* ctx); + static void DockContextClearNodes(ImGuiContext* ctx, ImGuiID root_id, bool clear_persistent_docking_references); // Set root_id==0 to clear all + static void DockContextBuildNodesFromSettings(ImGuiContext* ctx, ImGuiDockNodeSettings* node_settings_array, int node_settings_count); + static void DockContextBuildAddWindowsToNodes(ImGuiContext* ctx, ImGuiID root_id); // Use root_id==0 to add all // ImGuiDockNode static void DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window); @@ -9658,15 +9658,15 @@ namespace ImGui static int DockNodeGetTabOrder(ImGuiWindow* window); // ImGuiDockNode tree manipulations - static void DockNodeTreeSplit(ImGuiDockContext* ctx, ImGuiDockNode* parent_node, ImGuiAxis split_axis, int split_first_child, float split_ratio, ImGuiDockNode* new_node); - static void DockNodeTreeMerge(ImGuiDockContext* ctx, ImGuiDockNode* parent_node, ImGuiDockNode* merge_lead_child); + static void DockNodeTreeSplit(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiAxis split_axis, int split_first_child, float split_ratio, ImGuiDockNode* new_node); + static void DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiDockNode* merge_lead_child); static void DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 size); static void DockNodeTreeUpdateSplitter(ImGuiDockNode* node); static ImGuiDockNode* DockNodeTreeFindNodeByPos(ImGuiDockNode* node, ImVec2 pos); // Settings static void DockSettingsRemoveReferencesToNodes(ImGuiID* node_ids, int node_ids_count); - static ImGuiDockNodeSettings* DockSettingsFindNodeSettings(ImGuiDockContext* ctx, ImGuiID node_id); + static ImGuiDockNodeSettings* DockSettingsFindNodeSettings(ImGuiContext* ctx, ImGuiID node_id); static void* DockSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name); static void DockSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line); static void DockSettingsHandler_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf); @@ -9683,9 +9683,9 @@ namespace ImGui // This is convenient as docking reconfiguration can be implemented by mostly poking at the simpler setttings data. //----------------------------------------------------------------------------- -void ImGui::DockContextInitialize(ImGuiContext* imgui_context) +void ImGui::DockContextInitialize(ImGuiContext* ctx) { - ImGuiContext& g = *imgui_context; + ImGuiContext& g = *ctx; IM_ASSERT(g.DockContext == NULL); g.DockContext = IM_NEW(ImGuiDockContext)(); @@ -9699,43 +9699,44 @@ void ImGui::DockContextInitialize(ImGuiContext* imgui_context) g.SettingsHandlers.push_back(ini_handler); } -void ImGui::DockContextShutdown(ImGuiContext* imgui_context) +void ImGui::DockContextShutdown(ImGuiContext* ctx) { - ImGuiContext& g = *imgui_context; - ImGuiDockContext* ctx = g.DockContext; - for (int n = 0; n < ctx->Nodes.Data.Size; n++) - if (ImGuiDockNode* node = (ImGuiDockNode*)ctx->Nodes.Data[n].val_p) + ImGuiContext& g = *ctx; + ImGuiDockContext* dc = ctx->DockContext; + for (int n = 0; n < dc->Nodes.Data.Size; n++) + if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p) IM_DELETE(node); IM_DELETE(g.DockContext); g.DockContext = NULL; } -void ImGui::DockContextOnLoadSettings() +void ImGui::DockContextOnLoadSettings(ImGuiContext* ctx) { - ImGuiContext& g = *GImGui; - ImGuiDockContext* ctx = g.DockContext; + ImGuiDockContext* dc = ctx->DockContext; DockContextGcUnusedSettingsNodes(ctx); - DockContextBuildNodesFromSettings(ctx, ctx->SettingsNodes.Data, ctx->SettingsNodes.Size); + DockContextBuildNodesFromSettings(ctx, dc->SettingsNodes.Data, dc->SettingsNodes.Size); } // This function also acts as a de-facto test to make sure we can rebuild from scratch without a glitch -void ImGui::DockContextRebuild(ImGuiDockContext* ctx) +void ImGui::DockContextRebuild(ImGuiContext* ctx) { //IMGUI_DEBUG_LOG("[docking] full rebuild\n"); + ImGuiDockContext* dc = ctx->DockContext; SaveIniSettingsToMemory(); ImGuiID root_id = 0; // Rebuild all DockContextClearNodes(ctx, root_id, false); - DockContextBuildNodesFromSettings(ctx, ctx->SettingsNodes.Data, ctx->SettingsNodes.Size); + DockContextBuildNodesFromSettings(ctx, dc->SettingsNodes.Data, dc->SettingsNodes.Size); DockContextBuildAddWindowsToNodes(ctx, root_id); } // Docking context update function, called by NewFrame() -void ImGui::DockContextNewFrameUpdateUndocking(ImGuiDockContext* ctx) +void ImGui::DockContextNewFrameUpdateUndocking(ImGuiContext* ctx) { - ImGuiContext& g = *GImGui; + ImGuiContext& g = *ctx; + ImGuiDockContext* dc = ctx->DockContext; if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable)) { - if (ctx->Nodes.Data.Size > 0 || ctx->Requests.Size > 0) + if (dc->Nodes.Data.Size > 0 || dc->Requests.Size > 0) DockContextClearNodes(ctx, 0, true); return; } @@ -9744,51 +9745,52 @@ void ImGui::DockContextNewFrameUpdateUndocking(ImGuiDockContext* ctx) if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_C))) ctx->WantFullRebuild = true; #endif - if (ctx->WantFullRebuild) + if (dc->WantFullRebuild) { DockContextRebuild(ctx); - ctx->WantFullRebuild = false; + dc->WantFullRebuild = false; } // Process Undocking requests (we need to process them _before_ the UpdateMouseMovingWindow call in NewFrame) - for (int n = 0; n < ctx->Requests.Size; n++) - if (ctx->Requests[n].Type == ImGuiDockRequestType_Undock) - DockContextProcessUndock(ctx, ctx->Requests[n].UndockTarget); + for (int n = 0; n < dc->Requests.Size; n++) + if (dc->Requests[n].Type == ImGuiDockRequestType_Undock) + DockContextProcessUndock(ctx, dc->Requests[n].UndockTarget); } // Docking context update function, called by NewFrame() -void ImGui::DockContextNewFrameUpdateDocking(ImGuiDockContext* ctx) +void ImGui::DockContextNewFrameUpdateDocking(ImGuiContext* ctx) { - ImGuiContext& g = *GImGui; + ImGuiContext& g = *ctx; + ImGuiDockContext* dc = ctx->DockContext; if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable)) return; // Process Docking requests - for (int n = 0; n < ctx->Requests.Size; n++) - if (ctx->Requests[n].Type == ImGuiDockRequestType_Dock) - DockContextProcessDock(ctx, &ctx->Requests[n]); - ctx->Requests.resize(0); + for (int n = 0; n < dc->Requests.Size; n++) + if (dc->Requests[n].Type == ImGuiDockRequestType_Dock) + DockContextProcessDock(ctx, &dc->Requests[n]); + dc->Requests.resize(0); // Create windows for each automatic docking nodes // We can have NULL pointers when we delete nodes, but because ID are recycled this should amortize nicely (and our node count will never be very high) - for (int n = 0; n < ctx->Nodes.Data.Size; n++) - if (ImGuiDockNode* node = (ImGuiDockNode*)ctx->Nodes.Data[n].val_p) + for (int n = 0; n < dc->Nodes.Data.Size; n++) + if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p) if (!node->IsExplicitRoot && node->IsRootNode()) DockNodeUpdate(node); } // Docking context update function, called by EndFrame() -void ImGui::DockContextEndFrame(ImGuiDockContext* ctx) +void ImGui::DockContextEndFrame(ImGuiContext* ctx) { (void)ctx; } -static ImGuiDockNode* ImGui::DockContextFindNodeByID(ImGuiDockContext* ctx, ImGuiID id) +static ImGuiDockNode* ImGui::DockContextFindNodeByID(ImGuiContext* ctx, ImGuiID id) { - return (ImGuiDockNode*)ctx->Nodes.GetVoidPtr(id); + return (ImGuiDockNode*)ctx->DockContext->Nodes.GetVoidPtr(id); } -static ImGuiDockNode* ImGui::DockContextAddNode(ImGuiDockContext* ctx, ImGuiID id) +static ImGuiDockNode* ImGui::DockContextAddNode(ImGuiContext* ctx, ImGuiID id) { // Generate an ID for the new node (the exact ID value doesn't matter as long as it is not already used) and add the first window. if (id == (ImGuiID)-1) @@ -9801,12 +9803,15 @@ static ImGuiDockNode* ImGui::DockContextAddNode(ImGuiDockContext* ctx, ImGuiID i IM_ASSERT(DockContextFindNodeByID(ctx, id) == NULL); ImGuiDockNode* node = IM_NEW(ImGuiDockNode)(id); node->InitFromFirstWindow = true; - ctx->Nodes.SetVoidPtr(node->ID, node); + ctx->DockContext->Nodes.SetVoidPtr(node->ID, node); return node; } -static void ImGui::DockContextRemoveNode(ImGuiDockContext* ctx, ImGuiDockNode* node) +static void ImGui::DockContextRemoveNode(ImGuiContext* ctx, ImGuiDockNode* node) { + ImGuiContext& g = *ctx; + ImGuiDockContext* dc = ctx->DockContext; + //printf("[%05d] RemoveNode 0x%04X\n", node->ID); IM_ASSERT(DockContextFindNodeByID(ctx, node->ID) == node); IM_ASSERT(node->ChildNodes[0] == NULL && node->ChildNodes[1] == NULL); @@ -9819,21 +9824,21 @@ static void ImGui::DockContextRemoveNode(ImGuiDockContext* ctx, ImGuiDockNode* n { IM_ASSERT(parent_node->ChildNodes[0] == node || parent_node->ChildNodes[1] == node); ImGuiDockNode* sibling_node = (parent_node->ChildNodes[0] == node ? parent_node->ChildNodes[1] : parent_node->ChildNodes[0]); - DockNodeTreeMerge(ctx, parent_node, sibling_node); + DockNodeTreeMerge(&g, parent_node, sibling_node); } else { - ctx->Nodes.SetVoidPtr(node->ID, NULL); + dc->Nodes.SetVoidPtr(node->ID, NULL); IM_DELETE(node); } } -void ImGui::DockContextClearNodes(ImGuiDockContext* ctx, ImGuiID root_id, bool clear_persistent_docking_references) +void ImGui::DockContextClearNodes(ImGuiContext* ctx, ImGuiID root_id, bool clear_persistent_docking_references) { + ImGuiContext& g = *ctx; + ImGuiDockContext* dc = ctx->DockContext; SaveIniSettingsToMemory(NULL); - ImGuiContext& g = *GImGui; - // Clear references in windows for (int n = 0; n < g.Windows.Size; n++) { @@ -9849,44 +9854,45 @@ void ImGui::DockContextClearNodes(ImGuiDockContext* ctx, ImGuiID root_id, bool c } // Clear nodes - for (int n = 0; n < ctx->Nodes.Data.Size; n++) - if (ImGuiDockNode* node = (ImGuiDockNode*)ctx->Nodes.Data[n].val_p) + for (int n = 0; n < dc->Nodes.Data.Size; n++) + if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p) { bool want_removal = (root_id == 0 || DockNodeGetRootNode(node)->ID == root_id); if (want_removal) { IM_DELETE(node); - ctx->Nodes.Data[n].val_p = NULL; + dc->Nodes.Data[n].val_p = NULL; } } if (root_id == 0) - ctx->Nodes.Clear(); - ctx->Requests.clear(); + dc->Nodes.Clear(); + dc->Requests.clear(); } -static void ImGui::DockContextGcUnusedSettingsNodes(ImGuiDockContext* ctx) +static void ImGui::DockContextGcUnusedSettingsNodes(ImGuiContext* ctx) { - ImGuiContext& g = *GImGui; + ImGuiContext& g = *ctx; + ImGuiDockContext* dc = ctx->DockContext; IM_ASSERT(g.Windows.Size == 0); // Count reference to dock ids from window settings ImGuiStorage ref_count_map; // Map dock_id -> counter - ref_count_map.Data.reserve(ctx->SettingsNodes.Size); + ref_count_map.Data.reserve(dc->SettingsNodes.Size); for (int settings_n = 0; settings_n < g.SettingsWindows.Size; settings_n++) if (g.SettingsWindows[settings_n].DockId != 0) (*ref_count_map.GetIntRef(g.SettingsWindows[settings_n].DockId, 0))++; // Mark nodes that are parents ImGuiStorage is_parent_map; - is_parent_map.Data.reserve(ctx->SettingsNodes.Size); - for (int settings_n = 0; settings_n < ctx->SettingsNodes.Size; settings_n++) - if (ctx->SettingsNodes[settings_n].ParentID) - is_parent_map.SetInt(ctx->SettingsNodes[settings_n].ParentID, 1); + is_parent_map.Data.reserve(dc->SettingsNodes.Size); + for (int settings_n = 0; settings_n < dc->SettingsNodes.Size; settings_n++) + if (dc->SettingsNodes[settings_n].ParentID) + is_parent_map.SetInt(dc->SettingsNodes[settings_n].ParentID, 1); // If a root node has only 1 reference in window settings we clear it - for (int settings_n = 0; settings_n < ctx->SettingsNodes.Size; settings_n++) + for (int settings_n = 0; settings_n < dc->SettingsNodes.Size; settings_n++) { - ImGuiDockNodeSettings* settings = &ctx->SettingsNodes[settings_n]; + ImGuiDockNodeSettings* settings = &dc->SettingsNodes[settings_n]; int ref_count = ref_count_map.GetInt(settings->ID, 0); if (ref_count <= 1) { @@ -9902,7 +9908,7 @@ static void ImGui::DockContextGcUnusedSettingsNodes(ImGuiDockContext* ctx) } } -static void ImGui::DockContextBuildNodesFromSettings(ImGuiDockContext* ctx, ImGuiDockNodeSettings* node_settings_array, int node_settings_count) +static void ImGui::DockContextBuildNodesFromSettings(ImGuiContext* ctx, ImGuiDockNodeSettings* node_settings_array, int node_settings_count) { // Build nodes for (int node_n = 0; node_n < node_settings_count; node_n++) @@ -9926,10 +9932,10 @@ static void ImGui::DockContextBuildNodesFromSettings(ImGuiDockContext* ctx, ImGu } } -void ImGui::DockContextBuildAddWindowsToNodes(ImGuiDockContext* ctx, ImGuiID root_id) +void ImGui::DockContextBuildAddWindowsToNodes(ImGuiContext* ctx, ImGuiID root_id) { // Rebind all windows to nodes (they can also lazily rebind but we'll have a visible glitch during the first frame) - ImGuiContext& g = *GImGui; + ImGuiContext& g = *ctx; for (int n = 0; n < g.Windows.Size; n++) { ImGuiWindow* window = g.Windows[n]; @@ -9949,7 +9955,7 @@ void ImGui::DockContextBuildAddWindowsToNodes(ImGuiDockContext* ctx, ImGuiID roo // Docking: ImGuiDockContext Docking/Undocking functions //----------------------------------------------------------------------------- -void ImGui::DockContextQueueDock(ImGuiDockContext* ctx, ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, float split_ratio, bool split_outer) +void ImGui::DockContextQueueDock(ImGuiContext* ctx, ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, float split_ratio, bool split_outer) { ImGuiDockRequest req; req.Type = ImGuiDockRequestType_Dock; @@ -9959,20 +9965,20 @@ void ImGui::DockContextQueueDock(ImGuiDockContext* ctx, ImGuiWindow* target, ImG req.DockSplitDir = split_dir; req.DockSplitRatio = split_ratio; req.DockSplitOuter = split_outer; - ctx->Requests.push_back(req); + ctx->DockContext->Requests.push_back(req); } -void ImGui::DockContextQueueUndock(ImGuiDockContext* ctx, ImGuiWindow* window) +void ImGui::DockContextQueueUndock(ImGuiContext* ctx, ImGuiWindow* window) { ImGuiDockRequest req; req.Type = ImGuiDockRequestType_Undock; req.UndockTarget = window; - ctx->Requests.push_back(req); + ctx->DockContext->Requests.push_back(req); } -void ImGui::DockContextProcessDock(ImGuiDockContext* ctx, ImGuiDockRequest* req) +void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) { - ImGuiContext& g = *GImGui; + ImGuiContext& g = *ctx; ImGuiWindow* payload_window = req->DockPayload; ImGuiWindow* target_window = req->DockTarget; ImGuiDockNode* target_node = req->DockTargetNode; @@ -10081,10 +10087,9 @@ void ImGui::DockContextProcessDock(ImGuiDockContext* ctx, ImGuiDockRequest* req) target_node->TabBar->NextSelectedTabId = next_selected_id; } -void ImGui::DockContextProcessUndock(ImGuiDockContext* ctx, ImGuiWindow* window) +void ImGui::DockContextProcessUndock(ImGuiContext* ctx, ImGuiWindow* window) { - (void)ctx; - ImGuiContext& g = *GImGui; + ImGuiContext& g = *ctx; IM_ASSERT(window->LastFrameActive < g.FrameCount); if (window->DockNode) DockNodeRemoveWindow(window->DockNode, window, 0); @@ -10176,7 +10181,6 @@ static void ImGui::DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window) static void ImGui::DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window, ImGuiID save_dock_id) { ImGuiContext& g = *GImGui; - ImGuiDockContext* ctx = g.DockContext; IM_ASSERT(window->DockNode == node); //IM_ASSERT(window->RootWindow == node->HostWindow); //IM_ASSERT(window->LastFrameActive < g.FrameCount); // We may call this from Begin() @@ -10192,7 +10196,7 @@ static void ImGui::DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window { // Automatic dock node delete themselves if they are not holding at least one tab IM_ASSERT(node->Windows[0] == window); - DockContextRemoveNode(ctx, node); + DockContextRemoveNode(&g, node); return; } @@ -11022,9 +11026,9 @@ static void ImGui::DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDock // Docking: ImGuiDockNode Tree manipulation functions //----------------------------------------------------------------------------- -void ImGui::DockNodeTreeSplit(ImGuiDockContext* ctx, ImGuiDockNode* parent_node, ImGuiAxis split_axis, int split_inheritor_child_idx, float split_ratio, ImGuiDockNode* new_node) +void ImGui::DockNodeTreeSplit(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiAxis split_axis, int split_inheritor_child_idx, float split_ratio, ImGuiDockNode* new_node) { - ImGuiContext& g = *GImGui; + ImGuiContext& g = *ctx; IM_ASSERT(split_axis != ImGuiAxis_None); ImGuiDockNode* child_0 = (new_node && split_inheritor_child_idx != 0) ? new_node : DockContextAddNode(ctx, (ImGuiID)-1); @@ -11050,7 +11054,7 @@ void ImGui::DockNodeTreeSplit(ImGuiDockContext* ctx, ImGuiDockNode* parent_node, DockNodeTreeUpdatePosSize(parent_node, parent_node->Pos, parent_node->Size); } -void ImGui::DockNodeTreeMerge(ImGuiDockContext* ctx, ImGuiDockNode* parent_node, ImGuiDockNode* merge_lead_child) +void ImGui::DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiDockNode* merge_lead_child) { ImGuiDockNode* child_0 = parent_node->ChildNodes[0]; ImGuiDockNode* child_1 = parent_node->ChildNodes[1]; @@ -11068,8 +11072,8 @@ void ImGui::DockNodeTreeMerge(ImGuiDockContext* ctx, ImGuiDockNode* parent_node, parent_node->IsDocumentRoot = child_0->IsDocumentRoot || child_1->IsDocumentRoot; parent_node->LastExplicitSize = backup_last_explicit_size; - ctx->Nodes.SetVoidPtr(child_0->ID, NULL); - ctx->Nodes.SetVoidPtr(child_1->ID, NULL); + ctx->DockContext->Nodes.SetVoidPtr(child_0->ID, NULL); + ctx->DockContext->Nodes.SetVoidPtr(child_1->ID, NULL); IM_DELETE(child_0); IM_DELETE(child_1); } @@ -11308,8 +11312,8 @@ void ImGui::SetWindowDock(ImGuiWindow* window, ImGuiID dock_id, ImGuiCond cond) void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockSpaceFlags dock_space_flags, ImGuiID user_type_filter) { - ImGuiContext& g = *GImGui; - ImGuiDockContext* ctx = g.DockContext; + ImGuiContext* ctx = GImGui; + ImGuiContext& g = *ctx; ImGuiWindow* window = GetCurrentWindow(); @@ -11378,7 +11382,8 @@ void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockSpaceFlags do void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) { - ImGuiContext& g = *GImGui; + ImGuiContext* ctx = GImGui; + ImGuiContext& g = *ctx; // Calling SetNextWindowPos() undock windows by default (by setting PosUndock) bool want_undock = false; @@ -11387,7 +11392,7 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) g.NextWindowData.PosUndock = false; if (want_undock) { - DockContextProcessUndock(g.DockContext, window); + DockContextProcessUndock(ctx, window); return; } @@ -11395,13 +11400,13 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) ImGuiDockNode* dock_node = window->DockNode; if (window->DockId != 0 && dock_node == NULL) { - dock_node = DockContextFindNodeByID(g.DockContext, window->DockId); + dock_node = DockContextFindNodeByID(ctx, window->DockId); if (dock_node == NULL) - dock_node = DockContextAddNode(g.DockContext, window->DockId); + dock_node = DockContextAddNode(ctx, window->DockId); if (dock_node->IsSplitNode()) { - DockContextProcessUndock(g.DockContext, window); + DockContextProcessUndock(ctx, window); return; } @@ -11423,7 +11428,7 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) ImGuiDockNode* root_node = DockNodeGetRootNode(dock_node); if (root_node->LastFrameAlive < g.FrameCount) { - DockContextProcessUndock(g.DockContext, window); + DockContextProcessUndock(ctx, window); } else { @@ -11436,7 +11441,7 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) // Undock if we are submitted earlier than the host window if (dock_node->HostWindow && window->BeginOrderWithinContext < dock_node->HostWindow->BeginOrderWithinContext) { - DockContextProcessUndock(g.DockContext, window); + DockContextProcessUndock(ctx, window); return; } @@ -11506,7 +11511,9 @@ void ImGui::BeginAsDockableDragDropSource(ImGuiWindow* window) void ImGui::BeginAsDockableDragDropTarget(ImGuiWindow* window) { - ImGuiContext& g = *GImGui; + ImGuiContext* ctx = GImGui; + ImGuiContext& g = *ctx; + IM_ASSERT((window->Flags & ImGuiWindowFlags_NoDocking) == 0); if (!g.DragDropActive) return; @@ -11557,7 +11564,7 @@ void ImGui::BeginAsDockableDragDropTarget(ImGuiWindow* window) // Queue docking request if (split_data && split_data->IsDropAllowed && payload->IsDelivery()) - DockContextQueueDock(g.DockContext, window, split_data->SplitNode, payload_window, split_data->SplitDir, split_data->SplitRatio, split_data == &split_outer); + DockContextQueueDock(ctx, window, split_data->SplitNode, payload_window, split_data->SplitDir, split_data->SplitRatio, split_data == &split_outer); } } EndDragDropTarget(); @@ -11587,11 +11594,12 @@ static void ImGui::DockSettingsRemoveReferencesToNodes(ImGuiID* node_ids, int no } } -static ImGuiDockNodeSettings* ImGui::DockSettingsFindNodeSettings(ImGuiDockContext* ctx, ImGuiID id) +static ImGuiDockNodeSettings* ImGui::DockSettingsFindNodeSettings(ImGuiContext* ctx, ImGuiID id) { - for (int n = 0; n < ctx->SettingsNodes.Size; n++) - if (ctx->SettingsNodes[n].ID == id) - return &ctx->SettingsNodes[n]; + ImGuiDockContext* dc = ctx->DockContext; + for (int n = 0; n < dc->SettingsNodes.Size; n++) + if (dc->SettingsNodes[n].ID == id) + return &dc->SettingsNodes[n]; return NULL; } @@ -11602,7 +11610,7 @@ static void* ImGui::DockSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHan return (void*)1; } -static void ImGui::DockSettingsHandler_ReadLine(ImGuiContext* imgui_ctx, ImGuiSettingsHandler*, void*, const char* line) +static void ImGui::DockSettingsHandler_ReadLine(ImGuiContext* ctx, ImGuiSettingsHandler*, void*, const char* line) { char c = 0; int x = 0, y = 0; @@ -11632,14 +11640,14 @@ static void ImGui::DockSettingsHandler_ReadLine(ImGuiContext* imgui_ctx, ImGuiSe if (sscanf(line, " SelectedTab=0x%08X%n", &node.SelectedTabID,&r) == 1) { line += r; } //if (node.ParentID == 0 && node.SplitAxis == ImGuiAxis_None) // return; - ImGuiDockContext* ctx = imgui_ctx->DockContext; + ImGuiDockContext* dc = ctx->DockContext; if (node.ParentID != 0) if (ImGuiDockNodeSettings* parent_settings = DockSettingsFindNodeSettings(ctx, node.ParentID)) node.Depth = parent_settings->Depth + 1; - ctx->SettingsNodes.push_back(node); + dc->SettingsNodes.push_back(node); } -static void DockSettingsHandler_DockNodeToSettings(ImGuiDockContext* ctx, ImGuiDockNode* node, int depth) +static void DockSettingsHandler_DockNodeToSettings(ImGuiDockContext* dc, ImGuiDockNode* node, int depth) { ImGuiDockNodeSettings node_settings; IM_ASSERT(depth < (1 << (sizeof(node_settings.Depth) << 3))); @@ -11653,34 +11661,34 @@ static void DockSettingsHandler_DockNodeToSettings(ImGuiDockContext* ctx, ImGuiD node_settings.Pos = ImVec2ih((short)node->Pos.x, (short)node->Pos.y); node_settings.Size = ImVec2ih((short)node->Size.x, (short)node->Size.y); node_settings.LastExplicitSize = ImVec2ih((short)node->LastExplicitSize.x, (short)node->LastExplicitSize.y); - ctx->SettingsNodes.push_back(node_settings); + dc->SettingsNodes.push_back(node_settings); if (node->ChildNodes[0]) - DockSettingsHandler_DockNodeToSettings(ctx, node->ChildNodes[0], depth + 1); + DockSettingsHandler_DockNodeToSettings(dc, node->ChildNodes[0], depth + 1); if (node->ChildNodes[1]) - DockSettingsHandler_DockNodeToSettings(ctx, node->ChildNodes[1], depth + 1); + DockSettingsHandler_DockNodeToSettings(dc, node->ChildNodes[1], depth + 1); } -static void ImGui::DockSettingsHandler_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf) +static void ImGui::DockSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf) { // Gather settings data // (unlike our windows settings, because nodes are always built we can do a full rewrite of the SettingsNode buffer) - ImGuiDockContext* ctx = imgui_ctx->DockContext; - ctx->SettingsNodes.resize(0); - ctx->SettingsNodes.reserve(ctx->Nodes.Data.Size); - for (int n = 0; n < ctx->Nodes.Data.Size; n++) - if (ImGuiDockNode* node = (ImGuiDockNode*)ctx->Nodes.Data[n].val_p) + ImGuiDockContext* dc = ctx->DockContext; + dc->SettingsNodes.resize(0); + dc->SettingsNodes.reserve(dc->Nodes.Data.Size); + for (int n = 0; n < dc->Nodes.Data.Size; n++) + if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p) if (node->IsRootNode()) - DockSettingsHandler_DockNodeToSettings(ctx, node, 0); + DockSettingsHandler_DockNodeToSettings(dc, node, 0); int max_depth = 0; - for (int node_n = 0; node_n < ctx->SettingsNodes.Size; node_n++) - max_depth = ImMax((int)ctx->SettingsNodes[node_n].Depth, max_depth); + for (int node_n = 0; node_n < dc->SettingsNodes.Size; node_n++) + max_depth = ImMax((int)dc->SettingsNodes[node_n].Depth, max_depth); // Write to text buffer buf->appendf("[%s][Data]\n", handler->TypeName); - for (int node_n = 0; node_n < ctx->SettingsNodes.Size; node_n++) + for (int node_n = 0; node_n < dc->SettingsNodes.Size; node_n++) { - const ImGuiDockNodeSettings* node_settings = &ctx->SettingsNodes[node_n]; + const ImGuiDockNodeSettings* node_settings = &dc->SettingsNodes[node_n]; buf->appendf("%*sDockNode%*s", node_settings->Depth * 2, "", (max_depth - node_settings->Depth) * 2, ""); // Text align nodes to facilitate looking at .ini file buf->appendf(" ID=0x%08X", node_settings->ID); if (node_settings->ParentID) @@ -12008,7 +12016,7 @@ void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size) } ImGui::MemFree(buf); g.SettingsLoaded = true; - DockContextOnLoadSettings(); + DockContextOnLoadSettings(&g); } void ImGui::SaveIniSettingsToDisk(const char* ini_filename) @@ -12521,8 +12529,9 @@ void ImGui::ShowMetricsWindow(bool* p_open) void ImGui::ShowDockingDebug() { - ImGuiContext& g = *GImGui; - ImGuiDockContext* ctx = g.DockContext; + ImGuiContext* ctx = GImGui; + ImGuiContext& g = *ctx; + ImGuiDockContext* dc = ctx->DockContext; struct Funcs { @@ -12589,11 +12598,11 @@ void ImGui::ShowDockingDebug() if (ImGui::TreeNode("Dock nodes")) { - if (ImGui::SmallButton("Clear settings")) { DockContextClearNodes(ctx, 0, true); } + if (ImGui::SmallButton("Clear settings")) { DockContextClearNodes(&g, 0, true); } ImGui::SameLine(); - if (ImGui::SmallButton("Rebuild all")) { ctx->WantFullRebuild = true; } - for (int n = 0; n < ctx->Nodes.Data.Size; n++) - if (ImGuiDockNode* node = (ImGuiDockNode*)ctx->Nodes.Data[n].val_p) + if (ImGui::SmallButton("Rebuild all")) { dc->WantFullRebuild = true; } + for (int n = 0; n < dc->Nodes.Data.Size; n++) + if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p) if (node->IsRootNode()) Funcs::NodeDockNode(node, "Node"); ImGui::TreePop(); @@ -12620,9 +12629,9 @@ void ImGui::ShowDockingDebug() ImGui::BulletText("Window '%s' -> DockId %08X", g.SettingsWindows[n].Name, g.SettingsWindows[n].DockId); ImGui::Separator(); ImGui::Text("Dock Nodes:"); - for (int n = 0; n < ctx->SettingsNodes.Size; n++) + for (int n = 0; n < dc->SettingsNodes.Size; n++) { - ImGuiDockNodeSettings* settings = &ctx->SettingsNodes[n]; + ImGuiDockNodeSettings* settings = &dc->SettingsNodes[n]; const char* selected_tab_name = NULL; if (settings->SelectedTabID) { @@ -12638,8 +12647,8 @@ void ImGui::ShowDockingDebug() if (g.IO.KeyCtrl && show_window_dock_info) { - for (int n = 0; n < ctx->Nodes.Data.Size; n++) - if (ImGuiDockNode* node = (ImGuiDockNode*)ctx->Nodes.Data[n].val_p) + for (int n = 0; n < dc->Nodes.Data.Size; n++) + if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p) { ImGuiDockNode* root_node = DockNodeGetRootNode(node); if (ImGuiDockNode* hovered_node = DockNodeTreeFindNodeByPos(root_node, g.IO.MousePos)) diff --git a/imgui_internal.h b/imgui_internal.h index 79cea7b9643c..975eba98fc65 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1460,14 +1460,14 @@ namespace ImGui inline bool IsNavInputPressedAnyOfTwo(ImGuiNavInput n1, ImGuiNavInput n2, ImGuiInputReadMode mode) { return (GetNavInputAmount(n1, mode) + GetNavInputAmount(n2, mode)) > 0.0f; } // Docking - IMGUI_API void DockContextInitialize(ImGuiContext* imgui_context); - IMGUI_API void DockContextShutdown(ImGuiContext* imgui_context); - IMGUI_API void DockContextOnLoadSettings(); - IMGUI_API void DockContextRebuild(ImGuiDockContext* ctx); - IMGUI_API void DockContextNewFrameUpdateUndocking(ImGuiDockContext* ctx); - IMGUI_API void DockContextNewFrameUpdateDocking(ImGuiDockContext* ctx); - IMGUI_API void DockContextEndFrame(ImGuiDockContext* ctx); - IMGUI_API void DockContextQueueUndock(ImGuiDockContext* ctx, ImGuiWindow* window); + IMGUI_API void DockContextInitialize(ImGuiContext* ctx); + IMGUI_API void DockContextShutdown(ImGuiContext* ctx); + IMGUI_API void DockContextOnLoadSettings(ImGuiContext* ctx); + IMGUI_API void DockContextRebuild(ImGuiContext* ctx); + IMGUI_API void DockContextNewFrameUpdateUndocking(ImGuiContext* ctx); + IMGUI_API void DockContextNewFrameUpdateDocking(ImGuiContext* ctx); + IMGUI_API void DockContextEndFrame(ImGuiContext* ctx); + IMGUI_API void DockContextQueueUndock(ImGuiContext* ctx, ImGuiWindow* window); IMGUI_API void BeginDocked(ImGuiWindow* window, bool* p_open); IMGUI_API void BeginAsDockableDragDropSource(ImGuiWindow* window); IMGUI_API void BeginAsDockableDragDropTarget(ImGuiWindow* window); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 88c9e4c1c487..a84ea68782cb 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6455,8 +6455,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, // Undock if (undocking_tab && g.ActiveId == id && IsMouseDragging()) { - ImGuiDockContext* ctx = g.DockContext; - DockContextQueueUndock(ctx, docked_window); + DockContextQueueUndock(&g, docked_window); g.MovingWindow = docked_window; g.ActiveId = g.MovingWindow->MoveId; g.ActiveIdClickOffset -= g.MovingWindow->Pos - bb.Min; From 291bfe68418abe19bc7acb24a9dd2fb6d60368db Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 17 Sep 2018 18:32:10 +0200 Subject: [PATCH 258/828] Docking: work to allow programmatic control of dock nodes, various refactor + assert fix. Probably broke something (but I haven't found what yet!) --- imgui.cpp | 241 +++++++++++++++++++++++++++++++++++------------ imgui_demo.cpp | 2 +- imgui_internal.h | 6 ++ 3 files changed, 186 insertions(+), 63 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 04dd1b249ff2..f924df567f45 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -9518,6 +9518,7 @@ void ImGui::EndDragDropTarget() // Docking: ImGuiDockNode // Docking: ImGuiDockNode Tree manipulation functions // Docking: Public Functions (Dockspace, SetWindowDock) +// Docking: Public Builder Functions // Docking: Begin/End Functions (called from Begin/End) // Docking: Settings //----------------------------------------------------------------------------- @@ -9553,15 +9554,16 @@ enum ImGuiDockRequestType { ImGuiDockRequestType_None = 0, ImGuiDockRequestType_Dock, - ImGuiDockRequestType_Undock + ImGuiDockRequestType_Undock, + ImGuiDockRequestType_Split // Split is the same as Dock but without a DockPayload }; struct ImGuiDockRequest { ImGuiDockRequestType Type; - ImGuiWindow* DockTarget; // Destination/Target window to dock into (may be a loose window or a DockNode) - ImGuiDockNode* DockTargetNode; - ImGuiWindow* DockPayload; // Source/Payload window to dock (may be a loose window or a DockNode) + ImGuiWindow* DockTargetWindow; // Destination/Target Window to dock into (may be a loose window or a DockNode, might be NULL in which case DockTargetNode cannot be NULL) + ImGuiDockNode* DockTargetNode; // Destination/Target Node to dock into + ImGuiWindow* DockPayload; // Source/Payload window to dock (may be a loose window or a DockNode), [Optional] ImGuiDir DockSplitDir; float DockSplitRatio; bool DockSplitOuter; @@ -9570,7 +9572,7 @@ struct ImGuiDockRequest ImGuiDockRequest() { Type = ImGuiDockRequestType_None; - DockTarget = DockPayload = UndockTarget = NULL; + DockTargetWindow = DockPayload = UndockTarget = NULL; DockTargetNode = NULL; DockSplitDir = ImGuiDir_None; DockSplitRatio = 0.5f; @@ -9627,8 +9629,9 @@ namespace ImGui // ImGuiDockContext static ImGuiDockNode* DockContextFindNodeByID(ImGuiContext* ctx, ImGuiID id); static ImGuiDockNode* DockContextAddNode(ImGuiContext* ctx, ImGuiID id); - static void DockContextRemoveNode(ImGuiContext* ctx, ImGuiDockNode* node); + static void DockContextRemoveNode(ImGuiContext* ctx, ImGuiDockNode* node, bool merge_sibling_into_parent_node); static void DockContextQueueDock(ImGuiContext* ctx, ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, float split_ratio, bool split_outer); + static void DockContextQueueNotifyRemovedNode(ImGuiContext* ctx, ImGuiDockNode* node); static void DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req); static void DockContextProcessUndock(ImGuiContext* ctx, ImGuiWindow* window); static void DockContextGcUnusedSettingsNodes(ImGuiContext* ctx); @@ -9654,7 +9657,7 @@ namespace ImGui static void DockNodeCalcSplitRects(ImVec2& pos_old, ImVec2& size_old, ImVec2& pos_new, ImVec2& size_new, ImGuiDir dir, ImVec2 size_new_desired); static bool DockNodeCalcDropRects(const ImRect& parent, ImGuiDir dir, ImRect& out_draw, bool outer_docking); static ImGuiDockNode* DockNodeGetRootNode(ImGuiDockNode* node) { while (node->ParentNode) node = node->ParentNode; return node; } - static int DockNodeGetDepth(ImGuiDockNode* node) { int depth = 0; while (node->ParentNode) { node = node->ParentNode; depth++; } return depth; } + static int DockNodeGetDepth(const ImGuiDockNode* node) { int depth = 0; while (node->ParentNode) { node = node->ParentNode; depth++; } return depth; } static int DockNodeGetTabOrder(ImGuiWindow* window); // ImGuiDockNode tree manipulations @@ -9717,6 +9720,12 @@ void ImGui::DockContextOnLoadSettings(ImGuiContext* ctx) DockContextBuildNodesFromSettings(ctx, dc->SettingsNodes.Data, dc->SettingsNodes.Size); } +void ImGui::DockContextClearNodes(ImGuiContext* ctx, ImGuiID root_id, bool clear_persistent_docking_references) +{ + DockBuilderRemoveNodeDockedWindows(ctx, root_id, clear_persistent_docking_references); + DockBuilderRemoveNodeChildNodes(ctx, root_id); +} + // This function also acts as a de-facto test to make sure we can rebuild from scratch without a glitch void ImGui::DockContextRebuild(ImGuiContext* ctx) { @@ -9807,7 +9816,7 @@ static ImGuiDockNode* ImGui::DockContextAddNode(ImGuiContext* ctx, ImGuiID id) return node; } -static void ImGui::DockContextRemoveNode(ImGuiContext* ctx, ImGuiDockNode* node) +static void ImGui::DockContextRemoveNode(ImGuiContext* ctx, ImGuiDockNode* node, bool merge_sibling_into_parent_node) { ImGuiContext& g = *ctx; ImGuiDockContext* dc = ctx->DockContext; @@ -9815,12 +9824,14 @@ static void ImGui::DockContextRemoveNode(ImGuiContext* ctx, ImGuiDockNode* node) //printf("[%05d] RemoveNode 0x%04X\n", node->ID); IM_ASSERT(DockContextFindNodeByID(ctx, node->ID) == node); IM_ASSERT(node->ChildNodes[0] == NULL && node->ChildNodes[1] == NULL); - IM_ASSERT(node->Windows.Size == 1 || node->HostWindow->DockNodeAsHost == node); + IM_ASSERT(node->Windows.Size == 0); if (node->HostWindow) node->HostWindow->DockNodeAsHost = NULL; - if (ImGuiDockNode* parent_node = node->ParentNode) + ImGuiDockNode* parent_node = node->ParentNode; + const bool merge = (merge_sibling_into_parent_node && parent_node != NULL); + if (merge) { IM_ASSERT(parent_node->ChildNodes[0] == node || parent_node->ChildNodes[1] == node); ImGuiDockNode* sibling_node = (parent_node->ChildNodes[0] == node ? parent_node->ChildNodes[1] : parent_node->ChildNodes[0]); @@ -9828,45 +9839,80 @@ static void ImGui::DockContextRemoveNode(ImGuiContext* ctx, ImGuiDockNode* node) } else { + for (int n = 0; parent_node && n < IM_ARRAYSIZE(parent_node->ChildNodes); n++) + if (parent_node->ChildNodes[n] == node) + node->ParentNode->ChildNodes[n] = NULL; dc->Nodes.SetVoidPtr(node->ID, NULL); IM_DELETE(node); } } -void ImGui::DockContextClearNodes(ImGuiContext* ctx, ImGuiID root_id, bool clear_persistent_docking_references) +static int IMGUI_CDECL DockNodeComparerDepthMostFirst(const void* lhs, const void* rhs) +{ + const ImGuiDockNode* a = *(const ImGuiDockNode* const*)lhs; + const ImGuiDockNode* b = *(const ImGuiDockNode* const*)rhs; + return ImGui::DockNodeGetDepth(b) - ImGui::DockNodeGetDepth(a); +} + +void ImGui::DockBuilderRemoveNodeChildNodes(ImGuiContext* ctx, ImGuiID root_id) { - ImGuiContext& g = *ctx; ImGuiDockContext* dc = ctx->DockContext; - SaveIniSettingsToMemory(NULL); - // Clear references in windows - for (int n = 0; n < g.Windows.Size; n++) - { - ImGuiWindow* window = g.Windows[n]; - bool want_removal = root_id == 0 || (window->DockNode && DockNodeGetRootNode(window->DockNode)->ID == root_id) || (window->DockNodeAsHost && window->DockNodeAsHost->ID == root_id); - if (want_removal) - { - window->DockNode = window->DockNodeAsHost = NULL; - window->DockIsActive = false; - if (clear_persistent_docking_references) - window->DockId = 0; - } - } + ImGuiDockNode* root_node = root_id ? DockContextFindNodeByID(ctx, root_id) : NULL; + if (root_id && root_node == NULL) + return; + bool has_document_root = false; - // Clear nodes + ImVector nodes_to_remove; for (int n = 0; n < dc->Nodes.Data.Size; n++) if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p) { - bool want_removal = (root_id == 0 || DockNodeGetRootNode(node)->ID == root_id); + bool want_removal = (root_id == 0) || (node->ID != root_id && DockNodeGetRootNode(node)->ID == root_id); if (want_removal) { - IM_DELETE(node); - dc->Nodes.Data[n].val_p = NULL; + if (node->IsDocumentRoot) + has_document_root = true; + if (root_id != 0) + DockContextQueueNotifyRemovedNode(ctx, node); + if (root_node) + DockNodeMoveWindows(root_node, node); + nodes_to_remove.push_back(node); } } + + // Not really efficient, but easier to destroy a whole hierarchy considering DockContextRemoveNode is attempting to merge nodes + if (nodes_to_remove.Size > 1) + ImQsort(nodes_to_remove.Data, nodes_to_remove.Size, sizeof(ImGuiDockNode*), DockNodeComparerDepthMostFirst); + for (int n = 0; n < nodes_to_remove.Size; n++) + DockContextRemoveNode(ctx, nodes_to_remove[n], false); + if (root_id == 0) + { dc->Nodes.Clear(); - dc->Requests.clear(); + dc->Requests.clear(); + } + else if (has_document_root) + { + root_node->IsDocumentRoot = true; + } +} + +void ImGui::DockBuilderRemoveNodeDockedWindows(ImGuiContext* ctx, ImGuiID root_id, bool clear_persistent_docking_references) +{ + // Clear references in windows + ImGuiContext& g = *ctx; + for (int n = 0; n < g.Windows.Size; n++) + { + ImGuiWindow* window = g.Windows[n]; + bool want_removal = (root_id == 0) || (window->DockNode && DockNodeGetRootNode(window->DockNode)->ID == root_id) || (window->DockNodeAsHost && window->DockNodeAsHost->ID == root_id); + if (want_removal) + { + ImGuiID backup_dock_id = window->DockId; + DockContextProcessUndock(ctx, window); + if (!clear_persistent_docking_references) + window->DockId = backup_dock_id; + } + } } static void ImGui::DockContextGcUnusedSettingsNodes(ImGuiContext* ctx) @@ -9959,7 +10005,7 @@ void ImGui::DockContextQueueDock(ImGuiContext* ctx, ImGuiWindow* target, ImGuiDo { ImGuiDockRequest req; req.Type = ImGuiDockRequestType_Dock; - req.DockTarget = target; + req.DockTargetWindow = target; req.DockTargetNode = target_node; req.DockPayload = payload; req.DockSplitDir = split_dir; @@ -9976,26 +10022,41 @@ void ImGui::DockContextQueueUndock(ImGuiContext* ctx, ImGuiWindow* window) ctx->DockContext->Requests.push_back(req); } +void ImGui::DockContextQueueNotifyRemovedNode(ImGuiContext* ctx, ImGuiDockNode* node) +{ + ImGuiDockContext* dc = ctx->DockContext; + for (int n = 0; n < dc->Requests.Size; n++) + if (dc->Requests[n].DockTargetNode == node) + dc->Requests[n].Type = ImGuiDockRequestType_None; +} + void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) { + IM_ASSERT((req->Type == ImGuiDockRequestType_Dock && req->DockPayload != NULL) || (req->Type == ImGuiDockRequestType_Split && req->DockPayload == NULL)); + IM_ASSERT(req->DockTargetWindow != NULL || req->DockTargetNode != NULL); + ImGuiContext& g = *ctx; - ImGuiWindow* payload_window = req->DockPayload; - ImGuiWindow* target_window = req->DockTarget; + ImGuiWindow* payload_window = req->DockPayload; // Optional + ImGuiWindow* target_window = req->DockTargetWindow; ImGuiDockNode* target_node = req->DockTargetNode; // Decide which Tab will be selected at the end of the operation ImGuiID next_selected_id = 0; - ImGuiDockNode* payload_node = payload_window->DockNodeAsHost; - if (payload_node && !payload_node->IsSplitNode()) - next_selected_id = payload_node->TabBar->NextSelectedTabId ? payload_node->TabBar->NextSelectedTabId : payload_node->TabBar->SelectedTabId; - if (payload_node == NULL) - next_selected_id = payload_window->ID; + ImGuiDockNode* payload_node = NULL; + if (payload_window) + { + payload_node = payload_window->DockNodeAsHost; + if (payload_node && !payload_node->IsSplitNode()) + next_selected_id = payload_node->TabBar->NextSelectedTabId ? payload_node->TabBar->NextSelectedTabId : payload_node->TabBar->SelectedTabId; + if (payload_node == NULL) + next_selected_id = payload_window->ID; + } // FIXME-DOCK: When we are trying to dock an existing single-window node into a loose window, transfer Node ID as well if (target_node) IM_ASSERT(target_node->LastFrameAlive < g.FrameCount); - if (target_node && target_node == target_window->DockNodeAsHost) + if (target_node && target_window && target_node == target_window->DockNodeAsHost) IM_ASSERT(target_node->Windows.Size > 1 || target_node->IsSplitNode() || target_node->IsDocumentRoot); // Create new node and add existing window to it @@ -10037,7 +10098,7 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) if (target_node != payload_node) { - // Create tab bar before we call DockNoveMoveWindows (which would attempt to move the old tab-bar, which would lead us to payload tabs wrongly appearing before target tabs!) + // Create tab bar before we call DockNodeMoveWindows (which would attempt to move the old tab-bar, which would lead us to payload tabs wrongly appearing before target tabs!) if (target_node->Windows.Size > 0 && target_node->TabBar == NULL) { target_node->TabBar = IM_NEW(ImGuiTabBar)(); @@ -10072,9 +10133,9 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) { DockNodeMoveWindows(target_node, payload_node); } - DockContextRemoveNode(ctx, payload_node); + DockContextRemoveNode(ctx, payload_node, true); } - else + else if (payload_window) { // Transfer single window target_node->VisibleWindow = payload_window; @@ -10089,8 +10150,7 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) void ImGui::DockContextProcessUndock(ImGuiContext* ctx, ImGuiWindow* window) { - ImGuiContext& g = *ctx; - IM_ASSERT(window->LastFrameActive < g.FrameCount); + (void)ctx; if (window->DockNode) DockNodeRemoveWindow(window->DockNode, window, 0); else @@ -10192,24 +10252,25 @@ static void ImGui::DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window UpdateWindowParentAndRootLinks(window, window->Flags & ~ImGuiWindowFlags_ChildWindow, NULL); // Update immediately MarkIniSettingsDirty(); - if (node->Windows.Size == 1 && !node->IsDocumentRoot && window->DockId != node->ID) - { - // Automatic dock node delete themselves if they are not holding at least one tab - IM_ASSERT(node->Windows[0] == window); - DockContextRemoveNode(&g, node); - return; - } - bool erased = false; for (int n = 0; n < node->Windows.Size; n++) if (node->Windows[n] == window) { node->Windows.erase(node->Windows.Data + n); + if (node->TabBar) + TabBarRemoveTab(node->TabBar, window->ID); erased = true; break; } IM_ASSERT(erased); + if (node->Windows.Size == 0 && !node->IsDocumentRoot && window->DockId != node->ID) + { + // Automatic dock node delete themselves if they are not holding at least one tab + DockContextRemoveNode(&g, node, true); + return; + } + if (node->TabBar) TabBarRemoveTab(node->TabBar, window->ID); @@ -10254,7 +10315,7 @@ static void ImGui::DockNodeMoveChildNodes(ImGuiDockNode* dst_node, ImGuiDockNode static void ImGui::DockNodeMoveWindows(ImGuiDockNode* dst_node, ImGuiDockNode* src_node) { // Insert tabs in the same orders as currently ordered (node->Windows isn't ordered) - IM_ASSERT(dst_node != src_node); + IM_ASSERT(src_node && dst_node && dst_node != src_node); ImGuiTabBar* src_tab_bar = src_node->TabBar; if (src_tab_bar != NULL) IM_ASSERT(src_node->Windows.Size == src_node->TabBar->Tabs.Size); @@ -11060,12 +11121,16 @@ void ImGui::DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImG ImGuiDockNode* child_1 = parent_node->ChildNodes[1]; IM_ASSERT(child_0 && child_1); IM_ASSERT(merge_lead_child == child_0 || merge_lead_child == child_1); - IM_ASSERT(parent_node->TabBar == NULL); - IM_ASSERT(parent_node->Windows.Size == 0); + if (child_0->Windows.Size > 0 || child_1->Windows.Size > 0) + { + IM_ASSERT(parent_node->TabBar == NULL); + IM_ASSERT(parent_node->Windows.Size == 0); + } ImVec2 backup_last_explicit_size = parent_node->LastExplicitSize; DockNodeMoveChildNodes(parent_node, merge_lead_child); - DockNodeMoveWindows(parent_node, merge_lead_child); + DockNodeMoveWindows(parent_node, child_0); // Generally only 1 of the 2 child node will have windows + DockNodeMoveWindows(parent_node, child_1); DockNodeApplyPosSizeToWindows(parent_node); parent_node->InitFromFirstWindow = false; parent_node->VisibleWindow = merge_lead_child->VisibleWindow; @@ -11376,6 +11441,57 @@ void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockSpaceFlags do End(); } +//----------------------------------------------------------------------------- +// Docking: Builder Functions +//----------------------------------------------------------------------------- +// Very early end-user API to manipulate dock nodes. +//----------------------------------------------------------------------------- + +void ImGui::DockBuilderDockWindow(ImGuiContext*, const char* window_name, ImGuiID node_id) +{ + ImGuiID window_id = ImHash(window_name, 0); + if (ImGuiWindow* window = FindWindowByID(window_id)) + SetWindowDock(window, node_id, ImGuiCond_Always); + else if (ImGuiWindowSettings* settings = FindWindowSettings(window_id)) + settings->DockId = node_id; + else if (ImGuiWindowSettings* settings = CreateNewWindowSettings(window_name)) + settings->DockId = node_id; +} + +ImGuiID ImGui::DockBuilderSplitNode(ImGuiContext* ctx, ImGuiID id, ImGuiDir split_dir, float size_ratio_for_node_at_dir, ImGuiID* out_id_at_dir, ImGuiID* out_id_other) +{ + IM_ASSERT(split_dir != ImGuiDir_None); + + ImGuiDockNode* node = DockContextFindNodeByID(ctx, id); + if (node == NULL) + return 0; + + IM_ASSERT(!node->IsSplitNode()); // Already Split + + ImGuiDockRequest req; + req.Type = ImGuiDockRequestType_Split; + req.DockTargetWindow = NULL; + req.DockTargetNode = node; + req.DockPayload = NULL; + req.DockSplitDir = split_dir; + req.DockSplitRatio = ImSaturate((split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Up) ? size_ratio_for_node_at_dir : 1.0f - size_ratio_for_node_at_dir); + req.DockSplitOuter = false; + DockContextProcessDock(ctx, &req); + + ImGuiID id_at_dir = node->ChildNodes[(split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Up) ? 0 : 1]->ID; + ImGuiID id_other = node->ChildNodes[(split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Up) ? 1 : 0]->ID; + if (out_id_at_dir) + *out_id_at_dir = id_at_dir; + if (out_id_other) + *out_id_other = id_other; + return id_at_dir; +} + +void ImGui::DockBuilderFinish(ImGuiContext* ctx, ImGuiID root_id) +{ + DockContextBuildAddWindowsToNodes(ctx, root_id); +} + //----------------------------------------------------------------------------- // Docking: Begin/End Functions (called from Begin/End) //----------------------------------------------------------------------------- @@ -11654,7 +11770,7 @@ static void DockSettingsHandler_DockNodeToSettings(ImGuiDockContext* dc, ImGuiDo node_settings.ID = node->ID; node_settings.ParentID = node->ParentNode ? node->ParentNode->ID : 0; node_settings.SelectedTabID = node->SelectedTabID; - node_settings.SplitAxis = (char)node->SplitAxis; + node_settings.SplitAxis = node->IsSplitNode() ? (char)node->SplitAxis : ImGuiAxis_None; node_settings.Depth = (char)depth; node_settings.IsExplicitRoot = (char)node->IsExplicitRoot; node_settings.IsDocumentRoot = (char)node->IsDocumentRoot; @@ -11917,7 +12033,7 @@ void ImGui::MarkIniSettingsDirty(ImGuiWindow* window) g.SettingsDirtyTimer = g.IO.IniSavingRate; } -static ImGuiWindowSettings* CreateNewWindowSettings(const char* name) +ImGuiWindowSettings* ImGui::CreateNewWindowSettings(const char* name) { ImGuiContext& g = *GImGui; g.SettingsWindows.push_back(ImGuiWindowSettings()); @@ -12056,7 +12172,7 @@ static void* SettingsHandlerWindow_ReadOpen(ImGuiContext*, ImGuiSettingsHandler* { ImGuiWindowSettings* settings = ImGui::FindWindowSettings(ImHash(name, 0)); if (!settings) - settings = CreateNewWindowSettings(name); + settings = ImGui::CreateNewWindowSettings(name); return (void*)settings; } @@ -12091,7 +12207,7 @@ static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSetting ImGuiWindowSettings* settings = (window->SettingsIdx != -1) ? &g.SettingsWindows[window->SettingsIdx] : ImGui::FindWindowSettings(window->ID); if (!settings) { - settings = CreateNewWindowSettings(window->Name); + settings = ImGui::CreateNewWindowSettings(window->Name); window->SettingsIdx = g.SettingsWindows.index_from_pointer(settings); } IM_ASSERT(settings->ID == window->ID); @@ -12121,7 +12237,8 @@ static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSetting } if (settings->Pos.x != 0.0f || settings->Pos.y != 0.0f || settings->ViewportId == ImGui::IMGUI_VIEWPORT_DEFAULT_ID) buf->appendf("Pos=%d,%d\n", (int)settings->Pos.x, (int)settings->Pos.y); - buf->appendf("Size=%d,%d\n", (int)settings->Size.x, (int)settings->Size.y); + if (settings->Size.x != 0.0f || settings->Size.y != 0.0f) + buf->appendf("Size=%d,%d\n", (int)settings->Size.x, (int)settings->Size.y); buf->appendf("Collapsed=%d\n", settings->Collapsed); if (settings->DockId != 0) { diff --git a/imgui_demo.cpp b/imgui_demo.cpp index d49bf797e15c..6cbf0cdb3fb1 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3821,7 +3821,7 @@ struct ExampleAppDocuments ExampleAppDocuments() { - Documents.push_back(MyDocument("Radish", true, ImVec4(1.0f, 1.0f, 1.0f, 1.0f))); + Documents.push_back(MyDocument("Lettuce", true, ImVec4(0.4f, 0.8f, 0.4f, 1.0f))); Documents.push_back(MyDocument("Eggplant", true, ImVec4(0.8f, 0.5f, 1.0f, 1.0f))); Documents.push_back(MyDocument("Carrot", true, ImVec4(1.0f, 0.8f, 0.5f, 1.0f))); Documents.push_back(MyDocument("Tomato", false, ImVec4(1.0f, 0.3f, 0.4f, 1.0f))); diff --git a/imgui_internal.h b/imgui_internal.h index 975eba98fc65..ef0fee6211ae 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1474,6 +1474,12 @@ namespace ImGui IMGUI_API void SetWindowDock(ImGuiWindow* window, ImGuiID dock_id, ImGuiCond cond); IMGUI_API void ShowDockingDebug(); + IMGUI_API void DockBuilderRemoveNodeDockedWindows(ImGuiContext* ctx, ImGuiID root_id, bool clear_persistent_docking_references = true); + IMGUI_API void DockBuilderRemoveNodeChildNodes(ImGuiContext* ctx, ImGuiID root_id); + IMGUI_API void DockBuilderDockWindow(ImGuiContext* ctx, const char* window_name, ImGuiID node_id); + IMGUI_API ImGuiID DockBuilderSplitNode(ImGuiContext* ctx, ImGuiID id, ImGuiDir split_dir, float size_ratio_for_node_at_dir, ImGuiID* out_id_dir, ImGuiID* out_id_other); + IMGUI_API void DockBuilderFinish(ImGuiContext* ctx, ImGuiID root_id); + // Drag and Drop IMGUI_API bool BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id); IMGUI_API void ClearDragDrop(); From 455dc6e229b71a831bb791262390fcd40d8bce92 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 17 Sep 2018 22:35:30 +0200 Subject: [PATCH 259/828] Docking: Creating tab bar and adding window into tab bar immediately (not sure why I didn't do that before). Probably broke something subtle, will find out. *Edit* Initially broke restoring selected tab. --- imgui.cpp | 40 ++++++++++++++++++++++++---------------- imgui_widgets.cpp | 3 ++- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index f924df567f45..36cddee7d5f3 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -9523,6 +9523,7 @@ void ImGui::EndDragDropTarget() // Docking: Settings //----------------------------------------------------------------------------- // TODO: +// Bug: Fix when SelectedTab doesn't exist. // A~ document root node resizing behavior incorrect // A~ document root node retrieval of ID ? // A~ Unreal style document system (requires low-level controls of dockspace serialization fork/copy/delete) @@ -10228,8 +10229,12 @@ static void ImGui::DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window) if (node->Windows.Size == 2 && !node->IsExplicitRoot) node->InitFromFirstWindow = true; - if (node->TabBar) - node->TabBar->NextSelectedTabId = window->ID; + if (node->TabBar == NULL) + { + node->TabBar = IM_NEW(ImGuiTabBar)(); + node->TabBar->SelectedTabId = node->TabBar->NextSelectedTabId = node->SelectedTabID; + } + TabBarAddTab(node->TabBar, window); DockNodeUpdateVisibleFlag(node); @@ -10252,18 +10257,29 @@ static void ImGui::DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window UpdateWindowParentAndRootLinks(window, window->Flags & ~ImGuiWindowFlags_ChildWindow, NULL); // Update immediately MarkIniSettingsDirty(); + // Remove window bool erased = false; for (int n = 0; n < node->Windows.Size; n++) if (node->Windows[n] == window) { node->Windows.erase(node->Windows.Data + n); - if (node->TabBar) - TabBarRemoveTab(node->TabBar, window->ID); erased = true; break; } IM_ASSERT(erased); + // Remove tab and possibly tab bar + if (node->TabBar) + { + TabBarRemoveTab(node->TabBar, window->ID); + const int tab_count_threshold_for_tab_bar = node->IsDocumentRoot ? 1 : 2; + if (node->Windows.Size < tab_count_threshold_for_tab_bar) + { + IM_DELETE(node->TabBar); + node->TabBar = NULL; + } + } + if (node->Windows.Size == 0 && !node->IsDocumentRoot && window->DockId != node->ID) { // Automatic dock node delete themselves if they are not holding at least one tab @@ -10271,9 +10287,6 @@ static void ImGui::DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window return; } - if (node->TabBar) - TabBarRemoveTab(node->TabBar, window->ID); - if (node->Windows.Size == 1 && !node->IsDocumentRoot && node->HostWindow) { ImGuiWindow* remaining_window = node->Windows[0]; @@ -10287,13 +10300,6 @@ static void ImGui::DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window remaining_window->Collapsed = node->HostWindow->Collapsed; } - int tab_count_threshold_for_tab_bar = node->IsDocumentRoot ? 1 : 2; - if (node->Windows.Size < tab_count_threshold_for_tab_bar && node->TabBar) - { - IM_DELETE(node->TabBar); - node->TabBar = NULL; - } - // Update visibility immediately is required so the DockNodeUpdateRemoveInactiveChilds() processing can reflect changes up the tree DockNodeUpdateVisibleFlag(node); } @@ -10542,6 +10548,8 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) } // Update active node (the one whose title bar is highlight) within a node tree + if (node->IsSplitNode()) + IM_ASSERT(node->TabBar == NULL); if (!node->IsSplitNode()) node->LastFocusedNodeID = node->ID; // This also ensure on our creation frame we will receive the title screen highlight else if (g.NavWindow && g.NavWindow->RootWindowDockStop->DockNode && g.NavWindow->RootWindowDockStop->ParentWindow == host_window) @@ -10624,7 +10632,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w PushID(node->ID); ImGuiTabBar* tab_bar = node->TabBar; - bool tab_bar_is_new = (tab_bar == NULL); + bool tab_bar_is_recreated = (tab_bar == NULL); // Tab bar are automatically destroyed when a node gets hidden if (tab_bar == NULL) tab_bar = node->TabBar = IM_NEW(ImGuiTabBar)(); @@ -10692,7 +10700,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w ImQsort(tab_bar->Tabs.Data + tabs_count_old, tab_bar->Tabs.Size - tabs_count_old, sizeof(ImGuiTabItem), TabItemComparerByDockOrder); // Selected newly added tabs, or persistent tab ID if the tab bar was just recreated - if (tab_bar_is_new && TabBarFindTabByID(tab_bar, node->SelectedTabID) != NULL) + if (tab_bar_is_recreated && TabBarFindTabByID(tab_bar, node->SelectedTabID) != NULL) tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = node->SelectedTabID; else if (tab_bar->Tabs.Size > tabs_count_old) tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = tab_bar->Tabs.back().Window->ID; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index a84ea68782cb..c1ae9208e7f5 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6110,10 +6110,11 @@ void ImGui::TabBarRemoveTab(ImGuiTabBar* tab_bar, ImGuiID tab_id) tab_bar->Tabs.erase(tab); if (tab_bar->VisibleTabId == tab_id) { tab_bar->VisibleTabId = 0; } if (tab_bar->WantFocusTabId == tab_id) { tab_bar->WantFocusTabId = 0; } - if (tab_bar->SelectedTabId == tab_id) { tab_bar->SelectedTabId = 0; } + if (tab_bar->SelectedTabId == tab_id) { tab_bar->SelectedTabId = 0; } if (tab_bar->NextSelectedTabId == tab_id) { tab_bar->NextSelectedTabId = 0; } } +// Called on manual closure attempt void ImGui::TabBarCloseTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab) { if ((tab_bar->VisibleTabId == tab->ID) && !(tab->Flags & ImGuiTabItemFlags_UnsavedDocument)) From 95d49c3b985165fd03204acb4b507fda837a3877 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 19 Sep 2018 11:25:44 +0200 Subject: [PATCH 260/828] Docking: Internals renaming LastExplicitSize to SizeRef (invalidate .ini data) + todo --- imgui.cpp | 59 +++++++++++++++++++++++++----------------------- imgui_internal.h | 5 ++-- 2 files changed, 34 insertions(+), 30 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 36cddee7d5f3..936b56531249 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -9523,9 +9523,12 @@ void ImGui::EndDragDropTarget() // Docking: Settings //----------------------------------------------------------------------------- // TODO: -// Bug: Fix when SelectedTab doesn't exist. // A~ document root node resizing behavior incorrect // A~ document root node retrieval of ID ? +// A- fix when SelectedTab doesn't exist (easy to repro/fix with .ini mod, but would be nice to also find real repro) +// B- full rebuild make currently highlight title bar flicker (didn't use to) +// B- full rebuild loses viewport of floating dock nodes +// B- dock node inside its own viewports creates 1 temporary viewport per window on startup before ditching them // A~ Unreal style document system (requires low-level controls of dockspace serialization fork/copy/delete) // B- resize sibling locking behavior may be less desirable if we merged same-axis sibling in a same node level? // A- single visible node part of a hidden split hierarchy (OnlyNodeWithWindows) should show a normal title bar (not a tab bar) @@ -9608,7 +9611,7 @@ struct ImGuiDockNodeSettings char IsDocumentRoot; ImVec2ih Pos; ImVec2ih Size; - ImVec2ih LastExplicitSize; + ImVec2ih SizeRef; ImGuiDockNodeSettings() { ID = ParentID = SelectedTabID = 0; SplitAxis = ImGuiAxis_None; Depth = 0; IsExplicitRoot = IsDocumentRoot = 0; } }; @@ -9753,7 +9756,7 @@ void ImGui::DockContextNewFrameUpdateUndocking(ImGuiContext* ctx) #if 0 if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_C))) - ctx->WantFullRebuild = true; + dc->WantFullRebuild = true; #endif if (dc->WantFullRebuild) { @@ -9967,7 +9970,7 @@ static void ImGui::DockContextBuildNodesFromSettings(ImGuiContext* ctx, ImGuiDoc node->ParentNode = node_settings->ParentID ? DockContextFindNodeByID(ctx, node_settings->ParentID) : NULL; node->Pos = ImVec2(node_settings->Pos.x, node_settings->Pos.y); node->Size = ImVec2(node_settings->Size.x, node_settings->Size.y); - node->LastExplicitSize = ImVec2(node_settings->LastExplicitSize.x, node_settings->LastExplicitSize.y); + node->SizeRef = ImVec2(node_settings->SizeRef.x, node_settings->SizeRef.y); if (node->ParentNode && node->ParentNode->ChildNodes[0] == NULL) node->ParentNode->ChildNodes[0] = node; else if (node->ParentNode && node->ParentNode->ChildNodes[1] == NULL) @@ -10314,7 +10317,7 @@ static void ImGui::DockNodeMoveChildNodes(ImGuiDockNode* dst_node, ImGuiDockNode if (dst_node->ChildNodes[1]) dst_node->ChildNodes[1]->ParentNode = dst_node; dst_node->SplitAxis = src_node->SplitAxis; - dst_node->LastExplicitSize = src_node->LastExplicitSize; + dst_node->SizeRef = src_node->SizeRef; src_node->ChildNodes[0] = src_node->ChildNodes[1] = NULL; } @@ -11115,9 +11118,9 @@ void ImGui::DockNodeTreeSplit(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImG parent_node->VisibleWindow = NULL; float size_avail = (parent_node->Size[split_axis] - IMGUI_DOCK_SPLITTER_SIZE); - child_0->LastExplicitSize = child_1->LastExplicitSize = parent_node->Size; - child_0->LastExplicitSize[split_axis] = ImMax(g.Style.WindowMinSize[split_axis], ImFloor(size_avail * split_ratio)); - child_1->LastExplicitSize[split_axis] = ImMax(g.Style.WindowMinSize[split_axis], ImFloor(size_avail - child_0->LastExplicitSize[split_axis])); + child_0->SizeRef = child_1->SizeRef = parent_node->Size; + child_0->SizeRef[split_axis] = ImMax(g.Style.WindowMinSize[split_axis], ImFloor(size_avail * split_ratio)); + child_1->SizeRef[split_axis] = ImMax(g.Style.WindowMinSize[split_axis], ImFloor(size_avail - child_0->SizeRef[split_axis])); DockNodeMoveWindows(parent_node->ChildNodes[split_inheritor_child_idx], parent_node); DockNodeTreeUpdatePosSize(parent_node, parent_node->Pos, parent_node->Size); @@ -11135,7 +11138,7 @@ void ImGui::DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImG IM_ASSERT(parent_node->Windows.Size == 0); } - ImVec2 backup_last_explicit_size = parent_node->LastExplicitSize; + ImVec2 backup_last_explicit_size = parent_node->SizeRef; DockNodeMoveChildNodes(parent_node, merge_lead_child); DockNodeMoveWindows(parent_node, child_0); // Generally only 1 of the 2 child node will have windows DockNodeMoveWindows(parent_node, child_1); @@ -11143,7 +11146,7 @@ void ImGui::DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImG parent_node->InitFromFirstWindow = false; parent_node->VisibleWindow = merge_lead_child->VisibleWindow; parent_node->IsDocumentRoot = child_0->IsDocumentRoot || child_1->IsDocumentRoot; - parent_node->LastExplicitSize = backup_last_explicit_size; + parent_node->SizeRef = backup_last_explicit_size; ctx->DockContext->Nodes.SetVoidPtr(child_0->ID, NULL); ctx->DockContext->Nodes.SetVoidPtr(child_1->ID, NULL); @@ -11179,32 +11182,32 @@ void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 si if (child_0->WantLockSizeOnce) { child_0->WantLockSizeOnce = false; - child_0_size[axis] = child_0->LastExplicitSize[axis] = child_0->Size[axis]; - child_1_size[axis] = child_1->LastExplicitSize[axis] = (size_avail - child_0_size[axis]); + child_0_size[axis] = child_0->SizeRef[axis] = child_0->Size[axis]; + child_1_size[axis] = child_1->SizeRef[axis] = (size_avail - child_0_size[axis]); } else if (child_1->WantLockSizeOnce) { child_1->WantLockSizeOnce = false; - child_1_size[axis] = child_1->LastExplicitSize[axis] = child_1->Size[axis]; - child_0_size[axis] = child_0->LastExplicitSize[axis] = (size_avail - child_1_size[axis]); + child_1_size[axis] = child_1->SizeRef[axis] = child_1->Size[axis]; + child_0_size[axis] = child_0->SizeRef[axis] = (size_avail - child_1_size[axis]); } // 3) If one window is the document root (~ use remaining space, should be made explicit!), use explicit size from the other, and remainder for the document root - else if (child_1->IsDocumentRoot && child_0->LastExplicitSize[axis] != 0.0f) + else if (child_1->IsDocumentRoot && child_0->SizeRef[axis] != 0.0f) { - child_0_size[axis] = ImMin(size_avail - size_min_each, child_0->LastExplicitSize[axis]); + child_0_size[axis] = ImMin(size_avail - size_min_each, child_0->SizeRef[axis]); child_1_size[axis] = (size_avail - child_0_size[axis]); } - else if (child_0->IsDocumentRoot && child_1->LastExplicitSize[axis] != 0.0f) + else if (child_0->IsDocumentRoot && child_1->SizeRef[axis] != 0.0f) { - child_1_size[axis] = ImMin(size_avail - size_min_each, child_1->LastExplicitSize[axis]); + child_1_size[axis] = ImMin(size_avail - size_min_each, child_1->SizeRef[axis]); child_0_size[axis] = (size_avail - child_1_size[axis]); } else { - // 4) Otherwise distribute according to the relative ratio of each LastExplicitSize value - float split_ratio = child_0->LastExplicitSize[axis] / (child_0->LastExplicitSize[axis] + child_1->LastExplicitSize[axis]); + // 4) Otherwise distribute according to the relative ratio of each SizeRef value + float split_ratio = child_0->SizeRef[axis] / (child_0->SizeRef[axis] + child_1->SizeRef[axis]); child_0_size[axis] = ImMax(size_min_each, ImFloor(size_avail * split_ratio + 0.5F)); child_1_size[axis] = (size_avail - child_0_size[axis]); } @@ -11299,9 +11302,9 @@ void ImGui::DockNodeTreeUpdateSplitter(ImGuiDockNode* node) { if (touching_nodes[0].Size > 0 && touching_nodes[1].Size > 0) { - child_0->Size[axis] = child_0->LastExplicitSize[axis] = w1; + child_0->Size[axis] = child_0->SizeRef[axis] = w1; child_1->Pos[axis] -= w2 - child_1->Size[axis]; - child_1->Size[axis] = child_1->LastExplicitSize[axis] = w2; + child_1->Size[axis] = child_1->SizeRef[axis] = w2; // Lock the size of every node that is a sibling of the node we are touching // This might be less desirable if we can merge sibling of a same axis into the same parental level. @@ -11420,7 +11423,7 @@ void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockSpaceFlags do size.y = ImMax(content_avail.y + size.y, 4.0f); node->Pos = window->DC.CursorPos; - node->Size = node->LastExplicitSize = size; + node->Size = node->SizeRef = size; SetNextWindowPos(node->Pos); SetNextWindowSize(node->Size); g.NextWindowData.PosUndock = false; @@ -11756,7 +11759,7 @@ static void ImGui::DockSettingsHandler_ReadLine(ImGuiContext* ctx, ImGuiSettings } else { - if (sscanf(line, " LastExplicitSize=%i,%i%n", &x, &y, &r) == 2) { line += r; node.LastExplicitSize = ImVec2ih((short)x, (short)y); } + if (sscanf(line, " SizeRef=%i,%i%n", &x, &y, &r) == 2) { line += r; node.SizeRef = ImVec2ih((short)x, (short)y); } } if (sscanf(line, " Split=%c%n", &c, &r) == 1) { line += r; if (c == 'X') node.SplitAxis = ImGuiAxis_X; else if (c == 'Y') node.SplitAxis = ImGuiAxis_Y; } if (sscanf(line, " ExplicitRoot=%d%n", &x, &r) == 1) { line += r; node.IsExplicitRoot = (x != 0); } @@ -11784,7 +11787,7 @@ static void DockSettingsHandler_DockNodeToSettings(ImGuiDockContext* dc, ImGuiDo node_settings.IsDocumentRoot = (char)node->IsDocumentRoot; node_settings.Pos = ImVec2ih((short)node->Pos.x, (short)node->Pos.y); node_settings.Size = ImVec2ih((short)node->Size.x, (short)node->Size.y); - node_settings.LastExplicitSize = ImVec2ih((short)node->LastExplicitSize.x, (short)node->LastExplicitSize.y); + node_settings.SizeRef = ImVec2ih((short)node->SizeRef.x, (short)node->SizeRef.y); dc->SettingsNodes.push_back(node_settings); if (node->ChildNodes[0]) DockSettingsHandler_DockNodeToSettings(dc, node->ChildNodes[0], depth + 1); @@ -11816,7 +11819,7 @@ static void ImGui::DockSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettings buf->appendf("%*sDockNode%*s", node_settings->Depth * 2, "", (max_depth - node_settings->Depth) * 2, ""); // Text align nodes to facilitate looking at .ini file buf->appendf(" ID=0x%08X", node_settings->ID); if (node_settings->ParentID) - buf->appendf(" Parent=0x%08X LastExplicitSize=%d,%d", node_settings->ParentID, node_settings->LastExplicitSize.x, node_settings->LastExplicitSize.y); + buf->appendf(" Parent=0x%08X SizeRef=%d,%d", node_settings->ParentID, node_settings->SizeRef.x, node_settings->SizeRef.y); else buf->appendf(" Pos=%d,%d Size=%d,%d", node_settings->Pos.x, node_settings->Pos.y, node_settings->Size.x, node_settings->Size.y); if (node_settings->SplitAxis != ImGuiAxis_None) @@ -12674,7 +12677,7 @@ void ImGui::ShowDockingDebug() IM_ASSERT(node->ChildNodes[1] == NULL || node->ChildNodes[1]->ParentNode == node); ImGui::BulletText("Pos (%.0f,%.0f), Size (%.0f, %.0f), LastExplicit (%.0f, %.0f)", node->Pos.x, node->Pos.y, node->Size.x, node->Size.y, - node->LastExplicitSize.x, node->LastExplicitSize.y); + node->SizeRef.x, node->SizeRef.y); ImGui::BulletText("Flags %02X%s%s%s%s", node->Flags, node->IsExplicitRoot ? ", IsExplicitRoot" : "", node->IsDocumentRoot ? ", IsDocumentRoot" : "", (GImGui->FrameCount - node->LastFrameAlive < 2) ? ", IsAlive" : "", (GImGui->FrameCount - node->LastFrameActive < 2) ? ", IsActive" : ""); @@ -12784,7 +12787,7 @@ void ImGui::ShowDockingDebug() ImDrawList* overlay_draw_list = node->HostWindow ? GetOverlayDrawList(node->HostWindow) : GetOverlayDrawList((ImGuiViewportP*)GetMainViewport()); p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "DockId: %X%s\n", node->ID, node->IsDocumentRoot ? " *DocRoot*" : ""); p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "Size: (%.0f, %.0f)\n", node->Size.x, node->Size.y); - p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "Explicit: (%.0f, %.0f)\n", node->LastExplicitSize.x, node->LastExplicitSize.y); + p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "SizeRef: (%.0f, %.0f)\n", node->SizeRef.x, node->SizeRef.y); int depth = DockNodeGetDepth(node); overlay_draw_list->AddRect(node->Pos + ImVec2(3,3) * (float)depth, node->Pos + node->Size - ImVec2(3,3) * (float)depth, IM_COL32(200, 100, 100, 255)); ImVec2 pos = node->Pos + ImVec2(3,3) * (float)depth; diff --git a/imgui_internal.h b/imgui_internal.h index ef0fee6211ae..0477056f806c 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -748,8 +748,9 @@ struct ImGuiDockNode ImGuiDockNode* ChildNodes[2]; // [Split node only] Child nodes (left/right or top/bottom). Consider switching to an array. ImVector Windows; // Note: unordered list! Iterate TabBar->Tabs for user-order. ImGuiTabBar* TabBar; - ImVec2 Pos, Size; // Current position, size. - ImVec2 LastExplicitSize; // [Split node only] Last explicit size (overridden when using a splitter affecting the node) + ImVec2 Pos; // Current position + ImVec2 Size; // Current size + ImVec2 SizeRef; // [Split node only] Last explicitly written-to size (overridden when using a splitter affecting the node), used to calculate Size. int SplitAxis; // [Split node only] Split axis (X or Y) ImGuiWindow* HostWindow; From 82978fc88fe675c3504f731c86b801e43ef735f5 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 19 Sep 2018 18:45:18 +0200 Subject: [PATCH 261/828] Docking: Fix of title bar flicker during rebuild. Fixed dragging of DockNode always triggering BeginAsDockableDragDropSource() when io.ConfigDockingWithKeyMod is false. --- imgui.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 936b56531249..75469f7a26dc 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -9526,7 +9526,6 @@ void ImGui::EndDragDropTarget() // A~ document root node resizing behavior incorrect // A~ document root node retrieval of ID ? // A- fix when SelectedTab doesn't exist (easy to repro/fix with .ini mod, but would be nice to also find real repro) -// B- full rebuild make currently highlight title bar flicker (didn't use to) // B- full rebuild loses viewport of floating dock nodes // B- dock node inside its own viewports creates 1 temporary viewport per window on startup before ditching them // A~ Unreal style document system (requires low-level controls of dockspace serialization fork/copy/delete) @@ -9539,7 +9538,7 @@ void ImGui::EndDragDropTarget() // B- fix/disable auto-resize grip on split host nodes (~#2) // B- SetNextWindowFocus() doesn't seem to apply if the window is hidden this frame, need repro (#4) // B- drag from collapse button should drag entire dock node -// B- implicit per-viewport dockspace to dock to +// B- implicit, invisible per-viewport dockspace to dock to // B- resizing a dock tree small currently has glitches (overlapping collapse and close button, etc.) // B- tab bar: appearing on first frame with a dumb layout would do less harm that not appearing? (when behind dynamic branch) or store titles + render in EndTabBar() // B- tab bar: make selected tab always shows its full title? @@ -9661,6 +9660,7 @@ namespace ImGui static void DockNodeCalcSplitRects(ImVec2& pos_old, ImVec2& size_old, ImVec2& pos_new, ImVec2& size_new, ImGuiDir dir, ImVec2 size_new_desired); static bool DockNodeCalcDropRects(const ImRect& parent, ImGuiDir dir, ImRect& out_draw, bool outer_docking); static ImGuiDockNode* DockNodeGetRootNode(ImGuiDockNode* node) { while (node->ParentNode) node = node->ParentNode; return node; } + static const char* DockNodeGetHostWindowTitle(ImGuiDockNode* node, char* buf, int buf_size) { ImFormatString(buf, buf_size, "##DockNode_%02X", node->ID); return buf; } static int DockNodeGetDepth(const ImGuiDockNode* node) { int depth = 0; while (node->ParentNode) { node = node->ParentNode; depth++; } return depth; } static int DockNodeGetTabOrder(ImGuiWindow* window); @@ -9979,6 +9979,11 @@ static void ImGui::DockContextBuildNodesFromSettings(ImGuiContext* ctx, ImGuiDoc node->SplitAxis = node_settings->SplitAxis; node->IsExplicitRoot = node_settings->IsExplicitRoot != 0; node->IsDocumentRoot = node_settings->IsDocumentRoot != 0; + + // Bind host window immediately if it already exist (in case of a rebuild) + // This is useful as the RootWindowForTitleBarHighlight links necessary to highlight the currently focused node requires node->HostWindow to be set. + char host_window_title[32]; + node->HostWindow = FindWindowByName(DockNodeGetHostWindowTitle(node, host_window_title, IM_ARRAYSIZE(host_window_title))); } } @@ -10527,7 +10532,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) // Begin into the host window char window_label[20]; - ImFormatString(window_label, IM_ARRAYSIZE(window_label), "##DockNode_%02X", node->ID); + DockNodeGetHostWindowTitle(node, window_label, IM_ARRAYSIZE(window_label)); ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_DockNodeHost; window_flags |= ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus; window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse; @@ -10639,7 +10644,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w if (tab_bar == NULL) tab_bar = node->TabBar = IM_NEW(ImGuiTabBar)(); - // Decide if we should use focused color + // Decide if we should use a focused title bar color bool is_focused = false; ImGuiDockNode* root_node = DockNodeGetRootNode(node); if (g.NavWindowingTarget) @@ -11628,7 +11633,7 @@ void ImGui::BeginAsDockableDragDropSource(ImGuiWindow* window) window->DC.LastItemId = window->MoveId; window = window->RootWindow; IM_ASSERT((window->Flags & ImGuiWindowFlags_NoDocking) == 0); - bool is_drag_docking = (g.IO.ConfigDockingWithKeyMod) || (window->Flags & ImGuiWindowFlags_NoTitleBar) || ImRect(0, 0, window->SizeFull.x, window->TitleBarHeight()).Contains(g.ActiveIdClickOffset); + bool is_drag_docking = (g.IO.ConfigDockingWithKeyMod) || ImRect(0, 0, window->SizeFull.x, GetFrameHeight()).Contains(g.ActiveIdClickOffset); if (is_drag_docking && BeginDragDropSource(ImGuiDragDropFlags_SourceNoPreviewTooltip | ImGuiDragDropFlags_SourceNoHoldToOpenOthers | ImGuiDragDropFlags_SourceAutoExpirePayload)) { SetDragDropPayload(IMGUI_PAYLOAD_TYPE_WINDOW, &window, sizeof(window)); From e647f89c332da62d6a4f285f306b9079fc3c7253 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 19 Sep 2018 22:38:40 +0200 Subject: [PATCH 262/828] Docking: Added undocking of whole dock node by dragging from the Collapse button. Super useful and works great! --- imgui.cpp | 85 ++++++++++++++++++++++++++++++++++++----------- imgui_internal.h | 7 ++-- imgui_widgets.cpp | 28 ++++++++++++---- 3 files changed, 91 insertions(+), 29 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 75469f7a26dc..5409d56d9166 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5438,7 +5438,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Collapse button if (!(flags & ImGuiWindowFlags_NoCollapse)) - if (CollapseButton(window->GetID("#COLLAPSE"), window->Pos)) + if (CollapseButton(window->GetID("#COLLAPSE"), window->Pos, NULL)) window->WantCollapseToggle = true; // Defer collapsing to next frame as we are too far in the Begin() function // Close button @@ -9537,7 +9537,6 @@ void ImGui::EndDragDropTarget() // B- inconsistent clipping/border 1-pixel issue (#2) // B- fix/disable auto-resize grip on split host nodes (~#2) // B- SetNextWindowFocus() doesn't seem to apply if the window is hidden this frame, need repro (#4) -// B- drag from collapse button should drag entire dock node // B- implicit, invisible per-viewport dockspace to dock to // B- resizing a dock tree small currently has glitches (overlapping collapse and close button, etc.) // B- tab bar: appearing on first frame with a dumb layout would do less harm that not appearing? (when behind dynamic branch) or store titles + render in EndTabBar() @@ -9570,13 +9569,14 @@ struct ImGuiDockRequest ImGuiDir DockSplitDir; float DockSplitRatio; bool DockSplitOuter; - ImGuiWindow* UndockTarget; + ImGuiWindow* UndockTargetWindow; + ImGuiDockNode* UndockTargetNode; ImGuiDockRequest() { Type = ImGuiDockRequestType_None; - DockTargetWindow = DockPayload = UndockTarget = NULL; - DockTargetNode = NULL; + DockTargetWindow = DockPayload = UndockTargetWindow = NULL; + DockTargetNode = UndockTargetNode = NULL; DockSplitDir = ImGuiDir_None; DockSplitRatio = 0.5f; DockSplitOuter = false; @@ -9636,7 +9636,8 @@ namespace ImGui static void DockContextQueueDock(ImGuiContext* ctx, ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, float split_ratio, bool split_outer); static void DockContextQueueNotifyRemovedNode(ImGuiContext* ctx, ImGuiDockNode* node); static void DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req); - static void DockContextProcessUndock(ImGuiContext* ctx, ImGuiWindow* window); + static void DockContextProcessUndockWindow(ImGuiContext* ctx, ImGuiWindow* window); + static void DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node); static void DockContextGcUnusedSettingsNodes(ImGuiContext* ctx); static void DockContextClearNodes(ImGuiContext* ctx, ImGuiID root_id, bool clear_persistent_docking_references); // Set root_id==0 to clear all static void DockContextBuildNodesFromSettings(ImGuiContext* ctx, ImGuiDockNodeSettings* node_settings_array, int node_settings_count); @@ -9653,13 +9654,13 @@ namespace ImGui static void DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* node); static void DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_window); static void DockNodeUpdateVisibleFlag(ImGuiDockNode* node); + static void DockNodeStartMouseMovingWindow(ImGuiDockNode* node, ImGuiWindow* window); static bool DockNodeIsDropAllowed(ImGuiWindow* host_window, ImGuiWindow* payload_window); static bool DockNodePreviewDockCalc(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* payload_window, ImGuiDockPreviewData* preview_data, bool is_explicit_target, bool is_outer_docking); static void DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* payload_window, const ImGuiDockPreviewData* preview_data); static ImRect DockNodeCalcTabBarRect(const ImGuiDockNode* node); static void DockNodeCalcSplitRects(ImVec2& pos_old, ImVec2& size_old, ImVec2& pos_new, ImVec2& size_new, ImGuiDir dir, ImVec2 size_new_desired); static bool DockNodeCalcDropRects(const ImRect& parent, ImGuiDir dir, ImRect& out_draw, bool outer_docking); - static ImGuiDockNode* DockNodeGetRootNode(ImGuiDockNode* node) { while (node->ParentNode) node = node->ParentNode; return node; } static const char* DockNodeGetHostWindowTitle(ImGuiDockNode* node, char* buf, int buf_size) { ImFormatString(buf, buf_size, "##DockNode_%02X", node->ID); return buf; } static int DockNodeGetDepth(const ImGuiDockNode* node) { int depth = 0; while (node->ParentNode) { node = node->ParentNode; depth++; } return depth; } static int DockNodeGetTabOrder(ImGuiWindow* window); @@ -9766,8 +9767,13 @@ void ImGui::DockContextNewFrameUpdateUndocking(ImGuiContext* ctx) // Process Undocking requests (we need to process them _before_ the UpdateMouseMovingWindow call in NewFrame) for (int n = 0; n < dc->Requests.Size; n++) - if (dc->Requests[n].Type == ImGuiDockRequestType_Undock) - DockContextProcessUndock(ctx, dc->Requests[n].UndockTarget); + { + ImGuiDockRequest* req = &dc->Requests[n]; + if (req->Type == ImGuiDockRequestType_Undock && req->UndockTargetWindow) + DockContextProcessUndockWindow(ctx, req->UndockTargetWindow); + else if (req->Type == ImGuiDockRequestType_Undock && req->UndockTargetNode) + DockContextProcessUndockNode(ctx, req->UndockTargetNode); + } } // Docking context update function, called by NewFrame() @@ -9912,7 +9918,7 @@ void ImGui::DockBuilderRemoveNodeDockedWindows(ImGuiContext* ctx, ImGuiID root_i if (want_removal) { ImGuiID backup_dock_id = window->DockId; - DockContextProcessUndock(ctx, window); + DockContextProcessUndockWindow(ctx, window); if (!clear_persistent_docking_references) window->DockId = backup_dock_id; } @@ -10023,11 +10029,19 @@ void ImGui::DockContextQueueDock(ImGuiContext* ctx, ImGuiWindow* target, ImGuiDo ctx->DockContext->Requests.push_back(req); } -void ImGui::DockContextQueueUndock(ImGuiContext* ctx, ImGuiWindow* window) +void ImGui::DockContextQueueUndockWindow(ImGuiContext* ctx, ImGuiWindow* window) { ImGuiDockRequest req; req.Type = ImGuiDockRequestType_Undock; - req.UndockTarget = window; + req.UndockTargetWindow = window; + ctx->DockContext->Requests.push_back(req); +} + +void ImGui::DockContextQueueUndockNode(ImGuiContext* ctx, ImGuiDockNode* node) +{ + ImGuiDockRequest req; + req.Type = ImGuiDockRequestType_Undock; + req.UndockTargetNode = node; ctx->DockContext->Requests.push_back(req); } @@ -10118,7 +10132,7 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) if (payload_node != NULL) { // Transfer full payload node (with 1+ child windows or child nodes) - // FIXME-DOCK: Transition persistent DockId for all non-active windows? + // FIXME-DOCK: Transition persistent DockId for all non-active windows if (payload_node->IsSplitNode()) { if (target_node->Windows.Size > 0) @@ -10157,7 +10171,7 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) target_node->TabBar->NextSelectedTabId = next_selected_id; } -void ImGui::DockContextProcessUndock(ImGuiContext* ctx, ImGuiWindow* window) +void ImGui::DockContextProcessUndockWindow(ImGuiContext* ctx, ImGuiWindow* window) { (void)ctx; if (window->DockNode) @@ -10169,6 +10183,22 @@ void ImGui::DockContextProcessUndock(ImGuiContext* ctx, ImGuiWindow* window) window->DockTabIsVisible = false; } +// Extract a node out by creating a new one. +// In the case of a root node, a node will have to stay in place, otherwise the node will be hidden (and GC-ed later) +// (we could handle both cases differently with little benefit) +void ImGui::DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node) +{ + // FIXME-DOCK: Transition persistent DockId for all non-active windows + (void)ctx; + IM_ASSERT(!node->IsSplitNode()); + IM_ASSERT(node->Windows.Size >= 1); + ImGuiDockNode* new_node = DockContextAddNode(ctx, (ImGuiID)-1); + DockNodeMoveWindows(new_node, node); + for (int n = 0; n < new_node->Windows.Size; n++) + UpdateWindowParentAndRootLinks(new_node->Windows[n], new_node->Windows[n]->Flags, NULL); + new_node->WantMouseMove = true; +} + //----------------------------------------------------------------------------- // Docking: ImGuiDockNode //----------------------------------------------------------------------------- @@ -10188,7 +10218,7 @@ ImGuiDockNode::ImGuiDockNode(ImGuiID id) SelectedTabID = 0; WantCloseTabID = 0; IsVisible = true; - InitFromFirstWindow = IsExplicitRoot = IsDocumentRoot = HasCloseButton = HasCollapseButton = WantCloseAll = WantLockSizeOnce = false; + InitFromFirstWindow = IsExplicitRoot = IsDocumentRoot = HasCloseButton = HasCollapseButton = WantCloseAll = WantLockSizeOnce = WantMouseMove = false; } ImGuiDockNode::~ImGuiDockNode() @@ -10447,6 +10477,16 @@ static void ImGui::DockNodeUpdateVisibleFlag(ImGuiDockNode* node) node->IsVisible = is_visible; } +static void ImGui::DockNodeStartMouseMovingWindow(ImGuiDockNode* node, ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(node->WantMouseMove == true); + ImVec2 backup_active_click_offset = g.ActiveIdClickOffset; + StartMouseMovingWindow(window); + node->WantMouseMove = false; + g.ActiveIdClickOffset = backup_active_click_offset; +} + static void ImGui::DockNodeUpdate(ImGuiDockNode* node) { ImGuiContext& g = *GImGui; @@ -10488,6 +10528,9 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) node->WantCloseTabID = 0; node->HasCloseButton = node->HasCollapseButton = false; node->LastFrameActive = g.FrameCount; + + if (node->WantMouseMove) + DockNodeStartMouseMovingWindow(node, node->Windows[0]); return; } @@ -10553,6 +10596,8 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) node->HostWindow = host_window = node->ParentNode->HostWindow; } node->InitFromFirstWindow = false; + if (node->WantMouseMove && node->HostWindow) + DockNodeStartMouseMovingWindow(node, node->HostWindow); } // Update active node (the one whose title bar is highlight) within a node tree @@ -10681,7 +10726,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w host_window->DrawList->AddLine(title_bar_rect.GetBL(), title_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border), style.WindowBorderSize); // Collapse button - if (CollapseButton(host_window->GetID("#COLLAPSE"), title_bar_rect.Min)) + if (CollapseButton(host_window->GetID("#COLLAPSE"), title_bar_rect.Min, node)) OpenPopup("#TabListMenu"); if (IsItemActive()) focus_tab_id = tab_bar->SelectedTabId; @@ -11524,7 +11569,7 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) g.NextWindowData.PosUndock = false; if (want_undock) { - DockContextProcessUndock(ctx, window); + DockContextProcessUndockWindow(ctx, window); return; } @@ -11538,7 +11583,7 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) if (dock_node->IsSplitNode()) { - DockContextProcessUndock(ctx, window); + DockContextProcessUndockWindow(ctx, window); return; } @@ -11560,7 +11605,7 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) ImGuiDockNode* root_node = DockNodeGetRootNode(dock_node); if (root_node->LastFrameAlive < g.FrameCount) { - DockContextProcessUndock(ctx, window); + DockContextProcessUndockWindow(ctx, window); } else { @@ -11573,7 +11618,7 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) // Undock if we are submitted earlier than the host window if (dock_node->HostWindow && window->BeginOrderWithinContext < dock_node->HostWindow->BeginOrderWithinContext) { - DockContextProcessUndock(ctx, window); + DockContextProcessUndockWindow(ctx, window); return; } diff --git a/imgui_internal.h b/imgui_internal.h index 0477056f806c..03d9011f72d9 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -769,6 +769,7 @@ struct ImGuiDockNode bool HasCollapseButton :1; bool WantCloseAll :1; // Set when closing all tabs at once. bool WantLockSizeOnce :1; + bool WantMouseMove :1; // After a node extraction we need to transition toward moving the newly created hode window ImGuiDockNode(ImGuiID id); ~ImGuiDockNode(); @@ -1468,7 +1469,9 @@ namespace ImGui IMGUI_API void DockContextNewFrameUpdateUndocking(ImGuiContext* ctx); IMGUI_API void DockContextNewFrameUpdateDocking(ImGuiContext* ctx); IMGUI_API void DockContextEndFrame(ImGuiContext* ctx); - IMGUI_API void DockContextQueueUndock(ImGuiContext* ctx, ImGuiWindow* window); + IMGUI_API void DockContextQueueUndockWindow(ImGuiContext* ctx, ImGuiWindow* window); + IMGUI_API void DockContextQueueUndockNode(ImGuiContext* ctx, ImGuiDockNode* node); + inline ImGuiDockNode* DockNodeGetRootNode(ImGuiDockNode* node) { while (node->ParentNode) node = node->ParentNode; return node; } IMGUI_API void BeginDocked(ImGuiWindow* window, bool* p_open); IMGUI_API void BeginAsDockableDragDropSource(ImGuiWindow* window); IMGUI_API void BeginAsDockableDragDropTarget(ImGuiWindow* window); @@ -1531,7 +1534,7 @@ namespace ImGui // Widgets IMGUI_API bool ButtonEx(const char* label, const ImVec2& size_arg = ImVec2(0,0), ImGuiButtonFlags flags = 0); IMGUI_API bool CloseButton(ImGuiID id, const ImVec2& pos, float radius); - IMGUI_API bool CollapseButton(ImGuiID id, const ImVec2& pos); + IMGUI_API bool CollapseButton(ImGuiID id, const ImVec2& pos, ImGuiDockNode* dock_node); IMGUI_API bool ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size_arg, ImGuiButtonFlags flags); IMGUI_API void Scrollbar(ImGuiLayoutType direction); IMGUI_API void VerticalSeparator(); // Vertical separator, for menu bars (use current line height). Not exposed because it is misleading and it doesn't have an effect on regular layout. diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index c1ae9208e7f5..3a1415c59b07 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -669,7 +669,7 @@ bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos, float radius) return pressed; } -bool ImGui::CollapseButton(ImGuiID id, const ImVec2& pos) +bool ImGui::CollapseButton(ImGuiID id, const ImVec2& pos, ImGuiDockNode* dock_node) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; @@ -679,20 +679,34 @@ bool ImGui::CollapseButton(ImGuiID id, const ImVec2& pos) bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_None); - bool is_dock_menu = (window->DockNodeAsHost && !window->Collapsed); - ImVec2 off = is_dock_menu ? ImVec2((float)(int)(-g.Style.ItemInnerSpacing.x * 0.5f) + 0.5f, 0.0f) : ImVec2(0.0f, 0.0f); + //bool is_dock_menu = (window->DockNodeAsHost && !window->Collapsed); + ImVec2 off = dock_node ? ImVec2((float)(int)(-g.Style.ItemInnerSpacing.x * 0.5f) + 0.5f, 0.0f) : ImVec2(0.0f, 0.0f); ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); if (hovered || held) window->DrawList->AddCircleFilled(bb.GetCenter() + off + ImVec2(0,-0.5f), g.FontSize * 0.5f + 1.0f, col, 9); - if (is_dock_menu) + if (dock_node) RenderArrowDockMenu(window->DrawList, bb.Min + g.Style.FramePadding, g.FontSize, GetColorU32(ImGuiCol_Text)); else RenderArrow(bb.Min + g.Style.FramePadding, window->Collapsed ? ImGuiDir_Right : ImGuiDir_Down, 1.0f); // Switch to moving the window after mouse is moved beyond the initial drag threshold - if (IsItemActive() && IsMouseDragging()) - StartMouseMovingWindow(window); + if (IsItemActive() && IsMouseDragging(0)) + { + if (dock_node != NULL && DockNodeGetRootNode(dock_node)->OnlyNodeWithWindows != dock_node) + { + float threshold_base = g.FontSize; + float threshold_x = (threshold_base * 2.2f); + float threshold_y = (threshold_base * 1.5f); + IM_ASSERT(window->DockNodeAsHost != NULL); + if (g.IO.MouseDragMaxDistanceAbs[0].x > threshold_x || g.IO.MouseDragMaxDistanceAbs[0].y > threshold_y) + DockContextQueueUndockNode(&g, dock_node); + } + else + { + StartMouseMovingWindow(window); + } + } return pressed; } @@ -6456,7 +6470,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, // Undock if (undocking_tab && g.ActiveId == id && IsMouseDragging()) { - DockContextQueueUndock(&g, docked_window); + DockContextQueueUndockWindow(&g, docked_window); g.MovingWindow = docked_window; g.ActiveId = g.MovingWindow->MoveId; g.ActiveIdClickOffset -= g.MovingWindow->Pos - bb.Min; From 3e47978a80c16672957a52a354a337ccb40a7dff Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 20 Sep 2018 09:25:50 +0200 Subject: [PATCH 263/828] Docking: Renamed ImGuiDockSpaceFlags to ImGuiDockNodeFlags. Clarified in comments/demos that DockSpace creates a Node. Renamed IsExplicitRoot to IsDockSpace. Assert against explicitly calling DockSpace twice in a frame. --- imgui.cpp | 93 +++++++++++++++++++++++++++--------------------- imgui.h | 15 ++++---- imgui_demo.cpp | 12 +++---- imgui_internal.h | 8 ++--- 4 files changed, 71 insertions(+), 57 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 5409d56d9166..eefb6c51df27 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5616,7 +5616,7 @@ void ImGui::End() // Docking: report contents sizes to parent to allow for auto-resize if (window->DockNode && window->DockTabIsVisible) - if (ImGuiWindow* host_window = window->DockNode->HostWindow) // FIXME-DOCKSPACE + if (ImGuiWindow* host_window = window->DockNode->HostWindow) // FIXME-DOCK host_window->DC.CursorMaxPos = window->DC.CursorMaxPos + window->WindowPadding - host_window->WindowPadding; // Pop from window stack @@ -9517,7 +9517,7 @@ void ImGui::EndDragDropTarget() // Docking: ImGuiDockContext Docking/Undocking functions // Docking: ImGuiDockNode // Docking: ImGuiDockNode Tree manipulation functions -// Docking: Public Functions (Dockspace, SetWindowDock) +// Docking: Public Functions (SetWindowDock, DockSpace) // Docking: Public Builder Functions // Docking: Begin/End Functions (called from Begin/End) // Docking: Settings @@ -9606,12 +9606,12 @@ struct ImGuiDockNodeSettings ImGuiID SelectedTabID; char SplitAxis; char Depth; - char IsExplicitRoot; + char IsDockSpace; char IsDocumentRoot; ImVec2ih Pos; ImVec2ih Size; ImVec2ih SizeRef; - ImGuiDockNodeSettings() { ID = ParentID = SelectedTabID = 0; SplitAxis = ImGuiAxis_None; Depth = 0; IsExplicitRoot = IsDocumentRoot = 0; } + ImGuiDockNodeSettings() { ID = ParentID = SelectedTabID = 0; SplitAxis = ImGuiAxis_None; Depth = 0; IsDockSpace = IsDocumentRoot = 0; } }; struct ImGuiDockContext @@ -9794,7 +9794,7 @@ void ImGui::DockContextNewFrameUpdateDocking(ImGuiContext* ctx) // We can have NULL pointers when we delete nodes, but because ID are recycled this should amortize nicely (and our node count will never be very high) for (int n = 0; n < dc->Nodes.Data.Size; n++) if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p) - if (!node->IsExplicitRoot && node->IsRootNode()) + if (!node->IsDockSpace && node->IsRootNode()) DockNodeUpdate(node); } @@ -9983,7 +9983,7 @@ static void ImGui::DockContextBuildNodesFromSettings(ImGuiContext* ctx, ImGuiDoc node->ParentNode->ChildNodes[1] = node; node->SelectedTabID = node_settings->SelectedTabID; node->SplitAxis = node_settings->SplitAxis; - node->IsExplicitRoot = node_settings->IsExplicitRoot != 0; + node->IsDockSpace = node_settings->IsDockSpace != 0; node->IsDocumentRoot = node_settings->IsDocumentRoot != 0; // Bind host window immediately if it already exist (in case of a rebuild) @@ -10218,7 +10218,7 @@ ImGuiDockNode::ImGuiDockNode(ImGuiID id) SelectedTabID = 0; WantCloseTabID = 0; IsVisible = true; - InitFromFirstWindow = IsExplicitRoot = IsDocumentRoot = HasCloseButton = HasCollapseButton = WantCloseAll = WantLockSizeOnce = WantMouseMove = false; + InitFromFirstWindow = IsDockSpace = IsDocumentRoot = HasCloseButton = HasCollapseButton = WantCloseAll = WantLockSizeOnce = WantMouseMove = false; } ImGuiDockNode::~ImGuiDockNode() @@ -10242,7 +10242,7 @@ static void ImGui::DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window) ImGuiContext& g = *GImGui; (void)g; if (window->DockNode) { - // Can overwrite an existing window->DockNode (e.g. pointing to a disabled DockSpace) + // Can overwrite an existing window->DockNode (e.g. pointing to a disabled DockSpace node) IM_ASSERT(window->DockNode->ID != node->ID); DockNodeRemoveWindow(window->DockNode, window, 0); } @@ -10264,7 +10264,7 @@ static void ImGui::DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window) } // When reactivating a node from two loose window, the target window pos/size are authoritative - if (node->Windows.Size == 2 && !node->IsExplicitRoot) + if (node->Windows.Size == 2 && !node->IsDockSpace) node->InitFromFirstWindow = true; if (node->TabBar == NULL) @@ -10470,7 +10470,7 @@ static void ImGui::DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* nod static void ImGui::DockNodeUpdateVisibleFlag(ImGuiDockNode* node) { // Update visibility flag - bool is_visible = (node->ParentNode == 0) ? node->IsExplicitRoot : node->IsDocumentRoot; + bool is_visible = (node->ParentNode == 0) ? node->IsDockSpace : node->IsDocumentRoot; is_visible |= (node->Windows.Size > 0); is_visible |= (node->ChildNodes[0] && node->ChildNodes[0]->IsVisible); is_visible |= (node->ChildNodes[1] && node->ChildNodes[1]->IsVisible); @@ -10498,7 +10498,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) DockNodeUpdateVisibleFlagAndInactiveChilds(node); // Find if there's only a single visible window in the hierarchy (in which case we need to display a regular title bar, FIXME-DOCK: Not done yet!) - if (!node->IsExplicitRoot) + if (!node->IsDockSpace) { int count = 0; ImGuiDockNode* first_node_with_windows = NULL; @@ -10512,7 +10512,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) } // Early out for standalone floating window that are holding on a DockId (with an invisible dock node) - if (node->IsRootNode() && node->Windows.Size == 1 && !node->IsExplicitRoot) + if (node->IsRootNode() && node->Windows.Size == 1 && !node->IsDockSpace) { // Floating window pos/size is authoritative node->Pos = node->Windows[0]->Pos; @@ -10536,9 +10536,9 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) ImGuiWindow* host_window = NULL; bool beginned_into_host_window = false; - if (node->IsExplicitRoot) + if (node->IsDockSpace) { - // [Explicit root node] + // [Explicit root dockspace node] IM_ASSERT(node->HostWindow); node->HasCloseButton = false; node->HasCollapseButton = true; @@ -10674,9 +10674,9 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w node->WantCloseAll = false; node->WantCloseTabID = 0; - // Move ourselves to the Menu layer + Undo SkipItems flag in order to draw over the title bar (even if the window is collapsed) + // Move ourselves to the Menu layer (so we can be accessed by tapping Alt) + undo SkipItems flag in order to draw over the title bar even if the window is collapsed bool backup_skip_item = host_window->SkipItems; - if (!node->IsExplicitRoot) + if (!node->IsDockSpace) { host_window->SkipItems = false; host_window->DC.NavLayerCurrent++; @@ -10761,7 +10761,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w // Begin tab bar ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_Reorderable | ImGuiTabBarFlags_AutoSelectNewTabs | ImGuiTabBarFlags_NoTabListPopupButton;// | ImGuiTabBarFlags_NoTabListScrollingButtons); tab_bar_flags |= ImGuiTabBarFlags_SaveSettings; - tab_bar_flags |= ImGuiTabBarFlags_DockNode | (node->IsExplicitRoot ? ImGuiTabBarFlags_DockNodeExplicitRoot : 0); + tab_bar_flags |= ImGuiTabBarFlags_DockNode | (node->IsDockSpace ? ImGuiTabBarFlags_DockNodeExplicitRoot : 0); if (!host_window->Collapsed && is_focused) tab_bar_flags |= ImGuiTabBarFlags_IsFocused; BeginTabBarEx(node->TabBar, tab_bar_rect, tab_bar_flags, node); @@ -10789,6 +10789,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w if (tab_bar->VisibleTabId == window->ID) node->VisibleWindow = window; + // Store last item data so it can be queried with IsItemXXX functions after the user Begin() call window->DockTabItemStatusFlags = host_window->DC.LastItemStatusFlags; window->DockTabItemRect = host_window->DC.LastItemRect; @@ -10853,7 +10854,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w PopID(); // Restore SkipItems flag - if (!node->IsExplicitRoot) + if (!node->IsDockSpace) { host_window->DC.NavLayerCurrent--; host_window->DC.NavLayerCurrentMask >>= 1; @@ -10863,7 +10864,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w static bool DockNodeIsDropAllowedOne(ImGuiWindow* payload, ImGuiWindow* host_window) { - if ((host_window->Flags & ImGuiWindowFlags_DockNodeHost) && host_window->DockNodeAsHost->IsExplicitRoot && payload->BeginOrderWithinContext < host_window->BeginOrderWithinContext) + if ((host_window->Flags & ImGuiWindowFlags_DockNodeHost) && host_window->DockNodeAsHost->IsDockSpace && payload->BeginOrderWithinContext < host_window->BeginOrderWithinContext) return false; ImGuiID host_user_type_id = host_window->DockNodeAsHost ? host_window->DockNodeAsHost->UserTypeIdFilter : host_window->UserTypeId; @@ -10999,7 +11000,7 @@ static bool ImGui::DockNodePreviewDockCalc(ImGuiWindow* host_window, ImGuiDockNo data->IsCenterAvailable = false; data->IsSidesAvailable = true; - if (host_node && (host_node->Flags & ImGuiDockSpaceFlags_NoSplit)) + if (host_node && (host_node->Flags & ImGuiDockNodeFlags_NoSplit)) data->IsSidesAvailable = false; if (!is_outer_docking && host_node && host_node->ParentNode == NULL && host_node->IsDocumentRoot) data->IsSidesAvailable = false; @@ -11139,7 +11140,7 @@ static void ImGui::DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDock } // Stop after ImGuiDir_None - if (host_node && (host_node->Flags & ImGuiDockSpaceFlags_NoSplit)) + if (host_node && (host_node->Flags & ImGuiDockNodeFlags_NoSplit)) return; } } @@ -11418,7 +11419,7 @@ ImGuiDockNode* ImGui::DockNodeTreeFindNodeByPos(ImGuiDockNode* node, ImVec2 pos) } //----------------------------------------------------------------------------- -// Docking: Public Functions (SetWindowDock, Dockspace) +// Docking: Public Functions (SetWindowDock, DockSpace) //----------------------------------------------------------------------------- void ImGui::SetWindowDock(ImGuiWindow* window, ImGuiID dock_id, ImGuiCond cond) @@ -11436,14 +11437,14 @@ void ImGui::SetWindowDock(ImGuiWindow* window, ImGuiID dock_id, ImGuiCond cond) window->DockId = dock_id; } -void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockSpaceFlags dock_space_flags, ImGuiID user_type_filter) +// Create an explicit dockspace node within an existing window. Also expose dock node flags and creates a DocumentRoot node by default. +// The DocumentRoot node is always displayed even when empty and shrink/extend according to the requested size of its neighbors. +void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags dock_space_flags, ImGuiID user_type_filter) { ImGuiContext* ctx = GImGui; ImGuiContext& g = *ctx; - ImGuiWindow* window = GetCurrentWindow(); - // It is possible that the node has already been claimed by a docked window which appeared before the DockSpace(), so we overwrite IsExplicit again. ImGuiDockNode* node = DockContextFindNodeByID(ctx, id); if (!node) { @@ -11452,14 +11453,19 @@ void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockSpaceFlags do } node->Flags = dock_space_flags; node->UserTypeIdFilter = user_type_filter; - node->IsExplicitRoot = true; // When a Dockspace transitioned form implicit to explicit this may be called a second time + // It is possible that the node has already been claimed by a docked window which appeared before the DockSpace() node, so we overwrite IsDockSpace again. if (node->LastFrameActive == g.FrameCount) + { + IM_ASSERT(node->IsDockSpace == false && "Cannot call DockSpace() twice a frame with the same ID"); + node->IsDockSpace = true; return; + } + node->IsDockSpace = true; // Keep alive mode, this is allow windows docked into this node so stay docked even if they are not visible - if (dock_space_flags & ImGuiDockSpaceFlags_KeepAliveOnly) + if (dock_space_flags & ImGuiDockNodeFlags_KeepAliveOnly) { node->LastFrameAlive = g.FrameCount; return; @@ -11512,11 +11518,18 @@ void ImGui::DockBuilderDockWindow(ImGuiContext*, const char* window_name, ImGuiI { ImGuiID window_id = ImHash(window_name, 0); if (ImGuiWindow* window = FindWindowByID(window_id)) + { + // Apply to created window SetWindowDock(window, node_id, ImGuiCond_Always); - else if (ImGuiWindowSettings* settings = FindWindowSettings(window_id)) - settings->DockId = node_id; - else if (ImGuiWindowSettings* settings = CreateNewWindowSettings(window_name)) + } + else + { + // Apply to settings + ImGuiWindowSettings* settings = FindWindowSettings(window_id); + if (settings == NULL) + settings = CreateNewWindowSettings(window_name); settings->DockId = node_id; + } } ImGuiID ImGui::DockBuilderSplitNode(ImGuiContext* ctx, ImGuiID id, ImGuiDir split_dir, float size_ratio_for_node_at_dir, ImGuiID* out_id_at_dir, ImGuiID* out_id_other) @@ -11597,11 +11610,11 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) g.NextWindowData.PosUndock = false; } - // Undock if our dockspace disappeared - // Note how we are testing for LastFrameAlive and NOT LastFrameActive. A DockSpace can be maintained alive while being inactive with ImGuiDockSpaceFlags_KeepAliveOnly. + // Undock if our dockspace node disappeared + // Note how we are testing for LastFrameAlive and NOT LastFrameActive. A DockSpace node can be maintained alive while being inactive with ImGuiDockNodeFlags_KeepAliveOnly. if (dock_node->LastFrameAlive < g.FrameCount) { - // If the window has been orphaned (lost its dockspace), transition the docknode to an implicit node processed in DockContextUpdateDocking() + // If the window has been orphaned, transition the docknode to an implicit node processed in DockContextUpdateDocking() ImGuiDockNode* root_node = DockNodeGetRootNode(dock_node); if (root_node->LastFrameAlive < g.FrameCount) { @@ -11622,7 +11635,7 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) return; } - // FIXME-DOCKSPACE: replace ->HostWindow NULL compare with something more explicit (~was initially intended as a first frame test) + // FIXME-DOCK: replace ->HostWindow NULL compare with something more explicit (~was initially intended as a first frame test) if (dock_node->HostWindow == NULL) { window->DockTabIsVisible = false; @@ -11639,7 +11652,7 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) window->DockIsActive = true; window->DockTabIsVisible = false; - if (dock_node->Flags & ImGuiDockSpaceFlags_KeepAliveOnly) + if (dock_node->Flags & ImGuiDockNodeFlags_KeepAliveOnly) return; if (dock_node->TabBar && dock_node->TabBar->VisibleTabId == window->ID) @@ -11812,7 +11825,7 @@ static void ImGui::DockSettingsHandler_ReadLine(ImGuiContext* ctx, ImGuiSettings if (sscanf(line, " SizeRef=%i,%i%n", &x, &y, &r) == 2) { line += r; node.SizeRef = ImVec2ih((short)x, (short)y); } } if (sscanf(line, " Split=%c%n", &c, &r) == 1) { line += r; if (c == 'X') node.SplitAxis = ImGuiAxis_X; else if (c == 'Y') node.SplitAxis = ImGuiAxis_Y; } - if (sscanf(line, " ExplicitRoot=%d%n", &x, &r) == 1) { line += r; node.IsExplicitRoot = (x != 0); } + if (sscanf(line, " ExplicitRoot=%d%n", &x, &r) == 1) { line += r; node.IsDockSpace = (x != 0); } if (sscanf(line, " DocumentRoot=%d%n", &x, &r) == 1) { line += r; node.IsDocumentRoot = (x != 0); } if (sscanf(line, " SelectedTab=0x%08X%n", &node.SelectedTabID,&r) == 1) { line += r; } //if (node.ParentID == 0 && node.SplitAxis == ImGuiAxis_None) @@ -11833,7 +11846,7 @@ static void DockSettingsHandler_DockNodeToSettings(ImGuiDockContext* dc, ImGuiDo node_settings.SelectedTabID = node->SelectedTabID; node_settings.SplitAxis = node->IsSplitNode() ? (char)node->SplitAxis : ImGuiAxis_None; node_settings.Depth = (char)depth; - node_settings.IsExplicitRoot = (char)node->IsExplicitRoot; + node_settings.IsDockSpace = (char)node->IsDockSpace; node_settings.IsDocumentRoot = (char)node->IsDocumentRoot; node_settings.Pos = ImVec2ih((short)node->Pos.x, (short)node->Pos.y); node_settings.Size = ImVec2ih((short)node->Size.x, (short)node->Size.y); @@ -11874,8 +11887,8 @@ static void ImGui::DockSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettings buf->appendf(" Pos=%d,%d Size=%d,%d", node_settings->Pos.x, node_settings->Pos.y, node_settings->Size.x, node_settings->Size.y); if (node_settings->SplitAxis != ImGuiAxis_None) buf->appendf(" Split=%c", (node_settings->SplitAxis == ImGuiAxis_X) ? 'X' : 'Y'); - if (node_settings->IsExplicitRoot) - buf->appendf(" ExplicitRoot=%d", node_settings->IsExplicitRoot); + if (node_settings->IsDockSpace) + buf->appendf(" ExplicitRoot=%d", node_settings->IsDockSpace); if (node_settings->IsDocumentRoot) buf->appendf(" DocumentRoot=%d", node_settings->IsDocumentRoot); if (node_settings->SelectedTabID) @@ -12729,7 +12742,7 @@ void ImGui::ShowDockingDebug() node->Pos.x, node->Pos.y, node->Size.x, node->Size.y, node->SizeRef.x, node->SizeRef.y); ImGui::BulletText("Flags %02X%s%s%s%s", - node->Flags, node->IsExplicitRoot ? ", IsExplicitRoot" : "", node->IsDocumentRoot ? ", IsDocumentRoot" : "", + node->Flags, node->IsDockSpace ? ", IsDockSpace" : "", node->IsDocumentRoot ? ", IsDocumentRoot" : "", (GImGui->FrameCount - node->LastFrameAlive < 2) ? ", IsAlive" : "", (GImGui->FrameCount - node->LastFrameActive < 2) ? ", IsActive" : ""); if (node->ChildNodes[0]) NodeDockNode(node->ChildNodes[0], "Child[0]"); diff --git a/imgui.h b/imgui.h index cdb2b3bec4c1..0c1385924487 100644 --- a/imgui.h +++ b/imgui.h @@ -111,7 +111,7 @@ typedef int ImGuiColorEditFlags; // -> enum ImGuiColorEditFlags_ // Flags: f typedef int ImGuiColumnsFlags; // -> enum ImGuiColumnsFlags_ // Flags: for Columns(), BeginColumns() typedef int ImGuiConfigFlags; // -> enum ImGuiConfigFlags_ // Flags: for io.ConfigFlags typedef int ImGuiComboFlags; // -> enum ImGuiComboFlags_ // Flags: for BeginCombo() -typedef int ImGuiDockSpaceFlags; // -> enum ImGuiDockSpaceFlags_ // Flags: for DockSpace() +typedef int ImGuiDockNodeFlags; // -> enum ImGuiDockNodeFlags_ // Flags: for DockSpace() typedef int ImGuiDragDropFlags; // -> enum ImGuiDragDropFlags_ // Flags: for *DragDrop*() typedef int ImGuiFocusedFlags; // -> enum ImGuiFocusedFlags_ // Flags: for IsWindowFocused() typedef int ImGuiHoveredFlags; // -> enum ImGuiHoveredFlags_ // Flags: for IsItemHovered(), IsWindowHovered() etc. @@ -517,8 +517,9 @@ namespace ImGui // Docking // [BETA API] Enable with io.ConfigFlags |= ImGuiConfigFlags_DockingEnable. - // Note: you DO NOT need to call DockSpace() to use most Docking facilities! You can hold SHIFT anywhere while moving windows. Use DockSpace() if you need to create an explicit docking space _within_ an existing window. See Docking demo for details) - IMGUI_API void DockSpace(ImGuiID id, const ImVec2& size = ImVec2(0, 0), ImGuiDockSpaceFlags flags = 0, ImGuiID user_type_filter = 0); + // Note: you DO NOT need to call DockSpace() to use most Docking facilities! You can hold SHIFT anywhere while moving windows. + // Use DockSpace() to create an explicit dock node _within_ an existing window. See Docking demo for details. + IMGUI_API void DockSpace(ImGuiID id, const ImVec2& size = ImVec2(0, 0), ImGuiDockNodeFlags flags = 0, ImGuiID user_type_filter = 0); IMGUI_API void SetNextWindowDock(ImGuiID dock_id, ImGuiCond cond = 0); // set next window dock id (FIXME-DOCK) IMGUI_API void SetNextWindowUserType(ImGuiID user_type); // FIXME-DOCK: set next window user type (docking filters by same user_type) IMGUI_API bool IsWindowDocked(); // is current window docked into another window? @@ -782,11 +783,11 @@ enum ImGuiTabItemFlags_ }; // Flags for ImGui::DockSpace() -enum ImGuiDockSpaceFlags_ +enum ImGuiDockNodeFlags_ { - ImGuiDockSpaceFlags_None = 0, - ImGuiDockSpaceFlags_KeepAliveOnly = 1 << 0, // Don't create/display the dockspace but keep it alive. Windows docked into this dockspace won't be undocked. - ImGuiDockSpaceFlags_NoSplit = 1 << 1 // Disable splitting the dockspace into smaller nodes. Useful e.g. when embedding dockspaces into a main root one. + ImGuiDockNodeFlags_None = 0, + ImGuiDockNodeFlags_KeepAliveOnly = 1 << 0, // Don't display the dockspace node but keep it alive. Windows docked into this dockspace node won't be undocked. + ImGuiDockNodeFlags_NoSplit = 1 << 1 // Disable splitting the node into smaller nodes. Useful e.g. when embedding dockspaces into a main root one (the root one may have splitting disableed to reduce confusion) }; // Flags for ImGui::IsWindowFocused() diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 6cbf0cdb3fb1..b1997012caa3 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -175,7 +175,7 @@ void ImGui::ShowDemoWindow(bool* p_open) static bool show_app_window_titles = false; static bool show_app_custom_rendering = false; - if (show_app_dockspace) ShowExampleAppDockSpace(&show_app_dockspace); // Process the Docking app first, as explicit DockSpace() needs to be submitted early (read comments near the DockSpace function) + if (show_app_dockspace) ShowExampleAppDockSpace(&show_app_dockspace); // Process the Docking app first, as explicit DockSpace() nodes needs to be submitted early (read comments near the DockSpace function) if (show_app_documents) ShowExampleAppDocuments(&show_app_documents); // Process the Document app next, as it may also use a DockSpace() if (show_app_main_menu_bar) ShowExampleAppMainMenuBar(); if (show_app_console) ShowExampleAppConsole(&show_app_console); @@ -3692,7 +3692,7 @@ static void ShowExampleAppCustomRendering(bool* p_open) // [SECTION] Example App: Docking, DockSpace / ShowExampleAppDockSpace() //----------------------------------------------------------------------------- -// Demonstrate using DockSpace() to create an explicit docking spacing within an existing window. +// Demonstrate using DockSpace() to create an explicit docking node within an existing window. // Note that you already dock windows into each others _without_ a DockSpace() by just holding SHIFT when moving a window. // DockSpace() is only useful to construct to a central location for your application. void ShowExampleAppDockSpace(bool* p_open) @@ -3702,7 +3702,7 @@ void ShowExampleAppDockSpace(bool* p_open) // We are using the ImGuiWindowFlags_NoDocking flag to make the parent window not dockable into. // Because 1) it would be confusing to have two docking targets within each others. - // and 2) we want our main DockSpace to always be visible (never hidden within a tab bar): if the DockSpace disappear its child windows will be orphaned. + // and 2) we want our main DockSpace node to always be visible (never hidden within a tab bar): if the DockSpace node disappear its child windows will be orphaned. ImGuiWindowFlags flags = ImGuiWindowFlags_MenuBar; flags |= ImGuiWindowFlags_NoDocking; if (opt_fullscreen) @@ -3737,9 +3737,9 @@ void ShowExampleAppDockSpace(bool* p_open) ShowHelpMarker( "You can _always_ dock _any_ window into another by holding the SHIFT key while moving a window. Try it now!" "\n" "This demo app has nothing to do with it!" "\n\n" - "This demo app only demonstrate the use of ImGui::DockSpace() which allows you to specify a docking spot _within_ another window. This is useful so you can decorate your main application window (e.g. with a menu bar)." "\n\n" + "This demo app only demonstrate the use of ImGui::DockSpace() which allows you to manually create a docking node _within_ another window. This is useful so you can decorate your main application window (e.g. with a menu bar)." "\n\n" "ImGui::DockSpace() comes with one hard constraint: it needs to be submitted _before_ any window which may be docked into it. Therefore, if you use a dock spot as the central point of your application, you'll probably want it to be part of the very first window you are submitting to imgui every frame." "\n\n" - "(NB: because of this constraint, the implicit \"Debug\" window can not be docked into an explicit DockSpace(), because that window is submitted as part of the NewFrame() call. An easy workaround is that you can create your own implicit \"Debug##2\" window after calling DockSpace() and leave it in the window stack for anyone to use.)" + "(NB: because of this constraint, the implicit \"Debug\" window can not be docked into an explicit DockSpace() node, because that window is submitted as part of the NewFrame() call. An easy workaround is that you can create your own implicit \"Debug##2\" window after calling DockSpace() and leave it in the window stack for anyone to use.)" ); ImGui::EndMenuBar(); @@ -3959,7 +3959,7 @@ void ShowExampleAppDocuments(bool* p_open) { NotifyOfDocumentsClosedElsewhere(app); - // Create a DockSpace where any window can be docked + // Create a DockSpace node where any window can be docked ImGuiID dockspace_id = ImGui::GetID("MyDockSpace"); ImGui::DockSpace(dockspace_id); diff --git a/imgui_internal.h b/imgui_internal.h index 03d9011f72d9..8be1a1b10f19 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -743,7 +743,7 @@ struct ImGuiDockNode { ImGuiID ID; ImGuiID UserTypeIdFilter; - ImGuiDockSpaceFlags Flags; + ImGuiDockNodeFlags Flags; ImGuiDockNode* ParentNode; ImGuiDockNode* ChildNodes[2]; // [Split node only] Child nodes (left/right or top/bottom). Consider switching to an array. ImVector Windows; // Note: unordered list! Iterate TabBar->Tabs for user-order. @@ -756,20 +756,20 @@ struct ImGuiDockNode ImGuiWindow* HostWindow; ImGuiWindow* VisibleWindow; ImGuiDockNode* OnlyNodeWithWindows; // [Root node only] Set when there is a single visible node within the hierarchy - int LastFrameAlive; // Last frame number the node was updated or kept alive explicitly with DockSpace() + mGuiDockSpaceFlags_KeepAliveOnly + int LastFrameAlive; // Last frame number the node was updated or kept alive explicitly with DockSpace() + ImGuiDockNodeFlags_KeepAliveOnly int LastFrameActive; // Last frame number the node was updated. ImGuiID LastFocusedNodeID; // [Root node only] Which of our child node (any ancestor in the hierarchy) was last focused. ImGuiID SelectedTabID; // [Tab node only] Which of our tab is selected. ImGuiID WantCloseTabID; // [Tab node only] Set when closing a specific tab. bool InitFromFirstWindow :1; bool IsVisible :1; // Set to false when the node is hidden (usually disabled as it has no active window) - bool IsExplicitRoot :1; // Mark root node as explicit when created from a DockSpace() + bool IsDockSpace :1; // Root node was created by a DockSpace() call. bool IsDocumentRoot :1; bool HasCloseButton :1; bool HasCollapseButton :1; bool WantCloseAll :1; // Set when closing all tabs at once. bool WantLockSizeOnce :1; - bool WantMouseMove :1; // After a node extraction we need to transition toward moving the newly created hode window + bool WantMouseMove :1; // After a node extraction we need to transition toward moving the newly created host window ImGuiDockNode(ImGuiID id); ~ImGuiDockNode(); From b57baa5ba029f4a293f9db744a0534fdcd141d2e Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 20 Sep 2018 10:31:23 +0200 Subject: [PATCH 264/828] Viewport: Enabled viewports by default in more example apps. --- examples/example_glfw_opengl2/main.cpp | 9 +++++---- examples/example_glfw_opengl3/main.cpp | 8 ++++---- examples/example_glfw_vulkan/main.cpp | 9 +++++---- examples/example_sdl_opengl2/main.cpp | 5 ++++- examples/example_sdl_opengl3/main.cpp | 7 ++++--- examples/example_sdl_vulkan/main.cpp | 7 ++++--- examples/example_win32_directx10/main.cpp | 7 ++++--- examples/example_win32_directx11/main.cpp | 9 +++++---- examples/example_win32_directx12/main.cpp | 6 ++++-- examples/example_win32_directx9/main.cpp | 6 +++++- 10 files changed, 44 insertions(+), 29 deletions(-) diff --git a/examples/example_glfw_opengl2/main.cpp b/examples/example_glfw_opengl2/main.cpp index 073b22260cea..952dd840f08c 100644 --- a/examples/example_glfw_opengl2/main.cpp +++ b/examples/example_glfw_opengl2/main.cpp @@ -33,10 +33,11 @@ int main(int, char**) IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; - io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoTaskBarIcons; - //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls - //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows + //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoTaskBarIcons; + //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoMerge; ImGui_ImplGlfw_InitForOpenGL(window, true); ImGui_ImplOpenGL2_Init(); diff --git a/examples/example_glfw_opengl3/main.cpp b/examples/example_glfw_opengl3/main.cpp index fd8fcb7c2c40..287ad479d9e3 100644 --- a/examples/example_glfw_opengl3/main.cpp +++ b/examples/example_glfw_opengl3/main.cpp @@ -76,11 +76,11 @@ int main(int, char**) IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; - io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoTaskBarIcons; + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows + //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoTaskBarIcons; //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoMerge; - io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls - //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls ImGui_ImplGlfw_InitForOpenGL(window, true); ImGui_ImplOpenGL3_Init(glsl_version); diff --git a/examples/example_glfw_vulkan/main.cpp b/examples/example_glfw_vulkan/main.cpp index fae777c92e7e..3d269593370b 100644 --- a/examples/example_glfw_vulkan/main.cpp +++ b/examples/example_glfw_vulkan/main.cpp @@ -347,10 +347,11 @@ int main(int, char**) IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; - io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoTaskBarIcons; - //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls - //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows + //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoTaskBarIcons; + //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoMerge; // Setup GLFW binding ImGui_ImplGlfw_InitForVulkan(window, true); diff --git a/examples/example_sdl_opengl2/main.cpp b/examples/example_sdl_opengl2/main.cpp index 7e92b8535e26..d6c2a1eb59cc 100644 --- a/examples/example_sdl_opengl2/main.cpp +++ b/examples/example_sdl_opengl2/main.cpp @@ -38,7 +38,10 @@ int main(int, char**) IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows + //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoTaskBarIcons; + //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoMerge; ImGui_ImplSDL2_InitForOpenGL(window, gl_context); ImGui_ImplOpenGL2_Init(); diff --git a/examples/example_sdl_opengl3/main.cpp b/examples/example_sdl_opengl3/main.cpp index b016c3be424c..8bb38c0e3e51 100644 --- a/examples/example_sdl_opengl3/main.cpp +++ b/examples/example_sdl_opengl3/main.cpp @@ -76,9 +76,10 @@ int main(int, char**) IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; - io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoTaskBarIcons; - //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows + //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoTaskBarIcons; + //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoMerge; ImGui_ImplSDL2_InitForOpenGL(window, gl_context); ImGui_ImplOpenGL3_Init(glsl_version); diff --git a/examples/example_sdl_vulkan/main.cpp b/examples/example_sdl_vulkan/main.cpp index 6d94e8c03cb2..45d868cccf58 100644 --- a/examples/example_sdl_vulkan/main.cpp +++ b/examples/example_sdl_vulkan/main.cpp @@ -335,9 +335,10 @@ int main(int, char**) // Setup Dear ImGui binding ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; - io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoTaskBarIcons; - //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows + //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoTaskBarIcons; + //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoMerge; // Setup SDL binding ImGui_ImplSDL2_InitForVulkan(window); diff --git a/examples/example_win32_directx10/main.cpp b/examples/example_win32_directx10/main.cpp index 312e7f12f239..5194c9a0d57b 100644 --- a/examples/example_win32_directx10/main.cpp +++ b/examples/example_win32_directx10/main.cpp @@ -118,9 +118,10 @@ int main(int, char**) IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; - io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoTaskBarIcons; - //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows + //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoTaskBarIcons; + //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoMerge; ImGui_ImplWin32_Init(hwnd); ImGui_ImplDX10_Init(g_pd3dDevice); diff --git a/examples/example_win32_directx11/main.cpp b/examples/example_win32_directx11/main.cpp index d397c1bf4282..0456b8784890 100644 --- a/examples/example_win32_directx11/main.cpp +++ b/examples/example_win32_directx11/main.cpp @@ -134,12 +134,13 @@ int main(int, char**) IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoTaskBarIcons; //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoMerge; - io.ConfigFlags |= ImGuiConfigFlags_DpiEnableScaleFonts; // FIXME-DPI: THIS CURRENTLY DOESN'T WORK AS EXPECTED. DON'T USE IN USER APP! - io.ConfigFlags |= ImGuiConfigFlags_DpiEnableScaleViewports; - io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_DpiEnableScaleFonts; // FIXME-DPI: THIS CURRENTLY DOESN'T WORK AS EXPECTED. DON'T USE IN USER APP! + io.ConfigFlags |= ImGuiConfigFlags_DpiEnableScaleViewports; // FIXME-DPI ImGui_ImplWin32_Init(hwnd); ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext); diff --git a/examples/example_win32_directx12/main.cpp b/examples/example_win32_directx12/main.cpp index c22e48e68710..7eabfd65ee9e 100644 --- a/examples/example_win32_directx12/main.cpp +++ b/examples/example_win32_directx12/main.cpp @@ -290,8 +290,10 @@ int main(int, char**) IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - //io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; - //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + //io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows (FIXME: Currently broken in DX12 back-end, need some work!) + //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoTaskBarIcons; + //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoMerge; ImGui_ImplWin32_Init(hwnd); ImGui_ImplDX12_Init(g_pd3dDevice, NUM_FRAMES_IN_FLIGHT, diff --git a/examples/example_win32_directx9/main.cpp b/examples/example_win32_directx9/main.cpp index 7e5b957ae12a..75348496d402 100644 --- a/examples/example_win32_directx9/main.cpp +++ b/examples/example_win32_directx9/main.cpp @@ -79,7 +79,11 @@ int main(int, char**) IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows + //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoTaskBarIcons; + //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoMerge; + ImGui_ImplWin32_Init(hwnd); ImGui_ImplDX9_Init(g_pd3dDevice); From 7abc368927c4a90e9c4b847448292bc71b3cd144 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 20 Sep 2018 18:46:36 +0200 Subject: [PATCH 265/828] Viewport: GLFW: Build fix for pre GLFW 3.2 (#1542) --- examples/imgui_impl_glfw.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index f17151af3c8a..a9c2f74aa8fb 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -45,6 +45,7 @@ #define GLFW_HAS_WINDOW_ALPHA (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ glfwSetWindowOpacity #define GLFW_HAS_PER_MONITOR_DPI (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ glfwGetMonitorContentScale #define GLFW_HAS_VULKAN (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ glfwCreateWindowSurface +#define GLFW_HAS_FOCUS_WINDOW (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ glfwFocusWindow // Data enum GlfwClientApi @@ -505,8 +506,13 @@ static void ImGui_ImplGlfw_SetWindowTitle(ImGuiViewport* viewport, const char* t static void ImGui_ImplGlfw_SetWindowFocus(ImGuiViewport* viewport) { +#if GLFW_HAS_FOCUS_WINDOW ImGuiViewportDataGlfw* data = (ImGuiViewportDataGlfw*)viewport->PlatformUserData; glfwFocusWindow(data->Window); +#else + // FIXME: What are the effect of not having this function? At the moment imgui doesn't actually call SetWindowFocus - we set that up ahead, will answer that question later. + (void)viewport; +#endif } static bool ImGui_ImplGlfw_GetWindowFocus(ImGuiViewport* viewport) From 599d5f185cfbcfee316ce7ac90c9481a8d0d6da8 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 21 Sep 2018 11:02:03 +0200 Subject: [PATCH 266/828] TabBar: Recover if SelectedTabId doesn't exist anymore. --- imgui.cpp | 1 - imgui_widgets.cpp | 7 ++++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index eefb6c51df27..d83f2fe92226 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -9525,7 +9525,6 @@ void ImGui::EndDragDropTarget() // TODO: // A~ document root node resizing behavior incorrect // A~ document root node retrieval of ID ? -// A- fix when SelectedTab doesn't exist (easy to repro/fix with .ini mod, but would be nice to also find real repro) // B- full rebuild loses viewport of floating dock nodes // B- dock node inside its own viewports creates 1 temporary viewport per window on startup before ditching them // A~ Unreal style document system (requires low-level controls of dockspace serialization fork/copy/delete) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 3a1415c59b07..aa63059181a7 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -5966,6 +5966,7 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) // Compute ideal widths float width_total_contents = 0.0f; ImGuiTabItem* most_recently_selected_tab = NULL; + bool found_selected_tab_id = false; for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++) { ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; @@ -5973,6 +5974,8 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) if (most_recently_selected_tab == NULL || most_recently_selected_tab->LastFrameSelected < tab->LastFrameSelected) most_recently_selected_tab = tab; + if (tab->ID == tab_bar->SelectedTabId) + found_selected_tab_id = true; // Refresh tab width immediately if we can (for manual tab bar, WidthContent will lag by one frame which is mostly noticeable when changing style.FramePadding.x) // Additionally, when using TabBarAddTab() to manipulate tab bar order we occasionally insert new tabs that don't have a width yet, @@ -6043,7 +6046,9 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) if (ImGuiTabItem* tab_to_select = TabBarScrollingButtons(tab_bar)) // NB: Will alter BarRect.Max.x! scroll_track_selected_tab_id = tab_bar->SelectedTabId = tab_to_select->ID; - // If we have lost the selected tab, select the next most recently active one. + // If we have lost the selected tab, select the next most recently active one + if (found_selected_tab_id == false) + tab_bar->SelectedTabId = 0; if (tab_bar->SelectedTabId == 0 && tab_bar->NextSelectedTabId == 0 && most_recently_selected_tab != NULL) scroll_track_selected_tab_id = tab_bar->SelectedTabId = most_recently_selected_tab->ID; From 5eabf44021634bb5393d9b284c4763307968c3c8 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 21 Sep 2018 11:11:38 +0200 Subject: [PATCH 267/828] Nav: Use Platform_SetWindowFocus when CTRL+Tabbing to another viewport. --- imgui.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/imgui.cpp b/imgui.cpp index 31bd521c1aa2..3c72aad9b5a0 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -8603,6 +8603,7 @@ static void ImGui::NavUpdateWindowing() // Apply final focus if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindow)) { + ImGuiViewport* previous_viewport = g.NavWindow ? g.NavWindow->Viewport : NULL; g.NavDisableHighlight = false; g.NavDisableMouseHover = true; apply_focus_window = NavRestoreLastChildNavWindow(apply_focus_window); @@ -8614,6 +8615,10 @@ static void ImGui::NavUpdateWindowing() // If the window only has a menu layer, select it directly if (apply_focus_window->DC.NavLayerActiveMask == (1 << 1)) g.NavLayer = 1; + + // Request OS level focus + if (apply_focus_window->Viewport != previous_viewport && g.PlatformIO.Platform_SetWindowFocus) + g.PlatformIO.Platform_SetWindowFocus(apply_focus_window->Viewport); } if (apply_focus_window) g.NavWindowingTarget = NULL; From 840652830d91eb744d5de676b99830149b692603 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 21 Sep 2018 11:48:22 +0200 Subject: [PATCH 268/828] Docking: Fixed undocking on whole dock node from leaving undesirable empty node in the docking tree. Fixed calls to MarkIniSettingsDirty(). --- imgui.cpp | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index d83f2fe92226..63ee5e043089 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10068,6 +10068,7 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) if (payload_window) { payload_node = payload_window->DockNodeAsHost; + payload_window->DockNodeAsHost = NULL; // Important to clear this as the node will have its life as a child which might be merged/deleted later. if (payload_node && !payload_node->IsSplitNode()) next_selected_id = payload_node->TabBar->NextSelectedTabId ? payload_node->TabBar->NextSelectedTabId : payload_node->TabBar->SelectedTabId; if (payload_node == NULL) @@ -10168,6 +10169,7 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) // Update selection immediately if (target_node->TabBar) target_node->TabBar->NextSelectedTabId = next_selected_id; + MarkIniSettingsDirty(); } void ImGui::DockContextProcessUndockWindow(ImGuiContext* ctx, ImGuiWindow* window) @@ -10180,22 +10182,31 @@ void ImGui::DockContextProcessUndockWindow(ImGuiContext* ctx, ImGuiWindow* windo window->Collapsed = false; window->DockIsActive = false; window->DockTabIsVisible = false; + MarkIniSettingsDirty(); } -// Extract a node out by creating a new one. -// In the case of a root node, a node will have to stay in place, otherwise the node will be hidden (and GC-ed later) -// (we could handle both cases differently with little benefit) void ImGui::DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node) { - // FIXME-DOCK: Transition persistent DockId for all non-active windows (void)ctx; IM_ASSERT(!node->IsSplitNode()); IM_ASSERT(node->Windows.Size >= 1); + ImGuiDockNode* new_node = DockContextAddNode(ctx, (ImGuiID)-1); DockNodeMoveWindows(new_node, node); for (int n = 0; n < new_node->Windows.Size; n++) UpdateWindowParentAndRootLinks(new_node->Windows[n], new_node->Windows[n]->Flags, NULL); new_node->WantMouseMove = true; + + // In the case of a root node, a node will have to stay in place. Create a new node for this purpose. + // Otherwise delete the previous node by merging the other sibling back into the parent node. + // FIXME-DOCK: Transition persistent DockId for all non-active windows + if (!node->IsRootNode()) + { + IM_ASSERT(node->ParentNode->ChildNodes[0] == node || node->ParentNode->ChildNodes[1] == node); + ImGuiDockNode* lead_sibling = node->ParentNode->ChildNodes[(node->ParentNode->ChildNodes[0] == node) ? 1 : 0]; + DockNodeTreeMerge(ctx, node->ParentNode, lead_sibling); + } + MarkIniSettingsDirty(); } //----------------------------------------------------------------------------- @@ -10252,7 +10263,6 @@ static void ImGui::DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window) window->DockId = node->ID; window->DockIsActive = (node->Windows.Size > 1); window->DockTabWantClose = false; - MarkIniSettingsDirty(); // If 2+ windows appeared on the same frame, creating a new DockNode+TabBar from the second window, // then we need to hide the first one after the fact otherwise it would be visible as a standalone window for one frame. @@ -10292,7 +10302,6 @@ static void ImGui::DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window window->DockIsActive = window->DockTabWantClose = false; window->DockId = save_dock_id; UpdateWindowParentAndRootLinks(window, window->Flags & ~ImGuiWindowFlags_ChildWindow, NULL); // Update immediately - MarkIniSettingsDirty(); // Remove window bool erased = false; From 67be485e2432bafc6775d4bae0c81f783b6367e1 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 21 Sep 2018 15:59:31 +0200 Subject: [PATCH 269/828] Docking: Fixed losing tab bar selection when extracting a whole docked node + reusing existing dock node when possible. --- imgui.cpp | 101 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 66 insertions(+), 35 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 0b295460c6cf..140f75f3ebaa 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -9648,7 +9648,7 @@ namespace ImGui static void DockContextBuildAddWindowsToNodes(ImGuiContext* ctx, ImGuiID root_id); // Use root_id==0 to add all // ImGuiDockNode - static void DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window); + static void DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window, bool add_to_tab_bar); static void DockNodeMoveWindows(ImGuiDockNode* dst_node, ImGuiDockNode* src_node); static void DockNodeMoveChildNodes(ImGuiDockNode* dst_node, ImGuiDockNode* src_node); static void DockNodeApplyPosSizeToWindows(ImGuiDockNode* node); @@ -10012,7 +10012,7 @@ void ImGui::DockContextBuildAddWindowsToNodes(ImGuiContext* ctx, ImGuiID root_id ImGuiDockNode* dock_node = DockContextFindNodeByID(ctx, window->DockId); IM_ASSERT(dock_node != NULL); // This should have been called after DockContextBuildNodesFromSettings() if (root_id == 0 || DockNodeGetRootNode(dock_node)->ID == root_id) - DockNodeAddWindow(dock_node, window); + DockNodeAddWindow(dock_node, window, true); } } @@ -10095,7 +10095,7 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) target_node->Size = target_window->Size; if (target_window->DockNodeAsHost == NULL) { - DockNodeAddWindow(target_node, target_window); + DockNodeAddWindow(target_node, target_window, true); target_window->DockIsActive = true; } } @@ -10167,7 +10167,7 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) { // Transfer single window target_node->VisibleWindow = payload_window; - DockNodeAddWindow(target_node, payload_window); + DockNodeAddWindow(target_node, payload_window, true); } } @@ -10196,20 +10196,26 @@ void ImGui::DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node) IM_ASSERT(!node->IsSplitNode()); IM_ASSERT(node->Windows.Size >= 1); - ImGuiDockNode* new_node = DockContextAddNode(ctx, (ImGuiID)-1); - DockNodeMoveWindows(new_node, node); - for (int n = 0; n < new_node->Windows.Size; n++) - UpdateWindowParentAndRootLinks(new_node->Windows[n], new_node->Windows[n]->Flags, NULL); - new_node->WantMouseMove = true; - // In the case of a root node, a node will have to stay in place. Create a new node for this purpose. // Otherwise delete the previous node by merging the other sibling back into the parent node. - // FIXME-DOCK: Transition persistent DockId for all non-active windows - if (!node->IsRootNode()) + if (node->IsRootNode()) + { + // FIXME-DOCK: Transition persistent DockId for all non-active windows + ImGuiDockNode* new_node = DockContextAddNode(ctx, (ImGuiID)-1); + DockNodeMoveWindows(new_node, node); + for (int n = 0; n < new_node->Windows.Size; n++) + UpdateWindowParentAndRootLinks(new_node->Windows[n], new_node->Windows[n]->Flags, NULL); + new_node->WantMouseMove = true; + } + else { IM_ASSERT(node->ParentNode->ChildNodes[0] == node || node->ParentNode->ChildNodes[1] == node); - ImGuiDockNode* lead_sibling = node->ParentNode->ChildNodes[(node->ParentNode->ChildNodes[0] == node) ? 1 : 0]; - DockNodeTreeMerge(ctx, node->ParentNode, lead_sibling); + int index_in_parent = (node->ParentNode->ChildNodes[0] == node) ? 0 : 1; + node->ParentNode->ChildNodes[index_in_parent] = NULL; + DockNodeTreeMerge(ctx, node->ParentNode, node->ParentNode->ChildNodes[index_in_parent ^ 1]); + node->ParentNode = NULL; + node->InitFromFirstWindow = true; + node->WantMouseMove = true; } MarkIniSettingsDirty(); } @@ -10252,7 +10258,7 @@ int ImGui::DockNodeGetTabOrder(ImGuiWindow* window) return tab ? tab_bar->GetTabOrder(tab) : -1; } -static void ImGui::DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window) +static void ImGui::DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window, bool add_to_tab_bar) { ImGuiContext& g = *GImGui; (void)g; if (window->DockNode) @@ -10281,12 +10287,16 @@ static void ImGui::DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window) if (node->Windows.Size == 2 && !node->IsDockSpace) node->InitFromFirstWindow = true; - if (node->TabBar == NULL) + // Add to tab bar if requested + if (add_to_tab_bar) { - node->TabBar = IM_NEW(ImGuiTabBar)(); - node->TabBar->SelectedTabId = node->TabBar->NextSelectedTabId = node->SelectedTabID; + if (node->TabBar == NULL) + { + node->TabBar = IM_NEW(ImGuiTabBar)(); + node->TabBar->SelectedTabId = node->TabBar->NextSelectedTabId = node->SelectedTabID; + } + TabBarAddTab(node->TabBar, window); } - TabBarAddTab(node->TabBar, window); DockNodeUpdateVisibleFlag(node); @@ -10376,19 +10386,31 @@ static void ImGui::DockNodeMoveWindows(ImGuiDockNode* dst_node, ImGuiDockNode* s ImGuiTabBar* src_tab_bar = src_node->TabBar; if (src_tab_bar != NULL) IM_ASSERT(src_node->Windows.Size == src_node->TabBar->Tabs.Size); + + // If the dst_node is empty we can just move the entire tab bar (to preserve selection, scrolling, etc.) + bool move_tab_bar = (src_tab_bar != NULL) && (dst_node->TabBar == NULL); + if (move_tab_bar) + { + dst_node->TabBar = src_node->TabBar; + src_node->TabBar = NULL; + } + for (int n = 0; n < src_node->Windows.Size; n++) { ImGuiWindow* window = src_tab_bar ? src_tab_bar->Tabs[n].Window : src_node->Windows[n]; window->DockNode = NULL; window->DockIsActive = false; - DockNodeAddWindow(dst_node, window); + DockNodeAddWindow(dst_node, window, move_tab_bar ? false : true); } - if (dst_node->TabBar == NULL) - dst_node->TabBar = src_node->TabBar; - else - IM_DELETE(src_node->TabBar); - src_node->TabBar = NULL; src_node->Windows.clear(); + + if (!move_tab_bar && src_node->TabBar) + { + if (dst_node->TabBar) + dst_node->TabBar->SelectedTabId = src_node->TabBar->SelectedTabId; + IM_DELETE(src_node->TabBar); + src_node->TabBar = NULL; + } } static void ImGui::DockNodeApplyPosSizeToWindows(ImGuiDockNode* node) @@ -11192,11 +11214,12 @@ void ImGui::DockNodeTreeSplit(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImG void ImGui::DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiDockNode* merge_lead_child) { + // When called from DockContextProcessUndockNode() it is possible that one of the child is NULL. ImGuiDockNode* child_0 = parent_node->ChildNodes[0]; ImGuiDockNode* child_1 = parent_node->ChildNodes[1]; - IM_ASSERT(child_0 && child_1); + IM_ASSERT(child_0 || child_1); IM_ASSERT(merge_lead_child == child_0 || merge_lead_child == child_1); - if (child_0->Windows.Size > 0 || child_1->Windows.Size > 0) + if ((child_0 && child_0->Windows.Size > 0) || (child_1 && child_1->Windows.Size > 0)) { IM_ASSERT(parent_node->TabBar == NULL); IM_ASSERT(parent_node->Windows.Size == 0); @@ -11204,18 +11227,26 @@ void ImGui::DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImG ImVec2 backup_last_explicit_size = parent_node->SizeRef; DockNodeMoveChildNodes(parent_node, merge_lead_child); - DockNodeMoveWindows(parent_node, child_0); // Generally only 1 of the 2 child node will have windows - DockNodeMoveWindows(parent_node, child_1); + if (child_0) + DockNodeMoveWindows(parent_node, child_0); // Generally only 1 of the 2 child node will have windows + if (child_1) + DockNodeMoveWindows(parent_node, child_1); DockNodeApplyPosSizeToWindows(parent_node); parent_node->InitFromFirstWindow = false; parent_node->VisibleWindow = merge_lead_child->VisibleWindow; - parent_node->IsDocumentRoot = child_0->IsDocumentRoot || child_1->IsDocumentRoot; + parent_node->IsDocumentRoot = (child_0 && child_0->IsDocumentRoot) || (child_1 && child_1->IsDocumentRoot); parent_node->SizeRef = backup_last_explicit_size; - ctx->DockContext->Nodes.SetVoidPtr(child_0->ID, NULL); - ctx->DockContext->Nodes.SetVoidPtr(child_1->ID, NULL); - IM_DELETE(child_0); - IM_DELETE(child_1); + if (child_0) + { + ctx->DockContext->Nodes.SetVoidPtr(child_0->ID, NULL); + IM_DELETE(child_0); + } + if (child_1) + { + ctx->DockContext->Nodes.SetVoidPtr(child_1->ID, NULL); + IM_DELETE(child_1); + } } // Update Pos/Size for a node hierarchy (don't affect child Windows yet) @@ -11613,7 +11644,7 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) return; } - DockNodeAddWindow(dock_node, window); + DockNodeAddWindow(dock_node, window, true); IM_ASSERT(dock_node == window->DockNode); // Fix an edge case with auto-resizing windows: if they are created on the same frame they are creating their dock node, From 11278041092db5bf6b1576a91a0f438b81e528f0 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 21 Sep 2018 17:54:00 +0200 Subject: [PATCH 270/828] Viewport: Misc tweaks. --- imgui.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 3c72aad9b5a0..b8122f30bd4f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7365,9 +7365,7 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) ImGuiViewportP* main_viewport = g.Viewports[0]; if (!(g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) { - window->Viewport = main_viewport; - window->ViewportId = main_viewport->ID; - window->ViewportOwned = false; + SetWindowViewport(window, main_viewport); return; } @@ -7605,10 +7603,11 @@ static int ImGui::FindPlatformMonitorForRect(const ImRect& rect) for (int monitor_n = 0; monitor_n < g.PlatformIO.Monitors.Size && best_monitor_surface < surface_threshold; monitor_n++) { const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[monitor_n]; - if (ImRect(monitor.MainPos, monitor.MainPos + monitor.MainSize).Contains(rect)) + const ImRect monitor_rect = ImRect(monitor.MainPos, monitor.MainPos + monitor.MainSize); + if (monitor_rect.Contains(rect)) return monitor_n; ImRect overlapping_rect = rect; - overlapping_rect.ClipWithFull(ImRect(monitor.MainPos, monitor.MainPos + monitor.MainSize)); + overlapping_rect.ClipWithFull(monitor_rect); float overlapping_surface = overlapping_rect.GetWidth() * overlapping_rect.GetHeight(); if (overlapping_surface < best_monitor_surface) continue; @@ -9825,10 +9824,14 @@ void ImGui::ShowViewportThumbnails() // We don't display full monitor bounds (we could, but it often looks awkward), instead we display just enough to cover all of our viewports. float SCALE = 1.0f / 8.0f; ImRect bb_full; + //for (int n = 0; n < g.PlatformIO.Monitors.Size; n++) + // bb_full.Add(GetPlatformMonitorMainRect(g.PlatformIO.Monitors[n])); for (int n = 0; n < g.Viewports.Size; n++) bb_full.Add(g.Viewports[n]->GetRect()); ImVec2 p = window->DC.CursorPos; ImVec2 off = p - bb_full.Min * SCALE; + //for (int n = 0; n < g.PlatformIO.Monitors.Size; n++) + // window->DrawList->AddRect(off + g.PlatformIO.Monitors[n].MainPos * SCALE, off + (g.PlatformIO.Monitors[n].MainPos + g.PlatformIO.Monitors[n].MainSize) * SCALE, ImGui::GetColorU32(ImGuiCol_Border)); for (int n = 0; n < g.Viewports.Size; n++) { ImGuiViewportP* viewport = g.Viewports[n]; From 79c075ca09c94f863dc324d9d784ba8853dc86db Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 21 Sep 2018 18:50:45 +0200 Subject: [PATCH 271/828] Docking+Viewport: Fixed PlatformRequestClose (e.g. ALT-F4) being redrected to the first window of the docking node instead of the selected one. --- imgui.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 140f75f3ebaa..d4929341ca4b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5422,9 +5422,12 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Close from platform window if (p_open != NULL && window->Viewport->PlatformRequestClose && window->Viewport != GetMainViewport()) { - window->Viewport->PlatformRequestClose = false; - g.NavWindowingToggleLayer = false; // Assume user mapped PlatformRequestClose on ALT-F4 so we disable ALT for menu toggle. False positive not an issue. - *p_open = false; + if (!window->DockIsActive || window->DockTabIsVisible) + { + window->Viewport->PlatformRequestClose = false; + g.NavWindowingToggleLayer = false; // Assume user mapped PlatformRequestClose on ALT-F4 so we disable ALT for menu toggle. False positive not an issue. + *p_open = false; + } } // Title bar From ae4b838840c16a001f1650ec16185fb92004249c Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 24 Sep 2018 10:48:16 +0200 Subject: [PATCH 272/828] Docking, Viewport: Rework ownership transfer/stealing to fix issues where non-child windows could be stick marked as not owning their viewport. --- imgui.cpp | 54 +++++++++++++++++++++++++++++------------------------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index d4929341ca4b..117352fdf111 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -955,7 +955,7 @@ const ImGuiID IMGUI_VIEWPORT_DEFAULT_ID = 0x11111111; // Using an arbi static ImGuiViewportP* AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const ImVec2& platform_pos, const ImVec2& size, ImGuiViewportFlags flags); static void UpdateViewports(); static void UpdateSelectWindowViewport(ImGuiWindow* window); -static void UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* host_viewport); +static bool UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* host_viewport); static void SetCurrentViewport(ImGuiWindow* window, ImGuiViewportP* viewport); static bool GetWindowAlwaysWantOwnViewport(ImGuiWindow* window); static int FindPlatformMonitorForPos(const ImVec2& pos); @@ -7276,25 +7276,7 @@ ImGuiViewport* ImGui::FindViewportByPlatformHandle(void* platform_handle) void ImGui::SetCurrentViewport(ImGuiWindow* current_window, ImGuiViewportP* viewport) { ImGuiContext& g = *GImGui; - - // Dock host can steal ownership - // (We test for ImGuiWindowFlags_DockNodeHost instead of ->DockNodeAsHost because the later is set after the first call to Begin) - if (viewport && current_window && viewport->Window && (current_window->DockNode || (current_window->Flags & ImGuiWindowFlags_DockNodeHost))) - if (viewport->LastFrameActive < g.FrameCount && viewport->Window != current_window) - { - // When called from Begin() we don't have access to a proper version of the Hidden flag yet. - bool will_be_visible = true; - if (current_window->DockIsActive && !current_window->DockTabIsVisible) - will_be_visible = false; - - if (will_be_visible) - { - //printf("[%05d] Window '%s' steal Viewport %08X from Window '%s'\n", g.FrameCount, current_window->Name, viewport->ID, viewport->Window->Name); - viewport->Window = current_window; - viewport->ID = current_window->ID; - viewport->LastNameHash = 0; - } - } + (void)current_window; if (viewport) viewport->LastFrameActive = g.FrameCount; @@ -7326,17 +7308,17 @@ static bool ImGui::GetWindowAlwaysWantOwnViewport(ImGuiWindow* window) return false; } -static void ImGui::UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* viewport) +static bool ImGui::UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* viewport) { ImGuiContext& g = *GImGui; if (!(g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) - return; + return false; if (!(viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) || window->Viewport == viewport) - return; + return false; if (!viewport->GetRect().Contains(window->Rect())) - return; + return false; if (GetWindowAlwaysWantOwnViewport(window)) - return; + return false; // Move to the existing viewport, Move child/hosted windows as well (FIXME-OPT: iterate child) ImGuiViewportP* old_viewport = window->Viewport; @@ -7345,6 +7327,7 @@ static void ImGui::UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImG if (g.Windows[n]->Viewport == old_viewport) SetWindowViewport(g.Windows[n], viewport); SetWindowViewport(window, viewport); + return true; } // Scale all windows (position, size). Use when e.g. changing DPI. (This is a lossy operation!) @@ -7412,6 +7395,7 @@ static void ImGui::UpdateViewports() g.Viewports.erase(g.Viewports.Data + n); // Destroy + //IMGUI_DEBUG_LOG("Delete Viewport %08X (%s)\n", viewport->ID, viewport->Window ? viewport->Window->Name : "n/a"); DestroyPlatformWindow(viewport); // In most circumstances the platform window will already be destroyed here. IM_ASSERT(g.PlatformIO.Viewports.contains(viewport) == false); IM_DELETE(viewport); @@ -7535,6 +7519,7 @@ ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const viewport->Size = size; viewport->PlatformMonitor = FindPlatformMonitorForRect(viewport->GetRect()); g.Viewports.push_back(viewport); + //IMGUI_DEBUG_LOG("Add Viewport %08X (%s)\n", id, window->Name); if (window && (window->Flags & ImGuiWindowFlags_NoFocusOnAppearing)) flags |= ImGuiViewportFlags_NoFocusOnAppearing; @@ -7653,6 +7638,24 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) else window->ViewportAllowPlatformMonitorExtend = window->Viewport->PlatformMonitor; } + else if (window->Viewport && window != window->Viewport->Window && window->Viewport->Window && !(flags & ImGuiWindowFlags_ChildWindow)) + { + // When called from Begin() we don't have access to a proper version of the Hidden flag yet. + const bool will_be_visible = (window->DockIsActive && !window->DockTabIsVisible) ? false : true; + if ((window->Flags & ImGuiWindowFlags_DockNodeHost) && window->Viewport->LastFrameActive < g.FrameCount && will_be_visible) + { + // Steal/transfer ownership + //printf("[%05d] Window '%s' steal Viewport %08X from Window '%s'\n", g.FrameCount, window->Name, window->Viewport->ID, window->Viewport->Window->Name); + window->Viewport->Window = window; + window->Viewport->ID = window->ID; + window->Viewport->LastNameHash = 0; + } + else if (!UpdateTryMergeWindowIntoHostViewport(window, g.Viewports[0])) // Merge? + { + // New viewport + window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoDecoration | ImGuiViewportFlags_NoFocusOnAppearing); + } + } else if ((flags & ImGuiWindowFlags_DockNodeHost) && (window->Appearing)) { // Mark so the dock host can be on its own viewport @@ -7724,6 +7727,7 @@ void ImGui::UpdatePlatformWindows() bool is_new_window = (viewport->CreatedPlatformWindow == false); if (is_new_window) { + //IMGUI_DEBUG_LOG("Create Platform Window %08X (%s)\n", viewport->ID, viewport->Window ? viewport->Window->Name : "n/a"); g.PlatformIO.Platform_CreateWindow(viewport); if (g.PlatformIO.Renderer_CreateWindow != NULL) g.PlatformIO.Renderer_CreateWindow(viewport); From 2a5ce1849ab6ffc44ec9d3c821e45af0c8da3a9b Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 24 Sep 2018 10:53:51 +0200 Subject: [PATCH 273/828] Docking, Viewport: Reworked viewport inheritance/transition so that extracting a dock node from a split node owning its viewport will have both viewports be in the expected OS z-order. (The lower window re-use the exiting viewport, the detached node uses a new viewport: --- imgui.cpp | 42 ++++++++++++++++++++++++++---------------- imgui_internal.h | 3 ++- 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 117352fdf111..897bcea9cf63 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -9832,7 +9832,7 @@ static ImGuiDockNode* ImGui::DockContextAddNode(ImGuiContext* ctx, ImGuiID id) } IM_ASSERT(DockContextFindNodeByID(ctx, id) == NULL); ImGuiDockNode* node = IM_NEW(ImGuiDockNode)(id); - node->InitFromFirstWindow = true; + node->InitFromFirstWindowPosSize = node->InitFromFirstWindowViewport = true; ctx->DockContext->Nodes.SetVoidPtr(node->ID, node); return node; } @@ -10220,8 +10220,9 @@ void ImGui::DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node) int index_in_parent = (node->ParentNode->ChildNodes[0] == node) ? 0 : 1; node->ParentNode->ChildNodes[index_in_parent] = NULL; DockNodeTreeMerge(ctx, node->ParentNode, node->ParentNode->ChildNodes[index_in_parent ^ 1]); + node->ParentNode->InitFromFirstWindowViewport = true; // The node that stays in place keeps the viewport, so our newly dragged out node will create a new viewport node->ParentNode = NULL; - node->InitFromFirstWindow = true; + node->InitFromFirstWindowPosSize = true; node->WantMouseMove = true; } MarkIniSettingsDirty(); @@ -10246,7 +10247,8 @@ ImGuiDockNode::ImGuiDockNode(ImGuiID id) SelectedTabID = 0; WantCloseTabID = 0; IsVisible = true; - InitFromFirstWindow = IsDockSpace = IsDocumentRoot = HasCloseButton = HasCollapseButton = WantCloseAll = WantLockSizeOnce = WantMouseMove = false; + InitFromFirstWindowPosSize = InitFromFirstWindowViewport = false; + IsDockSpace = IsDocumentRoot = HasCloseButton = HasCollapseButton = WantCloseAll = WantLockSizeOnce = WantMouseMove = false; } ImGuiDockNode::~ImGuiDockNode() @@ -10292,7 +10294,7 @@ static void ImGui::DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window, b // When reactivating a node from two loose window, the target window pos/size are authoritative if (node->Windows.Size == 2 && !node->IsDockSpace) - node->InitFromFirstWindow = true; + node->InitFromFirstWindowPosSize = node->InitFromFirstWindowViewport = true; // Add to tab bar if requested if (add_to_tab_bar) @@ -10358,12 +10360,12 @@ static void ImGui::DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window if (node->Windows.Size == 1 && !node->IsDocumentRoot && node->HostWindow) { ImGuiWindow* remaining_window = node->Windows[0]; - if (node->HostWindow->ViewportOwned) + if (node->HostWindow->ViewportOwned && node->IsRootNode()) { // Transfer viewport back to the remaining loose window IM_ASSERT(node->HostWindow->Viewport->Window == node->HostWindow); - node->HostWindow->Viewport->Window = node->Windows[0]; - node->HostWindow->Viewport->ID = node->Windows[0]->ID; + node->HostWindow->Viewport->Window = remaining_window; + node->HostWindow->Viewport->ID = remaining_window->ID; } remaining_window->Collapsed = node->HostWindow->Collapsed; } @@ -10557,22 +10559,29 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) if (node->IsRootNode() && node->Windows.Size == 1 && !node->IsDockSpace) { // Floating window pos/size is authoritative - node->Pos = node->Windows[0]->Pos; - node->Size = node->Windows[0]->SizeFull; + ImGuiWindow* single_window = node->Windows[0]; + node->Pos = single_window->Pos; + node->Size = single_window->SizeFull; // Transfer focus immediately so when we revert to a regular window it is immediately selected if (node->HostWindow && g.NavWindow == node->HostWindow) - FocusWindow(node->Windows[0]); + FocusWindow(single_window); + if (node->HostWindow) + { + single_window->Viewport = node->HostWindow->Viewport; + single_window->ViewportId = node->HostWindow->ViewportId; + single_window->Viewport->Window = single_window; + } DockNodeHideHostWindow(node); - node->InitFromFirstWindow = false; + node->InitFromFirstWindowPosSize = node->InitFromFirstWindowViewport = false; node->WantCloseAll = false; node->WantCloseTabID = 0; node->HasCloseButton = node->HasCollapseButton = false; node->LastFrameActive = g.FrameCount; if (node->WantMouseMove) - DockNodeStartMouseMovingWindow(node, node->Windows[0]); + DockNodeStartMouseMovingWindow(node, single_window); return; } @@ -10600,12 +10609,11 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) if (node->IsRootNode() && node->IsVisible) { - if (node->InitFromFirstWindow && node->Windows.Size > 0) + if (node->InitFromFirstWindowPosSize && node->Windows.Size > 0) { ImGuiWindow* init_window = node->Windows[0]; SetNextWindowPos(init_window->Pos); SetNextWindowSize(init_window->SizeFull); - SetNextWindowViewport(init_window->ViewportId); SetNextWindowCollapsed(init_window->Collapsed); } else if (node->HostWindow == NULL) @@ -10614,6 +10622,8 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) SetNextWindowPos(node->Pos); SetNextWindowSize(node->Size); } + if (node->InitFromFirstWindowViewport && node->Windows.Size > 0) + SetNextWindowViewport(node->Windows[0]->ViewportId); // Begin into the host window char window_label[20]; @@ -10637,7 +10647,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) { node->HostWindow = host_window = node->ParentNode->HostWindow; } - node->InitFromFirstWindow = false; + node->InitFromFirstWindowPosSize = node->InitFromFirstWindowViewport = false; if (node->WantMouseMove && node->HostWindow) DockNodeStartMouseMovingWindow(node, node->HostWindow); } @@ -11239,7 +11249,7 @@ void ImGui::DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImG if (child_1) DockNodeMoveWindows(parent_node, child_1); DockNodeApplyPosSizeToWindows(parent_node); - parent_node->InitFromFirstWindow = false; + parent_node->InitFromFirstWindowPosSize = parent_node->InitFromFirstWindowViewport = false; parent_node->VisibleWindow = merge_lead_child->VisibleWindow; parent_node->IsDocumentRoot = (child_0 && child_0->IsDocumentRoot) || (child_1 && child_1->IsDocumentRoot); parent_node->SizeRef = backup_last_explicit_size; diff --git a/imgui_internal.h b/imgui_internal.h index 8be1a1b10f19..1c2b04455109 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -761,7 +761,8 @@ struct ImGuiDockNode ImGuiID LastFocusedNodeID; // [Root node only] Which of our child node (any ancestor in the hierarchy) was last focused. ImGuiID SelectedTabID; // [Tab node only] Which of our tab is selected. ImGuiID WantCloseTabID; // [Tab node only] Set when closing a specific tab. - bool InitFromFirstWindow :1; + bool InitFromFirstWindowPosSize :1; + bool InitFromFirstWindowViewport :1; bool IsVisible :1; // Set to false when the node is hidden (usually disabled as it has no active window) bool IsDockSpace :1; // Root node was created by a DockSpace() call. bool IsDocumentRoot :1; From fa0ce4b7d57674a4b16501962e73b029d0ae3e48 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 24 Sep 2018 11:06:31 +0200 Subject: [PATCH 274/828] Docking: Some DockBuilder functions are applied on settings data if windows are not present. Added DockBuilderCreateNode which needs a size else if we can't split properly. DockNodeTreeSplit() doesn't clamp SizeRef. (+1 squashed commits) --- imgui.cpp | 82 +++++++++++++++++++++++++++++++++++++---------- imgui_internal.h | 11 ++++--- imgui_widgets.cpp | 4 +-- 3 files changed, 74 insertions(+), 23 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 897bcea9cf63..cabb8d33bece 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -9641,7 +9641,6 @@ struct ImGuiDockContext namespace ImGui { // ImGuiDockContext - static ImGuiDockNode* DockContextFindNodeByID(ImGuiContext* ctx, ImGuiID id); static ImGuiDockNode* DockContextAddNode(ImGuiContext* ctx, ImGuiID id); static void DockContextRemoveNode(ImGuiContext* ctx, ImGuiDockNode* node, bool merge_sibling_into_parent_node); static void DockContextQueueDock(ImGuiContext* ctx, ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, float split_ratio, bool split_outer); @@ -9815,7 +9814,7 @@ void ImGui::DockContextEndFrame(ImGuiContext* ctx) (void)ctx; } -static ImGuiDockNode* ImGui::DockContextFindNodeByID(ImGuiContext* ctx, ImGuiID id) +ImGuiDockNode* ImGui::DockContextFindNodeByID(ImGuiContext* ctx, ImGuiID id) { return (ImGuiDockNode*)ctx->DockContext->Nodes.GetVoidPtr(id); } @@ -9884,6 +9883,7 @@ void ImGui::DockBuilderRemoveNodeChildNodes(ImGuiContext* ctx, ImGuiID root_id) return; bool has_document_root = false; + // Process active windows ImVector nodes_to_remove; for (int n = 0; n < dc->Nodes.Data.Size; n++) if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p) @@ -9901,6 +9901,16 @@ void ImGui::DockBuilderRemoveNodeChildNodes(ImGuiContext* ctx, ImGuiID root_id) } } + // Apply to settings + for (int settings_n = 0; settings_n < ctx->SettingsWindows.Size; settings_n++) + if (ImGuiID window_settings_dock_id = ctx->SettingsWindows[settings_n].DockId) + for (int n = 0; n < nodes_to_remove.Size; n++) + if (nodes_to_remove[n]->ID == window_settings_dock_id) + { + ctx->SettingsWindows[settings_n].DockId = root_id; + break; + } + // Not really efficient, but easier to destroy a whole hierarchy considering DockContextRemoveNode is attempting to merge nodes if (nodes_to_remove.Size > 1) ImQsort(nodes_to_remove.Data, nodes_to_remove.Size, sizeof(ImGuiDockNode*), DockNodeComparerDepthMostFirst); @@ -9920,8 +9930,24 @@ void ImGui::DockBuilderRemoveNodeChildNodes(ImGuiContext* ctx, ImGuiID root_id) void ImGui::DockBuilderRemoveNodeDockedWindows(ImGuiContext* ctx, ImGuiID root_id, bool clear_persistent_docking_references) { - // Clear references in windows + // Clear references in settings ImGuiContext& g = *ctx; + if (clear_persistent_docking_references) + { + for (int n = 0; n < g.SettingsWindows.Size; n++) + { + ImGuiWindowSettings* settings = &g.SettingsWindows[n]; + bool want_removal = (root_id == 0) || (settings->DockId == root_id); + if (!want_removal && settings->DockId != 0) + if (ImGuiDockNode* node = DockContextFindNodeByID(ctx, settings->DockId)) + if (DockNodeGetRootNode(node)->ID == root_id) + want_removal = true; + if (want_removal) + settings->DockId = 0; + } + } + + // Clear references in windows for (int n = 0; n < g.Windows.Size; n++) { ImGuiWindow* window = g.Windows[n]; @@ -9957,6 +9983,7 @@ static void ImGui::DockContextGcUnusedSettingsNodes(ImGuiContext* ctx) is_parent_map.SetInt(dc->SettingsNodes[settings_n].ParentID, 1); // If a root node has only 1 reference in window settings we clear it + // FIXME-DOCK: We should be able to merge unused nodes as well. for (int settings_n = 0; settings_n < dc->SettingsNodes.Size; settings_n++) { ImGuiDockNodeSettings* settings = &dc->SettingsNodes[settings_n]; @@ -10813,7 +10840,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w // Begin tab bar ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_Reorderable | ImGuiTabBarFlags_AutoSelectNewTabs | ImGuiTabBarFlags_NoTabListPopupButton;// | ImGuiTabBarFlags_NoTabListScrollingButtons); tab_bar_flags |= ImGuiTabBarFlags_SaveSettings; - tab_bar_flags |= ImGuiTabBarFlags_DockNode | (node->IsDockSpace ? ImGuiTabBarFlags_DockNodeExplicitRoot : 0); + tab_bar_flags |= ImGuiTabBarFlags_DockNode | (node->IsDockSpace ? ImGuiTabBarFlags_DockNodeIsDockSpace : 0); if (!host_window->Collapsed && is_focused) tab_bar_flags |= ImGuiTabBarFlags_IsFocused; BeginTabBarEx(node->TabBar, tab_bar_rect, tab_bar_flags, node); @@ -11203,7 +11230,6 @@ static void ImGui::DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDock void ImGui::DockNodeTreeSplit(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiAxis split_axis, int split_inheritor_child_idx, float split_ratio, ImGuiDockNode* new_node) { - ImGuiContext& g = *ctx; IM_ASSERT(split_axis != ImGuiAxis_None); ImGuiDockNode* child_0 = (new_node && split_inheritor_child_idx != 0) ? new_node : DockContextAddNode(ctx, (ImGuiID)-1); @@ -11221,9 +11247,10 @@ void ImGui::DockNodeTreeSplit(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImG parent_node->VisibleWindow = NULL; float size_avail = (parent_node->Size[split_axis] - IMGUI_DOCK_SPLITTER_SIZE); + IM_ASSERT(size_avail > 0.0f); child_0->SizeRef = child_1->SizeRef = parent_node->Size; - child_0->SizeRef[split_axis] = ImMax(g.Style.WindowMinSize[split_axis], ImFloor(size_avail * split_ratio)); - child_1->SizeRef[split_axis] = ImMax(g.Style.WindowMinSize[split_axis], ImFloor(size_avail - child_0->SizeRef[split_axis])); + child_0->SizeRef[split_axis] = ImFloor(size_avail * split_ratio); + child_1->SizeRef[split_axis] = ImFloor(size_avail - child_0->SizeRef[split_axis]); DockNodeMoveWindows(parent_node->ChildNodes[split_inheritor_child_idx], parent_node); DockNodeTreeUpdatePosSize(parent_node, parent_node->Pos, parent_node->Size); @@ -11517,7 +11544,7 @@ void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags doc // When a Dockspace transitioned form implicit to explicit this may be called a second time // It is possible that the node has already been claimed by a docked window which appeared before the DockSpace() node, so we overwrite IsDockSpace again. - if (node->LastFrameActive == g.FrameCount) + if (node->LastFrameActive == g.FrameCount && !(dock_space_flags & ImGuiDockNodeFlags_KeepAliveOnly)) { IM_ASSERT(node->IsDockSpace == false && "Cannot call DockSpace() twice a frame with the same ID"); node->IsDockSpace = true; @@ -11593,13 +11620,25 @@ void ImGui::DockBuilderDockWindow(ImGuiContext*, const char* window_name, ImGuiI } } +// Ensure a node is created +void ImGui::DockBuilderCreateNode(ImGuiContext* ctx, ImGuiID id, ImVec2 ref_size, ImGuiDockNodeFlags flags) +{ + DockSpace(id, ImVec2(0,0), flags | ImGuiDockNodeFlags_KeepAliveOnly); + ImGuiDockNode* node = DockContextFindNodeByID(ctx, id); + node->SizeRef = node->Size = ref_size; + node->LastFrameAlive = -1; +} + ImGuiID ImGui::DockBuilderSplitNode(ImGuiContext* ctx, ImGuiID id, ImGuiDir split_dir, float size_ratio_for_node_at_dir, ImGuiID* out_id_at_dir, ImGuiID* out_id_other) { IM_ASSERT(split_dir != ImGuiDir_None); ImGuiDockNode* node = DockContextFindNodeByID(ctx, id); if (node == NULL) + { + IM_ASSERT(node != NULL); return 0; + } IM_ASSERT(!node->IsSplitNode()); // Already Split @@ -11886,7 +11925,7 @@ static void ImGui::DockSettingsHandler_ReadLine(ImGuiContext* ctx, ImGuiSettings if (sscanf(line, " SizeRef=%i,%i%n", &x, &y, &r) == 2) { line += r; node.SizeRef = ImVec2ih((short)x, (short)y); } } if (sscanf(line, " Split=%c%n", &c, &r) == 1) { line += r; if (c == 'X') node.SplitAxis = ImGuiAxis_X; else if (c == 'Y') node.SplitAxis = ImGuiAxis_Y; } - if (sscanf(line, " ExplicitRoot=%d%n", &x, &r) == 1) { line += r; node.IsDockSpace = (x != 0); } + if (sscanf(line, " DockSpace=%d%n", &x, &r) == 1) { line += r; node.IsDockSpace = (x != 0); } if (sscanf(line, " DocumentRoot=%d%n", &x, &r) == 1) { line += r; node.IsDocumentRoot = (x != 0); } if (sscanf(line, " SelectedTab=0x%08X%n", &node.SelectedTabID,&r) == 1) { line += r; } //if (node.ParentID == 0 && node.SplitAxis == ImGuiAxis_None) @@ -11939,6 +11978,7 @@ static void ImGui::DockSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettings buf->appendf("[%s][Data]\n", handler->TypeName); for (int node_n = 0; node_n < dc->SettingsNodes.Size; node_n++) { + //const int line_start_pos = buf->size(); const ImGuiDockNodeSettings* node_settings = &dc->SettingsNodes[node_n]; buf->appendf("%*sDockNode%*s", node_settings->Depth * 2, "", (max_depth - node_settings->Depth) * 2, ""); // Text align nodes to facilitate looking at .ini file buf->appendf(" ID=0x%08X", node_settings->ID); @@ -11949,20 +11989,28 @@ static void ImGui::DockSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettings if (node_settings->SplitAxis != ImGuiAxis_None) buf->appendf(" Split=%c", (node_settings->SplitAxis == ImGuiAxis_X) ? 'X' : 'Y'); if (node_settings->IsDockSpace) - buf->appendf(" ExplicitRoot=%d", node_settings->IsDockSpace); + buf->appendf(" DockSpace=%d", node_settings->IsDockSpace); if (node_settings->IsDocumentRoot) buf->appendf(" DocumentRoot=%d", node_settings->IsDocumentRoot); if (node_settings->SelectedTabID) buf->appendf(" SelectedTab=0x%08X", node_settings->SelectedTabID); -#if 0 // [DEBUG] Include windows names in the .ini file +#if 0 // [DEBUG] Include comments in the .ini file to ease debugging if (ImGuiDockNode* node = DockContextFindNodeByID(ctx, node_settings->ID)) - if (node->Windows.Size > 0) - { - buf->appendf("%*s; recently: %d %s (", 15, "", node->Windows.Size, node->Windows.Size == 1 ? "window " : "windows"); - for (int window_n = 0; window_n < node->Windows.Size; window_n++) - buf->appendf("\"%s\"%s", node->Windows[window_n]->Name, (window_n + 1 < node->Windows.Size) ? ", " : ")"); - } + { + buf->appendf("%*s", ImMax(2, (line_start_pos + 90) - buf->size()), ""); // Align everything + if (node->IsDockSpace && node->HostWindow && node->HostWindow->ParentWindow) + buf->appendf(" ; in '%s'", node->HostWindow->ParentWindow->Name); + + int contains_window = 0; + for (int window_n = 0; window_n < ctx->SettingsWindows.Size; window_n++) + if (ctx->SettingsWindows[window_n].DockId == node_settings->ID) + { + if (contains_window++ == 0) + buf->appendf(" ; contains "); + buf->appendf("'%s' ", ctx->SettingsWindows[window_n].Name); + } + } #endif buf->appendf("\n"); } diff --git a/imgui_internal.h b/imgui_internal.h index 1c2b04455109..25eddf29bf7c 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1296,7 +1296,7 @@ struct ImGuiItemHoveredDataBackup enum ImGuiTabBarFlagsPrivate_ { ImGuiTabBarFlags_DockNode = 1 << 20, // Part of a dock node - ImGuiTabBarFlags_DockNodeExplicitRoot = 1 << 21, // Part of an explicit dock node + ImGuiTabBarFlags_DockNodeIsDockSpace = 1 << 21, // Part of an explicit dockspace node node ImGuiTabBarFlags_IsFocused = 1 << 22, ImGuiTabBarFlags_SaveSettings = 1 << 23 // FIXME: Settings are handled by the docking system, this only request the tab bar to mark settings dirty when reordering tabs }; @@ -1472,6 +1472,7 @@ namespace ImGui IMGUI_API void DockContextEndFrame(ImGuiContext* ctx); IMGUI_API void DockContextQueueUndockWindow(ImGuiContext* ctx, ImGuiWindow* window); IMGUI_API void DockContextQueueUndockNode(ImGuiContext* ctx, ImGuiDockNode* node); + IMGUI_API ImGuiDockNode*DockContextFindNodeByID(ImGuiContext* ctx, ImGuiID id); inline ImGuiDockNode* DockNodeGetRootNode(ImGuiDockNode* node) { while (node->ParentNode) node = node->ParentNode; return node; } IMGUI_API void BeginDocked(ImGuiWindow* window, bool* p_open); IMGUI_API void BeginAsDockableDragDropSource(ImGuiWindow* window); @@ -1479,11 +1480,13 @@ namespace ImGui IMGUI_API void SetWindowDock(ImGuiWindow* window, ImGuiID dock_id, ImGuiCond cond); IMGUI_API void ShowDockingDebug(); - IMGUI_API void DockBuilderRemoveNodeDockedWindows(ImGuiContext* ctx, ImGuiID root_id, bool clear_persistent_docking_references = true); - IMGUI_API void DockBuilderRemoveNodeChildNodes(ImGuiContext* ctx, ImGuiID root_id); + // Docking - Builder function needs to be generally called before the DockSpace() node is submitted. + IMGUI_API void DockBuilderRemoveNodeDockedWindows(ImGuiContext* ctx, ImGuiID root_node_id, bool clear_persistent_docking_references = true); + IMGUI_API void DockBuilderRemoveNodeChildNodes(ImGuiContext* ctx, ImGuiID root_node_id); // Remove all split/hierarchy. All remaining docked windows will be re-docked to the root. IMGUI_API void DockBuilderDockWindow(ImGuiContext* ctx, const char* window_name, ImGuiID node_id); + IMGUI_API void DockBuilderCreateNode(ImGuiContext* ctx, ImGuiID id, ImVec2 ref_size, ImGuiDockNodeFlags flags = 0); IMGUI_API ImGuiID DockBuilderSplitNode(ImGuiContext* ctx, ImGuiID id, ImGuiDir split_dir, float size_ratio_for_node_at_dir, ImGuiID* out_id_dir, ImGuiID* out_id_other); - IMGUI_API void DockBuilderFinish(ImGuiContext* ctx, ImGuiID root_id); + IMGUI_API void DockBuilderFinish(ImGuiContext* ctx, ImGuiID root_node_id); // Drag and Drop IMGUI_API bool BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index aa63059181a7..299b0d644854 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -5871,8 +5871,8 @@ bool ImGui::BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& tab_bar_bb, ImG } else { - const float separator_min_x = tab_bar->BarRect.Min.x - ((flags & ImGuiTabBarFlags_DockNodeExplicitRoot) ? 0.0f : window->WindowPadding.x); - const float separator_max_x = tab_bar->BarRect.Max.x + ((flags & ImGuiTabBarFlags_DockNodeExplicitRoot) ? 0.0f : window->WindowPadding.x); + const float separator_min_x = tab_bar->BarRect.Min.x - ((flags & ImGuiTabBarFlags_DockNodeIsDockSpace) ? 0.0f : window->WindowPadding.x); + const float separator_max_x = tab_bar->BarRect.Max.x + ((flags & ImGuiTabBarFlags_DockNodeIsDockSpace) ? 0.0f : window->WindowPadding.x); window->DrawList->AddLine(ImVec2(separator_min_x, y), ImVec2(separator_max_x, y), col, 1.0f); } return true; From 4021776d0f337516260b69a9e1885951a90cd5ed Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 24 Sep 2018 17:13:08 +0200 Subject: [PATCH 275/828] Docking: Fix for undocking node where the dockspace node is attached to an immovable parent window. + Tweak .ini output. --- imgui.cpp | 15 +++++++-------- imgui_internal.h | 10 +++++----- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index cabb8d33bece..094e1198e59e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10554,6 +10554,7 @@ static void ImGui::DockNodeStartMouseMovingWindow(ImGuiDockNode* node, ImGuiWind IM_ASSERT(node->WantMouseMove == true); ImVec2 backup_active_click_offset = g.ActiveIdClickOffset; StartMouseMovingWindow(window); + g.MovingWindow = window; // If we are docked into a non moveable root widnow, StartMouseMovingWindow() won't set g.MovingWindow. OVerride that decision. node->WantMouseMove = false; g.ActiveIdClickOffset = backup_active_click_offset; } @@ -11911,8 +11912,9 @@ static void ImGui::DockSettingsHandler_ReadLine(ImGuiContext* ctx, ImGuiSettings // " DockNode ID=0x00000002 Parent=0x00000001 " ImGuiDockNodeSettings node; line = ImStrSkipBlank(line); - if (strncmp(line, "DockNode", 8) != 0) return; - line = ImStrSkipBlank(line + strlen("DockNode")); + if (strncmp(line, "DockNode", 8) == 0) { line = ImStrSkipBlank(line + strlen("DockNode")); } + else if (strncmp(line, "DockSpace", 9) == 0) { line = ImStrSkipBlank(line + strlen("DockSpace")); node.IsDockSpace = true; } + else return; if (sscanf(line, "ID=0x%08X%n", &node.ID, &r) == 1) { line += r; } else return; if (sscanf(line, " Parent=0x%08X%n", &node.ParentID, &r) == 1) { line += r; if (node.ParentID == 0) return; } if (node.ParentID == 0) @@ -11925,8 +11927,7 @@ static void ImGui::DockSettingsHandler_ReadLine(ImGuiContext* ctx, ImGuiSettings if (sscanf(line, " SizeRef=%i,%i%n", &x, &y, &r) == 2) { line += r; node.SizeRef = ImVec2ih((short)x, (short)y); } } if (sscanf(line, " Split=%c%n", &c, &r) == 1) { line += r; if (c == 'X') node.SplitAxis = ImGuiAxis_X; else if (c == 'Y') node.SplitAxis = ImGuiAxis_Y; } - if (sscanf(line, " DockSpace=%d%n", &x, &r) == 1) { line += r; node.IsDockSpace = (x != 0); } - if (sscanf(line, " DocumentRoot=%d%n", &x, &r) == 1) { line += r; node.IsDocumentRoot = (x != 0); } + if (sscanf(line, " DocRoot=%d%n", &x, &r) == 1) { line += r; node.IsDocumentRoot = (x != 0); } if (sscanf(line, " SelectedTab=0x%08X%n", &node.SelectedTabID,&r) == 1) { line += r; } //if (node.ParentID == 0 && node.SplitAxis == ImGuiAxis_None) // return; @@ -11980,7 +11981,7 @@ static void ImGui::DockSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettings { //const int line_start_pos = buf->size(); const ImGuiDockNodeSettings* node_settings = &dc->SettingsNodes[node_n]; - buf->appendf("%*sDockNode%*s", node_settings->Depth * 2, "", (max_depth - node_settings->Depth) * 2, ""); // Text align nodes to facilitate looking at .ini file + buf->appendf("%*s%s%*s", node_settings->Depth * 2, "", node_settings->IsDockSpace ? "DockSpace" : "DockNode ", (max_depth - node_settings->Depth) * 2, ""); // Text align nodes to facilitate looking at .ini file buf->appendf(" ID=0x%08X", node_settings->ID); if (node_settings->ParentID) buf->appendf(" Parent=0x%08X SizeRef=%d,%d", node_settings->ParentID, node_settings->SizeRef.x, node_settings->SizeRef.y); @@ -11988,10 +11989,8 @@ static void ImGui::DockSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettings buf->appendf(" Pos=%d,%d Size=%d,%d", node_settings->Pos.x, node_settings->Pos.y, node_settings->Size.x, node_settings->Size.y); if (node_settings->SplitAxis != ImGuiAxis_None) buf->appendf(" Split=%c", (node_settings->SplitAxis == ImGuiAxis_X) ? 'X' : 'Y'); - if (node_settings->IsDockSpace) - buf->appendf(" DockSpace=%d", node_settings->IsDockSpace); if (node_settings->IsDocumentRoot) - buf->appendf(" DocumentRoot=%d", node_settings->IsDocumentRoot); + buf->appendf(" DocRoot=%d", node_settings->IsDocumentRoot); if (node_settings->SelectedTabID) buf->appendf(" SelectedTab=0x%08X", node_settings->SelectedTabID); diff --git a/imgui_internal.h b/imgui_internal.h index 25eddf29bf7c..55c29b6d4aa9 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1481,12 +1481,12 @@ namespace ImGui IMGUI_API void ShowDockingDebug(); // Docking - Builder function needs to be generally called before the DockSpace() node is submitted. - IMGUI_API void DockBuilderRemoveNodeDockedWindows(ImGuiContext* ctx, ImGuiID root_node_id, bool clear_persistent_docking_references = true); - IMGUI_API void DockBuilderRemoveNodeChildNodes(ImGuiContext* ctx, ImGuiID root_node_id); // Remove all split/hierarchy. All remaining docked windows will be re-docked to the root. + IMGUI_API void DockBuilderRemoveNodeDockedWindows(ImGuiContext* ctx, ImGuiID node_id, bool clear_persistent_docking_references = true); + IMGUI_API void DockBuilderRemoveNodeChildNodes(ImGuiContext* ctx, ImGuiID node_id); // Remove all split/hierarchy. All remaining docked windows will be re-docked to the root. IMGUI_API void DockBuilderDockWindow(ImGuiContext* ctx, const char* window_name, ImGuiID node_id); - IMGUI_API void DockBuilderCreateNode(ImGuiContext* ctx, ImGuiID id, ImVec2 ref_size, ImGuiDockNodeFlags flags = 0); - IMGUI_API ImGuiID DockBuilderSplitNode(ImGuiContext* ctx, ImGuiID id, ImGuiDir split_dir, float size_ratio_for_node_at_dir, ImGuiID* out_id_dir, ImGuiID* out_id_other); - IMGUI_API void DockBuilderFinish(ImGuiContext* ctx, ImGuiID root_node_id); + IMGUI_API void DockBuilderCreateNode(ImGuiContext* ctx, ImGuiID node_id, ImVec2 ref_size, ImGuiDockNodeFlags flags = 0); + IMGUI_API ImGuiID DockBuilderSplitNode(ImGuiContext* ctx, ImGuiID node_id, ImGuiDir split_dir, float size_ratio_for_node_at_dir, ImGuiID* out_id_dir, ImGuiID* out_id_other); + IMGUI_API void DockBuilderFinish(ImGuiContext* ctx, ImGuiID node_id); // Drag and Drop IMGUI_API bool BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id); From 9960ccddb2ef526882736b133661f3a0b9957fce Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 24 Sep 2018 23:16:19 +0200 Subject: [PATCH 276/828] Docking: Added DockBuilderRemoveNode() + various tweaks. Fixed dragging/undocking dock node from CollapseButton. --- imgui.cpp | 223 ++++++++++++++++++++++++---------------------- imgui_internal.h | 5 +- imgui_widgets.cpp | 2 + 3 files changed, 122 insertions(+), 108 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 094e1198e59e..64945f2a68a4 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -9874,94 +9874,6 @@ static int IMGUI_CDECL DockNodeComparerDepthMostFirst(const void* lhs, const voi return ImGui::DockNodeGetDepth(b) - ImGui::DockNodeGetDepth(a); } -void ImGui::DockBuilderRemoveNodeChildNodes(ImGuiContext* ctx, ImGuiID root_id) -{ - ImGuiDockContext* dc = ctx->DockContext; - - ImGuiDockNode* root_node = root_id ? DockContextFindNodeByID(ctx, root_id) : NULL; - if (root_id && root_node == NULL) - return; - bool has_document_root = false; - - // Process active windows - ImVector nodes_to_remove; - for (int n = 0; n < dc->Nodes.Data.Size; n++) - if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p) - { - bool want_removal = (root_id == 0) || (node->ID != root_id && DockNodeGetRootNode(node)->ID == root_id); - if (want_removal) - { - if (node->IsDocumentRoot) - has_document_root = true; - if (root_id != 0) - DockContextQueueNotifyRemovedNode(ctx, node); - if (root_node) - DockNodeMoveWindows(root_node, node); - nodes_to_remove.push_back(node); - } - } - - // Apply to settings - for (int settings_n = 0; settings_n < ctx->SettingsWindows.Size; settings_n++) - if (ImGuiID window_settings_dock_id = ctx->SettingsWindows[settings_n].DockId) - for (int n = 0; n < nodes_to_remove.Size; n++) - if (nodes_to_remove[n]->ID == window_settings_dock_id) - { - ctx->SettingsWindows[settings_n].DockId = root_id; - break; - } - - // Not really efficient, but easier to destroy a whole hierarchy considering DockContextRemoveNode is attempting to merge nodes - if (nodes_to_remove.Size > 1) - ImQsort(nodes_to_remove.Data, nodes_to_remove.Size, sizeof(ImGuiDockNode*), DockNodeComparerDepthMostFirst); - for (int n = 0; n < nodes_to_remove.Size; n++) - DockContextRemoveNode(ctx, nodes_to_remove[n], false); - - if (root_id == 0) - { - dc->Nodes.Clear(); - dc->Requests.clear(); - } - else if (has_document_root) - { - root_node->IsDocumentRoot = true; - } -} - -void ImGui::DockBuilderRemoveNodeDockedWindows(ImGuiContext* ctx, ImGuiID root_id, bool clear_persistent_docking_references) -{ - // Clear references in settings - ImGuiContext& g = *ctx; - if (clear_persistent_docking_references) - { - for (int n = 0; n < g.SettingsWindows.Size; n++) - { - ImGuiWindowSettings* settings = &g.SettingsWindows[n]; - bool want_removal = (root_id == 0) || (settings->DockId == root_id); - if (!want_removal && settings->DockId != 0) - if (ImGuiDockNode* node = DockContextFindNodeByID(ctx, settings->DockId)) - if (DockNodeGetRootNode(node)->ID == root_id) - want_removal = true; - if (want_removal) - settings->DockId = 0; - } - } - - // Clear references in windows - for (int n = 0; n < g.Windows.Size; n++) - { - ImGuiWindow* window = g.Windows[n]; - bool want_removal = (root_id == 0) || (window->DockNode && DockNodeGetRootNode(window->DockNode)->ID == root_id) || (window->DockNodeAsHost && window->DockNodeAsHost->ID == root_id); - if (want_removal) - { - ImGuiID backup_dock_id = window->DockId; - DockContextProcessUndockWindow(ctx, window); - if (!clear_persistent_docking_references) - window->DockId = backup_dock_id; - } - } -} - static void ImGui::DockContextGcUnusedSettingsNodes(ImGuiContext* ctx) { ImGuiContext& g = *ctx; @@ -11488,22 +11400,21 @@ ImGuiDockNode* ImGui::DockNodeTreeFindNodeByPos(ImGuiDockNode* node, ImVec2 pos) { if (!node->IsVisible) return NULL; - if (node->IsSplitNode()) - { - if (ImGuiDockNode* hovered_node = DockNodeTreeFindNodeByPos(node->ChildNodes[0], pos)) - return hovered_node; - if (ImGuiDockNode* hovered_node = DockNodeTreeFindNodeByPos(node->ChildNodes[1], pos)) - return hovered_node; - } - else - { - ImGuiContext& g = *GImGui; - const float dock_spacing = g.Style.ItemInnerSpacing.x; - ImRect r(node->Pos, node->Pos + node->Size); - r.Expand(dock_spacing * 0.5f); - if (r.Contains(pos)) - return node; - } + + ImGuiContext& g = *GImGui; + const float dock_spacing = g.Style.ItemInnerSpacing.x; + ImRect r(node->Pos, node->Pos + node->Size); + r.Expand(dock_spacing * 0.5f); + bool inside = r.Contains(pos); + if (!inside) + return NULL; + + if (!node->IsSplitNode()) + return node; + if (ImGuiDockNode* hovered_node = DockNodeTreeFindNodeByPos(node->ChildNodes[0], pos)) + return hovered_node; + if (ImGuiDockNode* hovered_node = DockNodeTreeFindNodeByPos(node->ChildNodes[1], pos)) + return hovered_node; return NULL; } @@ -11601,6 +11512,7 @@ void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags doc // Docking: Builder Functions //----------------------------------------------------------------------------- // Very early end-user API to manipulate dock nodes. +// It is expected that those functions are all called _before_ the dockspace node submission. //----------------------------------------------------------------------------- void ImGui::DockBuilderDockWindow(ImGuiContext*, const char* window_name, ImGuiID node_id) @@ -11621,8 +11533,7 @@ void ImGui::DockBuilderDockWindow(ImGuiContext*, const char* window_name, ImGuiI } } -// Ensure a node is created -void ImGui::DockBuilderCreateNode(ImGuiContext* ctx, ImGuiID id, ImVec2 ref_size, ImGuiDockNodeFlags flags) +void ImGui::DockBuilderAddNode(ImGuiContext* ctx, ImGuiID id, ImVec2 ref_size, ImGuiDockNodeFlags flags) { DockSpace(id, ImVec2(0,0), flags | ImGuiDockNodeFlags_KeepAliveOnly); ImGuiDockNode* node = DockContextFindNodeByID(ctx, id); @@ -11630,6 +11541,106 @@ void ImGui::DockBuilderCreateNode(ImGuiContext* ctx, ImGuiID id, ImVec2 ref_size node->LastFrameAlive = -1; } +void ImGui::DockBuilderRemoveNode(ImGuiContext* ctx, ImGuiID node_id) +{ + ImGuiDockNode* node = DockContextFindNodeByID(ctx, node_id); + if (node == NULL) + return; + DockBuilderRemoveNodeDockedWindows(ctx, node_id, true); + DockBuilderRemoveNodeChildNodes(ctx, node_id); + if (node->IsDocumentRoot && node->ParentNode) + node->ParentNode->IsDocumentRoot = true; + DockContextRemoveNode(ctx, node, true); +} + +void ImGui::DockBuilderRemoveNodeChildNodes(ImGuiContext* ctx, ImGuiID root_id) +{ + ImGuiDockContext* dc = ctx->DockContext; + + ImGuiDockNode* root_node = root_id ? DockContextFindNodeByID(ctx, root_id) : NULL; + if (root_id && root_node == NULL) + return; + bool has_document_root = false; + + // Process active windows + ImVector nodes_to_remove; + for (int n = 0; n < dc->Nodes.Data.Size; n++) + if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p) + { + bool want_removal = (root_id == 0) || (node->ID != root_id && DockNodeGetRootNode(node)->ID == root_id); + if (want_removal) + { + if (node->IsDocumentRoot) + has_document_root = true; + if (root_id != 0) + DockContextQueueNotifyRemovedNode(ctx, node); + if (root_node) + DockNodeMoveWindows(root_node, node); + nodes_to_remove.push_back(node); + } + } + + // Apply to settings + for (int settings_n = 0; settings_n < ctx->SettingsWindows.Size; settings_n++) + if (ImGuiID window_settings_dock_id = ctx->SettingsWindows[settings_n].DockId) + for (int n = 0; n < nodes_to_remove.Size; n++) + if (nodes_to_remove[n]->ID == window_settings_dock_id) + { + ctx->SettingsWindows[settings_n].DockId = root_id; + break; + } + + // Not really efficient, but easier to destroy a whole hierarchy considering DockContextRemoveNode is attempting to merge nodes + if (nodes_to_remove.Size > 1) + ImQsort(nodes_to_remove.Data, nodes_to_remove.Size, sizeof(ImGuiDockNode*), DockNodeComparerDepthMostFirst); + for (int n = 0; n < nodes_to_remove.Size; n++) + DockContextRemoveNode(ctx, nodes_to_remove[n], false); + + if (root_id == 0) + { + dc->Nodes.Clear(); + dc->Requests.clear(); + } + else if (has_document_root) + { + root_node->IsDocumentRoot = true; + } +} + +void ImGui::DockBuilderRemoveNodeDockedWindows(ImGuiContext* ctx, ImGuiID root_id, bool clear_persistent_docking_references) +{ + // Clear references in settings + ImGuiContext& g = *ctx; + if (clear_persistent_docking_references) + { + for (int n = 0; n < g.SettingsWindows.Size; n++) + { + ImGuiWindowSettings* settings = &g.SettingsWindows[n]; + bool want_removal = (root_id == 0) || (settings->DockId == root_id); + if (!want_removal && settings->DockId != 0) + if (ImGuiDockNode* node = DockContextFindNodeByID(ctx, settings->DockId)) + if (DockNodeGetRootNode(node)->ID == root_id) + want_removal = true; + if (want_removal) + settings->DockId = 0; + } + } + + // Clear references in windows + for (int n = 0; n < g.Windows.Size; n++) + { + ImGuiWindow* window = g.Windows[n]; + bool want_removal = (root_id == 0) || (window->DockNode && DockNodeGetRootNode(window->DockNode)->ID == root_id) || (window->DockNodeAsHost && window->DockNodeAsHost->ID == root_id); + if (want_removal) + { + ImGuiID backup_dock_id = window->DockId; + DockContextProcessUndockWindow(ctx, window); + if (!clear_persistent_docking_references) + window->DockId = backup_dock_id; + } + } +} + ImGuiID ImGui::DockBuilderSplitNode(ImGuiContext* ctx, ImGuiID id, ImGuiDir split_dir, float size_ratio_for_node_at_dir, ImGuiID* out_id_at_dir, ImGuiID* out_id_other) { IM_ASSERT(split_dir != ImGuiDir_None); diff --git a/imgui_internal.h b/imgui_internal.h index 55c29b6d4aa9..4727564f6208 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1481,10 +1481,11 @@ namespace ImGui IMGUI_API void ShowDockingDebug(); // Docking - Builder function needs to be generally called before the DockSpace() node is submitted. + IMGUI_API void DockBuilderDockWindow(ImGuiContext* ctx, const char* window_name, ImGuiID node_id); + IMGUI_API void DockBuilderAddNode(ImGuiContext* ctx, ImGuiID node_id, ImVec2 ref_size, ImGuiDockNodeFlags flags = 0); + IMGUI_API void DockBuilderRemoveNode(ImGuiContext* ctx, ImGuiID node_id); IMGUI_API void DockBuilderRemoveNodeDockedWindows(ImGuiContext* ctx, ImGuiID node_id, bool clear_persistent_docking_references = true); IMGUI_API void DockBuilderRemoveNodeChildNodes(ImGuiContext* ctx, ImGuiID node_id); // Remove all split/hierarchy. All remaining docked windows will be re-docked to the root. - IMGUI_API void DockBuilderDockWindow(ImGuiContext* ctx, const char* window_name, ImGuiID node_id); - IMGUI_API void DockBuilderCreateNode(ImGuiContext* ctx, ImGuiID node_id, ImVec2 ref_size, ImGuiDockNodeFlags flags = 0); IMGUI_API ImGuiID DockBuilderSplitNode(ImGuiContext* ctx, ImGuiID node_id, ImGuiDir split_dir, float size_ratio_for_node_at_dir, ImGuiID* out_id_dir, ImGuiID* out_id_other); IMGUI_API void DockBuilderFinish(ImGuiContext* ctx, ImGuiID node_id); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 299b0d644854..827eb9ad56e4 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -704,7 +704,9 @@ bool ImGui::CollapseButton(ImGuiID id, const ImVec2& pos, ImGuiDockNode* dock_no } else { + ImVec2 backup_active_click_offset = g.ActiveIdClickOffset; StartMouseMovingWindow(window); + g.ActiveIdClickOffset = backup_active_click_offset; } } From 85e1e2b0c8ce0c07015c0cb69fda41d5a194e07c Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 25 Sep 2018 14:12:16 +0200 Subject: [PATCH 277/828] Docking: Added DockBuilderGetNode() wrapper for consistency. --- imgui.cpp | 13 +++++++++++-- imgui_internal.h | 3 ++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 64945f2a68a4..706edfa0afef 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -9540,6 +9540,7 @@ void ImGui::EndDragDropTarget() // B- full rebuild loses viewport of floating dock nodes // B- dock node inside its own viewports creates 1 temporary viewport per window on startup before ditching them // A~ Unreal style document system (requires low-level controls of dockspace serialization fork/copy/delete) +// B- implicit, invisible per-viewport dockspace to dock to. // B- resize sibling locking behavior may be less desirable if we merged same-axis sibling in a same node level? // A- single visible node part of a hidden split hierarchy (OnlyNodeWithWindows) should show a normal title bar (not a tab bar) // B~ SetNextWindowDock() calls (with conditional) -> defer everything to DockContextUpdate (repro: Documents->[X]Windows->Dock 1 elsewhere->Click Redock All @@ -9548,13 +9549,15 @@ void ImGui::EndDragDropTarget() // B- inconsistent clipping/border 1-pixel issue (#2) // B- fix/disable auto-resize grip on split host nodes (~#2) // B- SetNextWindowFocus() doesn't seem to apply if the window is hidden this frame, need repro (#4) -// B- implicit, invisible per-viewport dockspace to dock to // B- resizing a dock tree small currently has glitches (overlapping collapse and close button, etc.) +// B- dpi: look at interaction with the hi-dpi and multi-dpi stuff. // B- tab bar: appearing on first frame with a dumb layout would do less harm that not appearing? (when behind dynamic branch) or store titles + render in EndTabBar() // B- tab bar: make selected tab always shows its full title? // B- tab bar: the order/focus restoring code could be part of TabBar and not DockNode? (#8) // B- nav: CTRL+TAB highlighting tabs shows the mismatch between focus-stack and tab-order (not visible in VS because it doesn't highlight the tabs) // B- nav: design interactions so nav controls can dock/undock +// B- dockspace: flag to lock the dock tree and/or sizes +// C- allow dragging a non-floating dock node by clicking on the title-bar-looking section (not just the collapse/menu button) //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- @@ -9649,6 +9652,7 @@ namespace ImGui static void DockContextProcessUndockWindow(ImGuiContext* ctx, ImGuiWindow* window); static void DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node); static void DockContextGcUnusedSettingsNodes(ImGuiContext* ctx); + static ImGuiDockNode* DockContextFindNodeByID(ImGuiContext* ctx, ImGuiID id); static void DockContextClearNodes(ImGuiContext* ctx, ImGuiID root_id, bool clear_persistent_docking_references); // Set root_id==0 to clear all static void DockContextBuildNodesFromSettings(ImGuiContext* ctx, ImGuiDockNodeSettings* node_settings_array, int node_settings_count); static void DockContextBuildAddWindowsToNodes(ImGuiContext* ctx, ImGuiID root_id); // Use root_id==0 to add all @@ -9814,7 +9818,7 @@ void ImGui::DockContextEndFrame(ImGuiContext* ctx) (void)ctx; } -ImGuiDockNode* ImGui::DockContextFindNodeByID(ImGuiContext* ctx, ImGuiID id) +static ImGuiDockNode* ImGui::DockContextFindNodeByID(ImGuiContext* ctx, ImGuiID id) { return (ImGuiDockNode*)ctx->DockContext->Nodes.GetVoidPtr(id); } @@ -11533,6 +11537,11 @@ void ImGui::DockBuilderDockWindow(ImGuiContext*, const char* window_name, ImGuiI } } +ImGuiDockNode* ImGui::DockBuilderGetNode(ImGuiContext* ctx, ImGuiID node_id) +{ + return DockContextFindNodeByID(ctx, node_id); +} + void ImGui::DockBuilderAddNode(ImGuiContext* ctx, ImGuiID id, ImVec2 ref_size, ImGuiDockNodeFlags flags) { DockSpace(id, ImVec2(0,0), flags | ImGuiDockNodeFlags_KeepAliveOnly); diff --git a/imgui_internal.h b/imgui_internal.h index 4727564f6208..aeabbdba70f4 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1472,7 +1472,6 @@ namespace ImGui IMGUI_API void DockContextEndFrame(ImGuiContext* ctx); IMGUI_API void DockContextQueueUndockWindow(ImGuiContext* ctx, ImGuiWindow* window); IMGUI_API void DockContextQueueUndockNode(ImGuiContext* ctx, ImGuiDockNode* node); - IMGUI_API ImGuiDockNode*DockContextFindNodeByID(ImGuiContext* ctx, ImGuiID id); inline ImGuiDockNode* DockNodeGetRootNode(ImGuiDockNode* node) { while (node->ParentNode) node = node->ParentNode; return node; } IMGUI_API void BeginDocked(ImGuiWindow* window, bool* p_open); IMGUI_API void BeginAsDockableDragDropSource(ImGuiWindow* window); @@ -1482,11 +1481,13 @@ namespace ImGui // Docking - Builder function needs to be generally called before the DockSpace() node is submitted. IMGUI_API void DockBuilderDockWindow(ImGuiContext* ctx, const char* window_name, ImGuiID node_id); + IMGUI_API ImGuiDockNode*DockBuilderGetNode(ImGuiContext* ctx, ImGuiID node_id); IMGUI_API void DockBuilderAddNode(ImGuiContext* ctx, ImGuiID node_id, ImVec2 ref_size, ImGuiDockNodeFlags flags = 0); IMGUI_API void DockBuilderRemoveNode(ImGuiContext* ctx, ImGuiID node_id); IMGUI_API void DockBuilderRemoveNodeDockedWindows(ImGuiContext* ctx, ImGuiID node_id, bool clear_persistent_docking_references = true); IMGUI_API void DockBuilderRemoveNodeChildNodes(ImGuiContext* ctx, ImGuiID node_id); // Remove all split/hierarchy. All remaining docked windows will be re-docked to the root. IMGUI_API ImGuiID DockBuilderSplitNode(ImGuiContext* ctx, ImGuiID node_id, ImGuiDir split_dir, float size_ratio_for_node_at_dir, ImGuiID* out_id_dir, ImGuiID* out_id_other); + //IMGUI_API void DockBuilderForkNode(ImGuiContext* ctx, ImGuiID src_node_id, ImGuiID new_node_id); IMGUI_API void DockBuilderFinish(ImGuiContext* ctx, ImGuiID node_id); // Drag and Drop From 53a5d32df1ef3a8729d02460614cbf8173b0d6f0 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 25 Sep 2018 15:57:47 +0200 Subject: [PATCH 278/828] Docking: Reworked SetNextWindowUserType() as SetNextWindowDockFamily(), allowing multiple scene to be tagged with their scene id and not receive tabs from other scene + persistent storage for family id to allow for docking tree fork/remap. --- imgui.cpp | 43 +++++++++++++++++++++++++++++++------------ imgui.h | 14 ++++++++++++-- imgui_internal.h | 13 +++++++------ 3 files changed, 50 insertions(+), 20 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 706edfa0afef..a73b38c32aab 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2371,7 +2371,6 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) HiddenFramesRegular = HiddenFramesForResize = 0; SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = SetWindowDockAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing; SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX); - UserTypeId = 0; LastFrameActive = -1; ItemWidthDefault = 0.0f; @@ -4969,7 +4968,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) { window->SizeContentsExplicit = ImVec2(0.0f, 0.0f); } - window->UserTypeId = g.NextWindowData.UserTypeId; + window->DockFamily = g.NextWindowData.DockFamily; if (g.NextWindowData.CollapsedCond) SetWindowCollapsed(window, g.NextWindowData.CollapsedVal, g.NextWindowData.CollapsedCond); if (g.NextWindowData.FocusCond) @@ -6357,10 +6356,10 @@ void ImGui::SetNextWindowDock(ImGuiID id, ImGuiCond cond) g.NextWindowData.DockId = id; } -void ImGui::SetNextWindowUserType(ImGuiID user_type) +void ImGui::SetNextWindowDockFamily(const ImGuiDockFamily* family) { ImGuiContext& g = *GImGui; - g.NextWindowData.UserTypeId = user_type; + g.NextWindowData.DockFamily = *family; } // In window space (not screen space!) @@ -10178,19 +10177,19 @@ void ImGui::DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node) ImGuiDockNode::ImGuiDockNode(ImGuiID id) { ID = id; - UserTypeIdFilter = 0; Flags = 0; ParentNode = ChildNodes[0] = ChildNodes[1] = NULL; TabBar = NULL; SplitAxis = ImGuiAxis_None; + HostWindow = VisibleWindow = NULL; OnlyNodeWithWindows = NULL; LastFrameAlive = LastFrameActive = -1; LastFocusedNodeID = 0; SelectedTabID = 0; WantCloseTabID = 0; - IsVisible = true; InitFromFirstWindowPosSize = InitFromFirstWindowViewport = false; + IsVisible = true; IsDockSpace = IsDocumentRoot = HasCloseButton = HasCollapseButton = WantCloseAll = WantLockSizeOnce = WantMouseMove = false; } @@ -10493,9 +10492,18 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) DockNodeUpdateFindOnlyNodeWithWindowsRec(node, &count, &first_node_with_windows); node->OnlyNodeWithWindows = (count == 1 ? first_node_with_windows : NULL); - // Copy the user type from _any_ of our window so it can be used for proper dock filtering. + // Copy the dock family from of our window so it can be used for proper dock filtering. + // When node has mixed windows, prioritize the family with the most constraint (CompatibleWithNeutral = false) as the reference to copy. if (first_node_with_windows) - node->UserTypeIdFilter = first_node_with_windows->Windows[0]->UserTypeId; + { + node->DockFamily = first_node_with_windows->Windows[0]->DockFamily; + for (int n = 1; n < first_node_with_windows->Windows.Size; n++) + if (first_node_with_windows->Windows[n]->DockFamily.CompatibleWithFamilyZero == false) + { + node->DockFamily = first_node_with_windows->Windows[n]->DockFamily; + break; + } + } } } @@ -10863,9 +10871,16 @@ static bool DockNodeIsDropAllowedOne(ImGuiWindow* payload, ImGuiWindow* host_win if ((host_window->Flags & ImGuiWindowFlags_DockNodeHost) && host_window->DockNodeAsHost->IsDockSpace && payload->BeginOrderWithinContext < host_window->BeginOrderWithinContext) return false; - ImGuiID host_user_type_id = host_window->DockNodeAsHost ? host_window->DockNodeAsHost->UserTypeIdFilter : host_window->UserTypeId; - if (payload->UserTypeId != host_user_type_id) + ImGuiDockFamily* host_family = host_window->DockNodeAsHost ? &host_window->DockNodeAsHost->DockFamily : &host_window->DockFamily; + ImGuiDockFamily* payload_family = &payload->DockFamily; + if (host_family->FamilyId != payload_family->FamilyId) + { + if (host_family->FamilyId != 0 && host_family->CompatibleWithFamilyZero && payload_family->FamilyId == 0) + return true; + if (payload_family->FamilyId != 0 && payload_family->CompatibleWithFamilyZero && host_family->FamilyId == 0) + return true; return false; + } return true; } @@ -11443,7 +11458,7 @@ void ImGui::SetWindowDock(ImGuiWindow* window, ImGuiID dock_id, ImGuiCond cond) // Create an explicit dockspace node within an existing window. Also expose dock node flags and creates a DocumentRoot node by default. // The DocumentRoot node is always displayed even when empty and shrink/extend according to the requested size of its neighbors. -void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags dock_space_flags, ImGuiID user_type_filter) +void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags dock_space_flags, const ImGuiDockFamily* dock_family) { ImGuiContext* ctx = GImGui; ImGuiContext& g = *ctx; @@ -11456,7 +11471,7 @@ void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags doc node->IsDocumentRoot = true; } node->Flags = dock_space_flags; - node->UserTypeIdFilter = user_type_filter; + node->DockFamily = dock_family ? *dock_family : ImGuiDockFamily(); // When a Dockspace transitioned form implicit to explicit this may be called a second time // It is possible that the node has already been claimed by a docked window which appeared before the DockSpace() node, so we overwrite IsDockSpace again. @@ -12391,6 +12406,7 @@ static void SettingsHandlerWindow_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, else if (sscanf(line, "Collapsed=%d", &i) == 1) { settings->Collapsed = (i != 0); } else if (sscanf(line, "DockId=0x%X,%d", &u1, &i) == 2) { settings->DockId = u1; settings->DockOrder = (short)i; } else if (sscanf(line, "DockId=0x%X", &u1) == 1) { settings->DockId = u1; settings->DockOrder = -1; } + else if (sscanf(line, "DockFamilyId=0x%X", &u1) == 1) { settings->DockFamilyId = u1; } } static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf) @@ -12419,6 +12435,7 @@ static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSetting settings->ViewportPos = window->ViewportPos; IM_ASSERT(window->DockNode == NULL || window->DockNode->ID == window->DockId); settings->DockId = window->DockId; + settings->DockFamilyId = window->DockFamily.FamilyId; settings->DockOrder = window->DockOrder; settings->Collapsed = window->Collapsed; } @@ -12449,6 +12466,8 @@ static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSetting buf->appendf("DockId=0x%08X\n", settings->DockId); else buf->appendf("DockId=0x%08X,%d\n", settings->DockId, settings->DockOrder); + if (settings->DockFamilyId != 0) + buf->appendf("DockFamilyId=0x%08X\n", settings->DockFamilyId); } buf->appendf("\n"); } diff --git a/imgui.h b/imgui.h index 0c1385924487..c91e29d88d0d 100644 --- a/imgui.h +++ b/imgui.h @@ -77,6 +77,7 @@ struct ImColor; // Helper functions to create a color that c typedef void* ImTextureID; // User data to identify a texture (this is whatever to you want it to be! read the FAQ about ImTextureID in imgui.cpp) #endif struct ImGuiContext; // ImGui context (opaque) +struct ImGuiDockFamily; // Docking family for dock filtering struct ImGuiIO; // Main configuration and I/O between your application and ImGui struct ImGuiInputTextCallbackData; // Shared state of InputText() when using custom ImGuiInputTextCallback (rare/advanced use) struct ImGuiListClipper; // Helper to manually clip large list of items @@ -519,9 +520,9 @@ namespace ImGui // [BETA API] Enable with io.ConfigFlags |= ImGuiConfigFlags_DockingEnable. // Note: you DO NOT need to call DockSpace() to use most Docking facilities! You can hold SHIFT anywhere while moving windows. // Use DockSpace() to create an explicit dock node _within_ an existing window. See Docking demo for details. - IMGUI_API void DockSpace(ImGuiID id, const ImVec2& size = ImVec2(0, 0), ImGuiDockNodeFlags flags = 0, ImGuiID user_type_filter = 0); + IMGUI_API void DockSpace(ImGuiID id, const ImVec2& size = ImVec2(0, 0), ImGuiDockNodeFlags flags = 0, const ImGuiDockFamily* dock_family = NULL); IMGUI_API void SetNextWindowDock(ImGuiID dock_id, ImGuiCond cond = 0); // set next window dock id (FIXME-DOCK) - IMGUI_API void SetNextWindowUserType(ImGuiID user_type); // FIXME-DOCK: set next window user type (docking filters by same user_type) + IMGUI_API void SetNextWindowDockFamily(const ImGuiDockFamily* dock_family); // FIXME-DOCK: set next window user type (docking filters by same user_type) IMGUI_API bool IsWindowDocked(); // is current window docked into another window? // Logging/Capture: all text output from interface is captured to tty/file/clipboard. By default, tree nodes are automatically opened during logging. @@ -1563,6 +1564,15 @@ struct ImGuiSizeCallbackData ImVec2 DesiredSize; // Read-write. Desired size, based on user's mouse position. Write to this field to restrain resizing. }; +// For SetNextWindowDockFamily() and DockSpace() function +struct ImGuiDockFamily +{ + ImGuiID FamilyId; // 0 = unaffiliated + bool CompatibleWithFamilyZero; // true = can be docked/merged with an unaffiliated window + + ImGuiDockFamily() { FamilyId = 0; CompatibleWithFamilyZero = true; } +}; + // Data payload for Drag and Drop operations struct ImGuiPayload { diff --git a/imgui_internal.h b/imgui_internal.h index aeabbdba70f4..0d8f2d1b271a 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -541,10 +541,11 @@ struct ImGuiWindowSettings ImVec2 ViewportPos; ImGuiID ViewportId; ImGuiID DockId; // ID of last known DockNode (even if the DockNode is invisible because it has only 1 active window), or 0 if none. + ImGuiID DockFamilyId; // ID of dock family if specified short DockOrder; // Order of the last time the window was visible within its DockNode. This is used to reorder windows that are reappearing on the same frame. Same value between windows that were active and windows that were none are possible. bool Collapsed; - ImGuiWindowSettings() { Name = NULL; ID = 0; Pos = Size = ViewportPos = ImVec2(0, 0); ViewportId = DockId = 0; DockOrder = -1; Collapsed = false; } + ImGuiWindowSettings() { Name = NULL; ID = 0; Pos = Size = ViewportPos = ImVec2(0, 0); ViewportId = DockId = DockFamilyId = 0; DockOrder = -1; Collapsed = false; } }; struct ImGuiSettingsHandler @@ -704,7 +705,7 @@ struct ImGuiNextWindowData float BgAlphaVal; ImGuiID ViewportId; ImGuiID DockId; - ImGuiID UserTypeId; + ImGuiDockFamily DockFamily; ImVec2 MenuBarOffsetMinVal; // This is not exposed publicly, so we don't clear it. ImGuiNextWindowData() @@ -717,14 +718,14 @@ struct ImGuiNextWindowData SizeCallback = NULL; SizeCallbackUserData = NULL; BgAlphaVal = FLT_MAX; - ViewportId = DockId = UserTypeId = 0; + ViewportId = DockId = 0; MenuBarOffsetMinVal = ImVec2(0.0f, 0.0f); } void Clear() { PosCond = SizeCond = ContentSizeCond = CollapsedCond = SizeConstraintCond = FocusCond = BgAlphaCond = ViewportCond = DockCond = 0; - UserTypeId = 0; + DockFamily = ImGuiDockFamily(); } }; @@ -742,7 +743,6 @@ struct ImGuiTabBarSortItem struct ImGuiDockNode { ImGuiID ID; - ImGuiID UserTypeIdFilter; ImGuiDockNodeFlags Flags; ImGuiDockNode* ParentNode; ImGuiDockNode* ChildNodes[2]; // [Split node only] Child nodes (left/right or top/bottom). Consider switching to an array. @@ -752,6 +752,7 @@ struct ImGuiDockNode ImVec2 Size; // Current size ImVec2 SizeRef; // [Split node only] Last explicitly written-to size (overridden when using a splitter affecting the node), used to calculate Size. int SplitAxis; // [Split node only] Split axis (X or Y) + ImGuiDockFamily DockFamily; ImGuiWindow* HostWindow; ImGuiWindow* VisibleWindow; @@ -1208,7 +1209,7 @@ struct IMGUI_API ImGuiWindow ImGuiCond SetWindowDockAllowFlags; // store acceptable condition flags for SetNextWindowDock() use. ImVec2 SetWindowPosVal; // store window position when using a non-zero Pivot (position set needs to be processed when we know the window size) ImVec2 SetWindowPosPivot; // store window pivot for positioning. ImVec2(0,0) when positioning from top-left corner; ImVec2(0.5f,0.5f) for centering; ImVec2(1,1) for bottom right. - ImGuiID UserTypeId; // user value set with SetNextWindowUserType(const char*) + ImGuiDockFamily DockFamily; // set with SetNextWindowDockFamily() ImGuiWindowTempData DC; // Temporary per-window data, reset at the beginning of the frame. This used to be called ImGuiDrawContext, hence the "DC" variable name. ImVector IDStack; // ID stack. ID are hashes seeded with the value at the top of the stack From d3e8e5731ac04131bd9f22e33b2b7cb27c354103 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 25 Sep 2018 17:45:06 +0200 Subject: [PATCH 279/828] Docking: Renamed SetNextWindowDock() to SetNextWindowDockId(). Added GetWindowDockId(). --- imgui.cpp | 10 ++++++++-- imgui.h | 3 ++- imgui_demo.cpp | 2 +- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index a73b38c32aab..c4f94012f650 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6103,10 +6103,16 @@ bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags) } } +ImGuiID ImGui::GetWindowDockId() +{ + ImGuiContext& g = *GImGui; + return g.CurrentWindow->DockId; +} + bool ImGui::IsWindowDocked() { ImGuiContext& g = *GImGui; - return (g.CurrentWindow->DockIsActive); + return g.CurrentWindow->DockIsActive; } // Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext) @@ -6349,7 +6355,7 @@ void ImGui::SetNextWindowViewport(ImGuiID id) g.NextWindowData.ViewportId = id; } -void ImGui::SetNextWindowDock(ImGuiID id, ImGuiCond cond) +void ImGui::SetNextWindowDockId(ImGuiID id, ImGuiCond cond) { ImGuiContext& g = *GImGui; g.NextWindowData.DockCond = cond ? cond : ImGuiCond_Always; diff --git a/imgui.h b/imgui.h index c91e29d88d0d..98c56bee99ec 100644 --- a/imgui.h +++ b/imgui.h @@ -521,8 +521,9 @@ namespace ImGui // Note: you DO NOT need to call DockSpace() to use most Docking facilities! You can hold SHIFT anywhere while moving windows. // Use DockSpace() to create an explicit dock node _within_ an existing window. See Docking demo for details. IMGUI_API void DockSpace(ImGuiID id, const ImVec2& size = ImVec2(0, 0), ImGuiDockNodeFlags flags = 0, const ImGuiDockFamily* dock_family = NULL); - IMGUI_API void SetNextWindowDock(ImGuiID dock_id, ImGuiCond cond = 0); // set next window dock id (FIXME-DOCK) + IMGUI_API void SetNextWindowDockId(ImGuiID dock_id, ImGuiCond cond = 0); // set next window dock id (FIXME-DOCK) IMGUI_API void SetNextWindowDockFamily(const ImGuiDockFamily* dock_family); // FIXME-DOCK: set next window user type (docking filters by same user_type) + IMGUI_API ImGuiID GetWindowDockId(); IMGUI_API bool IsWindowDocked(); // is current window docked into another window? // Logging/Capture: all text output from interface is captured to tty/file/clipboard. By default, tree nodes are automatically opened during logging. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index b1997012caa3..4140cec1a04f 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3973,7 +3973,7 @@ void ShowExampleAppDocuments(bool* p_open) // FIXME-DOCK: SetNextWindowDock() //ImGuiID default_dock_id = GetDockspaceRootDocumentDockID(); //ImGuiID default_dock_id = GetDockspacePreferedDocumentDockID(); - ImGui::SetNextWindowDock(dockspace_id, redock_all ? ImGuiCond_Always : ImGuiCond_FirstUseEver); + ImGui::SetNextWindowDockId(dockspace_id, redock_all ? ImGuiCond_Always : ImGuiCond_FirstUseEver); ImGuiWindowFlags window_flags = (doc->Dirty ? ImGuiWindowFlags_UnsavedDocument : 0); bool visible = ImGui::Begin(doc->Name, &doc->Open, window_flags); From dc924313283397813aade270c583762a2858b305 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 25 Sep 2018 22:05:53 +0200 Subject: [PATCH 280/828] Docking: Minor renaming. DockContextAddNode() uses 0 as marker for automatic ID selection + TODO update and moved docking entries to docs/TODO.txt --- docs/TODO.txt | 26 ++++++++++++++++++++++++-- imgui.cpp | 48 +++++++++++------------------------------------- imgui.h | 4 ++-- 3 files changed, 37 insertions(+), 41 deletions(-) diff --git a/docs/TODO.txt b/docs/TODO.txt index 49706570fcd3..10b7b9514377 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -123,8 +123,30 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - splitter/separator: formalize the splitter idiom into an official api (we want to handle n-way split) (#319) - - dock: docking extension - - dock: dock out from a collapsing header? would work nicely but need emitting window to keep submitting the code. + - dock: A~ Unreal style document system (requires low-level controls of dockspace serialization fork/copy/delete) + - dock: A- implicit, invisible per-viewport dockspace to dock to. + - dock: B~ document root node resizing behavior incorrect + - dock: B~ document root node retrieval of ID ? + - dock: B- full rebuild loses viewport of floating dock nodes + - dock: B- dock node inside its own viewports creates 1 temporary viewport per window on startup before ditching them + - dock: B- resize sibling locking behavior may be less desirable if we merged same-axis sibling in a same node level? + - dock: A- single visible node part of a hidden split hierarchy (OnlyNodeWithWindows != NULL) should show a normal title bar (not a tab bar) + - dock: B~ SetNextWindowDock() calls (with conditional) -> defer everything to DockContextUpdate (repro: Documents->[X]Windows->Dock 1 elsewhere->Click Redock All + - dock: B~ tidy up tab list popup buttons features (available with manual tab-bar, see ImGuiTabBarFlags_NoTabListPopupButton code, not used by docking nodes) + - dock: B- DockSpace() border issues + - dock: B- inconsistent clipping/border 1-pixel issue (#2) + - dock: B- fix/disable auto-resize grip on split host nodes (~#2) + - dock: B- SetNextWindowFocus() doesn't seem to apply if the window is hidden this frame, need repro (#4) + - dock: B- resizing a dock tree small currently has glitches (overlapping collapse and close button, etc.) + - dock: B- dpi: look at interaction with the hi-dpi and multi-dpi stuff. + - dock: B- tab bar: appearing on first frame with a dumb layout would do less harm that not appearing? (when behind dynamic branch) or store titles + render in EndTabBar() + - dock: B- tab bar: make selected tab always shows its full title? + - dock: B- tab bar: the order/focus restoring code could be part of TabBar and not DockNode? (#8) + - dock: B- nav: design interactions so nav controls can dock/undock + - dock: B- dockspace: flag to lock the dock tree and/or sizes + - dock: B- reintroduce collapsing a floating dock node. also collapsing a docked dock node! + - dock: B- allow dragging a non-floating dock node by clicking on the title-bar-looking section (not just the collapse/menu button) + - dock: C- nav: CTRL+TAB highlighting tabs shows the mismatch between focus-stack and tab-order (not visible in VS because it doesn't highlight the tabs) - tabs: re-ordering, close buttons, context menu, persistent order (#261, #351) diff --git a/imgui.cpp b/imgui.cpp index c4f94012f650..53460148fee7 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -9539,31 +9539,6 @@ void ImGui::EndDragDropTarget() // Docking: Begin/End Functions (called from Begin/End) // Docking: Settings //----------------------------------------------------------------------------- -// TODO: -// A~ document root node resizing behavior incorrect -// A~ document root node retrieval of ID ? -// B- full rebuild loses viewport of floating dock nodes -// B- dock node inside its own viewports creates 1 temporary viewport per window on startup before ditching them -// A~ Unreal style document system (requires low-level controls of dockspace serialization fork/copy/delete) -// B- implicit, invisible per-viewport dockspace to dock to. -// B- resize sibling locking behavior may be less desirable if we merged same-axis sibling in a same node level? -// A- single visible node part of a hidden split hierarchy (OnlyNodeWithWindows) should show a normal title bar (not a tab bar) -// B~ SetNextWindowDock() calls (with conditional) -> defer everything to DockContextUpdate (repro: Documents->[X]Windows->Dock 1 elsewhere->Click Redock All -// B~ tidy up tab list popup buttons (see old ImGuiTabBarFlags_NoTabListPopupButton code) -// B- DockSpace() border issues -// B- inconsistent clipping/border 1-pixel issue (#2) -// B- fix/disable auto-resize grip on split host nodes (~#2) -// B- SetNextWindowFocus() doesn't seem to apply if the window is hidden this frame, need repro (#4) -// B- resizing a dock tree small currently has glitches (overlapping collapse and close button, etc.) -// B- dpi: look at interaction with the hi-dpi and multi-dpi stuff. -// B- tab bar: appearing on first frame with a dumb layout would do less harm that not appearing? (when behind dynamic branch) or store titles + render in EndTabBar() -// B- tab bar: make selected tab always shows its full title? -// B- tab bar: the order/focus restoring code could be part of TabBar and not DockNode? (#8) -// B- nav: CTRL+TAB highlighting tabs shows the mismatch between focus-stack and tab-order (not visible in VS because it doesn't highlight the tabs) -// B- nav: design interactions so nav controls can dock/undock -// B- dockspace: flag to lock the dock tree and/or sizes -// C- allow dragging a non-floating dock node by clicking on the title-bar-looking section (not just the collapse/menu button) -//----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // Docking: Internal Types @@ -9831,7 +9806,7 @@ static ImGuiDockNode* ImGui::DockContextFindNodeByID(ImGuiContext* ctx, ImGuiID static ImGuiDockNode* ImGui::DockContextAddNode(ImGuiContext* ctx, ImGuiID id) { // Generate an ID for the new node (the exact ID value doesn't matter as long as it is not already used) and add the first window. - if (id == (ImGuiID)-1) + if (id == 0) { // FIXME-OPT: This is suboptimal, even if the node count is small enough not to be a worry. We could poke in ctx->Nodes to find a suitable ID faster. id = 0x0001; @@ -10045,7 +10020,7 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) // Create new node and add existing window to it if (target_node == NULL) { - target_node = DockContextAddNode(ctx, (ImGuiID)-1); + target_node = DockContextAddNode(ctx, 0); target_node->Pos = target_window->Pos; target_node->Size = target_window->Size; if (target_window->DockNodeAsHost == NULL) @@ -10156,7 +10131,7 @@ void ImGui::DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node) if (node->IsRootNode()) { // FIXME-DOCK: Transition persistent DockId for all non-active windows - ImGuiDockNode* new_node = DockContextAddNode(ctx, (ImGuiID)-1); + ImGuiDockNode* new_node = DockContextAddNode(ctx, 0); DockNodeMoveWindows(new_node, node); for (int n = 0; n < new_node->Windows.Size; n++) UpdateWindowParentAndRootLinks(new_node->Windows[n], new_node->Windows[n]->Flags, NULL); @@ -10879,11 +10854,11 @@ static bool DockNodeIsDropAllowedOne(ImGuiWindow* payload, ImGuiWindow* host_win ImGuiDockFamily* host_family = host_window->DockNodeAsHost ? &host_window->DockNodeAsHost->DockFamily : &host_window->DockFamily; ImGuiDockFamily* payload_family = &payload->DockFamily; - if (host_family->FamilyId != payload_family->FamilyId) + if (host_family->ID != payload_family->ID) { - if (host_family->FamilyId != 0 && host_family->CompatibleWithFamilyZero && payload_family->FamilyId == 0) + if (host_family->ID != 0 && host_family->CompatibleWithFamilyZero && payload_family->ID == 0) return true; - if (payload_family->FamilyId != 0 && payload_family->CompatibleWithFamilyZero && host_family->FamilyId == 0) + if (payload_family->ID != 0 && payload_family->CompatibleWithFamilyZero && host_family->ID == 0) return true; return false; } @@ -11170,10 +11145,10 @@ void ImGui::DockNodeTreeSplit(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImG { IM_ASSERT(split_axis != ImGuiAxis_None); - ImGuiDockNode* child_0 = (new_node && split_inheritor_child_idx != 0) ? new_node : DockContextAddNode(ctx, (ImGuiID)-1); + ImGuiDockNode* child_0 = (new_node && split_inheritor_child_idx != 0) ? new_node : DockContextAddNode(ctx, 0); child_0->ParentNode = parent_node; - ImGuiDockNode* child_1 = (new_node && split_inheritor_child_idx != 1) ? new_node : DockContextAddNode(ctx, (ImGuiID)-1); + ImGuiDockNode* child_1 = (new_node && split_inheritor_child_idx != 1) ? new_node : DockContextAddNode(ctx, 0); child_1->ParentNode = parent_node; ImGuiDockNode* child_inheritor = (split_inheritor_child_idx == 0) ? child_0 : child_1; @@ -11970,8 +11945,6 @@ static void ImGui::DockSettingsHandler_ReadLine(ImGuiContext* ctx, ImGuiSettings if (sscanf(line, " Split=%c%n", &c, &r) == 1) { line += r; if (c == 'X') node.SplitAxis = ImGuiAxis_X; else if (c == 'Y') node.SplitAxis = ImGuiAxis_Y; } if (sscanf(line, " DocRoot=%d%n", &x, &r) == 1) { line += r; node.IsDocumentRoot = (x != 0); } if (sscanf(line, " SelectedTab=0x%08X%n", &node.SelectedTabID,&r) == 1) { line += r; } - //if (node.ParentID == 0 && node.SplitAxis == ImGuiAxis_None) - // return; ImGuiDockContext* dc = ctx->DockContext; if (node.ParentID != 0) if (ImGuiDockNodeSettings* parent_settings = DockSettingsFindNodeSettings(ctx, node.ParentID)) @@ -12035,7 +12008,8 @@ static void ImGui::DockSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettings if (node_settings->SelectedTabID) buf->appendf(" SelectedTab=0x%08X", node_settings->SelectedTabID); -#if 0 // [DEBUG] Include comments in the .ini file to ease debugging +#if 0 + // [DEBUG] Include comments in the .ini file to ease debugging if (ImGuiDockNode* node = DockContextFindNodeByID(ctx, node_settings->ID)) { buf->appendf("%*s", ImMax(2, (line_start_pos + 90) - buf->size()), ""); // Align everything @@ -12441,7 +12415,7 @@ static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSetting settings->ViewportPos = window->ViewportPos; IM_ASSERT(window->DockNode == NULL || window->DockNode->ID == window->DockId); settings->DockId = window->DockId; - settings->DockFamilyId = window->DockFamily.FamilyId; + settings->DockFamilyId = window->DockFamily.ID; settings->DockOrder = window->DockOrder; settings->Collapsed = window->Collapsed; } diff --git a/imgui.h b/imgui.h index 98c56bee99ec..17f22741b12f 100644 --- a/imgui.h +++ b/imgui.h @@ -1568,10 +1568,10 @@ struct ImGuiSizeCallbackData // For SetNextWindowDockFamily() and DockSpace() function struct ImGuiDockFamily { - ImGuiID FamilyId; // 0 = unaffiliated + ImGuiID ID; // 0 = unaffiliated bool CompatibleWithFamilyZero; // true = can be docked/merged with an unaffiliated window - ImGuiDockFamily() { FamilyId = 0; CompatibleWithFamilyZero = true; } + ImGuiDockFamily() { ID = 0; CompatibleWithFamilyZero = true; } }; // Data payload for Drag and Drop operations From 45731cca19394ef8fe4e067c23c4b850df0802c8 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 26 Sep 2018 13:09:41 +0200 Subject: [PATCH 281/828] Docking: Added DockBuilderForkNode() primitive for usage for user code to fork/copy entire layouts. This will probably need to be added to as I find ways to migrate patterns from user code to the library. Added IMGUI_DEBUG_DOCKING_INI helper to facilitate debugging. (+1 squashed commits) --- imgui.cpp | 62 +++++++++++++++++++++++++++++++++++++++++------- imgui.h | 5 ++-- imgui_internal.h | 2 +- 3 files changed, 57 insertions(+), 12 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 53460148fee7..3f283483180f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -867,8 +867,9 @@ CODE #include // intptr_t #endif -#define IMGUI_DEBUG_NAV_SCORING 0 -#define IMGUI_DEBUG_NAV_RECTS 0 +#define IMGUI_DEBUG_NAV_SCORING 0 // Display navigation scoring preview when hovering items. Display last moving direction matches when holding CTRL +#define IMGUI_DEBUG_NAV_RECTS 0 // Display the reference navigation rectangle for each window +#define IMGUI_DEBUG_DOCKING_INI 0 // Save additional comments in .ini file (makes saving slower) // Visual Studio warnings #ifdef _MSC_VER @@ -9631,7 +9632,7 @@ namespace ImGui static void DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req); static void DockContextProcessUndockWindow(ImGuiContext* ctx, ImGuiWindow* window); static void DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node); - static void DockContextGcUnusedSettingsNodes(ImGuiContext* ctx); + static void DockContextPruneUnusedSettingsNodes(ImGuiContext* ctx); static ImGuiDockNode* DockContextFindNodeByID(ImGuiContext* ctx, ImGuiID id); static void DockContextClearNodes(ImGuiContext* ctx, ImGuiID root_id, bool clear_persistent_docking_references); // Set root_id==0 to clear all static void DockContextBuildNodesFromSettings(ImGuiContext* ctx, ImGuiDockNodeSettings* node_settings_array, int node_settings_count); @@ -9715,7 +9716,7 @@ void ImGui::DockContextShutdown(ImGuiContext* ctx) void ImGui::DockContextOnLoadSettings(ImGuiContext* ctx) { ImGuiDockContext* dc = ctx->DockContext; - DockContextGcUnusedSettingsNodes(ctx); + DockContextPruneUnusedSettingsNodes(ctx); DockContextBuildNodesFromSettings(ctx, dc->SettingsNodes.Data, dc->SettingsNodes.Size); } @@ -9725,7 +9726,7 @@ void ImGui::DockContextClearNodes(ImGuiContext* ctx, ImGuiID root_id, bool clear DockBuilderRemoveNodeChildNodes(ctx, root_id); } -// This function also acts as a de-facto test to make sure we can rebuild from scratch without a glitch +// This function also acts as a defacto test to make sure we can rebuild from scratch without a glitch void ImGui::DockContextRebuild(ImGuiContext* ctx) { //IMGUI_DEBUG_LOG("[docking] full rebuild\n"); @@ -9858,7 +9859,8 @@ static int IMGUI_CDECL DockNodeComparerDepthMostFirst(const void* lhs, const voi return ImGui::DockNodeGetDepth(b) - ImGui::DockNodeGetDepth(a); } -static void ImGui::DockContextGcUnusedSettingsNodes(ImGuiContext* ctx) +// Garbage collect unused nodes (run once at init time) +static void ImGui::DockContextPruneUnusedSettingsNodes(ImGuiContext* ctx) { ImGuiContext& g = *ctx; ImGuiDockContext* dc = ctx->DockContext; @@ -11678,6 +11680,47 @@ ImGuiID ImGui::DockBuilderSplitNode(ImGuiContext* ctx, ImGuiID id, ImGuiDir spli return id_at_dir; } +static ImGuiDockNode* DockBuilderCloneNodeForFork(ImGuiContext* ctx, ImGuiDockNode* src_node, ImGuiID dst_node_id_if_known, ImVector* out_node_remap_pairs) +{ + ImGuiDockNode* dst_node = ImGui::DockContextAddNode(ctx, dst_node_id_if_known); + dst_node->Flags = src_node->Flags; + dst_node->Pos = src_node->Pos; + dst_node->Size = src_node->Size; + dst_node->SizeRef = src_node->SizeRef; + dst_node->SplitAxis = src_node->SplitAxis; + dst_node->IsDockSpace = src_node->IsDockSpace; + dst_node->IsDocumentRoot = src_node->IsDocumentRoot; + + out_node_remap_pairs->push_back(src_node->ID); + out_node_remap_pairs->push_back(dst_node->ID); + + for (int child_n = 0; child_n < IM_ARRAYSIZE(src_node->ChildNodes); child_n++) + if (src_node->ChildNodes[child_n]) + { + dst_node->ChildNodes[child_n] = DockBuilderCloneNodeForFork(ctx, src_node->ChildNodes[child_n], 0, out_node_remap_pairs); + dst_node->ChildNodes[child_n]->ParentNode = dst_node; + } + + //IMGUI_DEBUG_LOG("Fork node %08X -> %08X (%d childs)\n", src_node->ID, dst_node->ID, dst_node->IsSplitNode() ? 2 : 0); + return dst_node; +} + +void ImGui::DockBuilderForkNode(ImGuiContext* ctx, ImGuiID src_node_id, ImGuiID dst_node_id, ImVector* out_node_remap_pairs) +{ + IM_ASSERT(src_node_id != 0); + IM_ASSERT(dst_node_id != 0); + IM_ASSERT(out_node_remap_pairs != NULL); + + ImGuiDockNode* src_node = DockContextFindNodeByID(ctx, src_node_id); + IM_ASSERT(src_node != NULL); + + out_node_remap_pairs->clear(); + DockBuilderRemoveNode(ctx, dst_node_id); + DockBuilderCloneNodeForFork(ctx, src_node, dst_node_id, out_node_remap_pairs); + + IM_ASSERT((out_node_remap_pairs->Size % 2) == 0); +} + void ImGui::DockBuilderFinish(ImGuiContext* ctx, ImGuiID root_id) { DockContextBuildAddWindowsToNodes(ctx, root_id); @@ -11993,7 +12036,9 @@ static void ImGui::DockSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettings buf->appendf("[%s][Data]\n", handler->TypeName); for (int node_n = 0; node_n < dc->SettingsNodes.Size; node_n++) { - //const int line_start_pos = buf->size(); +#if IMGUI_DEBUG_DOCKING_INI + const int line_start_pos = buf->size(); +#endif const ImGuiDockNodeSettings* node_settings = &dc->SettingsNodes[node_n]; buf->appendf("%*s%s%*s", node_settings->Depth * 2, "", node_settings->IsDockSpace ? "DockSpace" : "DockNode ", (max_depth - node_settings->Depth) * 2, ""); // Text align nodes to facilitate looking at .ini file buf->appendf(" ID=0x%08X", node_settings->ID); @@ -12008,14 +12053,13 @@ static void ImGui::DockSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettings if (node_settings->SelectedTabID) buf->appendf(" SelectedTab=0x%08X", node_settings->SelectedTabID); -#if 0 +#if IMGUI_DEBUG_DOCKING_INI // [DEBUG] Include comments in the .ini file to ease debugging if (ImGuiDockNode* node = DockContextFindNodeByID(ctx, node_settings->ID)) { buf->appendf("%*s", ImMax(2, (line_start_pos + 90) - buf->size()), ""); // Align everything if (node->IsDockSpace && node->HostWindow && node->HostWindow->ParentWindow) buf->appendf(" ; in '%s'", node->HostWindow->ParentWindow->Name); - int contains_window = 0; for (int window_n = 0; window_n < ctx->SettingsWindows.Size; window_n++) if (ctx->SettingsWindows[window_n].DockId == node_settings->ID) diff --git a/imgui.h b/imgui.h index 17f22741b12f..a15f121f9c37 100644 --- a/imgui.h +++ b/imgui.h @@ -1565,13 +1565,14 @@ struct ImGuiSizeCallbackData ImVec2 DesiredSize; // Read-write. Desired size, based on user's mouse position. Write to this field to restrain resizing. }; -// For SetNextWindowDockFamily() and DockSpace() function +// [BETA] For SetNextWindowDockFamily() and DockSpace() function struct ImGuiDockFamily { ImGuiID ID; // 0 = unaffiliated bool CompatibleWithFamilyZero; // true = can be docked/merged with an unaffiliated window - ImGuiDockFamily() { ID = 0; CompatibleWithFamilyZero = true; } + ImGuiDockFamily() { ID = 0; CompatibleWithFamilyZero = true; } + ImGuiDockFamily(ImGuiID id, bool compatible_with_family_zero = true) { ID = id; CompatibleWithFamilyZero = compatible_with_family_zero; } }; // Data payload for Drag and Drop operations diff --git a/imgui_internal.h b/imgui_internal.h index 0d8f2d1b271a..bd86988e2047 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1488,7 +1488,7 @@ namespace ImGui IMGUI_API void DockBuilderRemoveNodeDockedWindows(ImGuiContext* ctx, ImGuiID node_id, bool clear_persistent_docking_references = true); IMGUI_API void DockBuilderRemoveNodeChildNodes(ImGuiContext* ctx, ImGuiID node_id); // Remove all split/hierarchy. All remaining docked windows will be re-docked to the root. IMGUI_API ImGuiID DockBuilderSplitNode(ImGuiContext* ctx, ImGuiID node_id, ImGuiDir split_dir, float size_ratio_for_node_at_dir, ImGuiID* out_id_dir, ImGuiID* out_id_other); - //IMGUI_API void DockBuilderForkNode(ImGuiContext* ctx, ImGuiID src_node_id, ImGuiID new_node_id); + IMGUI_API void DockBuilderForkNode(ImGuiContext* ctx, ImGuiID src_node_id, ImGuiID dst_node_id, ImVector* out_node_remap_pairs); IMGUI_API void DockBuilderFinish(ImGuiContext* ctx, ImGuiID node_id); // Drag and Drop From 4053d9d638f258da513f65f4bbf02926222b10eb Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 26 Sep 2018 17:07:58 +0200 Subject: [PATCH 282/828] Docking: Fixed tab order restoring (follow up to 67be485e, broken in 455dc6e2) --- imgui.cpp | 23 +++++++++++++++-------- imgui_internal.h | 7 ++++--- imgui_widgets.cpp | 12 +++++------- 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 3f283483180f..3b46886e8e37 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10229,7 +10229,7 @@ static void ImGui::DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window, b node->TabBar = IM_NEW(ImGuiTabBar)(); node->TabBar->SelectedTabId = node->TabBar->NextSelectedTabId = node->SelectedTabID; } - TabBarAddTab(node->TabBar, window); + TabBarAddTab(node->TabBar, window, ImGuiTabItemFlags_Unsorted); } DockNodeUpdateVisibleFlag(node); @@ -10729,15 +10729,22 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w tab_bar->SelectedTabId = focused_id; } - // Submit tabs order - // If multiple tabs are appearing on the same frame, we add them ahead sorted based on their persistent DockOrder value - int tabs_count_old = tab_bar->Tabs.Size; + // Submit new tabs + const int tabs_count_old = tab_bar->Tabs.Size; for (int window_n = 0; window_n < node->Windows.Size; window_n++) if (TabBarFindTabByID(tab_bar, node->Windows[window_n]->ID) == NULL) - TabBarAddTab(tab_bar, node->Windows[window_n]); - //printf("[%05d] Sorting %d new appearing tabs\n", g.FrameCount, tab_bar->Tabs.Size - tabs_count_old); - if (tab_bar->Tabs.Size > tabs_count_old + 1) - ImQsort(tab_bar->Tabs.Data + tabs_count_old, tab_bar->Tabs.Size - tabs_count_old, sizeof(ImGuiTabItem), TabItemComparerByDockOrder); + TabBarAddTab(tab_bar, node->Windows[window_n], ImGuiTabItemFlags_Unsorted); + + // If multiple tabs are appearing on the same frame, sort them based on their persistent DockOrder value + int tabs_unsorted_start = tab_bar->Tabs.Size; + for (int tab_n = tab_bar->Tabs.Size - 1; tab_n >= 0 && (tab_bar->Tabs[tab_n].Flags & ImGuiTabItemFlags_Unsorted); tab_n--) + { + tab_bar->Tabs[tab_n].Flags &= ~ImGuiTabItemFlags_Unsorted; + tabs_unsorted_start = tab_n; + } + //printf("[%05d] Sorting %d new appearing tabs\n", g.FrameCount, tab_bar->Tabs.Size - tabs_unsorted_start); + if (tab_bar->Tabs.Size > tabs_unsorted_start + 1) + ImQsort(tab_bar->Tabs.Data + tabs_unsorted_start, tab_bar->Tabs.Size - tabs_unsorted_start, sizeof(ImGuiTabItem), TabItemComparerByDockOrder); // Selected newly added tabs, or persistent tab ID if the tab bar was just recreated if (tab_bar_is_recreated && TabBarFindTabByID(tab_bar, node->SelectedTabID) != NULL) diff --git a/imgui_internal.h b/imgui_internal.h index bd86988e2047..4a8c79e6cdcf 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1304,8 +1304,9 @@ enum ImGuiTabBarFlagsPrivate_ enum ImGuiTabItemFlagsPrivate_ { - ImGuiTabItemFlags_DockedWindow = 1 << 20, - ImGuiTabItemFlags_Preview = 1 << 21 + ImGuiTabItemFlags_DockedWindow = 1 << 20, // [Docking] + ImGuiTabItemFlags_Unsorted = 1 << 22, // [Docking] Trailing tabs with the _Unsorted flag will be sorted based on the DockOrder of their Window. + ImGuiTabItemFlags_Preview = 1 << 21 // [Docking] Display tab shape for docking preview (height is adjusted slightly to compensate for the yet missing tab bar) }; // Storage for one active tab item (sizeof() 32~40 bytes) @@ -1504,7 +1505,7 @@ namespace ImGui // Tab Bars IMGUI_API bool BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& bb, ImGuiTabBarFlags flags, ImGuiDockNode* dock_node); IMGUI_API ImGuiTabItem* TabBarFindTabByID(ImGuiTabBar* tab_bar, ImGuiID tab_id); - IMGUI_API void TabBarAddTab(ImGuiTabBar* tab_bar, ImGuiWindow* window); + IMGUI_API void TabBarAddTab(ImGuiTabBar* tab_bar, ImGuiWindow* window, ImGuiTabItemFlags tab_flags = ImGuiTabItemFlags_None); IMGUI_API void TabBarRemoveTab(ImGuiTabBar* tab_bar, ImGuiID tab_id); IMGUI_API void TabBarCloseTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab); IMGUI_API void TabBarQueueChangeTabOrder(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, int dir); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 827eb9ad56e4..274b7bf4fbf7 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6106,22 +6106,20 @@ ImGuiTabItem* ImGui::TabBarFindTabByID(ImGuiTabBar* tab_bar, ImGuiID tab_id) // The purpose of this call is to register tab in advance so we can control their order at the time they appear. // Otherwise calling this is unnecessary as tabs are appending as needed by the BeginTabItem() function. -void ImGui::TabBarAddTab(ImGuiTabBar* tab_bar, ImGuiWindow* window) +void ImGui::TabBarAddTab(ImGuiTabBar* tab_bar, ImGuiWindow* window, ImGuiTabItemFlags tab_flags) { ImGuiContext& g = *GImGui; IM_ASSERT(TabBarFindTabByID(tab_bar, window->ID) == NULL); - IM_ASSERT(g.CurrentTabBar.empty()); // Can't work while the tab bar is active as our tab doesn't have an X offset yet + IM_ASSERT(g.CurrentTabBar.empty()); // Can't work while the tab bar is active as our tab doesn't have an X offset yet ImGuiTabItem new_tab; new_tab.ID = window->ID; - new_tab.LastFrameVisible = tab_bar->CurrFrameVisible; // Required so BeginTabBar doesn't ditch the tab + new_tab.Flags = tab_flags; + new_tab.LastFrameVisible = tab_bar->CurrFrameVisible; // Required so BeginTabBar() doesn't ditch the tab if (new_tab.LastFrameVisible == -1) new_tab.LastFrameVisible = g.FrameCount - 1; new_tab.Window = window; // Required so tab bar layout can compute the tab width before tab submission - if (tab_bar->Tabs.empty()) - tab_bar->Tabs.push_back(new_tab); - else - tab_bar->Tabs.insert(tab_bar->Tabs.Data + tab_bar->Tabs.Size, new_tab); + tab_bar->Tabs.push_back(new_tab); } // The *TabId fields be already set by the docking system _before_ the actual TabItem was created, so we clear them regardless. From c0a89f8f4e0b71d4fddaf700952b1a5d788c8dc1 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 26 Sep 2018 21:51:30 +0200 Subject: [PATCH 283/828] ImPool: Added Reserve() helper function. --- imgui_internal.h | 1 + 1 file changed, 1 insertion(+) diff --git a/imgui_internal.h b/imgui_internal.h index 4a8c79e6cdcf..4094fa6fe0b1 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -241,6 +241,7 @@ struct IMGUI_API ImPool T* Add() { int idx = FreeIdx; if (idx == Data.Size) { Data.resize(Data.Size + 1); FreeIdx++; } else { FreeIdx = *(int*)&Data[idx]; } IM_PLACEMENT_NEW(&Data[idx]) T(); return &Data[idx]; } void Remove(ImGuiID key, const T* p) { Remove(key, GetIndex(p)); } void Remove(ImGuiID key, int idx) { Data[idx].~T(); *(int*)&Data[idx] = FreeIdx; FreeIdx = idx; Map.SetInt(key, -1); } + void Reserve(int capacity) { Data.reserve(capacity); Map.Data.reserve(capacity); } }; typedef int ImPoolIdx; From 18ffb7dd863f1480ff815badb9f97f5078ac0bb3 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 26 Sep 2018 22:18:07 +0200 Subject: [PATCH 284/828] Docking: Reworked DockContextPruneUnusedSettingsNodes() to prune entire unused trees. --- docs/TODO.txt | 1 + imgui.cpp | 63 ++++++++++++++++++++++++++++++++------------------- 2 files changed, 41 insertions(+), 23 deletions(-) diff --git a/docs/TODO.txt b/docs/TODO.txt index 10b7b9514377..f6d233041e76 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -125,6 +125,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - dock: A~ Unreal style document system (requires low-level controls of dockspace serialization fork/copy/delete) - dock: A- implicit, invisible per-viewport dockspace to dock to. + - dock: B: when docking outer, perform size locking on neighbors nodes the same way we do it with splitters, so other nodes are not resized - dock: B~ document root node resizing behavior incorrect - dock: B~ document root node retrieval of ID ? - dock: B- full rebuild loses viewport of floating dock nodes diff --git a/imgui.cpp b/imgui.cpp index 405d49c6ca57..595272c91cde 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -9866,36 +9866,52 @@ static void ImGui::DockContextPruneUnusedSettingsNodes(ImGuiContext* ctx) ImGuiDockContext* dc = ctx->DockContext; IM_ASSERT(g.Windows.Size == 0); - // Count reference to dock ids from window settings - ImGuiStorage ref_count_map; // Map dock_id -> counter - ref_count_map.Data.reserve(dc->SettingsNodes.Size); - for (int settings_n = 0; settings_n < g.SettingsWindows.Size; settings_n++) - if (g.SettingsWindows[settings_n].DockId != 0) - (*ref_count_map.GetIntRef(g.SettingsWindows[settings_n].DockId, 0))++; + struct NodeData + { + int CountWindows, CountChildWindows, CountChildNodes; + ImGuiID RootID; + NodeData() { CountWindows = CountChildWindows = CountChildNodes = 0; RootID = 0; } + }; + ImPool pool; + pool.Reserve(dc->SettingsNodes.Size); - // Mark nodes that are parents - ImGuiStorage is_parent_map; - is_parent_map.Data.reserve(dc->SettingsNodes.Size); + // Count child nodes and compute RootID for (int settings_n = 0; settings_n < dc->SettingsNodes.Size; settings_n++) - if (dc->SettingsNodes[settings_n].ParentID) - is_parent_map.SetInt(dc->SettingsNodes[settings_n].ParentID, 1); + { + ImGuiDockNodeSettings* settings = &dc->SettingsNodes[settings_n]; + NodeData* parent_data = settings->ParentID ? pool.GetByKey(settings->ParentID) : 0; + pool.GetOrAddByKey(settings->ID)->RootID = parent_data ? parent_data->RootID : settings->ID; + if (settings->ParentID) + pool.GetOrAddByKey(settings->ParentID)->CountChildNodes++; + } - // If a root node has only 1 reference in window settings we clear it - // FIXME-DOCK: We should be able to merge unused nodes as well. + // Count reference to dock ids from window settings + for (int settings_n = 0; settings_n < g.SettingsWindows.Size; settings_n++) + if (ImGuiID dock_id = g.SettingsWindows[settings_n].DockId) + if (NodeData* data = pool.GetByKey(dock_id)) + { + NodeData* data_root = (data->RootID == dock_id) ? data : pool.GetByKey(data->RootID); + data->CountWindows++; + data_root->CountChildWindows++; + } + + // Prune for (int settings_n = 0; settings_n < dc->SettingsNodes.Size; settings_n++) { ImGuiDockNodeSettings* settings = &dc->SettingsNodes[settings_n]; - int ref_count = ref_count_map.GetInt(settings->ID, 0); - if (ref_count <= 1) + NodeData* data = pool.GetByKey(settings->ID); + if (data->CountWindows > 1) + continue; + NodeData* data_root = (data->RootID == settings->ID) ? data : pool.GetByKey(data->RootID); + + bool remove = false; + remove |= (data->CountWindows == 1 && settings->ParentID == 0 && data->CountChildNodes == 0 && !settings->IsDocumentRoot); // Floating root node with only 1 window + remove |= (data->CountWindows == 0 && settings->ParentID == 0 && data->CountChildNodes == 0); // Leaf nodes with 0 window + remove |= (data_root->CountChildWindows == 0); + if (remove) { - bool remove = false; - remove |= (ref_count == 1 && settings->ParentID == 0 && !settings->IsDocumentRoot); // Root node with only 1 window - remove |= (ref_count == 0 && settings->ParentID == 0 && is_parent_map.GetInt(settings->ID, 0) == 0); // Leaf nodes with 0 window - if (remove) - { - DockSettingsRemoveReferencesToNodes(&settings->ID, 1); - settings->ID = 0; - } + DockSettingsRemoveReferencesToNodes(&settings->ID, 1); + settings->ID = 0; } } } @@ -11953,6 +11969,7 @@ static void ImGui::DockSettingsRemoveReferencesToNodes(ImGuiID* node_ids, int no static ImGuiDockNodeSettings* ImGui::DockSettingsFindNodeSettings(ImGuiContext* ctx, ImGuiID id) { + // FIXME-OPT ImGuiDockContext* dc = ctx->DockContext; for (int n = 0; n < dc->SettingsNodes.Size; n++) if (dc->SettingsNodes[n].ID == id) From 93896d550ef80da6fb3bd2874b31978a67eb4c52 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 26 Sep 2018 22:52:24 +0200 Subject: [PATCH 285/828] Added FindOrCreateWindowSettings() internal helper. --- imgui.cpp | 7 +++++++ imgui_internal.h | 1 + 2 files changed, 8 insertions(+) diff --git a/imgui.cpp b/imgui.cpp index 595272c91cde..3a63a5f59c88 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -12317,6 +12317,13 @@ ImGuiWindowSettings* ImGui::FindWindowSettings(ImGuiID id) return NULL; } +ImGuiWindowSettings* ImGui::FindOrCreateWindowSettings(const char* name) +{ + if (ImGuiWindowSettings* settings = FindWindowSettings(ImHash(name, 0))) + return settings; + return CreateNewWindowSettings(name); +} + void ImGui::LoadIniSettingsFromDisk(const char* ini_filename) { size_t file_data_size = 0; diff --git a/imgui_internal.h b/imgui_internal.h index 4094fa6fe0b1..c30c7b18070b 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1406,6 +1406,7 @@ namespace ImGui IMGUI_API void MarkIniSettingsDirty(ImGuiWindow* window); IMGUI_API ImGuiWindowSettings* CreateNewWindowSettings(const char* name); IMGUI_API ImGuiWindowSettings* FindWindowSettings(ImGuiID id); + IMGUI_API ImGuiWindowSettings* FindOrCreateWindowSettings(const char* name); IMGUI_API ImGuiSettingsHandler* FindSettingsHandler(const char* type_name); // Basic Accessors From 876a3299fca90cd7a944124baa8d13bc9c3efa47 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 28 Sep 2018 14:48:15 +0200 Subject: [PATCH 286/828] Docking: Added DockBuilderCopyDockspace() wip. Added DockBuilderCopyWindowSettings(), renamed DockBuilderForkNode() to DockBuilderCopyNode(). --- imgui.cpp | 114 ++++++++++++++++++++++++++++++++++++++++++++--- imgui_internal.h | 6 ++- 2 files changed, 113 insertions(+), 7 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 3a63a5f59c88..85e5a4a5aab4 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11703,7 +11703,7 @@ ImGuiID ImGui::DockBuilderSplitNode(ImGuiContext* ctx, ImGuiID id, ImGuiDir spli return id_at_dir; } -static ImGuiDockNode* DockBuilderCloneNodeForFork(ImGuiContext* ctx, ImGuiDockNode* src_node, ImGuiID dst_node_id_if_known, ImVector* out_node_remap_pairs) +static ImGuiDockNode* DockBuilderCopyNodeRec(ImGuiContext* ctx, ImGuiDockNode* src_node, ImGuiID dst_node_id_if_known, ImVector* out_node_remap_pairs) { ImGuiDockNode* dst_node = ImGui::DockContextAddNode(ctx, dst_node_id_if_known); dst_node->Flags = src_node->Flags; @@ -11720,7 +11720,7 @@ static ImGuiDockNode* DockBuilderCloneNodeForFork(ImGuiContext* ctx, ImGuiDockNo for (int child_n = 0; child_n < IM_ARRAYSIZE(src_node->ChildNodes); child_n++) if (src_node->ChildNodes[child_n]) { - dst_node->ChildNodes[child_n] = DockBuilderCloneNodeForFork(ctx, src_node->ChildNodes[child_n], 0, out_node_remap_pairs); + dst_node->ChildNodes[child_n] = DockBuilderCopyNodeRec(ctx, src_node->ChildNodes[child_n], 0, out_node_remap_pairs); dst_node->ChildNodes[child_n]->ParentNode = dst_node; } @@ -11728,7 +11728,7 @@ static ImGuiDockNode* DockBuilderCloneNodeForFork(ImGuiContext* ctx, ImGuiDockNo return dst_node; } -void ImGui::DockBuilderForkNode(ImGuiContext* ctx, ImGuiID src_node_id, ImGuiID dst_node_id, ImVector* out_node_remap_pairs) +void ImGui::DockBuilderCopyNode(ImGuiContext* ctx, ImGuiID src_node_id, ImGuiID dst_node_id, ImVector* out_node_remap_pairs) { IM_ASSERT(src_node_id != 0); IM_ASSERT(dst_node_id != 0); @@ -11739,13 +11739,117 @@ void ImGui::DockBuilderForkNode(ImGuiContext* ctx, ImGuiID src_node_id, ImGuiID out_node_remap_pairs->clear(); DockBuilderRemoveNode(ctx, dst_node_id); - DockBuilderCloneNodeForFork(ctx, src_node, dst_node_id, out_node_remap_pairs); + DockBuilderCopyNodeRec(ctx, src_node, dst_node_id, out_node_remap_pairs); IM_ASSERT((out_node_remap_pairs->Size % 2) == 0); } +void ImGui::DockBuilderCopyWindowSettings(ImGuiContext* ctx, const char* src_name, const char* dst_name) +{ + (void)ctx; + ImGuiWindow* src_window = FindWindowByName(src_name); + if (src_window == NULL) + return; + if (ImGuiWindow* dst_window = FindWindowByName(dst_name)) + { + dst_window->Pos = src_window->Pos; + dst_window->Size = src_window->Size; + dst_window->SizeFull = src_window->SizeFull; + dst_window->Collapsed = src_window->Collapsed; + } + else if (ImGuiWindowSettings* dst_settings = FindOrCreateWindowSettings(dst_name)) + { + if (src_window->ViewportId != 0 && src_window->ViewportId != IMGUI_VIEWPORT_DEFAULT_ID) + { + dst_settings->ViewportPos = src_window->Pos; + dst_settings->ViewportId = src_window->ViewportId; + dst_settings->Pos = ImVec2(0.0f, 0.0f); + } + else + { + dst_settings->Pos = src_window->Pos; + } + dst_settings->Size = src_window->SizeFull; + dst_settings->Collapsed = src_window->Collapsed; + } +} + +// FIXME: Will probably want to change this signature, in particular how the window remapping pairs are passed. +void ImGui::DockBuilderCopyDockspace(ImGuiContext* ctx, ImGuiID src_dockspace_id, ImGuiID dst_dockspace_id, ImVector* in_window_remap_pairs) +{ + IM_ASSERT(src_dockspace_id != 0); + IM_ASSERT(dst_dockspace_id != 0); + IM_ASSERT(in_window_remap_pairs != NULL); + IM_ASSERT((in_window_remap_pairs->Size % 2) == 0); + + // Duplicate entire dock + // FIXME: When overwriting dst_dockspace_id, windows that aren't part of our dockspace family but that are docked in a same node will be split apart, + // whereas we could attempt to at least keep them together in a new, same floating node. + ImVector node_remap_pairs; + ImGui::DockBuilderCopyNode(ctx, src_dockspace_id, dst_dockspace_id, &node_remap_pairs); + + // Attempt to transition all the upcoming windows associated to dst_dockspace_id into the newly created hierarchy of dock nodes + // (The windows associated to src_dockspace_id are staying in place) + ImVector src_windows; + for (int remap_window_n = 0; remap_window_n < in_window_remap_pairs->Size; remap_window_n += 2) + { + const char* src_window_name = (*in_window_remap_pairs)[remap_window_n]; + const char* dst_window_name = (*in_window_remap_pairs)[remap_window_n + 1]; + ImGuiID src_window_id = ImHash(src_window_name, 0); + src_windows.push_back(src_window_id); + + // Search in the remapping tables + ImGuiID src_dock_id = 0; + if (ImGuiWindow* src_window = FindWindowByID(src_window_id)) + src_dock_id = src_window->DockId; + else if (ImGuiWindowSettings* src_window_settings = FindWindowSettings(src_window_id)) + src_dock_id = src_window_settings->DockId; + ImGuiID dst_dock_id = 0; + for (int dock_remap_n = 0; dock_remap_n < node_remap_pairs.Size; dock_remap_n += 2) + if (node_remap_pairs[dock_remap_n] == src_dock_id) + { + dst_dock_id = node_remap_pairs[dock_remap_n + 1]; + //node_remap_pairs[dock_remap_n] = node_remap_pairs[dock_remap_n + 1] = 0; // Clear + break; + } + + if (dst_dock_id != 0) + { + // Docked windows gets redocked into the new node hierarchy. + IMGUI_DEBUG_LOG("Remap window '%s' %08X -> %08X\n", dst_window_name, src_dock_id, dst_dock_id); + ImGui::DockBuilderDockWindow(ctx, dst_window_name, dst_dock_id); + } + else + { + // Floating windows gets their settings transferred (regardless of whether the new window already exist or not) + // When this is leading to a Copy and not a Move, we would get two overlapping floating windows. Could we possibly dock them together? + ImGui::DockBuilderCopyWindowSettings(ctx, src_window_name, dst_window_name); + } + } + + // Anything else in the source nodes of 'node_remap_pairs' are windows that were docked in src_dockspace_id but are not owned by it (unaffiliated windows, e.g. "ImGui Demo") + // Find those windows and move to them to the cloned dock node. This may be optional? + for (int dock_remap_n = 0; dock_remap_n < node_remap_pairs.Size; dock_remap_n += 2) + if (ImGuiID src_dock_id = node_remap_pairs[dock_remap_n]) + { + ImGuiID dst_dock_id = node_remap_pairs[dock_remap_n + 1]; + ImGuiDockNode* dock_node = ImGui::DockBuilderGetNode(ctx, src_dock_id); + for (int window_n = 0; window_n < dock_node->Windows.Size; window_n++) + { + ImGuiWindow* window = dock_node->Windows[window_n]; + if (src_windows.contains(window->ID)) + continue; + + // Docked windows gets redocked into the new node hierarchy. + IMGUI_DEBUG_LOG("Remap window '%s' %08X -> %08X\n", window->Name, src_dock_id, dst_dock_id); + ImGui::DockBuilderDockWindow(ctx, window->Name, dst_dock_id); + } + } +} + void ImGui::DockBuilderFinish(ImGuiContext* ctx, ImGuiID root_id) { + //DockContextRebuild(ctx); DockContextBuildAddWindowsToNodes(ctx, root_id); } @@ -12081,7 +12185,7 @@ static void ImGui::DockSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettings // [DEBUG] Include comments in the .ini file to ease debugging if (ImGuiDockNode* node = DockContextFindNodeByID(ctx, node_settings->ID)) { - buf->appendf("%*s", ImMax(2, (line_start_pos + 90) - buf->size()), ""); // Align everything + buf->appendf("%*s", ImMax(2, (line_start_pos + 92) - buf->size()), ""); // Align everything if (node->IsDockSpace && node->HostWindow && node->HostWindow->ParentWindow) buf->appendf(" ; in '%s'", node->HostWindow->ParentWindow->Name); int contains_window = 0; diff --git a/imgui_internal.h b/imgui_internal.h index c30c7b18070b..e2001b4361ee 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1487,11 +1487,13 @@ namespace ImGui IMGUI_API void DockBuilderDockWindow(ImGuiContext* ctx, const char* window_name, ImGuiID node_id); IMGUI_API ImGuiDockNode*DockBuilderGetNode(ImGuiContext* ctx, ImGuiID node_id); IMGUI_API void DockBuilderAddNode(ImGuiContext* ctx, ImGuiID node_id, ImVec2 ref_size, ImGuiDockNodeFlags flags = 0); - IMGUI_API void DockBuilderRemoveNode(ImGuiContext* ctx, ImGuiID node_id); + IMGUI_API void DockBuilderRemoveNode(ImGuiContext* ctx, ImGuiID node_id); // Remove node and all its child, undock all windows IMGUI_API void DockBuilderRemoveNodeDockedWindows(ImGuiContext* ctx, ImGuiID node_id, bool clear_persistent_docking_references = true); IMGUI_API void DockBuilderRemoveNodeChildNodes(ImGuiContext* ctx, ImGuiID node_id); // Remove all split/hierarchy. All remaining docked windows will be re-docked to the root. IMGUI_API ImGuiID DockBuilderSplitNode(ImGuiContext* ctx, ImGuiID node_id, ImGuiDir split_dir, float size_ratio_for_node_at_dir, ImGuiID* out_id_dir, ImGuiID* out_id_other); - IMGUI_API void DockBuilderForkNode(ImGuiContext* ctx, ImGuiID src_node_id, ImGuiID dst_node_id, ImVector* out_node_remap_pairs); + IMGUI_API void DockBuilderCopyDockspace(ImGuiContext* ctx, ImGuiID src_dockspace_id, ImGuiID dst_dockspace_id, ImVector* in_window_remap_pairs); + IMGUI_API void DockBuilderCopyNode(ImGuiContext* ctx, ImGuiID src_node_id, ImGuiID dst_node_id, ImVector* out_node_remap_pairs); + IMGUI_API void DockBuilderCopyWindowSettings(ImGuiContext* ctx, const char* src_name, const char* dst_name); IMGUI_API void DockBuilderFinish(ImGuiContext* ctx, ImGuiID node_id); // Drag and Drop From 35d1fb7b34afbdd6e56e0c650414befd78ff6fa3 Mon Sep 17 00:00:00 2001 From: omar Date: Sun, 30 Sep 2018 18:41:07 +0200 Subject: [PATCH 287/828] Style: Added style.TabRounding setting. --- imgui.cpp | 5 ++++- imgui.h | 4 +++- imgui_demo.cpp | 5 +++-- imgui_widgets.cpp | 2 +- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 85e5a4a5aab4..b7ff283d1cf3 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1013,7 +1013,6 @@ ImGuiStyle::ImGuiStyle() FramePadding = ImVec2(4,3); // Padding within a framed rectangle (used by most widgets) FrameRounding = 0.0f; // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets). FrameBorderSize = 0.0f; // Thickness of border around frames. Generally set to 0.0f or 1.0f. Other values not well tested. - TabBorderSize = 0.0f; // Thickness of border around tabs. ItemSpacing = ImVec2(8,4); // Horizontal and vertical spacing between widgets/lines ItemInnerSpacing = ImVec2(4,4); // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label) TouchExtraPadding = ImVec2(0,0); // Expand reactive bounding box for touch-based system where touch position is not accurate enough. Unfortunately we don't sort widgets so priority on overlap will always be given to the first widget. So don't grow this too much! @@ -1023,6 +1022,8 @@ ImGuiStyle::ImGuiStyle() ScrollbarRounding = 9.0f; // Radius of grab corners rounding for scrollbar GrabMinSize = 10.0f; // Minimum width/height of a grab box for slider/scrollbar GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs. + TabRounding = 4.0f; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs. + TabBorderSize = 0.0f; // Thickness of border around tabs. ButtonTextAlign = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text. DisplayWindowPadding = ImVec2(20,20); // Window position are clamped to be visible within the display area or monitors by at least this amount. Only applies to regular windows. DisplaySafeAreaPadding = ImVec2(3,3); // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows. @@ -1046,6 +1047,7 @@ void ImGuiStyle::ScaleAllSizes(float scale_factor) PopupRounding = ImFloor(PopupRounding * scale_factor); FramePadding = ImFloor(FramePadding * scale_factor); FrameRounding = ImFloor(FrameRounding * scale_factor); + TabRounding = ImFloor(TabRounding * scale_factor); ItemSpacing = ImFloor(ItemSpacing * scale_factor); ItemInnerSpacing = ImFloor(ItemInnerSpacing * scale_factor); TouchExtraPadding = ImFloor(TouchExtraPadding * scale_factor); @@ -5916,6 +5918,7 @@ static const ImGuiStyleVarInfo GStyleVarInfo[] = { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarRounding) }, // ImGuiStyleVar_ScrollbarRounding { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabMinSize) }, // ImGuiStyleVar_GrabMinSize { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabRounding) }, // ImGuiStyleVar_GrabRounding + { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, TabRounding) }, // ImGuiStyleVar_TabRounding { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ButtonTextAlign) }, // ImGuiStyleVar_ButtonTextAlign }; diff --git a/imgui.h b/imgui.h index a837bbe4261e..d5f37659917e 100644 --- a/imgui.h +++ b/imgui.h @@ -1061,6 +1061,7 @@ enum ImGuiStyleVar_ ImGuiStyleVar_ScrollbarRounding, // float ScrollbarRounding ImGuiStyleVar_GrabMinSize, // float GrabMinSize ImGuiStyleVar_GrabRounding, // float GrabRounding + ImGuiStyleVar_TabRounding, // float TabRounding ImGuiStyleVar_ButtonTextAlign, // ImVec2 ButtonTextAlign ImGuiStyleVar_COUNT @@ -1157,7 +1158,6 @@ struct ImGuiStyle ImVec2 FramePadding; // Padding within a framed rectangle (used by most widgets). float FrameRounding; // Radius of frame corners rounding. Set to 0.0f to have rectangular frame (used by most widgets). float FrameBorderSize; // Thickness of border around frames. Generally set to 0.0f or 1.0f. (Other values are not well tested and more CPU/GPU costly). - float TabBorderSize; // Thickness of border around tabs. ImVec2 ItemSpacing; // Horizontal and vertical spacing between widgets/lines. ImVec2 ItemInnerSpacing; // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label). ImVec2 TouchExtraPadding; // Expand reactive bounding box for touch-based system where touch position is not accurate enough. Unfortunately we don't sort widgets so priority on overlap will always be given to the first widget. So don't grow this too much! @@ -1167,6 +1167,8 @@ struct ImGuiStyle float ScrollbarRounding; // Radius of grab corners for scrollbar. float GrabMinSize; // Minimum width/height of a grab box for slider/scrollbar. float GrabRounding; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs. + float TabRounding; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs. + float TabBorderSize; // Thickness of border around tabs. ImVec2 ButtonTextAlign; // Alignment of button text when button is larger than text. Defaults to (0.5f,0.5f) for horizontally+vertically centered. ImVec2 DisplayWindowPadding; // Window position are clamped to be visible within the display area or monitors by at least this amount. Only applies to regular windows. ImVec2 DisplaySafeAreaPadding; // If you cannot see the edges of your screen (e.g. on a TV) increase the safe area padding. Apply to popups/tooltips as well regular windows. NB: Prefer configuring your TV sets correctly! diff --git a/imgui_demo.cpp b/imgui_demo.cpp index d764f9690259..feff358442d6 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2606,11 +2606,12 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) ImGui::SliderFloat("FrameBorderSize", &style.FrameBorderSize, 0.0f, 1.0f, "%.0f"); ImGui::SliderFloat("TabBorderSize", &style.TabBorderSize, 0.0f, 1.0f, "%.0f"); ImGui::Text("Rounding"); - ImGui::SliderFloat("WindowRounding", &style.WindowRounding, 0.0f, 14.0f, "%.0f"); - ImGui::SliderFloat("ChildRounding", &style.ChildRounding, 0.0f, 16.0f, "%.0f"); + ImGui::SliderFloat("WindowRounding", &style.WindowRounding, 0.0f, 12.0f, "%.0f"); + ImGui::SliderFloat("ChildRounding", &style.ChildRounding, 0.0f, 12.0f, "%.0f"); ImGui::SliderFloat("FrameRounding", &style.FrameRounding, 0.0f, 12.0f, "%.0f"); ImGui::SliderFloat("ScrollbarRounding", &style.ScrollbarRounding, 0.0f, 12.0f, "%.0f"); ImGui::SliderFloat("GrabRounding", &style.GrabRounding, 0.0f, 12.0f, "%.0f"); + ImGui::SliderFloat("TabRounding", &style.TabRounding, 0.0f, 12.0f, "%.0f"); ImGui::Text("Alignment"); ImGui::SliderFloat2("WindowTitleAlign", (float*)&style.WindowTitleAlign, 0.0f, 1.0f, "%.2f"); ImGui::SliderFloat2("ButtonTextAlign", (float*)&style.ButtonTextAlign, 0.0f, 1.0f, "%.2f"); ImGui::SameLine(); ShowHelpMarker("Alignment applies when a button is larger than its text content."); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 725ab81c603a..91c0c488f38e 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6571,7 +6571,7 @@ void ImGui::TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabI ImGuiContext& g = *GImGui; const float width = bb.GetWidth(); IM_ASSERT(width > 0.0f); - const float rounding = ImMax(0.0f, ImMin(g.FontSize * 0.35f, width * 0.5f - 1.0f)); // FIXME-DOCK: g.Style.TabRounding? + const float rounding = ImMax(0.0f, ImMin(g.Style.TabRounding, width * 0.5f - 1.0f)); float y1 = bb.Min.y + 1.0f; float y2 = bb.Max.y + ((flags & ImGuiTabItemFlags_Preview) ? 0.0f : -1.0f); draw_list->PathLineTo(ImVec2(bb.Min.x, y2)); From 46dff422398580091a79cabdf775b4e8af4c10e1 Mon Sep 17 00:00:00 2001 From: omar Date: Sun, 30 Sep 2018 19:30:39 +0200 Subject: [PATCH 288/828] Docking: Fixed dock node with single window accidental stealing of non-owned host viewport (leading to general havoc). --- imgui.cpp | 7 ++++++- imgui_internal.h | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index b7ff283d1cf3..7eb02cd5c8ae 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7381,6 +7381,7 @@ static void ImGui::UpdateViewports() // Update main viewport with current platform position and size ImGuiViewportP* main_viewport = g.Viewports[0]; IM_ASSERT(main_viewport->ID == IMGUI_VIEWPORT_DEFAULT_ID); + IM_ASSERT(main_viewport->Window == NULL); ImVec2 main_viewport_platform_pos = ImVec2(0.0f, 0.0f); if ((g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) main_viewport_platform_pos = g.PlatformIO.Platform_GetWindowPos(main_viewport); @@ -10524,7 +10525,11 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) { single_window->Viewport = node->HostWindow->Viewport; single_window->ViewportId = node->HostWindow->ViewportId; - single_window->Viewport->Window = single_window; + if (node->HostWindow->ViewportOwned) + { + single_window->Viewport->Window = single_window; + single_window->ViewportOwned = true; + } } DockNodeHideHostWindow(node); diff --git a/imgui_internal.h b/imgui_internal.h index e2001b4361ee..42a6564ae3f1 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -657,7 +657,7 @@ struct ImGuiViewportP : public ImGuiViewport float Alpha; // Window opacity (when dragging dockable windows/viewports we make them transparent) float LastAlpha; int PlatformMonitor; - ImGuiWindow* Window; + ImGuiWindow* Window; // Set when the viewport is owned by a window ImDrawList* OverlayDrawList; // For convenience, a draw list we can render to that's always rendered last (we use it to draw software mouse cursor when io.MouseDrawCursor is set) ImDrawData DrawDataP; ImDrawDataBuilder DrawDataBuilder; From 1ae0a1e65800167dd7cdcb536b183c67b3ab3953 Mon Sep 17 00:00:00 2001 From: omar Date: Sun, 30 Sep 2018 19:34:03 +0200 Subject: [PATCH 289/828] Docking: Fix DockContextRebuild() temporarily invalidating dockid reference leading to undesirable merging of nodes. --- imgui.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 7eb02cd5c8ae..4a2d8a00e070 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -9634,11 +9634,11 @@ namespace ImGui static void DockContextQueueDock(ImGuiContext* ctx, ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, float split_ratio, bool split_outer); static void DockContextQueueNotifyRemovedNode(ImGuiContext* ctx, ImGuiDockNode* node); static void DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req); - static void DockContextProcessUndockWindow(ImGuiContext* ctx, ImGuiWindow* window); + static void DockContextProcessUndockWindow(ImGuiContext* ctx, ImGuiWindow* window, bool clear_persistent_docking_ref = true); static void DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node); static void DockContextPruneUnusedSettingsNodes(ImGuiContext* ctx); static ImGuiDockNode* DockContextFindNodeByID(ImGuiContext* ctx, ImGuiID id); - static void DockContextClearNodes(ImGuiContext* ctx, ImGuiID root_id, bool clear_persistent_docking_references); // Set root_id==0 to clear all + static void DockContextClearNodes(ImGuiContext* ctx, ImGuiID root_id, bool clear_persistent_docking_refs); // Set root_id==0 to clear all static void DockContextBuildNodesFromSettings(ImGuiContext* ctx, ImGuiDockNodeSettings* node_settings_array, int node_settings_count); static void DockContextBuildAddWindowsToNodes(ImGuiContext* ctx, ImGuiID root_id); // Use root_id==0 to add all @@ -10129,11 +10129,11 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) MarkIniSettingsDirty(); } -void ImGui::DockContextProcessUndockWindow(ImGuiContext* ctx, ImGuiWindow* window) +void ImGui::DockContextProcessUndockWindow(ImGuiContext* ctx, ImGuiWindow* window, bool clear_persistent_docking_ref) { (void)ctx; if (window->DockNode) - DockNodeRemoveWindow(window->DockNode, window, 0); + DockNodeRemoveWindow(window->DockNode, window, clear_persistent_docking_ref ? 0 : window->DockId); else window->DockId = 0; window->Collapsed = false; @@ -11671,10 +11671,10 @@ void ImGui::DockBuilderRemoveNodeDockedWindows(ImGuiContext* ctx, ImGuiID root_i bool want_removal = (root_id == 0) || (window->DockNode && DockNodeGetRootNode(window->DockNode)->ID == root_id) || (window->DockNodeAsHost && window->DockNodeAsHost->ID == root_id); if (want_removal) { - ImGuiID backup_dock_id = window->DockId; - DockContextProcessUndockWindow(ctx, window); + const ImGuiID backup_dock_id = window->DockId; + DockContextProcessUndockWindow(ctx, window, clear_persistent_docking_references); if (!clear_persistent_docking_references) - window->DockId = backup_dock_id; + IM_ASSERT(window->DockId == backup_dock_id); } } } From 6ebc63d3ef4f0efeaed7e28a4e8390e44ee9109d Mon Sep 17 00:00:00 2001 From: omar Date: Sun, 30 Sep 2018 21:24:53 +0200 Subject: [PATCH 290/828] Docking: Various sanity fixes + fixed tab-bar items sorting issue when docking single window with a dock node into single window without dock node. --- imgui.cpp | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 4a2d8a00e070..5e71c77002e3 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4826,8 +4826,9 @@ void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags window->RootWindow = window->RootWindowDockStop = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window; if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip)) window->RootWindow = parent_window->RootWindow; - if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip) && !window->DockIsActive) - window->RootWindowDockStop = parent_window->RootWindowDockStop; + if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip)) + if (!window->DockIsActive && !(parent_window->Flags & ImGuiWindowFlags_DockNodeHost)) + window->RootWindowDockStop = parent_window->RootWindowDockStop; if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup))) window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight; while (window->RootWindowForNav->Flags & ImGuiWindowFlags_NavFlattened) @@ -9945,7 +9946,8 @@ static void ImGui::DockContextBuildNodesFromSettings(ImGuiContext* ctx, ImGuiDoc // Bind host window immediately if it already exist (in case of a rebuild) // This is useful as the RootWindowForTitleBarHighlight links necessary to highlight the currently focused node requires node->HostWindow to be set. char host_window_title[32]; - node->HostWindow = FindWindowByName(DockNodeGetHostWindowTitle(node, host_window_title, IM_ARRAYSIZE(host_window_title))); + ImGuiDockNode* root_node = DockNodeGetRootNode(node); + node->HostWindow = FindWindowByName(DockNodeGetHostWindowTitle(root_node, host_window_title, IM_ARRAYSIZE(host_window_title))); } } @@ -10048,16 +10050,13 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) if (target_window->DockNodeAsHost == NULL) { DockNodeAddWindow(target_node, target_window, true); + target_node->TabBar->Tabs[0].Flags &= ~ImGuiTabItemFlags_Unsorted; target_window->DockIsActive = true; } } ImGuiDir split_dir = req->DockSplitDir; - if (split_dir == ImGuiDir_None) - { - target_node->LastFocusedNodeID = target_node ? target_node->ID : 0; - } - else + if (split_dir != ImGuiDir_None) { // Split into one, one side will be our payload node unless we are dropping a loose window const ImGuiAxis split_axis = (split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y; @@ -10066,7 +10065,6 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) DockNodeTreeSplit(ctx, target_node, split_axis, split_inheritor_child_idx, split_ratio, payload_node); // payload_node may be NULL here! ImGuiDockNode* inheritor_node = target_node->ChildNodes[split_inheritor_child_idx]; ImGuiDockNode* new_node = target_node->ChildNodes[split_inheritor_child_idx ^ 1]; - target_node->LastFocusedNodeID = new_node->ID; new_node->HostWindow = target_node->HostWindow; if (target_node) { @@ -10083,7 +10081,7 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) { target_node->TabBar = IM_NEW(ImGuiTabBar)(); for (int n = 0; n < target_node->Windows.Size; n++) - TabBarAddTab(target_node->TabBar, target_node->Windows[n]); + TabBarAddTab(target_node->TabBar, target_node->Windows[n], ImGuiTabItemFlags_None); } if (payload_node != NULL) @@ -10102,7 +10100,7 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) if (visible_node->TabBar) IM_ASSERT(visible_node->TabBar->Tabs.Size > 0); for (int n = 0; n < visible_node->Windows.Size; n++) - TabBarAddTab(target_node->TabBar, visible_node->Windows[n]); + TabBarAddTab(target_node->TabBar, visible_node->Windows[n], ImGuiTabItemFlags_None); DockNodeMoveWindows(target_node, visible_node); DockNodeMoveWindows(visible_node, target_node); } @@ -10743,7 +10741,6 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w focus_tab_id = tab_bar->SelectedTabId; // Create tab bar and setup initial selection - ImRect tab_bar_rect = DockNodeCalcTabBarRect(node); if (g.NavWindow && g.NavWindow->RootWindowDockStop->DockNode == node) { //printf("[%05d] tab bar create focus '%s'\n", g.FrameCount, window_focused->Name); @@ -10777,12 +10774,13 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = tab_bar->Tabs.back().Window->ID; // Begin tab bar + const ImRect tab_bar_rect = DockNodeCalcTabBarRect(node); ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_Reorderable | ImGuiTabBarFlags_AutoSelectNewTabs | ImGuiTabBarFlags_NoTabListPopupButton;// | ImGuiTabBarFlags_NoTabListScrollingButtons); tab_bar_flags |= ImGuiTabBarFlags_SaveSettings; tab_bar_flags |= ImGuiTabBarFlags_DockNode | (node->IsDockSpace ? ImGuiTabBarFlags_DockNodeIsDockSpace : 0); if (!host_window->Collapsed && is_focused) tab_bar_flags |= ImGuiTabBarFlags_IsFocused; - BeginTabBarEx(node->TabBar, tab_bar_rect, tab_bar_flags, node); + BeginTabBarEx(tab_bar, tab_bar_rect, tab_bar_flags, node); //host_window->DrawList->AddRect(tab_bar_rect.Min, tab_bar_rect.Max, IM_COL32(255,0,255,255)); // Submit actual tabs @@ -10823,7 +10821,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w root_node->VisibleWindow = node->VisibleWindow; // Close button (after VisibleWindow was updated) - // Note that VisibleWindow may have been overrided by CTRL+Tabbing, so VisibleWindow->ID may be != from node->TabBar->SelectedTabId + // Note that VisibleWindow may have been overrided by CTRL+Tabbing, so VisibleWindow->ID may be != from tab_bar->SelectedTabId if (node->VisibleWindow) { if (!node->VisibleWindow->HasCloseButton) @@ -10856,13 +10854,13 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w if (g.HoveredWindow == host_window && g.HoveredId == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && IsMouseClicked(0)) { focus_tab_id = tab_bar->SelectedTabId; - if (ImGuiTabItem* tab = TabBarFindTabByID(node->TabBar, focus_tab_id)) + if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, focus_tab_id)) StartMouseMovingWindow(tab->Window); } // Apply navigation focus if (focus_tab_id != 0) - if (ImGuiTabItem* tab = TabBarFindTabByID(node->TabBar, focus_tab_id)) + if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, focus_tab_id)) { FocusWindow(tab->Window); NavInitWindow(tab->Window, false); @@ -11115,7 +11113,7 @@ static void ImGui::DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDock else if (!(host_window->Flags & ImGuiWindowFlags_DockNodeHost)) tab_pos.x += g.Style.ItemInnerSpacing.x + TabItemCalcSize(host_window->Name, host_window->HasCloseButton).x; // Account for slight offset which will be added when changing from title bar to tab bar - // Draw tab shape/label preview (payload may be a loose window or a host window carrying multiple tabbed windows) + // Draw tab shape/label preview (payload may be a loose window or a host window carrying multiple tabbed windows) if (root_payload->DockNodeAsHost) IM_ASSERT(root_payload->DockNodeAsHost->Windows.Size == root_payload->DockNodeAsHost->TabBar->Tabs.Size); const int payload_count = root_payload->DockNodeAsHost ? root_payload->DockNodeAsHost->TabBar->Tabs.Size : 1; From 9cfc40c2cce05f78681f5e9bc02b858305321c4b Mon Sep 17 00:00:00 2001 From: omar Date: Sun, 30 Sep 2018 22:31:50 +0200 Subject: [PATCH 291/828] Docking: Demo: Displaying a message if master docking flag is disabled. + DockSpace() early out + comments. --- imgui.cpp | 2 ++ imgui.h | 5 +-- imgui_demo.cpp | 87 ++++++++++++++++++++++++++++++++------------------ 3 files changed, 61 insertions(+), 33 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 5e71c77002e3..2ab7d2facd7e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11475,6 +11475,8 @@ void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags doc ImGuiContext* ctx = GImGui; ImGuiContext& g = *ctx; ImGuiWindow* window = GetCurrentWindow(); + if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable)) + return; ImGuiDockNode* node = DockContextFindNodeByID(ctx, id); if (!node) diff --git a/imgui.h b/imgui.h index d5f37659917e..c3e5add5eb35 100644 --- a/imgui.h +++ b/imgui.h @@ -524,11 +524,12 @@ namespace ImGui // Docking // [BETA API] Enable with io.ConfigFlags |= ImGuiConfigFlags_DockingEnable. - // Note: you DO NOT need to call DockSpace() to use most Docking facilities! You can hold SHIFT anywhere while moving windows. + // Note: you DO NOT need to call DockSpace() to use most Docking facilities! + // To dock windows: hold SHIFT anywhere while moving windows (if io.ConfigDockingWithKeyMod == true) or drag windows from their title bar (if io.ConfigDockingWithKeyMod = false) // Use DockSpace() to create an explicit dock node _within_ an existing window. See Docking demo for details. IMGUI_API void DockSpace(ImGuiID id, const ImVec2& size = ImVec2(0, 0), ImGuiDockNodeFlags flags = 0, const ImGuiDockFamily* dock_family = NULL); IMGUI_API void SetNextWindowDockId(ImGuiID dock_id, ImGuiCond cond = 0); // set next window dock id (FIXME-DOCK) - IMGUI_API void SetNextWindowDockFamily(const ImGuiDockFamily* dock_family); // FIXME-DOCK: set next window user type (docking filters by same user_type) + IMGUI_API void SetNextWindowDockFamily(const ImGuiDockFamily* dock_family); // set next window user type (docking filters by same user_type) IMGUI_API ImGuiID GetWindowDockId(); IMGUI_API bool IsWindowDocked(); // is current window docked into another window? diff --git a/imgui_demo.cpp b/imgui_demo.cpp index feff358442d6..41e759c749d5 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -129,6 +129,16 @@ static void ShowHelpMarker(const char* desc) } } +static void ShowDockingDisabledMessage() +{ + ImGuiIO& io = ImGui::GetIO(); + ImGui::Text("ERROR: Docking is not enabled! See Demo > Configuration."); + ImGui::Text("Set io.ConfigFlags |= ImGuiConfigFlags_DockingEnable in your code, or "); + ImGui::SameLine(0.0f, 0.0f); + if (ImGui::SmallButton("click here")) + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; +} + // Helper to display basic user controls. void ImGui::ShowUserGuide() { @@ -3752,10 +3762,18 @@ void ShowExampleAppDockSpace(bool* p_open) ImGui::EndMenuBar(); } - //ImGui::PushStyleColor(ImGuiCol_DockingBg, ImVec4(0.2f, 0.2f, 0.2f, 1.0f)); - ImGuiID dockspace_id = ImGui::GetID("MyDockspace"); - ImGui::DockSpace(dockspace_id); - //ImGui::PopStyleColor(); + ImGuiIO& io = ImGui::GetIO(); + if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable) + { + //ImGui::PushStyleColor(ImGuiCol_DockingBg, ImVec4(0.2f, 0.2f, 0.2f, 1.0f)); + ImGuiID dockspace_id = ImGui::GetID("MyDockspace"); + ImGui::DockSpace(dockspace_id); + //ImGui::PopStyleColor(); + } + else + { + ShowDockingDisabledMessage(); + } ImGui::End(); if (opt_fullscreen) @@ -3964,38 +3982,45 @@ void ShowExampleAppDocuments(bool* p_open) } else if (opt_target == Target_Window) { - NotifyOfDocumentsClosedElsewhere(app); - - // Create a DockSpace node where any window can be docked - ImGuiID dockspace_id = ImGui::GetID("MyDockSpace"); - ImGui::DockSpace(dockspace_id); - - // Create Windows - for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++) + if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_DockingEnable) { - MyDocument* doc = &app.Documents[doc_n]; - if (!doc->Open) - continue; - - // FIXME-DOCK: SetNextWindowDock() - //ImGuiID default_dock_id = GetDockspaceRootDocumentDockID(); - //ImGuiID default_dock_id = GetDockspacePreferedDocumentDockID(); - ImGui::SetNextWindowDockId(dockspace_id, redock_all ? ImGuiCond_Always : ImGuiCond_FirstUseEver); - ImGuiWindowFlags window_flags = (doc->Dirty ? ImGuiWindowFlags_UnsavedDocument : 0); - bool visible = ImGui::Begin(doc->Name, &doc->Open, window_flags); + NotifyOfDocumentsClosedElsewhere(app); + + // Create a DockSpace node where any window can be docked + ImGuiID dockspace_id = ImGui::GetID("MyDockSpace"); + ImGui::DockSpace(dockspace_id); - // Cancel attempt to close when unsaved add to save queue so we can display a popup. - if (!doc->Open && doc->Dirty) + // Create Windows + for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++) { - doc->Open = true; - doc->DoQueueClose(); - } + MyDocument* doc = &app.Documents[doc_n]; + if (!doc->Open) + continue; + + // FIXME-DOCK: SetNextWindowDock() + //ImGuiID default_dock_id = GetDockspaceRootDocumentDockID(); + //ImGuiID default_dock_id = GetDockspacePreferedDocumentDockID(); + ImGui::SetNextWindowDockId(dockspace_id, redock_all ? ImGuiCond_Always : ImGuiCond_FirstUseEver); + ImGuiWindowFlags window_flags = (doc->Dirty ? ImGuiWindowFlags_UnsavedDocument : 0); + bool visible = ImGui::Begin(doc->Name, &doc->Open, window_flags); + + // Cancel attempt to close when unsaved add to save queue so we can display a popup. + if (!doc->Open && doc->Dirty) + { + doc->Open = true; + doc->DoQueueClose(); + } - MyDocument::DisplayContextMenu(doc); - if (visible) - MyDocument::DisplayContents(doc); + MyDocument::DisplayContextMenu(doc); + if (visible) + MyDocument::DisplayContents(doc); - ImGui::End(); + ImGui::End(); + } + } + else + { + ShowDockingDisabledMessage(); } } From ed3c015f4e3bf9e48c472339ce9062d8e7cc52fd Mon Sep 17 00:00:00 2001 From: omar Date: Sun, 30 Sep 2018 22:57:04 +0200 Subject: [PATCH 292/828] Docking: Reorganizing some of the tab-bar selection and window focus related code. --- imgui.cpp | 44 ++++++++++++++++++++++++-------------------- imgui_widgets.cpp | 4 ++-- 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 2ab7d2facd7e..623fac4667e1 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5700,6 +5700,10 @@ void ImGui::FocusWindow(ImGuiWindow* window) if (!window) return; + // Select in dock node + if (window->DockNode && window->DockNode->TabBar) + window->DockNode->TabBar->SelectedTabId = window->ID; + // Move the root window to the top of the pile if (window->RootWindow) window = window->RootWindow; @@ -10122,8 +10126,8 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) } // Update selection immediately - if (target_node->TabBar) - target_node->TabBar->NextSelectedTabId = next_selected_id; + if (ImGuiTabBar* tab_bar = target_node->TabBar) + tab_bar->NextSelectedTabId = tab_bar->WantFocusTabId = next_selected_id; MarkIniSettingsDirty(); } @@ -10492,6 +10496,8 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) ImGuiDockNode* first_node_with_windows = NULL; DockNodeUpdateFindOnlyNodeWithWindowsRec(node, &count, &first_node_with_windows); node->OnlyNodeWithWindows = (count == 1 ? first_node_with_windows : NULL); + if (node->LastFocusedNodeID == 0 && first_node_with_windows != NULL) + node->LastFocusedNodeID = first_node_with_windows->ID; // Copy the dock family from of our window so it can be used for proper dock filtering. // When node has mixed windows, prioritize the family with the most constraint (CompatibleWithNeutral = false) as the reference to copy. @@ -10586,6 +10592,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) char window_label[20]; DockNodeGetHostWindowTitle(node, window_label, IM_ARRAYSIZE(window_label)); ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_DockNodeHost; + //window_flags |= ImGuiWindowFlags_NoFocusOnAppearing; window_flags |= ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus; window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse; @@ -10612,10 +10619,14 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) // Update active node (the one whose title bar is highlight) within a node tree if (node->IsSplitNode()) IM_ASSERT(node->TabBar == NULL); - if (!node->IsSplitNode()) - node->LastFocusedNodeID = node->ID; // This also ensure on our creation frame we will receive the title screen highlight - else if (g.NavWindow && g.NavWindow->RootWindowDockStop->DockNode && g.NavWindow->RootWindowDockStop->ParentWindow == host_window) - node->LastFocusedNodeID = g.NavWindow->RootWindowDockStop->DockNode->ID; + if (node->IsRootNode()) + { + //if (!node->IsSplitNode()) + // node->LastFocusedNodeID = node->ID; // This also ensure on our creation frame we will receive the title screen highlight + //else + if (g.NavWindow && g.NavWindow->RootWindowDockStop->DockNode && g.NavWindow->RootWindowDockStop->ParentWindow == host_window) + node->LastFocusedNodeID = g.NavWindow->RootWindowDockStop->DockNode->ID; + } // Draw and populate Tab Bar if (host_window && node->Windows.Size > 0) @@ -10718,7 +10729,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; IM_ASSERT(tab->Window != NULL); if (Selectable(tab->Window->Name, tab->ID == tab_bar->SelectedTabId)) - tab_bar->NextSelectedTabId = tab->ID; + tab_bar->NextSelectedTabId = tab_bar->WantFocusTabId = tab->ID; SameLine(); Text(" "); } @@ -10740,16 +10751,6 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w if (IsItemActive()) focus_tab_id = tab_bar->SelectedTabId; - // Create tab bar and setup initial selection - if (g.NavWindow && g.NavWindow->RootWindowDockStop->DockNode == node) - { - //printf("[%05d] tab bar create focus '%s'\n", g.FrameCount, window_focused->Name); - ImGuiID focused_id = g.NavWindow->RootWindowDockStop->ID; - if (focused_id != tab_bar->SelectedTabId) - if (focused_id != tab_bar->NextSelectedTabId || (tab_bar->NextSelectedTabId == 0 && focused_id != tab_bar->SelectedTabId)) - tab_bar->SelectedTabId = focused_id; - } - // Submit new tabs const int tabs_count_old = tab_bar->Tabs.Size; for (int window_n = 0; window_n < node->Windows.Size; window_n++) @@ -10846,8 +10847,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w } // When clicked on a tab we requested focus to the docked child - // Since we are processing this on the frame after the click, make sure focus hasn't already been taken by someone else. - if (node->TabBar->WantFocusTabId && g.NavWindow && g.NavWindow->RootWindow == host_window) + if (tab_bar->WantFocusTabId) focus_tab_id = tab_bar->WantFocusTabId; // When clicking on the title bar outside of tabs, we still focus the selected tab for that node @@ -10858,6 +10858,10 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w StartMouseMovingWindow(tab->Window); } + // Forward focus from host node to selected window + if (is_focused && g.NavWindow == host_window && !g.NavWindowingTarget) + focus_tab_id = tab_bar->SelectedTabId; + // Apply navigation focus if (focus_tab_id != 0) if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, focus_tab_id)) @@ -12022,7 +12026,7 @@ void ImGui::BeginAsDockableDragDropTarget(ImGuiWindow* window) ImGuiDockNode* target_node = NULL; if (window->DockNodeAsHost) target_node = DockNodeTreeFindNodeByPos(window->DockNodeAsHost, g.IO.MousePos); - else if (window->DockNode && window->DockIsActive) + else if (window->DockNode) // && window->DockIsActive) target_node = window->DockNode; else allow_null_target_node = true; // Dock into a regular window diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 91c0c488f38e..e9425758b0b9 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -5934,7 +5934,7 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) tab_bar->WantFocusTabId = 0; if (tab_bar->NextSelectedTabId) { - tab_bar->SelectedTabId = tab_bar->WantFocusTabId = tab_bar->NextSelectedTabId; + tab_bar->SelectedTabId = tab_bar->NextSelectedTabId; tab_bar->NextSelectedTabId = 0; scroll_track_selected_tab_id = tab_bar->SelectedTabId; } @@ -6419,7 +6419,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags); hovered |= (g.HoveredId == id); if (pressed || ((flags & ImGuiTabItemFlags_SetSelected) && !tab_contents_visible)) // SetSelected can only be passed on explicit tab bar, so we don't need to set WantFocusTabId - tab_bar->NextSelectedTabId = id; + tab_bar->NextSelectedTabId = tab_bar->WantFocusTabId = id; // Allow the close button to overlap unless we are dragging (in which case we don't want any overlapping tabs to be hovered) if (!held) From ae657a349ae48088552e71ceb5a06116c32cbbd9 Mon Sep 17 00:00:00 2001 From: omar Date: Sun, 30 Sep 2018 23:06:46 +0200 Subject: [PATCH 293/828] Docking: Fixed extracting a dock node from a document root: document root should be preserved. --- imgui.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 623fac4667e1..28fa9e999570 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10150,9 +10150,9 @@ void ImGui::DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node) IM_ASSERT(!node->IsSplitNode()); IM_ASSERT(node->Windows.Size >= 1); - // In the case of a root node, a node will have to stay in place. Create a new node for this purpose. + // In the case of a root node or document root, the node will have to stay in place. Create a new node to receive the payload. // Otherwise delete the previous node by merging the other sibling back into the parent node. - if (node->IsRootNode()) + if (node->IsRootNode() || node->IsDocumentRoot) { // FIXME-DOCK: Transition persistent DockId for all non-active windows ImGuiDockNode* new_node = DockContextAddNode(ctx, 0); From 4e717b524ca7bddc5a77d68abdb4f6842dd185da Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 1 Oct 2018 10:40:43 +0200 Subject: [PATCH 294/828] Docking: Fixes for C++03 compilers. --- imgui.cpp | 26 ++++++++++++++------------ imgui.h | 2 +- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 28fa9e999570..17e5dbac88b3 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -9868,6 +9868,14 @@ static int IMGUI_CDECL DockNodeComparerDepthMostFirst(const void* lhs, const voi return ImGui::DockNodeGetDepth(b) - ImGui::DockNodeGetDepth(a); } +// Pre C++0x doesn't allow us to use a local type (without linkage) as template parameter, so we moved this here. +struct ImGuiDockContextPruneNodeData +{ + int CountWindows, CountChildWindows, CountChildNodes; + ImGuiID RootID; + ImGuiDockContextPruneNodeData() { CountWindows = CountChildWindows = CountChildNodes = 0; RootID = 0; } +}; + // Garbage collect unused nodes (run once at init time) static void ImGui::DockContextPruneUnusedSettingsNodes(ImGuiContext* ctx) { @@ -9875,20 +9883,14 @@ static void ImGui::DockContextPruneUnusedSettingsNodes(ImGuiContext* ctx) ImGuiDockContext* dc = ctx->DockContext; IM_ASSERT(g.Windows.Size == 0); - struct NodeData - { - int CountWindows, CountChildWindows, CountChildNodes; - ImGuiID RootID; - NodeData() { CountWindows = CountChildWindows = CountChildNodes = 0; RootID = 0; } - }; - ImPool pool; + ImPool pool; pool.Reserve(dc->SettingsNodes.Size); // Count child nodes and compute RootID for (int settings_n = 0; settings_n < dc->SettingsNodes.Size; settings_n++) { ImGuiDockNodeSettings* settings = &dc->SettingsNodes[settings_n]; - NodeData* parent_data = settings->ParentID ? pool.GetByKey(settings->ParentID) : 0; + ImGuiDockContextPruneNodeData* parent_data = settings->ParentID ? pool.GetByKey(settings->ParentID) : 0; pool.GetOrAddByKey(settings->ID)->RootID = parent_data ? parent_data->RootID : settings->ID; if (settings->ParentID) pool.GetOrAddByKey(settings->ParentID)->CountChildNodes++; @@ -9897,9 +9899,9 @@ static void ImGui::DockContextPruneUnusedSettingsNodes(ImGuiContext* ctx) // Count reference to dock ids from window settings for (int settings_n = 0; settings_n < g.SettingsWindows.Size; settings_n++) if (ImGuiID dock_id = g.SettingsWindows[settings_n].DockId) - if (NodeData* data = pool.GetByKey(dock_id)) + if (ImGuiDockContextPruneNodeData* data = pool.GetByKey(dock_id)) { - NodeData* data_root = (data->RootID == dock_id) ? data : pool.GetByKey(data->RootID); + ImGuiDockContextPruneNodeData* data_root = (data->RootID == dock_id) ? data : pool.GetByKey(data->RootID); data->CountWindows++; data_root->CountChildWindows++; } @@ -9908,10 +9910,10 @@ static void ImGui::DockContextPruneUnusedSettingsNodes(ImGuiContext* ctx) for (int settings_n = 0; settings_n < dc->SettingsNodes.Size; settings_n++) { ImGuiDockNodeSettings* settings = &dc->SettingsNodes[settings_n]; - NodeData* data = pool.GetByKey(settings->ID); + ImGuiDockContextPruneNodeData* data = pool.GetByKey(settings->ID); if (data->CountWindows > 1) continue; - NodeData* data_root = (data->RootID == settings->ID) ? data : pool.GetByKey(data->RootID); + ImGuiDockContextPruneNodeData* data_root = (data->RootID == settings->ID) ? data : pool.GetByKey(data->RootID); bool remove = false; remove |= (data->CountWindows == 1 && settings->ParentID == 0 && data->CountChildNodes == 0 && !settings->IsDocumentRoot); // Floating root node with only 1 window diff --git a/imgui.h b/imgui.h index c3e5add5eb35..c800b7a5e4dc 100644 --- a/imgui.h +++ b/imgui.h @@ -969,7 +969,7 @@ enum ImGuiBackendFlags_ // [BETA] Viewports ImGuiBackendFlags_PlatformHasViewports = 1 << 10, // Back-end Platform supports multiple viewports. ImGuiBackendFlags_HasMouseHoveredViewport=1 << 11, // Back-end Platform supports setting io.MouseHoveredViewport to the viewport directly under the mouse _IGNORING_ viewports with the ImGuiViewportFlags_NoInputs flag and _REGARDLESS_ of whether another viewport is focused and may be capturing the mouse. This information is _NOT EASY_ to provide correctly with most high-level engines! Don't set this without studying how the examples/ back-end handle it! - ImGuiBackendFlags_RendererHasViewports = 1 << 12, // Back-end Renderer supports multiple viewports. + ImGuiBackendFlags_RendererHasViewports = 1 << 12 // Back-end Renderer supports multiple viewports. }; // Enumeration for PushStyleColor() / PopStyleColor() From b48ed9ebc0ac2e2c1f705805fcc53b3617acd73f Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 1 Oct 2018 11:56:26 +0200 Subject: [PATCH 295/828] Docking: Better tracking of current dock id for inactive and uncreated windows (in settings etc.). + Fixed assert when docking a single-visible leaf node of a hierarchy into another --- docs/TODO.txt | 1 + imgui.cpp | 82 +++++++++++++++++++++++++++++++++++---------------- 2 files changed, 57 insertions(+), 26 deletions(-) diff --git a/docs/TODO.txt b/docs/TODO.txt index f6d233041e76..72d9200bb63e 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -148,6 +148,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - dock: B- reintroduce collapsing a floating dock node. also collapsing a docked dock node! - dock: B- allow dragging a non-floating dock node by clicking on the title-bar-looking section (not just the collapse/menu button) - dock: C- nav: CTRL+TAB highlighting tabs shows the mismatch between focus-stack and tab-order (not visible in VS because it doesn't highlight the tabs) + - dock: C- after a dock/undock, the Scrollbar Status update in Begin() should use an updated e.g. size_y_for_scrollbars to avoid a 1 frame scrollbar flicker. - tabs: re-ordering, close buttons, context menu, persistent order (#261, #351) diff --git a/imgui.cpp b/imgui.cpp index 17e5dbac88b3..1b6748c2d19e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3819,7 +3819,8 @@ void ImGui::EndFrame() AddWindowToSortBuffer(&g.WindowsSortBuffer, window); } - IM_ASSERT(g.Windows.Size == g.WindowsSortBuffer.Size); // we done something wrong + // This usually assert if there is a mismatch between the ImGuiWindowFlags_ChildWindow / ParentWindow values and DC.ChildWindows[] in parents, aka we've done something wrong. + IM_ASSERT(g.Windows.Size == g.WindowsSortBuffer.Size); g.Windows.swap(g.WindowsSortBuffer); g.IO.MetricsActiveWindows = g.WindowsActiveCount; @@ -9677,6 +9678,7 @@ namespace ImGui static ImGuiDockNode* DockNodeTreeFindNodeByPos(ImGuiDockNode* node, ImVec2 pos); // Settings + static void DockSettingsMoveDockReferencesInInactiveWindow(ImGuiID old_dock_id, ImGuiID new_dock_id); static void DockSettingsRemoveReferencesToNodes(ImGuiID* node_ids, int node_ids_count); static ImGuiDockNodeSettings* DockSettingsFindNodeSettings(ImGuiContext* ctx, ImGuiID node_id); static void* DockSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name); @@ -9951,7 +9953,7 @@ static void ImGui::DockContextBuildNodesFromSettings(ImGuiContext* ctx, ImGuiDoc // Bind host window immediately if it already exist (in case of a rebuild) // This is useful as the RootWindowForTitleBarHighlight links necessary to highlight the currently focused node requires node->HostWindow to be set. - char host_window_title[32]; + char host_window_title[20]; ImGuiDockNode* root_node = DockNodeGetRootNode(node); node->HostWindow = FindWindowByName(DockNodeGetHostWindowTitle(root_node, host_window_title, IM_ARRAYSIZE(host_window_title))); } @@ -10090,25 +10092,24 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) TabBarAddTab(target_node->TabBar, target_node->Windows[n], ImGuiTabItemFlags_None); } + const ImGuiID payload_node_id = payload_node ? payload_node->ID : payload_window->DockId; if (payload_node != NULL) { // Transfer full payload node (with 1+ child windows or child nodes) - // FIXME-DOCK: Transition persistent DockId for all non-active windows if (payload_node->IsSplitNode()) { if (target_node->Windows.Size > 0) { // We can dock into a node that already has windows _only_ if our payload is a node tree with a single visible node. // In this situation, we move the windows of the target node into the currently visible node of the payload. - // This allows us to preserve some of the underlying settings nicely. - IM_ASSERT(payload_node->OnlyNodeWithWindows != NULL); + // This allows us to preserve some of the underlying dock tree settings nicely. + IM_ASSERT(payload_node->OnlyNodeWithWindows != NULL); // The docking should have been blocked by DockNodePreviewDockCalc() early on and never submitted. ImGuiDockNode* visible_node = payload_node->OnlyNodeWithWindows; if (visible_node->TabBar) IM_ASSERT(visible_node->TabBar->Tabs.Size > 0); - for (int n = 0; n < visible_node->Windows.Size; n++) - TabBarAddTab(target_node->TabBar, visible_node->Windows[n], ImGuiTabItemFlags_None); DockNodeMoveWindows(target_node, visible_node); DockNodeMoveWindows(visible_node, target_node); + DockSettingsMoveDockReferencesInInactiveWindow(target_node->ID, visible_node->ID); } IM_ASSERT(target_node->Windows.Size == 0); DockNodeMoveChildNodes(target_node, payload_node); @@ -10116,6 +10117,7 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) else { DockNodeMoveWindows(target_node, payload_node); + DockSettingsMoveDockReferencesInInactiveWindow(payload_node_id, target_node->ID); } DockContextRemoveNode(ctx, payload_node, true); } @@ -10124,6 +10126,8 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) // Transfer single window target_node->VisibleWindow = payload_window; DockNodeAddWindow(target_node, payload_window, true); + if (payload_node_id != 0) + DockSettingsMoveDockReferencesInInactiveWindow(payload_node_id, target_node->ID); } } @@ -10156,9 +10160,9 @@ void ImGui::DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node) // Otherwise delete the previous node by merging the other sibling back into the parent node. if (node->IsRootNode() || node->IsDocumentRoot) { - // FIXME-DOCK: Transition persistent DockId for all non-active windows ImGuiDockNode* new_node = DockContextAddNode(ctx, 0); DockNodeMoveWindows(new_node, node); + DockSettingsMoveDockReferencesInInactiveWindow(node->ID, new_node->ID); for (int n = 0; n < new_node->Windows.Size; n++) UpdateWindowParentAndRootLinks(new_node->Windows[n], new_node->Windows[n]->Flags, NULL); new_node->WantMouseMove = true; @@ -10516,25 +10520,28 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) } } - // Early out for standalone floating window that are holding on a DockId (with an invisible dock node) - if (node->IsRootNode() && node->Windows.Size == 1 && !node->IsDockSpace) + // Early out for hidden root dock nodes (when all DockId references are in inactive windows, or there is only 1 floating window holding on the DockId) + if (node->IsRootNode() && !node->IsSplitNode() && node->Windows.Size <= 1 && !node->IsDockSpace) { - // Floating window pos/size is authoritative - ImGuiWindow* single_window = node->Windows[0]; - node->Pos = single_window->Pos; - node->Size = single_window->SizeFull; - - // Transfer focus immediately so when we revert to a regular window it is immediately selected - if (node->HostWindow && g.NavWindow == node->HostWindow) - FocusWindow(single_window); - if (node->HostWindow) + if (node->Windows.Size == 1) { - single_window->Viewport = node->HostWindow->Viewport; - single_window->ViewportId = node->HostWindow->ViewportId; - if (node->HostWindow->ViewportOwned) + // Floating window pos/size is authoritative + ImGuiWindow* single_window = node->Windows[0]; + node->Pos = single_window->Pos; + node->Size = single_window->SizeFull; + + // Transfer focus immediately so when we revert to a regular window it is immediately selected + if (node->HostWindow && g.NavWindow == node->HostWindow) + FocusWindow(single_window); + if (node->HostWindow) { - single_window->Viewport->Window = single_window; - single_window->ViewportOwned = true; + single_window->Viewport = node->HostWindow->Viewport; + single_window->ViewportId = node->HostWindow->ViewportId; + if (node->HostWindow->ViewportOwned) + { + single_window->Viewport->Window = single_window; + single_window->ViewportOwned = true; + } } } @@ -10545,8 +10552,8 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) node->HasCloseButton = node->HasCollapseButton = false; node->LastFrameActive = g.FrameCount; - if (node->WantMouseMove) - DockNodeStartMouseMovingWindow(node, single_window); + if (node->WantMouseMove && node->Windows.Size == 1) + DockNodeStartMouseMovingWindow(node, node->Windows[0]); return; } @@ -11222,9 +11229,15 @@ void ImGui::DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImG ImVec2 backup_last_explicit_size = parent_node->SizeRef; DockNodeMoveChildNodes(parent_node, merge_lead_child); if (child_0) + { DockNodeMoveWindows(parent_node, child_0); // Generally only 1 of the 2 child node will have windows + DockSettingsMoveDockReferencesInInactiveWindow(child_0->ID, parent_node->ID); + } if (child_1) + { DockNodeMoveWindows(parent_node, child_1); + DockSettingsMoveDockReferencesInInactiveWindow(child_1->ID, parent_node->ID); + } DockNodeApplyPosSizeToWindows(parent_node); parent_node->InitFromFirstWindowPosSize = parent_node->InitFromFirstWindowViewport = false; parent_node->VisibleWindow = merge_lead_child->VisibleWindow; @@ -12065,6 +12078,23 @@ void ImGui::BeginAsDockableDragDropTarget(ImGuiWindow* window) // Docking: Settings //----------------------------------------------------------------------------- +static void ImGui::DockSettingsMoveDockReferencesInInactiveWindow(ImGuiID old_dock_id, ImGuiID new_dock_id) +{ + ImGuiContext& g = *GImGui; + for (int window_n = 0; window_n < g.Windows.Size; window_n++) + { + ImGuiWindow* window = g.Windows[window_n]; + if (window->DockId == old_dock_id && window->DockNode == NULL) + window->DockId = new_dock_id; + } + for (int settings_n = 0; settings_n < g.SettingsWindows.Size; settings_n++) // FIXME-OPT: We could remove this loop by storing the index in the map + { + ImGuiWindowSettings* window_settings = &g.SettingsWindows[settings_n]; + if (window_settings->DockId == old_dock_id) + window_settings->DockId = new_dock_id; + } +} + // Remove references stored in ImGuiWindowSettings to the given ImGuiDockNodeSettings static void ImGui::DockSettingsRemoveReferencesToNodes(ImGuiID* node_ids, int node_ids_count) { From 2cff3f6a65663abe1a29720a3376d7611b1594ba Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 1 Oct 2018 16:23:13 +0200 Subject: [PATCH 296/828] Docking: Renamed io.ConfigDockingWithKeyMode to io.ConfigDockingWithShift. --- docs/CHANGELOG.txt | 10 +++++++--- docs/TODO.txt | 12 ++++++------ examples/example_win32_directx11/main.cpp | 2 +- imgui.cpp | 10 +++++----- imgui.h | 6 +++--- imgui_demo.cpp | 4 ++-- imgui_widgets.cpp | 4 ++-- 7 files changed, 26 insertions(+), 22 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 4502520eafd5..0f1670fb563c 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -36,18 +36,22 @@ HOW TO UPDATE? - Added ImGuiConfigFlags_DockingEnable flag to enable Docking. [BETA] Set with `io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;`. - Added BeginTabBar(), EndTabBar(), BeginTabItem(), EndTabItem(), SetTabItemClosed() API. (#261, #351) +- Added ImGuiTabBarFlags flags for BeginTabBar(). +- Added ImGuiTabItemFlags flags for BeginTabItem(). - Added DockSpace() API. (#351) -- Added SetNextWindowDock() API. (#351) -- Added IsWindowDocked() API. (#351) +- Added ImGuiDockNodeFlags flags for DockSpace(). +- Added SetNextWindowDock(), SetNextWindowDockFamily() API. (#351) +- Added GetWindowDockId(), IsWindowDocked() API. (#351) - Added ImGuiWindowFlags_NoDocking window flag to disable the possibility for a window to be docked. Popup, Menu and Child windows always have the ImGuiWindowFlags_NoDocking flag set. (#351) - Added ImGuiWindowFlags_UnsavedDocument window flag to append '*' to title without altering the ID, as a convenience to avoid using the ### operator. -- Added io.ConfigDockingWithKeyMod option to configure docking mode. +- Added io.ConfigDockingWithShift option to configure docking mode. - Style: Added ImGuiCol_DockingPreview, ImGuiCol_DockingBg colors. (#351) - Style: Added ImGuiCol_Tab, ImGuiCol_TabHovered, ImGuiCol_TabActive, ImGuiCol_TabUnfocused, ImGuiCol_TabUnfocusedActive colors. (#261, #351) - Demo: Added Layout->Tabs demo code. (#261, #351) - Demo: Added "Documents" example app showcasing possible use for tabs. (#261, #351) +- Demo: Added "DockSpace" example app showcasing use of explicit dockspace nodes. (#351) ----------------------------------------------------------------------- diff --git a/docs/TODO.txt b/docs/TODO.txt index 72d9200bb63e..31536e21ae78 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -123,15 +123,15 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - splitter/separator: formalize the splitter idiom into an official api (we want to handle n-way split) (#319) - - dock: A~ Unreal style document system (requires low-level controls of dockspace serialization fork/copy/delete) + - dock: A~ Unreal style document system (requires low-level controls of dockspace serialization fork/copy/delete). this is mostly working but the DockBuilderXXX api are not exposed/finished. - dock: A- implicit, invisible per-viewport dockspace to dock to. - - dock: B: when docking outer, perform size locking on neighbors nodes the same way we do it with splitters, so other nodes are not resized - - dock: B~ document root node resizing behavior incorrect + - dock: B: when docking outer, perform size locking on neighbors nodes the same way we do it with splitters, so other nodes are not resized. + - dock: B~ document root node resizing behavior incorrect. - dock: B~ document root node retrieval of ID ? - - dock: B- full rebuild loses viewport of floating dock nodes - - dock: B- dock node inside its own viewports creates 1 temporary viewport per window on startup before ditching them + - dock: B- debug full rebuild loses viewport of floating dock nodes. + - dock: B- dock node inside its own viewports creates 1 temporary viewport per window on startup before ditching them (doesn't affect the user nor request platform windows to be created, but unnecessary) - dock: B- resize sibling locking behavior may be less desirable if we merged same-axis sibling in a same node level? - - dock: A- single visible node part of a hidden split hierarchy (OnlyNodeWithWindows != NULL) should show a normal title bar (not a tab bar) + - dock: B- single visible node part of a hidden split hierarchy (OnlyNodeWithWindows != NULL) should show a normal title bar (not a tab bar) - dock: B~ SetNextWindowDock() calls (with conditional) -> defer everything to DockContextUpdate (repro: Documents->[X]Windows->Dock 1 elsewhere->Click Redock All - dock: B~ tidy up tab list popup buttons features (available with manual tab-bar, see ImGuiTabBarFlags_NoTabListPopupButton code, not used by docking nodes) - dock: B- DockSpace() border issues diff --git a/examples/example_win32_directx11/main.cpp b/examples/example_win32_directx11/main.cpp index 16c03ed57bef..565313404658 100644 --- a/examples/example_win32_directx11/main.cpp +++ b/examples/example_win32_directx11/main.cpp @@ -142,7 +142,7 @@ int main(int, char**) io.ConfigFlags |= ImGuiConfigFlags_DpiEnableScaleFonts; // FIXME-DPI: THIS CURRENTLY DOESN'T WORK AS EXPECTED. DON'T USE IN USER APP! io.ConfigFlags |= ImGuiConfigFlags_DpiEnableScaleViewports; // FIXME-DPI io.ConfigResizeWindowsFromEdges = true; - io.ConfigDockingWithKeyMod = true; + io.ConfigDockingWithShift = true; ImGui_ImplWin32_Init(hwnd); ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext); diff --git a/imgui.cpp b/imgui.cpp index 1b6748c2d19e..cea99d5b2d49 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1090,7 +1090,7 @@ ImGuiIO::ImGuiIO() DisplayFramebufferScale = ImVec2(1.0f, 1.0f); // Miscellaneous configuration options - ConfigDockingWithKeyMod = true; + ConfigDockingWithShift = false; #ifdef __APPLE__ ConfigMacOSXBehaviors = true; // Set Mac OS X style defaults based on __APPLE__ compile time flag #else @@ -5519,7 +5519,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) { // Docking: Dragging a dockable window (or any of its child) turns it into a drag and drop source. // We need to do this _before_ we overwrite window->DC.LastItemId below because BeginAsDockableDragDropSource() also overwrites it. - if ((g.ActiveId == window->MoveId) && ((g.IO.ConfigDockingWithKeyMod && g.IO.KeyShift) || (!g.IO.ConfigDockingWithKeyMod))) + if ((g.ActiveId == window->MoveId) && ((g.IO.ConfigDockingWithShift && g.IO.KeyShift) || (!g.IO.ConfigDockingWithShift))) if ((window->RootWindow->Flags & (ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDocking)) == 0) BeginAsDockableDragDropSource(window); @@ -11062,7 +11062,7 @@ static bool ImGui::DockNodePreviewDockCalc(ImGuiWindow* host_window, ImGuiDockNo // When docking without holding Shift, we only allow and preview docking when hovering over a drop rect or over the title bar data->IsDropAllowed = (data->SplitDir != ImGuiDir_None) || (data->IsCenterAvailable); - if (!is_explicit_target && !data->IsSplitDirExplicit && !g.IO.ConfigDockingWithKeyMod) + if (!is_explicit_target && !data->IsSplitDirExplicit && !g.IO.ConfigDockingWithShift) data->IsDropAllowed = false; // Calculate split area @@ -12005,7 +12005,7 @@ void ImGui::BeginAsDockableDragDropSource(ImGuiWindow* window) window->DC.LastItemId = window->MoveId; window = window->RootWindow; IM_ASSERT((window->Flags & ImGuiWindowFlags_NoDocking) == 0); - bool is_drag_docking = (g.IO.ConfigDockingWithKeyMod) || ImRect(0, 0, window->SizeFull.x, GetFrameHeight()).Contains(g.ActiveIdClickOffset); + bool is_drag_docking = (g.IO.ConfigDockingWithShift) || ImRect(0, 0, window->SizeFull.x, GetFrameHeight()).Contains(g.ActiveIdClickOffset); if (is_drag_docking && BeginDragDropSource(ImGuiDragDropFlags_SourceNoPreviewTooltip | ImGuiDragDropFlags_SourceNoHoldToOpenOthers | ImGuiDragDropFlags_SourceAutoExpirePayload)) { SetDragDropPayload(IMGUI_PAYLOAD_TYPE_WINDOW, &window, sizeof(window)); @@ -12047,7 +12047,7 @@ void ImGui::BeginAsDockableDragDropTarget(ImGuiWindow* window) allow_null_target_node = true; // Dock into a regular window const ImRect explicit_target_rect = (target_node && target_node->TabBar) ? target_node->TabBar->BarRect : ImRect(window->Pos, window->Pos + ImVec2(window->Size.x, GetFrameHeight())); - const bool is_explicit_target = g.IO.ConfigDockingWithKeyMod || IsMouseHoveringRect(explicit_target_rect.Min, explicit_target_rect.Max); + const bool is_explicit_target = g.IO.ConfigDockingWithShift || IsMouseHoveringRect(explicit_target_rect.Min, explicit_target_rect.Max); // Preview docking request and find out split direction/ratio //const bool do_preview = true; // Ignore testing for payload->IsPreview() which removes one frame of delay, but breaks overlapping drop targets within the same window. diff --git a/imgui.h b/imgui.h index c800b7a5e4dc..c89aca776c79 100644 --- a/imgui.h +++ b/imgui.h @@ -525,7 +525,7 @@ namespace ImGui // Docking // [BETA API] Enable with io.ConfigFlags |= ImGuiConfigFlags_DockingEnable. // Note: you DO NOT need to call DockSpace() to use most Docking facilities! - // To dock windows: hold SHIFT anywhere while moving windows (if io.ConfigDockingWithKeyMod == true) or drag windows from their title bar (if io.ConfigDockingWithKeyMod = false) + // To dock windows: hold SHIFT anywhere while moving windows (if io.ConfigDockingWithShift == true) or drag windows from their title bar (if io.ConfigDockingWithShift = false) // Use DockSpace() to create an explicit dock node _within_ an existing window. See Docking demo for details. IMGUI_API void DockSpace(ImGuiID id, const ImVec2& size = ImVec2(0, 0), ImGuiDockNodeFlags flags = 0, const ImGuiDockFamily* dock_family = NULL); IMGUI_API void SetNextWindowDockId(ImGuiID dock_id, ImGuiCond cond = 0); // set next window dock id (FIXME-DOCK) @@ -945,7 +945,7 @@ enum ImGuiConfigFlags_ ImGuiConfigFlags_NoMouseCursorChange = 1 << 5, // Instruct back-end to not alter mouse cursor shape and visibility. Use if the back-end cursor changes are interfering with yours and you don't want to use SetMouseCursor() to change mouse cursor. You may want to honor requests from imgui by reading GetMouseCursor() yourself instead. // [BETA] Docking - ImGuiConfigFlags_DockingEnable = 1 << 6, // Docking enable flags. Use SHIFT to dock window into another (or without SHIFT if io.ConfigDockingWithKeyMod = false). + ImGuiConfigFlags_DockingEnable = 1 << 6, // Docking enable flags. Use SHIFT to dock window into another (or without SHIFT if io.ConfigDockingWithShift = false). // [BETA] Viewports ImGuiConfigFlags_ViewportsEnable = 1 << 10, // Viewport enable flags (require both ImGuiConfigFlags_PlatformHasViewports + ImGuiConfigFlags_RendererHasViewports set by the respective back-ends) @@ -1214,7 +1214,7 @@ struct ImGuiIO // Miscellaneous configuration options bool MouseDrawCursor; // = false // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). Cannot be easily renamed to 'io.ConfigXXX' because this is frequently used by back-end implementations. - bool ConfigDockingWithKeyMod; // = true // Enable docking with holding Shift key (reduce visual noise, allows dropping in wider space) + bool ConfigDockingWithShift; // = true // Enable docking with holding Shift key (reduce visual noise, allows dropping in wider space) bool ConfigMacOSXBehaviors; // = defined(__APPLE__) // OS X style: Text editing cursor movement using Alt instead of Ctrl, Shortcuts using Cmd/Super instead of Ctrl, Line/Text Start and End using Cmd+Arrows instead of Home/End, Double click selects by word instead of selecting whole text, Multi-selection in lists uses Cmd/Super instead of Ctrl (was called io.OptMacOSXBehaviors prior to 1.63) bool ConfigInputTextCursorBlink; // = true // Set to false to disable blinking cursor, for users who consider it distracting. (was called: io.OptCursorBlink prior to 1.63) bool ConfigResizeWindowsFromEdges; // = true // [BETA] Enable resizing of windows from their edges and from the lower-left corner. This requires (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) because it needs mouse cursor feedback. (This used to be the ImGuiWindowFlags_ResizeFromAnySide flag) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 41e759c749d5..27c87c07d6ae 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -334,7 +334,7 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::SameLine(); ShowHelpMarker("Instruct back-end to not alter mouse cursor shape and visibility."); ImGui::CheckboxFlags("io.ConfigFlags: DockingEnable", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_DockingEnable); - ImGui::SameLine(); ShowHelpMarker("Use SHIFT to dock window into another (or without SHIFT if io.ConfigDockingWithKeyMod == false)"); + ImGui::SameLine(); ShowHelpMarker("Use SHIFT to dock window into another (or without SHIFT if io.ConfigDockingWithShift == false)"); ImGui::CheckboxFlags("io.ConfigFlags: ViewportsEnable", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_ViewportsEnable); ImGui::SameLine(); ShowHelpMarker("Toggling this at runtime is normally unsupported (it will offset your windows)."); @@ -343,7 +343,7 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::CheckboxFlags("io.ConfigFlags: ViewportsNoMerge", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_ViewportsNoMerge); ImGui::SameLine(); ShowHelpMarker("All floating windows will always create their own viewport and platform window."); - ImGui::Checkbox("io.ConfigDockingWithKeyMod", &io.ConfigDockingWithKeyMod); + ImGui::Checkbox("io.ConfigDockingWithShift", &io.ConfigDockingWithShift); ImGui::SameLine(); ShowHelpMarker("Enable docking when holding Shift only (allows to drop in wider space, reduce visual noise)"); ImGui::Checkbox("io.ConfigInputTextCursorBlink", &io.ConfigInputTextCursorBlink); ImGui::SameLine(); ShowHelpMarker("Set to false to disable blinking cursor, for users who consider it distracting"); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index e9425758b0b9..7053d44da5d0 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6455,10 +6455,10 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, bool undocking_tab = (g.DragDropActive && g.DragDropPayload.SourceId == id); if (!undocking_tab && held)// && (drag_delta.x != 0.0f || drag_delta.y != 0.0f)) { - //if (!g.IO.ConfigDockingWithKeyMod || g.IO.KeyShift) + //if (!g.IO.ConfigDockingWithShift || g.IO.KeyShift) { float threshold_base = g.FontSize; - //float threshold_base = g.IO.ConfigDockingWithKeyMod ? g.FontSize * 0.5f : g.FontSize; + //float threshold_base = g.IO.ConfigDockingWithShift ? g.FontSize * 0.5f : g.FontSize; float threshold_x = (threshold_base * 2.2f); float threshold_y = (threshold_base * 1.5f) + ImClamp((ImFabs(g.IO.MouseDragMaxDistanceAbs[0].x) - threshold_base * 2.0f) * 0.20f, 0.0f, threshold_base * 4.0f); //GetOverlayDrawList(window)->AddRect(ImVec2(bb.Min.x - threshold_x, bb.Min.y - threshold_y), ImVec2(bb.Max.x + threshold_x, bb.Max.y + threshold_y), IM_COL32_WHITE); // [DEBUG] From 44bfa24cc30118ea24dc6e185c1e066f39bda338 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 2 Oct 2018 11:09:53 +0200 Subject: [PATCH 297/828] Docking: Fixed crash using DockBuilderSplitNode(). (#2109) --- imgui.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index cea99d5b2d49..583004c05d7f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10092,7 +10092,6 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) TabBarAddTab(target_node->TabBar, target_node->Windows[n], ImGuiTabItemFlags_None); } - const ImGuiID payload_node_id = payload_node ? payload_node->ID : payload_window->DockId; if (payload_node != NULL) { // Transfer full payload node (with 1+ child windows or child nodes) @@ -10116,18 +10115,20 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) } else { + const ImGuiID payload_dock_id = payload_node->ID; DockNodeMoveWindows(target_node, payload_node); - DockSettingsMoveDockReferencesInInactiveWindow(payload_node_id, target_node->ID); + DockSettingsMoveDockReferencesInInactiveWindow(payload_dock_id, target_node->ID); } DockContextRemoveNode(ctx, payload_node, true); } else if (payload_window) { // Transfer single window + const ImGuiID payload_dock_id = payload_window->DockId; target_node->VisibleWindow = payload_window; DockNodeAddWindow(target_node, payload_window, true); - if (payload_node_id != 0) - DockSettingsMoveDockReferencesInInactiveWindow(payload_node_id, target_node->ID); + if (payload_dock_id != 0) + DockSettingsMoveDockReferencesInInactiveWindow(payload_dock_id, target_node->ID); } } From bd82539ad595fcf3b0c2c28058f1a74b585a2415 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 3 Oct 2018 11:24:52 +0200 Subject: [PATCH 298/828] Docking: Fixed DockSpace() child window displaying a scrollbar behind the node backgrounds. Rename ImGuiCol_DockingBg to ImGuiCol_DockingEmptyBg. Added ImGuiDockNode::IsLeaftNode(). (#2109) --- docs/CHANGELOG.txt | 2 +- imgui.cpp | 28 +++++++++++++--------------- imgui.h | 2 +- imgui_demo.cpp | 2 -- imgui_draw.cpp | 6 +++--- imgui_internal.h | 1 + 6 files changed, 19 insertions(+), 22 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 53bb92d82f7a..21a7ae8b57bb 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -47,7 +47,7 @@ HOW TO UPDATE? - Added ImGuiWindowFlags_UnsavedDocument window flag to append '*' to title without altering the ID, as a convenience to avoid using the ### operator. - Added io.ConfigDockingWithShift option to configure docking mode. -- Style: Added ImGuiCol_DockingPreview, ImGuiCol_DockingBg colors. (#351) +- Style: Added ImGuiCol_DockingPreview, ImGuiCol_DockingEmptyBg colors. (#351) - Style: Added ImGuiCol_Tab, ImGuiCol_TabHovered, ImGuiCol_TabActive, ImGuiCol_TabUnfocused, ImGuiCol_TabUnfocusedActive colors. (#261, #351) - Demo: Added Layout->Tabs demo code. (#261, #351) - Demo: Added "Documents" example app showcasing possible use for tabs. (#261, #351) diff --git a/imgui.cpp b/imgui.cpp index c17e0b0df788..4ae4cec72c76 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6040,7 +6040,7 @@ const char* ImGui::GetStyleColorName(ImGuiCol idx) case ImGuiCol_TabUnfocused: return "TabUnfocused"; case ImGuiCol_TabUnfocusedActive: return "TabUnfocusedActive"; case ImGuiCol_DockingPreview: return "DockingPreview"; - case ImGuiCol_DockingBg: return "DockingBg"; + case ImGuiCol_DockingEmptyBg: return "DockingEmptyBg"; case ImGuiCol_PlotLines: return "PlotLines"; case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered"; case ImGuiCol_PlotHistogram: return "PlotHistogram"; @@ -10059,7 +10059,7 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) { payload_node = payload_window->DockNodeAsHost; payload_window->DockNodeAsHost = NULL; // Important to clear this as the node will have its life as a child which might be merged/deleted later. - if (payload_node && !payload_node->IsSplitNode()) + if (payload_node && payload_node->IsLeafNode()) next_selected_id = payload_node->TabBar->NextSelectedTabId ? payload_node->TabBar->NextSelectedTabId : payload_node->TabBar->SelectedTabId; if (payload_node == NULL) next_selected_id = payload_window->ID; @@ -10177,7 +10177,7 @@ void ImGui::DockContextProcessUndockWindow(ImGuiContext* ctx, ImGuiWindow* windo void ImGui::DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node) { (void)ctx; - IM_ASSERT(!node->IsSplitNode()); + IM_ASSERT(node->IsLeafNode()); IM_ASSERT(node->Windows.Size >= 1); // In the case of a root node or document root, the node will have to stay in place. Create a new node to receive the payload. @@ -10545,7 +10545,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) } // Early out for hidden root dock nodes (when all DockId references are in inactive windows, or there is only 1 floating window holding on the DockId) - if (node->IsRootNode() && !node->IsSplitNode() && node->Windows.Size <= 1 && !node->IsDockSpace) + if (node->IsRootNode() && node->IsLeafNode() && node->Windows.Size <= 1 && !node->IsDockSpace) { if (node->Windows.Size == 1) { @@ -10649,14 +10649,11 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) DockNodeStartMouseMovingWindow(node, node->HostWindow); } - // Update active node (the one whose title bar is highlight) within a node tree + // Update focused node (the one whose title bar is highlight) within a node tree if (node->IsSplitNode()) IM_ASSERT(node->TabBar == NULL); if (node->IsRootNode()) { - //if (!node->IsSplitNode()) - // node->LastFocusedNodeID = node->ID; // This also ensure on our creation frame we will receive the title screen highlight - //else if (g.NavWindow && g.NavWindow->RootWindowDockStop->DockNode && g.NavWindow->RootWindowDockStop->ParentWindow == host_window) node->LastFocusedNodeID = g.NavWindow->RootWindowDockStop->DockNode->ID; } @@ -10679,7 +10676,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) { // Background for empty nodes if (node->Windows.Size == 0 && !node->IsSplitNode()) - host_window->DrawList->AddRectFilled(node->Pos, node->Pos + node->Size, GetColorU32(ImGuiCol_DockingBg)); + host_window->DrawList->AddRectFilled(node->Pos, node->Pos + node->Size, GetColorU32(ImGuiCol_DockingEmptyBg)); // Drop target if (node->IsRootNode() && (g.MovingWindow == NULL || g.MovingWindow->RootWindow != host_window)) @@ -11285,7 +11282,7 @@ void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 si { node->Pos = pos; node->Size = size; - if (!node->IsSplitNode()) + if (node->IsLeafNode()) return; ImGuiDockNode* child_0 = node->ChildNodes[0]; @@ -11347,7 +11344,7 @@ void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 si static void DockNodeTreeUpdateSplitterFindTouchingNode(ImGuiDockNode* node, ImGuiAxis axis, int side, ImVector* touching_nodes) { - if (!node->IsSplitNode()) + if (node->IsLeafNode()) { touching_nodes->push_back(node); return; @@ -11362,7 +11359,7 @@ static void DockNodeTreeUpdateSplitterFindTouchingNode(ImGuiDockNode* node, ImGu void ImGui::DockNodeTreeUpdateSplitter(ImGuiDockNode* node) { - if (!node->IsSplitNode()) + if (node->IsLeafNode()) return; ImGuiContext& g = *GImGui; @@ -11483,7 +11480,7 @@ ImGuiDockNode* ImGui::DockNodeTreeFindNodeByPos(ImGuiDockNode* node, ImVec2 pos) if (!inside) return NULL; - if (!node->IsSplitNode()) + if (node->IsLeafNode()) return node; if (ImGuiDockNode* hovered_node = DockNodeTreeFindNodeByPos(node->ChildNodes[0], pos)) return hovered_node; @@ -11562,6 +11559,7 @@ void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags doc ImGuiWindowFlags window_flags = ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_DockNodeHost; window_flags |= ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar; + window_flags |= ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse; char title[256]; ImFormatString(title, IM_ARRAYSIZE(title), "%s/DockSpace_%08X", window->Name, id); @@ -11733,7 +11731,7 @@ ImGuiID ImGui::DockBuilderSplitNode(ImGuiContext* ctx, ImGuiID id, ImGuiDir spli return 0; } - IM_ASSERT(!node->IsSplitNode()); // Already Split + IM_ASSERT(!node->IsSplitNode()); // Assert if already Split ImGuiDockRequest req; req.Type = ImGuiDockRequestType_Split; @@ -11981,7 +11979,7 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) } IM_ASSERT(dock_node->HostWindow); - IM_ASSERT(!dock_node->IsSplitNode()); + IM_ASSERT(dock_node->IsLeafNode()); // Position window SetNextWindowPos(dock_node->Pos); diff --git a/imgui.h b/imgui.h index b8b563f3ab47..6527623c07a1 100644 --- a/imgui.h +++ b/imgui.h @@ -1015,7 +1015,7 @@ enum ImGuiCol_ ImGuiCol_TabUnfocused, ImGuiCol_TabUnfocusedActive, ImGuiCol_DockingPreview, - ImGuiCol_DockingBg, // Empty node + ImGuiCol_DockingEmptyBg, // Background color for empty node (e.g. DocRoot node with no window docked into it) ImGuiCol_PlotLines, ImGuiCol_PlotLinesHovered, ImGuiCol_PlotHistogram, diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 27c87c07d6ae..ef9d680e4bcf 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3765,10 +3765,8 @@ void ShowExampleAppDockSpace(bool* p_open) ImGuiIO& io = ImGui::GetIO(); if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable) { - //ImGui::PushStyleColor(ImGuiCol_DockingBg, ImVec4(0.2f, 0.2f, 0.2f, 1.0f)); ImGuiID dockspace_id = ImGui::GetID("MyDockspace"); ImGui::DockSpace(dockspace_id); - //ImGui::PopStyleColor(); } else { diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 2a45b2e60b42..8a48bd4f9773 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -211,7 +211,7 @@ void ImGui::StyleColorsDark(ImGuiStyle* dst) colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f); colors[ImGuiCol_TabHovered] = colors[ImGuiCol_HeaderHovered]; colors[ImGuiCol_DockingPreview] = colors[ImGuiCol_HeaderActive] * ImVec4(1.0f, 1.0f, 1.0f, 0.7f); - colors[ImGuiCol_DockingBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f);; + colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f);; colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f); colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f); colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); @@ -268,7 +268,7 @@ void ImGui::StyleColorsClassic(ImGuiStyle* dst) colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f); colors[ImGuiCol_TabHovered] = colors[ImGuiCol_HeaderHovered]; colors[ImGuiCol_DockingPreview] = colors[ImGuiCol_Header] * ImVec4(1.0f, 1.0f, 1.0f, 0.7f); - colors[ImGuiCol_DockingBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f);; + colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f);; colors[ImGuiCol_PlotLines] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); colors[ImGuiCol_PlotLinesHovered] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); @@ -326,7 +326,7 @@ void ImGui::StyleColorsLight(ImGuiStyle* dst) colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f); colors[ImGuiCol_TabHovered] = colors[ImGuiCol_HeaderHovered]; colors[ImGuiCol_DockingPreview] = colors[ImGuiCol_Header] * ImVec4(1.0f, 1.0f, 1.0f, 0.7f); - colors[ImGuiCol_DockingBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f);; + colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f);; colors[ImGuiCol_PlotLines] = ImVec4(0.39f, 0.39f, 0.39f, 1.00f); colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f); colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); diff --git a/imgui_internal.h b/imgui_internal.h index 42a6564ae3f1..76ec1b351dd3 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -778,6 +778,7 @@ struct ImGuiDockNode ~ImGuiDockNode(); bool IsRootNode() const { return ParentNode == NULL; } bool IsSplitNode() const { return ChildNodes[0] != NULL; } + bool IsLeafNode() const { return ChildNodes[0] == NULL; } bool IsEmpty() const { return ChildNodes[0] == NULL && Windows.Size == 0; } ImRect Rect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } }; From 1d3862b6b3fad2cf7b2e934b95d5710f38632b32 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 3 Oct 2018 14:51:31 +0200 Subject: [PATCH 299/828] Docking: Added ImGuiDockNodeFlags_NoDockingInsideDocRootNode flag. Honoring ImGuiDockNodeFlags_NoSplit in child node is already split (so we can use DockBuilder and then lock the layout). Added those options to the demo. (#2109) --- imgui.cpp | 13 +++++++++++++ imgui.h | 3 ++- imgui_demo.cpp | 44 +++++++++++++++++++++++++------------------- 3 files changed, 40 insertions(+), 20 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 4ae4cec72c76..411a60b3312a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10452,6 +10452,10 @@ static void ImGui::DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* nod IM_ASSERT(node->ParentNode == NULL || node->ParentNode->ChildNodes[0] == node || node->ParentNode->ChildNodes[1] == node); + // Inherit flags + if (node->ParentNode) + node->Flags = node->ParentNode->Flags; + // Recurse into children // There is the possibility that one of our child becoming empty will delete itself and moving its sibling contents into 'node'. // If 'node->ChildNode[0]' delete itself, then 'node->ChildNode[1]->Windows' will be moved into 'node' @@ -11055,6 +11059,8 @@ static bool ImGui::DockNodePreviewDockCalc(ImGuiWindow* host_window, ImGuiDockNo data->IsCenterAvailable = !is_outer_docking; if (src_is_visibly_splitted && (!host_node || !host_node->IsEmpty())) data->IsCenterAvailable = false; + if (host_node && (host_node->Flags & ImGuiDockNodeFlags_NoDockingInsideDocRootNode) && host_node->IsDocumentRoot) + data->IsCenterAvailable = false; data->IsSidesAvailable = true; if (host_node && (host_node->Flags & ImGuiDockNodeFlags_NoSplit)) @@ -11946,6 +11952,13 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) g.NextWindowData.PosUndock = false; } + // Undock if the ImGuiDockNodeFlags_NoDockingInDocRootNode got set + if (dock_node->IsDocumentRoot && (dock_node->Flags & ImGuiDockNodeFlags_NoDockingInsideDocRootNode)) + { + DockContextProcessUndockWindow(ctx, window); + return; + } + // Undock if our dockspace node disappeared // Note how we are testing for LastFrameAlive and NOT LastFrameActive. A DockSpace node can be maintained alive while being inactive with ImGuiDockNodeFlags_KeepAliveOnly. if (dock_node->LastFrameAlive < g.FrameCount) diff --git a/imgui.h b/imgui.h index 6527623c07a1..d7828a742f67 100644 --- a/imgui.h +++ b/imgui.h @@ -797,7 +797,8 @@ enum ImGuiDockNodeFlags_ { ImGuiDockNodeFlags_None = 0, ImGuiDockNodeFlags_KeepAliveOnly = 1 << 0, // Don't display the dockspace node but keep it alive. Windows docked into this dockspace node won't be undocked. - ImGuiDockNodeFlags_NoSplit = 1 << 1 // Disable splitting the node into smaller nodes. Useful e.g. when embedding dockspaces into a main root one (the root one may have splitting disableed to reduce confusion) + ImGuiDockNodeFlags_NoSplit = 1 << 1, // Disable splitting the node into smaller nodes. Useful e.g. when embedding dockspaces into a main root one (the root one may have splitting disabled to reduce confusion) + ImGuiDockNodeFlags_NoDockingInsideDocRootNode = 1 << 2 // Disable docking inside the DocumentRoot node. Useful if it is kept empty and invisible. }; // Flags for ImGui::IsWindowFocused() diff --git a/imgui_demo.cpp b/imgui_demo.cpp index ef9d680e4bcf..3e52d6606aed 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3732,25 +3732,42 @@ void ShowExampleAppDockSpace(bool* p_open) flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove; flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus; } + + static ImGuiDockNodeFlags dockspace_flags = ImGuiDockNodeFlags_None; ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); ImGui::Begin("DockSpace Demo", p_open, flags); ImGui::PopStyleVar(); + // Dockspace + ImGuiIO& io = ImGui::GetIO(); + if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable) + { + ImGuiID dockspace_id = ImGui::GetID("MyDockspace"); + ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags); + } + else + { + ShowDockingDisabledMessage(); + } + if (ImGui::BeginMenuBar()) { - if (ImGui::BeginMenu("Docking")) + if (ImGui::BeginMenu("Options")) { if (ImGui::MenuItem("Remove DockSpace", NULL, false, p_open != NULL)) *p_open = false; + ImGui::Separator(); + + if (ImGui::MenuItem("Flag: NoSplit", "", (dockspace_flags & ImGuiDockNodeFlags_NoSplit) != 0)) + dockspace_flags ^= ImGuiDockNodeFlags_NoSplit; + if (ImGui::MenuItem("Flag: NoDockingInsideDocRootNode", "", (dockspace_flags & ImGuiDockNodeFlags_NoDockingInsideDocRootNode) != 0)) + dockspace_flags ^= ImGuiDockNodeFlags_NoDockingInsideDocRootNode; + + // Disabling fullscreen would allow the window to be moved to the front of other windows, + // which we can't undo at the moment without finer window depth/z control. + //ImGui::MenuItem("Fullscreen", NULL, &opt_fullscreen_persistant); ImGui::EndMenu(); } - // Disabling fullscreen would allow the window to be moved to the front of other windows, - // which we can't undo at the moment without finer window depth/z control. - /*if (ImGui::BeginMenu("Options")) - { - ImGui::MenuItem("Fullscreen", NULL, &opt_fullscreen_persistant); - ImGui::EndMenu(); - }*/ ShowHelpMarker( "You can _always_ dock _any_ window into another by holding the SHIFT key while moving a window. Try it now!" "\n" "This demo app has nothing to do with it!" "\n\n" @@ -3762,17 +3779,6 @@ void ShowExampleAppDockSpace(bool* p_open) ImGui::EndMenuBar(); } - ImGuiIO& io = ImGui::GetIO(); - if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable) - { - ImGuiID dockspace_id = ImGui::GetID("MyDockspace"); - ImGui::DockSpace(dockspace_id); - } - else - { - ShowDockingDisabledMessage(); - } - ImGui::End(); if (opt_fullscreen) ImGui::PopStyleVar(); From d348d86df4e1da145086ae6754d670b435ab3232 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 3 Oct 2018 15:48:54 +0200 Subject: [PATCH 300/828] Docking: Renamed "DocRoot/DocumentRoot" to "CentralNode", more self explanatory. Moved Splitter update higher up in DockNodeUpdate() + minor misc tweak. (#2109) --- docs/TODO.txt | 4 +- imgui.cpp | 107 +++++++++++++++++++++++++---------------------- imgui.h | 4 +- imgui_demo.cpp | 4 +- imgui_internal.h | 2 +- 5 files changed, 63 insertions(+), 58 deletions(-) diff --git a/docs/TODO.txt b/docs/TODO.txt index 31536e21ae78..5b1f18e475f2 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -126,8 +126,8 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - dock: A~ Unreal style document system (requires low-level controls of dockspace serialization fork/copy/delete). this is mostly working but the DockBuilderXXX api are not exposed/finished. - dock: A- implicit, invisible per-viewport dockspace to dock to. - dock: B: when docking outer, perform size locking on neighbors nodes the same way we do it with splitters, so other nodes are not resized. - - dock: B~ document root node resizing behavior incorrect. - - dock: B~ document root node retrieval of ID ? + - dock: B~ central node resizing behavior incorrect. + - dock: B~ central node ID retrieval API? - dock: B- debug full rebuild loses viewport of floating dock nodes. - dock: B- dock node inside its own viewports creates 1 temporary viewport per window on startup before ditching them (doesn't affect the user nor request platform windows to be created, but unnecessary) - dock: B- resize sibling locking behavior may be less desirable if we merged same-axis sibling in a same node level? diff --git a/imgui.cpp b/imgui.cpp index 411a60b3312a..205653e2b940 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -9635,11 +9635,11 @@ struct ImGuiDockNodeSettings char SplitAxis; char Depth; char IsDockSpace; - char IsDocumentRoot; + char IsCentralNode; ImVec2ih Pos; ImVec2ih Size; ImVec2ih SizeRef; - ImGuiDockNodeSettings() { ID = ParentID = SelectedTabID = 0; SplitAxis = ImGuiAxis_None; Depth = 0; IsDockSpace = IsDocumentRoot = 0; } + ImGuiDockNodeSettings() { ID = ParentID = SelectedTabID = 0; SplitAxis = ImGuiAxis_None; Depth = 0; IsDockSpace = IsCentralNode = 0; } }; struct ImGuiDockContext @@ -9941,7 +9941,7 @@ static void ImGui::DockContextPruneUnusedSettingsNodes(ImGuiContext* ctx) ImGuiDockContextPruneNodeData* data_root = (data->RootID == settings->ID) ? data : pool.GetByKey(data->RootID); bool remove = false; - remove |= (data->CountWindows == 1 && settings->ParentID == 0 && data->CountChildNodes == 0 && !settings->IsDocumentRoot); // Floating root node with only 1 window + remove |= (data->CountWindows == 1 && settings->ParentID == 0 && data->CountChildNodes == 0 && !settings->IsCentralNode); // Floating root node with only 1 window remove |= (data->CountWindows == 0 && settings->ParentID == 0 && data->CountChildNodes == 0); // Leaf nodes with 0 window remove |= (data_root->CountChildWindows == 0); if (remove) @@ -9972,7 +9972,7 @@ static void ImGui::DockContextBuildNodesFromSettings(ImGuiContext* ctx, ImGuiDoc node->SelectedTabID = node_settings->SelectedTabID; node->SplitAxis = node_settings->SplitAxis; node->IsDockSpace = node_settings->IsDockSpace != 0; - node->IsDocumentRoot = node_settings->IsDocumentRoot != 0; + node->IsCentralNode = node_settings->IsCentralNode != 0; // Bind host window immediately if it already exist (in case of a rebuild) // This is useful as the RootWindowForTitleBarHighlight links necessary to highlight the currently focused node requires node->HostWindow to be set. @@ -10070,7 +10070,7 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) if (target_node) IM_ASSERT(target_node->LastFrameAlive < g.FrameCount); if (target_node && target_window && target_node == target_window->DockNodeAsHost) - IM_ASSERT(target_node->Windows.Size > 1 || target_node->IsSplitNode() || target_node->IsDocumentRoot); + IM_ASSERT(target_node->Windows.Size > 1 || target_node->IsSplitNode() || target_node->IsCentralNode); // Create new node and add existing window to it if (target_node == NULL) @@ -10099,8 +10099,8 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) new_node->HostWindow = target_node->HostWindow; if (target_node) { - inheritor_node->IsDocumentRoot = target_node->IsDocumentRoot; - target_node->IsDocumentRoot = false; + inheritor_node->IsCentralNode = target_node->IsCentralNode; + target_node->IsCentralNode = false; } target_node = new_node; } @@ -10180,9 +10180,9 @@ void ImGui::DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node) IM_ASSERT(node->IsLeafNode()); IM_ASSERT(node->Windows.Size >= 1); - // In the case of a root node or document root, the node will have to stay in place. Create a new node to receive the payload. + // In the case of a root node or central node, the node will have to stay in place. Create a new node to receive the payload. // Otherwise delete the previous node by merging the other sibling back into the parent node. - if (node->IsRootNode() || node->IsDocumentRoot) + if (node->IsRootNode() || node->IsCentralNode) { ImGuiDockNode* new_node = DockContextAddNode(ctx, 0); DockNodeMoveWindows(new_node, node); @@ -10225,7 +10225,7 @@ ImGuiDockNode::ImGuiDockNode(ImGuiID id) WantCloseTabID = 0; InitFromFirstWindowPosSize = InitFromFirstWindowViewport = false; IsVisible = true; - IsDockSpace = IsDocumentRoot = HasCloseButton = HasCollapseButton = WantCloseAll = WantLockSizeOnce = WantMouseMove = false; + IsDockSpace = IsCentralNode = HasCloseButton = HasCollapseButton = WantCloseAll = WantLockSizeOnce = WantMouseMove = false; } ImGuiDockNode::~ImGuiDockNode() @@ -10319,7 +10319,7 @@ static void ImGui::DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window if (node->TabBar) { TabBarRemoveTab(node->TabBar, window->ID); - const int tab_count_threshold_for_tab_bar = node->IsDocumentRoot ? 1 : 2; + const int tab_count_threshold_for_tab_bar = node->IsCentralNode ? 1 : 2; if (node->Windows.Size < tab_count_threshold_for_tab_bar) { IM_DELETE(node->TabBar); @@ -10327,14 +10327,14 @@ static void ImGui::DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window } } - if (node->Windows.Size == 0 && !node->IsDocumentRoot && window->DockId != node->ID) + if (node->Windows.Size == 0 && !node->IsCentralNode && window->DockId != node->ID) { // Automatic dock node delete themselves if they are not holding at least one tab DockContextRemoveNode(&g, node, true); return; } - if (node->Windows.Size == 1 && !node->IsDocumentRoot && node->HostWindow) + if (node->Windows.Size == 1 && !node->IsCentralNode && node->HostWindow) { ImGuiWindow* remaining_window = node->Windows[0]; if (node->HostWindow->ViewportOwned && node->IsRootNode()) @@ -10479,7 +10479,7 @@ static void ImGui::DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* nod if (!remove) continue; window->DockTabWantClose = false; - if (node->Windows.Size == 1 && !node->IsDocumentRoot) + if (node->Windows.Size == 1 && !node->IsCentralNode) { DockNodeHideHostWindow(node); DockNodeRemoveWindow(node, window, node->ID); // Will delete the node so it'll be invalid on return @@ -10495,7 +10495,7 @@ static void ImGui::DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* nod static void ImGui::DockNodeUpdateVisibleFlag(ImGuiDockNode* node) { // Update visibility flag - bool is_visible = (node->ParentNode == 0) ? node->IsDockSpace : node->IsDocumentRoot; + bool is_visible = (node->ParentNode == 0) ? node->IsDockSpace : node->IsCentralNode; is_visible |= (node->Windows.Size > 0); is_visible |= (node->ChildNodes[0] && node->ChildNodes[0]->IsVisible); is_visible |= (node->ChildNodes[1] && node->ChildNodes[1]->IsVisible); @@ -10523,7 +10523,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) { DockNodeUpdateVisibleFlagAndInactiveChilds(node); - // Find if there's only a single visible window in the hierarchy (in which case we need to display a regular title bar, FIXME-DOCK: Not done yet!) + // Find if there's only a single visible window in the hierarchy (in which case we need to display a regular title bar -> FIXME-DOCK: that last part is not done yet!) if (!node->IsDockSpace) { int count = 0; @@ -10657,9 +10657,14 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) if (node->IsSplitNode()) IM_ASSERT(node->TabBar == NULL); if (node->IsRootNode()) - { if (g.NavWindow && g.NavWindow->RootWindowDockStop->DockNode && g.NavWindow->RootWindowDockStop->ParentWindow == host_window) node->LastFocusedNodeID = g.NavWindow->RootWindowDockStop->DockNode->ID; + + // Update position/size, process and draw resizing splitters + if (node->IsRootNode() && host_window) + { + DockNodeTreeUpdatePosSize(node, host_window->Pos, host_window->Size); + DockNodeTreeUpdateSplitter(node); } // Draw and populate Tab Bar @@ -10682,20 +10687,16 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) if (node->Windows.Size == 0 && !node->IsSplitNode()) host_window->DrawList->AddRectFilled(node->Pos, node->Pos + node->Size, GetColorU32(ImGuiCol_DockingEmptyBg)); - // Drop target + // Draw drop target if (node->IsRootNode() && (g.MovingWindow == NULL || g.MovingWindow->RootWindow != host_window)) BeginAsDockableDragDropTarget(host_window); } + node->LastFrameActive = g.FrameCount; - // Update pos/size and recurse into children + // Recurse into children if (host_window) { - if (node->IsRootNode()) - { - DockNodeTreeUpdatePosSize(node, host_window->Pos, host_window->Size); - DockNodeTreeUpdateSplitter(node); - } if (node->ChildNodes[0]) DockNodeUpdate(node->ChildNodes[0]); if (node->ChildNodes[1]) @@ -11059,13 +11060,13 @@ static bool ImGui::DockNodePreviewDockCalc(ImGuiWindow* host_window, ImGuiDockNo data->IsCenterAvailable = !is_outer_docking; if (src_is_visibly_splitted && (!host_node || !host_node->IsEmpty())) data->IsCenterAvailable = false; - if (host_node && (host_node->Flags & ImGuiDockNodeFlags_NoDockingInsideDocRootNode) && host_node->IsDocumentRoot) + if (host_node && (host_node->Flags & ImGuiDockNodeFlags_NoDockingInsideCentralNode) && host_node->IsCentralNode) data->IsCenterAvailable = false; data->IsSidesAvailable = true; if (host_node && (host_node->Flags & ImGuiDockNodeFlags_NoSplit)) data->IsSidesAvailable = false; - if (!is_outer_docking && host_node && host_node->ParentNode == NULL && host_node->IsDocumentRoot) + if (!is_outer_docking && host_node && host_node->ParentNode == NULL && host_node->IsCentralNode) data->IsSidesAvailable = false; // Calculate drop shapes geometry for allowed splitting directions @@ -11268,7 +11269,7 @@ void ImGui::DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImG DockNodeApplyPosSizeToWindows(parent_node); parent_node->InitFromFirstWindowPosSize = parent_node->InitFromFirstWindowViewport = false; parent_node->VisibleWindow = merge_lead_child->VisibleWindow; - parent_node->IsDocumentRoot = (child_0 && child_0->IsDocumentRoot) || (child_1 && child_1->IsDocumentRoot); + parent_node->IsCentralNode = (child_0 && child_0->IsCentralNode) || (child_1 && child_1->IsCentralNode); parent_node->SizeRef = backup_last_explicit_size; if (child_0) @@ -11322,13 +11323,13 @@ void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 si child_0_size[axis] = child_0->SizeRef[axis] = (size_avail - child_1_size[axis]); } - // 3) If one window is the document root (~ use remaining space, should be made explicit!), use explicit size from the other, and remainder for the document root - else if (child_1->IsDocumentRoot && child_0->SizeRef[axis] != 0.0f) + // 3) If one window is the central node (~ use remaining space, should be made explicit!), use explicit size from the other, and remainder for the central node + else if (child_1->IsCentralNode && child_0->SizeRef[axis] != 0.0f) { child_0_size[axis] = ImMin(size_avail - size_min_each, child_0->SizeRef[axis]); child_1_size[axis] = (size_avail - child_0_size[axis]); } - else if (child_0->IsDocumentRoot && child_1->SizeRef[axis] != 0.0f) + else if (child_0->IsCentralNode && child_1->SizeRef[axis] != 0.0f) { child_1_size[axis] = ImMin(size_avail - size_min_each, child_1->SizeRef[axis]); child_0_size[axis] = (size_avail - child_1_size[axis]); @@ -11375,6 +11376,7 @@ void ImGui::DockNodeTreeUpdateSplitter(ImGuiDockNode* node) if (child_0->IsVisible && child_1->IsVisible) { // Extend hovering range past the displayed border + // FIXME-DOCKING: This is not working as expected. float HOVER_EXTEND = 4.0f; // Use a short delay before highlighting the splitter (and changing the mouse cursor) in order for regular mouse movement to not highlight many splitters @@ -11514,9 +11516,9 @@ void ImGui::SetWindowDock(ImGuiWindow* window, ImGuiID dock_id, ImGuiCond cond) window->DockId = dock_id; } -// Create an explicit dockspace node within an existing window. Also expose dock node flags and creates a DocumentRoot node by default. -// The DocumentRoot node is always displayed even when empty and shrink/extend according to the requested size of its neighbors. -void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags dock_space_flags, const ImGuiDockFamily* dock_family) +// Create an explicit dockspace node within an existing window. Also expose dock node flags and creates a CentralNode by default. +// The Central Node is always displayed even when empty and shrink/extend according to the requested size of its neighbors. +void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags dockspace_flags, const ImGuiDockFamily* dock_family) { ImGuiContext* ctx = GImGui; ImGuiContext& g = *ctx; @@ -11528,14 +11530,14 @@ void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags doc if (!node) { node = DockContextAddNode(ctx, id); - node->IsDocumentRoot = true; + node->IsCentralNode = true; } - node->Flags = dock_space_flags; + node->Flags = dockspace_flags; node->DockFamily = dock_family ? *dock_family : ImGuiDockFamily(); // When a Dockspace transitioned form implicit to explicit this may be called a second time // It is possible that the node has already been claimed by a docked window which appeared before the DockSpace() node, so we overwrite IsDockSpace again. - if (node->LastFrameActive == g.FrameCount && !(dock_space_flags & ImGuiDockNodeFlags_KeepAliveOnly)) + if (node->LastFrameActive == g.FrameCount && !(dockspace_flags & ImGuiDockNodeFlags_KeepAliveOnly)) { IM_ASSERT(node->IsDockSpace == false && "Cannot call DockSpace() twice a frame with the same ID"); node->IsDockSpace = true; @@ -11544,7 +11546,7 @@ void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags doc node->IsDockSpace = true; // Keep alive mode, this is allow windows docked into this node so stay docked even if they are not visible - if (dock_space_flags & ImGuiDockNodeFlags_KeepAliveOnly) + if (dockspace_flags & ImGuiDockNodeFlags_KeepAliveOnly) { node->LastFrameAlive = g.FrameCount; return; @@ -11633,8 +11635,8 @@ void ImGui::DockBuilderRemoveNode(ImGuiContext* ctx, ImGuiID node_id) return; DockBuilderRemoveNodeDockedWindows(ctx, node_id, true); DockBuilderRemoveNodeChildNodes(ctx, node_id); - if (node->IsDocumentRoot && node->ParentNode) - node->ParentNode->IsDocumentRoot = true; + if (node->IsCentralNode && node->ParentNode) + node->ParentNode->IsCentralNode = true; DockContextRemoveNode(ctx, node, true); } @@ -11655,7 +11657,7 @@ void ImGui::DockBuilderRemoveNodeChildNodes(ImGuiContext* ctx, ImGuiID root_id) bool want_removal = (root_id == 0) || (node->ID != root_id && DockNodeGetRootNode(node)->ID == root_id); if (want_removal) { - if (node->IsDocumentRoot) + if (node->IsCentralNode) has_document_root = true; if (root_id != 0) DockContextQueueNotifyRemovedNode(ctx, node); @@ -11688,7 +11690,7 @@ void ImGui::DockBuilderRemoveNodeChildNodes(ImGuiContext* ctx, ImGuiID root_id) } else if (has_document_root) { - root_node->IsDocumentRoot = true; + root_node->IsCentralNode = true; } } @@ -11767,7 +11769,7 @@ static ImGuiDockNode* DockBuilderCopyNodeRec(ImGuiContext* ctx, ImGuiDockNode* s dst_node->SizeRef = src_node->SizeRef; dst_node->SplitAxis = src_node->SplitAxis; dst_node->IsDockSpace = src_node->IsDockSpace; - dst_node->IsDocumentRoot = src_node->IsDocumentRoot; + dst_node->IsCentralNode = src_node->IsCentralNode; out_node_remap_pairs->push_back(src_node->ID); out_node_remap_pairs->push_back(dst_node->ID); @@ -11952,8 +11954,8 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) g.NextWindowData.PosUndock = false; } - // Undock if the ImGuiDockNodeFlags_NoDockingInDocRootNode got set - if (dock_node->IsDocumentRoot && (dock_node->Flags & ImGuiDockNodeFlags_NoDockingInsideDocRootNode)) + // Undock if the ImGuiDockNodeFlags_NoDockingInCentralNode got set + if (dock_node->IsCentralNode && (dock_node->Flags & ImGuiDockNodeFlags_NoDockingInsideCentralNode)) { DockContextProcessUndockWindow(ctx, window); return; @@ -12091,7 +12093,7 @@ void ImGui::BeginAsDockableDragDropTarget(ImGuiWindow* window) { ImGuiDockPreviewData split_inner, split_outer; ImGuiDockPreviewData* split_data = &split_inner; - if (target_node && (target_node->ParentNode || target_node->IsDocumentRoot)) + if (target_node && (target_node->ParentNode || target_node->IsCentralNode)) if (ImGuiDockNode* root_node = DockNodeGetRootNode(target_node)) if (DockNodePreviewDockCalc(window, root_node, payload_window, &split_outer, is_explicit_target, true)) split_data = &split_outer; @@ -12193,7 +12195,10 @@ static void ImGui::DockSettingsHandler_ReadLine(ImGuiContext* ctx, ImGuiSettings if (sscanf(line, " SizeRef=%i,%i%n", &x, &y, &r) == 2) { line += r; node.SizeRef = ImVec2ih((short)x, (short)y); } } if (sscanf(line, " Split=%c%n", &c, &r) == 1) { line += r; if (c == 'X') node.SplitAxis = ImGuiAxis_X; else if (c == 'Y') node.SplitAxis = ImGuiAxis_Y; } - if (sscanf(line, " DocRoot=%d%n", &x, &r) == 1) { line += r; node.IsDocumentRoot = (x != 0); } +#if 1 // FIXME-DOCK FIXME-LEGACY + if (sscanf(line, " DocRoot=%d%n", &x, &r) == 1) { line += r; node.IsCentralNode = (x != 0); } +#endif + if (sscanf(line, " CentralNode=%d%n", &x, &r) == 1) { line += r; node.IsCentralNode = (x != 0); } if (sscanf(line, " SelectedTab=0x%08X%n", &node.SelectedTabID,&r) == 1) { line += r; } ImGuiDockContext* dc = ctx->DockContext; if (node.ParentID != 0) @@ -12212,7 +12217,7 @@ static void DockSettingsHandler_DockNodeToSettings(ImGuiDockContext* dc, ImGuiDo node_settings.SplitAxis = node->IsSplitNode() ? (char)node->SplitAxis : ImGuiAxis_None; node_settings.Depth = (char)depth; node_settings.IsDockSpace = (char)node->IsDockSpace; - node_settings.IsDocumentRoot = (char)node->IsDocumentRoot; + node_settings.IsCentralNode = (char)node->IsCentralNode; node_settings.Pos = ImVec2ih((short)node->Pos.x, (short)node->Pos.y); node_settings.Size = ImVec2ih((short)node->Size.x, (short)node->Size.y); node_settings.SizeRef = ImVec2ih((short)node->SizeRef.x, (short)node->SizeRef.y); @@ -12255,8 +12260,8 @@ static void ImGui::DockSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettings buf->appendf(" Pos=%d,%d Size=%d,%d", node_settings->Pos.x, node_settings->Pos.y, node_settings->Size.x, node_settings->Size.y); if (node_settings->SplitAxis != ImGuiAxis_None) buf->appendf(" Split=%c", (node_settings->SplitAxis == ImGuiAxis_X) ? 'X' : 'Y'); - if (node_settings->IsDocumentRoot) - buf->appendf(" DocRoot=%d", node_settings->IsDocumentRoot); + if (node_settings->IsCentralNode) + buf->appendf(" CentralNode=%d", node_settings->IsCentralNode); if (node_settings->SelectedTabID) buf->appendf(" SelectedTab=0x%08X", node_settings->SelectedTabID); @@ -13131,7 +13136,7 @@ void ImGui::ShowDockingDebug() node->Pos.x, node->Pos.y, node->Size.x, node->Size.y, node->SizeRef.x, node->SizeRef.y); ImGui::BulletText("Flags %02X%s%s%s%s", - node->Flags, node->IsDockSpace ? ", IsDockSpace" : "", node->IsDocumentRoot ? ", IsDocumentRoot" : "", + node->Flags, node->IsDockSpace ? ", IsDockSpace" : "", node->IsCentralNode ? ", IsCentralNode" : "", (GImGui->FrameCount - node->LastFrameAlive < 2) ? ", IsAlive" : "", (GImGui->FrameCount - node->LastFrameActive < 2) ? ", IsActive" : ""); if (node->ChildNodes[0]) NodeDockNode(node->ChildNodes[0], "Child[0]"); @@ -13237,7 +13242,7 @@ void ImGui::ShowDockingDebug() char buf[64] = ""; char* p = buf; ImDrawList* overlay_draw_list = node->HostWindow ? GetOverlayDrawList(node->HostWindow) : GetOverlayDrawList((ImGuiViewportP*)GetMainViewport()); - p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "DockId: %X%s\n", node->ID, node->IsDocumentRoot ? " *DocRoot*" : ""); + p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "DockId: %X%s\n", node->ID, node->IsCentralNode ? " *CentralNode*" : ""); p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "Size: (%.0f, %.0f)\n", node->Size.x, node->Size.y); p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "SizeRef: (%.0f, %.0f)\n", node->SizeRef.x, node->SizeRef.y); int depth = DockNodeGetDepth(node); diff --git a/imgui.h b/imgui.h index d7828a742f67..c299c6ebc912 100644 --- a/imgui.h +++ b/imgui.h @@ -798,7 +798,7 @@ enum ImGuiDockNodeFlags_ ImGuiDockNodeFlags_None = 0, ImGuiDockNodeFlags_KeepAliveOnly = 1 << 0, // Don't display the dockspace node but keep it alive. Windows docked into this dockspace node won't be undocked. ImGuiDockNodeFlags_NoSplit = 1 << 1, // Disable splitting the node into smaller nodes. Useful e.g. when embedding dockspaces into a main root one (the root one may have splitting disabled to reduce confusion) - ImGuiDockNodeFlags_NoDockingInsideDocRootNode = 1 << 2 // Disable docking inside the DocumentRoot node. Useful if it is kept empty and invisible. + ImGuiDockNodeFlags_NoDockingInsideCentralNode = 1 << 2 // Disable docking inside the central node (which can stay empty). Useful if it is kept empty and invisible. }; // Flags for ImGui::IsWindowFocused() @@ -1016,7 +1016,7 @@ enum ImGuiCol_ ImGuiCol_TabUnfocused, ImGuiCol_TabUnfocusedActive, ImGuiCol_DockingPreview, - ImGuiCol_DockingEmptyBg, // Background color for empty node (e.g. DocRoot node with no window docked into it) + ImGuiCol_DockingEmptyBg, // Background color for empty node (e.g. CentralNode with no window docked into it) ImGuiCol_PlotLines, ImGuiCol_PlotLinesHovered, ImGuiCol_PlotHistogram, diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 3e52d6606aed..b1dada134067 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3760,8 +3760,8 @@ void ShowExampleAppDockSpace(bool* p_open) if (ImGui::MenuItem("Flag: NoSplit", "", (dockspace_flags & ImGuiDockNodeFlags_NoSplit) != 0)) dockspace_flags ^= ImGuiDockNodeFlags_NoSplit; - if (ImGui::MenuItem("Flag: NoDockingInsideDocRootNode", "", (dockspace_flags & ImGuiDockNodeFlags_NoDockingInsideDocRootNode) != 0)) - dockspace_flags ^= ImGuiDockNodeFlags_NoDockingInsideDocRootNode; + if (ImGui::MenuItem("Flag: NoDockingInsideCentralNode", "", (dockspace_flags & ImGuiDockNodeFlags_NoDockingInsideCentralNode) != 0)) + dockspace_flags ^= ImGuiDockNodeFlags_NoDockingInsideCentralNode; // Disabling fullscreen would allow the window to be moved to the front of other windows, // which we can't undo at the moment without finer window depth/z control. diff --git a/imgui_internal.h b/imgui_internal.h index 76ec1b351dd3..5dcaf2212082 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -767,7 +767,7 @@ struct ImGuiDockNode bool InitFromFirstWindowViewport :1; bool IsVisible :1; // Set to false when the node is hidden (usually disabled as it has no active window) bool IsDockSpace :1; // Root node was created by a DockSpace() call. - bool IsDocumentRoot :1; + bool IsCentralNode :1; bool HasCloseButton :1; bool HasCollapseButton :1; bool WantCloseAll :1; // Set when closing all tabs at once. From 059560d28b63d307bbb7cff09af8bef967450172 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 3 Oct 2018 18:11:35 +0200 Subject: [PATCH 301/828] ButtonBehavior, ImGuiButtonFlags_FlattenChildren flag can be used from a child window. Would typically affect calling SplitterBehavior() from a child window. --- imgui_widgets.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 7053d44da5d0..c2e9014b440a 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -392,7 +392,8 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool flags |= ImGuiButtonFlags_PressedOnClickRelease; ImGuiWindow* backup_hovered_window = g.HoveredWindow; - if ((flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredRootWindow == window) + const bool flatten_hovered_children = (flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredRootWindow == window->RootWindow; + if (flatten_hovered_children) g.HoveredWindow = window; bool pressed = false; @@ -415,7 +416,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool } } - if ((flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredRootWindow == window) + if (flatten_hovered_children) g.HoveredWindow = backup_hovered_window; // AllowOverlap mode (rarely used) requires previous frame HoveredId to be null or to match. This allows using patterns where a later submitted widget overlaps a previous one. From 2dd8338e7d97574abbda5bf187cfc15af8207987 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 3 Oct 2018 18:30:36 +0200 Subject: [PATCH 302/828] Docking: Added ImGuiDockNodeFlags_NoOuterBorder, tweaked DockSpace demo to remove window border. Made docking splitter use the same standard setting as resizing from edges. (#2109) --- imgui.cpp | 22 ++++++++++------------ imgui.h | 3 ++- imgui_demo.cpp | 12 +++++++----- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 205653e2b940..d6582a936f43 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -9578,7 +9578,7 @@ void ImGui::EndDragDropTarget() // Docking: Internal Types //----------------------------------------------------------------------------- -static float IMGUI_DOCK_SPLITTER_SIZE = 4.0f; +static float IMGUI_DOCK_SPLITTER_SIZE = 2.0f; enum ImGuiDockRequestType { @@ -11375,13 +11375,6 @@ void ImGui::DockNodeTreeUpdateSplitter(ImGuiDockNode* node) ImGuiDockNode* child_1 = node->ChildNodes[1]; if (child_0->IsVisible && child_1->IsVisible) { - // Extend hovering range past the displayed border - // FIXME-DOCKING: This is not working as expected. - float HOVER_EXTEND = 4.0f; - - // Use a short delay before highlighting the splitter (and changing the mouse cursor) in order for regular mouse movement to not highlight many splitters - float HOVER_VISIBILITY_DELAY = 0.040f; - // Bounding box of the splitter cover the space between both nodes (w = Spacing, h = Size[xy^1] for when splitting horizontally) const ImGuiAxis axis = (ImGuiAxis)node->SplitAxis; IM_ASSERT(axis != ImGuiAxis_None); @@ -11390,12 +11383,12 @@ void ImGui::DockNodeTreeUpdateSplitter(ImGuiDockNode* node) bb.Max = child_1->Pos; bb.Min[axis] += child_0->Size[axis]; bb.Max[axis ^ 1] += child_1->Size[axis ^ 1]; - //GetOverlayDrawList(g.CurrentWindow->Viewport)->AddRect(bb.Min, bb.Max, IM_COL32(255,0,255,255)); + //if (g.IO.KeyCtrl) GetOverlayDrawList(g.CurrentWindow->Viewport)->AddRect(bb.Min, bb.Max, IM_COL32(255,0,255,255)); float w1 = child_0->Size[axis]; float w2 = child_1->Size[axis]; - bb.Min[axis] += 1; // Display a little inward so highlight doesn't connect with nearby tabs on the neighbor node. - bb.Max[axis] -= 1; + //bb.Min[axis] += 1; // Display a little inward so highlight doesn't connect with nearby tabs on the neighbor node. + //bb.Max[axis] -= 1; PushID(node->ID); // Gather list of nodes that are touching the splitter line. Find resizing limits based on those nodes. @@ -11427,9 +11420,10 @@ void ImGui::DockNodeTreeUpdateSplitter(ImGuiDockNode* node) */ } + // Use a short delay before highlighting the splitter (and changing the mouse cursor) in order for regular mouse movement to not highlight many splitters float min_size_0 = resize_limits[0] - child_0->Pos[axis]; float min_size_1 = child_1->Pos[axis] + child_1->Size[axis] - resize_limits[1]; - if (SplitterBehavior(bb, GetID("##Splitter"), axis, &w1, &w2, min_size_0, min_size_1, HOVER_EXTEND, HOVER_VISIBILITY_DELAY)) + if (SplitterBehavior(bb, GetID("##Splitter"), axis, &w1, &w2, min_size_0, min_size_1, RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS, RESIZE_WINDOWS_FROM_EDGES_FEEDBACK_TIMER)) { if (touching_nodes[0].Size > 0 && touching_nodes[1].Size > 0) { @@ -11574,7 +11568,11 @@ void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags doc if (node->Windows.Size > 0 || node->IsSplitNode()) PushStyleColor(ImGuiCol_ChildBg, IM_COL32(0, 0, 0, 0)); + if (dockspace_flags & ImGuiDockNodeFlags_NoOuterBorder) + PushStyleVar(ImGuiStyleVar_ChildBorderSize, 0.0f); Begin(title, NULL, window_flags); + if (dockspace_flags & ImGuiDockNodeFlags_NoOuterBorder) + PopStyleVar(); if (node->Windows.Size > 0 || node->IsSplitNode()) PopStyleColor(); diff --git a/imgui.h b/imgui.h index c299c6ebc912..730b856deff5 100644 --- a/imgui.h +++ b/imgui.h @@ -798,7 +798,8 @@ enum ImGuiDockNodeFlags_ ImGuiDockNodeFlags_None = 0, ImGuiDockNodeFlags_KeepAliveOnly = 1 << 0, // Don't display the dockspace node but keep it alive. Windows docked into this dockspace node won't be undocked. ImGuiDockNodeFlags_NoSplit = 1 << 1, // Disable splitting the node into smaller nodes. Useful e.g. when embedding dockspaces into a main root one (the root one may have splitting disabled to reduce confusion) - ImGuiDockNodeFlags_NoDockingInsideCentralNode = 1 << 2 // Disable docking inside the central node (which can stay empty). Useful if it is kept empty and invisible. + ImGuiDockNodeFlags_NoOuterBorder = 1 << 2, // Disable outer border on a DockSpace() node. + ImGuiDockNodeFlags_NoDockingInsideCentralNode = 1 << 3 // Disable docking inside the central node (which can stay empty). Useful if it is kept empty and invisible. }; // Flags for ImGui::IsWindowFocused() diff --git a/imgui_demo.cpp b/imgui_demo.cpp index b1dada134067..d7ab5caa7e5d 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3735,15 +3735,16 @@ void ShowExampleAppDockSpace(bool* p_open) static ImGuiDockNodeFlags dockspace_flags = ImGuiDockNodeFlags_None; ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); ImGui::Begin("DockSpace Demo", p_open, flags); - ImGui::PopStyleVar(); + ImGui::PopStyleVar(2); // Dockspace ImGuiIO& io = ImGui::GetIO(); if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable) { ImGuiID dockspace_id = ImGui::GetID("MyDockspace"); - ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags); + ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags | ImGuiDockNodeFlags_NoOuterBorder); } else { @@ -3758,14 +3759,15 @@ void ShowExampleAppDockSpace(bool* p_open) *p_open = false; ImGui::Separator(); + // Disabling fullscreen would allow the window to be moved to the front of other windows, + // which we can't undo at the moment without finer window depth/z control. + //ImGui::MenuItem("Fullscreen", NULL, &opt_fullscreen_persistant); + if (ImGui::MenuItem("Flag: NoSplit", "", (dockspace_flags & ImGuiDockNodeFlags_NoSplit) != 0)) dockspace_flags ^= ImGuiDockNodeFlags_NoSplit; if (ImGui::MenuItem("Flag: NoDockingInsideCentralNode", "", (dockspace_flags & ImGuiDockNodeFlags_NoDockingInsideCentralNode) != 0)) dockspace_flags ^= ImGuiDockNodeFlags_NoDockingInsideCentralNode; - // Disabling fullscreen would allow the window to be moved to the front of other windows, - // which we can't undo at the moment without finer window depth/z control. - //ImGui::MenuItem("Fullscreen", NULL, &opt_fullscreen_persistant); ImGui::EndMenu(); } ShowHelpMarker( From 4e30698706e5c0b6cae5bd45d633de15d98ea445 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 3 Oct 2018 22:32:23 +0200 Subject: [PATCH 303/828] Docking: Added ImGuiDockNodeFlags_PassthruDockspace mode (subdivided in three flags: ImGuiDockNodeFlags_NoDockingInCentralNode, ImGuiDockNodeFlags_PassthruInEmptyNodes, ImGuiDockNodeFlags_RenderWindowBg). Added internal facility for register a rectangular hit-test hole in window. Updated DockSpace demo accordingly. (#2109) --- docs/CHANGELOG.txt | 40 ++++++------ imgui.cpp | 153 +++++++++++++++++++++++++++++++++------------ imgui.h | 8 ++- imgui_demo.cpp | 51 ++++++++------- imgui_internal.h | 1 + 5 files changed, 167 insertions(+), 86 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 21a7ae8b57bb..e1f5e30c879f 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -33,25 +33,27 @@ HOW TO UPDATE? DOCKING BRANCH (In Progress) ----------------------------------------------------------------------- -- Added ImGuiConfigFlags_DockingEnable flag to enable Docking. [BETA] - Set with `io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;`. -- Added BeginTabBar(), EndTabBar(), BeginTabItem(), EndTabItem(), SetTabItemClosed() API. (#261, #351) -- Added ImGuiTabBarFlags flags for BeginTabBar(). -- Added ImGuiTabItemFlags flags for BeginTabItem(). -- Added DockSpace() API. (#351) -- Added ImGuiDockNodeFlags flags for DockSpace(). -- Added SetNextWindowDock(), SetNextWindowDockFamily() API. (#351) -- Added GetWindowDockId(), IsWindowDocked() API. (#351) -- Added ImGuiWindowFlags_NoDocking window flag to disable the possibility for a window to be docked. - Popup, Menu and Child windows always have the ImGuiWindowFlags_NoDocking flag set. (#351) -- Added ImGuiWindowFlags_UnsavedDocument window flag to append '*' to title without altering the ID, - as a convenience to avoid using the ### operator. -- Added io.ConfigDockingWithShift option to configure docking mode. -- Style: Added ImGuiCol_DockingPreview, ImGuiCol_DockingEmptyBg colors. (#351) -- Style: Added ImGuiCol_Tab, ImGuiCol_TabHovered, ImGuiCol_TabActive, ImGuiCol_TabUnfocused, ImGuiCol_TabUnfocusedActive colors. (#261, #351) -- Demo: Added Layout->Tabs demo code. (#261, #351) -- Demo: Added "Documents" example app showcasing possible use for tabs. (#261, #351) -- Demo: Added "DockSpace" example app showcasing use of explicit dockspace nodes. (#351) +- Added Docking system: [BETA] (#2109, #351) + - Added ImGuiConfigFlags_DockingEnable flag to enable Docking. + Set with `io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;`. + - Added DockSpace() API. + - Added ImGuiDockNodeFlags flags for DockSpace(). + - Added SetNextWindowDock(), SetNextWindowDockFamily() API. + - Added GetWindowDockId(), IsWindowDocked() API. + - Added ImGuiWindowFlags_NoDocking window flag to disable the possibility for a window to be docked. + Popup, Menu and Child windows always have the ImGuiWindowFlags_NoDocking flag set. + - Added io.ConfigDockingWithShift option to configure docking mode. + - Style: Added ImGuiCol_DockingPreview, ImGuiCol_DockingEmptyBg colors. + - Demo: Added "DockSpace" example app showcasing use of explicit dockspace nodes. +- Added Tab Bar/Tabs widgets: (#261, #351) + - Added BeginTabBar(), EndTabBar(), BeginTabItem(), EndTabItem(), SetTabItemClosed() API. + - Added ImGuiTabBarFlags flags for BeginTabBar(). + - Added ImGuiTabItemFlags flags for BeginTabItem(). + - Style: Added ImGuiCol_Tab, ImGuiCol_TabHovered, ImGuiCol_TabActive, ImGuiCol_TabUnfocused, ImGuiCol_TabUnfocusedActive colors. + - Demo: Added Layout->Tabs demo code. + - Demo: Added "Documents" example app showcasing possible use for tabs. +- Added ImGuiWindowFlags_UnsavedDocument window flag to append '*' to title without altering + the ID, as a convenience to avoid using the ### operator. ----------------------------------------------------------------------- diff --git a/imgui.cpp b/imgui.cpp index d6582a936f43..c453acad1248 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -918,6 +918,7 @@ static void SetCurrentWindow(ImGuiWindow* window); static void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond); static void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond); static void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond); +static void SetWindowHitTestHole(ImGuiWindow* window, const ImVec2& pos, const ImVec2& size); static void FindHoveredWindow(); static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags); static void CheckStacksSize(ImGuiWindow* window, bool write); @@ -4002,6 +4003,15 @@ static void FindHoveredWindow() bb.Expand(padding_for_resize_from_edges); if (!bb.Contains(g.IO.MousePos)) continue; + + if (window->HitTestHoleSize.x != 0) + { + // FIXME: Consider generalizing hit-testing override (with more generic data, callback, etc.) (#1512) + ImRect hole_bb((float)(window->HitTestHoleOffset.x), (float)(window->HitTestHoleOffset.y), + (float)(window->HitTestHoleOffset.x + window->HitTestHoleSize.x), (float)(window->HitTestHoleOffset.y + window->HitTestHoleSize.y)); + if (hole_bb.Contains(g.IO.MousePos - window->Pos)) + continue; + } if (hovered_window == NULL) hovered_window = window; @@ -5501,6 +5511,9 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) } } + // Clear hit test shape every frame + window->HitTestHoleSize.x = window->HitTestHoleSize.y = 0; + // Save clipped aabb so we can access it in constant-time in FindHoveredWindow() window->OuterRectClipped = window->Rect(); if (window->DockIsActive) @@ -6271,6 +6284,13 @@ static void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond co window->Collapsed = collapsed; } +static void SetWindowHitTestHole(ImGuiWindow* window, const ImVec2& pos, const ImVec2& size) +{ + IM_ASSERT(window->HitTestHoleSize.x == 0); // We don't support multiple holes/hit test filters + window->HitTestHoleSize = ImVec2ih((short)size.x, (short)size.y); + window->HitTestHoleOffset = ImVec2ih((short)(pos.x - window->Pos.x), (short)(pos.y - window->Pos.y)); +} + void ImGui::SetWindowCollapsed(bool collapsed, ImGuiCond cond) { SetWindowCollapsed(GImGui->CurrentWindow, collapsed, cond); @@ -10430,20 +10450,35 @@ static void ImGui::DockNodeHideHostWindow(ImGuiDockNode* node) } } -static void DockNodeUpdateFindOnlyNodeWithWindowsRec(ImGuiDockNode* node, int* p_count, ImGuiDockNode** p_first_node_with_windows) +struct ImGuiDockNodeUpdateScanResults +{ + ImGuiDockNode* CentralNode; + ImGuiDockNode* FirstNodeWithWindows; + int CountNodesWithWindows; + ImGuiDockFamily DockFamilyForMerges; + + ImGuiDockNodeUpdateScanResults() { CentralNode = FirstNodeWithWindows = NULL; CountNodesWithWindows = 0; } +}; + +static void DockNodeUpdateScanRec(ImGuiDockNode* node, ImGuiDockNodeUpdateScanResults* results) { if (node->Windows.Size > 0) { - if (*p_first_node_with_windows == NULL) - *p_first_node_with_windows = node; - (*p_count)++; + if (results->FirstNodeWithWindows == NULL) + results->FirstNodeWithWindows = node; + results->CountNodesWithWindows++; } - if (*p_count > 1) + if (node->IsCentralNode) + { + IM_ASSERT(results->CentralNode == NULL); // Should be only one + results->CentralNode = node; + } + if (results->CountNodesWithWindows > 1 && results->CentralNode != NULL) return; if (node->ChildNodes[0]) - DockNodeUpdateFindOnlyNodeWithWindowsRec(node->ChildNodes[0], p_count, p_first_node_with_windows); + DockNodeUpdateScanRec(node->ChildNodes[0], results); if (node->ChildNodes[1]) - DockNodeUpdateFindOnlyNodeWithWindowsRec(node->ChildNodes[1], p_count, p_first_node_with_windows); + DockNodeUpdateScanRec(node->ChildNodes[1], results); } static void ImGui::DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* node) @@ -10519,32 +10554,31 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) IM_ASSERT(node->LastFrameActive != g.FrameCount); node->LastFrameAlive = g.FrameCount; + ImGuiDockNode* central_node = NULL; if (node->IsRootNode()) { DockNodeUpdateVisibleFlagAndInactiveChilds(node); // Find if there's only a single visible window in the hierarchy (in which case we need to display a regular title bar -> FIXME-DOCK: that last part is not done yet!) - if (!node->IsDockSpace) - { - int count = 0; - ImGuiDockNode* first_node_with_windows = NULL; - DockNodeUpdateFindOnlyNodeWithWindowsRec(node, &count, &first_node_with_windows); - node->OnlyNodeWithWindows = (count == 1 ? first_node_with_windows : NULL); - if (node->LastFocusedNodeID == 0 && first_node_with_windows != NULL) - node->LastFocusedNodeID = first_node_with_windows->ID; - - // Copy the dock family from of our window so it can be used for proper dock filtering. - // When node has mixed windows, prioritize the family with the most constraint (CompatibleWithNeutral = false) as the reference to copy. - if (first_node_with_windows) - { - node->DockFamily = first_node_with_windows->Windows[0]->DockFamily; - for (int n = 1; n < first_node_with_windows->Windows.Size; n++) - if (first_node_with_windows->Windows[n]->DockFamily.CompatibleWithFamilyZero == false) - { - node->DockFamily = first_node_with_windows->Windows[n]->DockFamily; - break; - } - } + ImGuiDockNodeUpdateScanResults results; + DockNodeUpdateScanRec(node, &results); + node->OnlyNodeWithWindows = (results.CountNodesWithWindows == 1 ? results.FirstNodeWithWindows : NULL); + if (node->LastFocusedNodeID == 0 && results.FirstNodeWithWindows != NULL) + node->LastFocusedNodeID = results.FirstNodeWithWindows->ID; + central_node = results.CentralNode; + + // Copy the dock family from of our window so it can be used for proper dock filtering. + // When node has mixed windows, prioritize the family with the most constraint (CompatibleWithNeutral = false) as the reference to copy. + // FIXME-DOCK: We don't recurse properly, this code could be reworked to work from DockNodeUpdateScanRec. + if (ImGuiDockNode* first_node_with_windows = results.FirstNodeWithWindows) + { + node->DockFamily = first_node_with_windows->Windows[0]->DockFamily; + for (int n = 1; n < first_node_with_windows->Windows.Size; n++) + if (first_node_with_windows->Windows[n]->DockFamily.CompatibleWithFamilyZero == false) + { + node->DockFamily = first_node_with_windows->Windows[n]->DockFamily; + break; + } } } @@ -10660,6 +10694,35 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) if (g.NavWindow && g.NavWindow->RootWindowDockStop->DockNode && g.NavWindow->RootWindowDockStop->ParentWindow == host_window) node->LastFocusedNodeID = g.NavWindow->RootWindowDockStop->DockNode->ID; + // We need to draw a background if requested by ImGuiDockNodeFlags_RenderWindowBg, but we will only know the correct pos/size after + // processing the resizing splitters. So we are using the DrawList channel splitting facility to submit drawing primitives out of order! + const bool render_dockspace_bg = node->IsRootNode() && host_window && (node->Flags & ImGuiDockNodeFlags_RenderWindowBg) != 0; + if (render_dockspace_bg) + { + host_window->DrawList->ChannelsSplit(2); + host_window->DrawList->ChannelsSetCurrent(1); + } + + // Register a hit-test hole in the window unless we are currently dragging a window that is compatible our dockspace + bool central_node_hole = node->IsRootNode() && host_window && (node->Flags & ImGuiDockNodeFlags_PassthruInEmptyNodes) != 0 && central_node != NULL && central_node->IsEmpty(); + bool central_node_hole_register_hit_test_hole = central_node_hole; + if (central_node_hole) + if (const ImGuiPayload* payload = ImGui::GetDragDropPayload()) + if (payload->IsDataType(IMGUI_PAYLOAD_TYPE_WINDOW) && DockNodeIsDropAllowed(host_window, *(ImGuiWindow**)payload->Data)) + central_node_hole_register_hit_test_hole = false; + if (central_node_hole_register_hit_test_hole) + { + // Add a little padding to match the "resize from edges" behavior and allow grabbing the splitter easily. + IM_ASSERT(node->IsDockSpace); // We cannot pass this flag without the DockSpace() api. Testing this because we also setup the hole in host_window->ParentNode + ImRect central_hole(central_node->Pos, central_node->Pos + central_node->Size); + central_hole.Expand(ImVec2(-RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS, -RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS)); + if (central_node_hole && !central_hole.IsInverted()) + { + SetWindowHitTestHole(host_window, central_hole.Min, central_hole.Max - central_hole.Min); + SetWindowHitTestHole(host_window->ParentWindow, central_hole.Min, central_hole.Max - central_hole.Min); + } + } + // Update position/size, process and draw resizing splitters if (node->IsRootNode() && host_window) { @@ -10667,6 +10730,21 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) DockNodeTreeUpdateSplitter(node); } + // Draw empty node background (currently can only be the Central Node) + if (host_window && node->IsEmpty() && node->IsVisible && !(node->Flags & ImGuiDockNodeFlags_PassthruInEmptyNodes)) + host_window->DrawList->AddRectFilled(node->Pos, node->Pos + node->Size, GetColorU32(ImGuiCol_DockingEmptyBg)); + + // Draw whole dockspace background if ImGuiDockNodeFlags_RenderWindowBg if set. + if (render_dockspace_bg && node->IsVisible) + { + host_window->DrawList->ChannelsSetCurrent(0); + if (central_node_hole) + RenderRectFilledWithHole(host_window->DrawList, node->Rect(), central_node->Rect(), GetColorU32(ImGuiCol_WindowBg), 0.0f); + else + host_window->DrawList->AddRectFilled(node->Pos, node->Pos + node->Size, GetColorU32(ImGuiCol_WindowBg), 0.0f); + host_window->DrawList->ChannelsMerge(); + } + // Draw and populate Tab Bar if (host_window && node->Windows.Size > 0) { @@ -10681,16 +10759,11 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) if (node->Windows.Size > 0) node->SelectedTabID = node->Windows[0]->ID; } - if (host_window && node->IsVisible) - { - // Background for empty nodes - if (node->Windows.Size == 0 && !node->IsSplitNode()) - host_window->DrawList->AddRectFilled(node->Pos, node->Pos + node->Size, GetColorU32(ImGuiCol_DockingEmptyBg)); - // Draw drop target + // Draw payload drop target + if (host_window && node->IsVisible) if (node->IsRootNode() && (g.MovingWindow == NULL || g.MovingWindow->RootWindow != host_window)) BeginAsDockableDragDropTarget(host_window); - } node->LastFrameActive = g.FrameCount; @@ -11060,7 +11133,7 @@ static bool ImGui::DockNodePreviewDockCalc(ImGuiWindow* host_window, ImGuiDockNo data->IsCenterAvailable = !is_outer_docking; if (src_is_visibly_splitted && (!host_node || !host_node->IsEmpty())) data->IsCenterAvailable = false; - if (host_node && (host_node->Flags & ImGuiDockNodeFlags_NoDockingInsideCentralNode) && host_node->IsCentralNode) + if (host_node && (host_node->Flags & ImGuiDockNodeFlags_NoDockingInCentralNode) && host_node->IsCentralNode) data->IsCenterAvailable = false; data->IsSidesAvailable = true; @@ -11568,11 +11641,9 @@ void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags doc if (node->Windows.Size > 0 || node->IsSplitNode()) PushStyleColor(ImGuiCol_ChildBg, IM_COL32(0, 0, 0, 0)); - if (dockspace_flags & ImGuiDockNodeFlags_NoOuterBorder) - PushStyleVar(ImGuiStyleVar_ChildBorderSize, 0.0f); + PushStyleVar(ImGuiStyleVar_ChildBorderSize, 0.0f); Begin(title, NULL, window_flags); - if (dockspace_flags & ImGuiDockNodeFlags_NoOuterBorder) - PopStyleVar(); + PopStyleVar(); if (node->Windows.Size > 0 || node->IsSplitNode()) PopStyleColor(); @@ -11953,7 +12024,7 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) } // Undock if the ImGuiDockNodeFlags_NoDockingInCentralNode got set - if (dock_node->IsCentralNode && (dock_node->Flags & ImGuiDockNodeFlags_NoDockingInsideCentralNode)) + if (dock_node->IsCentralNode && (dock_node->Flags & ImGuiDockNodeFlags_NoDockingInCentralNode)) { DockContextProcessUndockWindow(ctx, window); return; diff --git a/imgui.h b/imgui.h index 730b856deff5..4299af319a7a 100644 --- a/imgui.h +++ b/imgui.h @@ -798,8 +798,12 @@ enum ImGuiDockNodeFlags_ ImGuiDockNodeFlags_None = 0, ImGuiDockNodeFlags_KeepAliveOnly = 1 << 0, // Don't display the dockspace node but keep it alive. Windows docked into this dockspace node won't be undocked. ImGuiDockNodeFlags_NoSplit = 1 << 1, // Disable splitting the node into smaller nodes. Useful e.g. when embedding dockspaces into a main root one (the root one may have splitting disabled to reduce confusion) - ImGuiDockNodeFlags_NoOuterBorder = 1 << 2, // Disable outer border on a DockSpace() node. - ImGuiDockNodeFlags_NoDockingInsideCentralNode = 1 << 3 // Disable docking inside the central node (which can stay empty). Useful if it is kept empty and invisible. + //ImGuiDockNodeFlags_NoCentralNode = 1 << 2, // Disable Central Node (the node which can stay empty) + //ImGuiDockNodeFlags_NoOuterBorder = 1 << 3, // Disable outer border on a DockSpace() node. + ImGuiDockNodeFlags_NoDockingInCentralNode = 1 << 4, // Disable docking inside the Central Node, which will be always kept empty. + ImGuiDockNodeFlags_PassthruInEmptyNodes = 1 << 5, // When Central Node is empty: let inputs pass-through + won't display a DockingEmptyBg background. + ImGuiDockNodeFlags_RenderWindowBg = 1 << 6, // DockSpace() will render a ImGuiCol_WindowBg background covering everything excepted the Central Node (when empty). Meaning the host window should properly use SetNextWindowBgAlpha(0.0f) + ImGuiDockNodeFlags_NoOuterBorder prior to Begin() when using this. + ImGuiDockNodeFlags_PassthruDockspace = ImGuiDockNodeFlags_NoDockingInCentralNode | ImGuiDockNodeFlags_RenderWindowBg | ImGuiDockNodeFlags_PassthruInEmptyNodes }; // Flags for ImGui::IsWindowFocused() diff --git a/imgui_demo.cpp b/imgui_demo.cpp index d7ab5caa7e5d..b8aac46bd8ca 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3715,13 +3715,12 @@ static void ShowExampleAppCustomRendering(bool* p_open) void ShowExampleAppDockSpace(bool* p_open) { static bool opt_fullscreen_persistant = true; + static ImGuiDockNodeFlags opt_flags = ImGuiDockNodeFlags_None; bool opt_fullscreen = opt_fullscreen_persistant; - // We are using the ImGuiWindowFlags_NoDocking flag to make the parent window not dockable into. - // Because 1) it would be confusing to have two docking targets within each others. - // and 2) we want our main DockSpace node to always be visible (never hidden within a tab bar): if the DockSpace node disappear its child windows will be orphaned. - ImGuiWindowFlags flags = ImGuiWindowFlags_MenuBar; - flags |= ImGuiWindowFlags_NoDocking; + // We are using the ImGuiWindowFlags_NoDocking flag to make the parent window not dockable into, + // because it would be confusing to have two docking targets within each others. + ImGuiWindowFlags window_flags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking; if (opt_fullscreen) { ImGuiViewport* viewport = ImGui::GetMainViewport(); @@ -3729,22 +3728,28 @@ void ShowExampleAppDockSpace(bool* p_open) ImGui::SetNextWindowSize(viewport->Size); ImGui::SetNextWindowViewport(viewport->ID); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); - flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove; - flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus; + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); + window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove; + window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus; } - static ImGuiDockNodeFlags dockspace_flags = ImGuiDockNodeFlags_None; + // When using ImGuiDockNodeFlags_RenderWindowBg or ImGuiDockNodeFlags_InvisibleDockspace, DockSpace() will render our background and handle the pass-thru hole, so we ask Begin() to not render a background. + if (opt_flags & ImGuiDockNodeFlags_RenderWindowBg) + ImGui::SetNextWindowBgAlpha(0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); - ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); - ImGui::Begin("DockSpace Demo", p_open, flags); - ImGui::PopStyleVar(2); + ImGui::Begin("DockSpace Demo", p_open, window_flags); + ImGui::PopStyleVar(); + + if (opt_fullscreen) + ImGui::PopStyleVar(2); // Dockspace ImGuiIO& io = ImGui::GetIO(); if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable) { ImGuiID dockspace_id = ImGui::GetID("MyDockspace"); - ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags | ImGuiDockNodeFlags_NoOuterBorder); + ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), opt_flags); } else { @@ -3753,21 +3758,21 @@ void ShowExampleAppDockSpace(bool* p_open) if (ImGui::BeginMenuBar()) { - if (ImGui::BeginMenu("Options")) + if (ImGui::BeginMenu("Docking")) { - if (ImGui::MenuItem("Remove DockSpace", NULL, false, p_open != NULL)) - *p_open = false; - ImGui::Separator(); - // Disabling fullscreen would allow the window to be moved to the front of other windows, // which we can't undo at the moment without finer window depth/z control. //ImGui::MenuItem("Fullscreen", NULL, &opt_fullscreen_persistant); - if (ImGui::MenuItem("Flag: NoSplit", "", (dockspace_flags & ImGuiDockNodeFlags_NoSplit) != 0)) - dockspace_flags ^= ImGuiDockNodeFlags_NoSplit; - if (ImGui::MenuItem("Flag: NoDockingInsideCentralNode", "", (dockspace_flags & ImGuiDockNodeFlags_NoDockingInsideCentralNode) != 0)) - dockspace_flags ^= ImGuiDockNodeFlags_NoDockingInsideCentralNode; - + if (ImGui::MenuItem("Flag: NoSplit", "", (opt_flags & ImGuiDockNodeFlags_NoSplit) != 0)) opt_flags ^= ImGuiDockNodeFlags_NoSplit; + if (ImGui::MenuItem("Flag: NoDockingInCentralNode", "", (opt_flags & ImGuiDockNodeFlags_NoDockingInCentralNode) != 0)) opt_flags ^= ImGuiDockNodeFlags_NoDockingInCentralNode; + if (ImGui::MenuItem("Flag: PassthruInEmptyNodes", "", (opt_flags & ImGuiDockNodeFlags_PassthruInEmptyNodes) != 0)) opt_flags ^= ImGuiDockNodeFlags_PassthruInEmptyNodes; + if (ImGui::MenuItem("Flag: RenderWindowBg", "", (opt_flags & ImGuiDockNodeFlags_RenderWindowBg) != 0)) opt_flags ^= ImGuiDockNodeFlags_RenderWindowBg; + if (ImGui::MenuItem("Flag: PassthruDockspace (all 3 above)", "", (opt_flags & ImGuiDockNodeFlags_PassthruDockspace) == ImGuiDockNodeFlags_PassthruDockspace)) + opt_flags = (opt_flags & ~ImGuiDockNodeFlags_PassthruDockspace) | ((opt_flags & ImGuiDockNodeFlags_PassthruDockspace) == ImGuiDockNodeFlags_PassthruDockspace) ? 0 : ImGuiDockNodeFlags_PassthruDockspace; + ImGui::Separator(); + if (ImGui::MenuItem("Close DockSpace", NULL, false, p_open != NULL)) + *p_open = false; ImGui::EndMenu(); } ShowHelpMarker( @@ -3782,8 +3787,6 @@ void ShowExampleAppDockSpace(bool* p_open) } ImGui::End(); - if (opt_fullscreen) - ImGui::PopStyleVar(); } //----------------------------------------------------------------------------- diff --git a/imgui_internal.h b/imgui_internal.h index 5dcaf2212082..e958102ad7f3 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1218,6 +1218,7 @@ struct IMGUI_API ImGuiWindow ImRect ClipRect; // Current clipping rectangle. = DrawList->clip_rect_stack.back(). Scissoring / clipping rectangle. x1, y1, x2, y2. ImRect OuterRectClipped; // = WindowRect just after setup in Begin(). == window->Rect() for root window. ImRect InnerMainRect, InnerClipRect; + ImVec2ih HitTestHoleSize, HitTestHoleOffset; ImRect ContentsRegionRect; // FIXME: This is currently confusing/misleading. Maximum visible content position ~~ Pos + (SizeContentsExplicit ? SizeContentsExplicit : Size - ScrollbarSizes) - CursorStartPos, per axis int LastFrameActive; // Last frame number the window was Active. float ItemWidthDefault; From 407822e6a59c6f40be6fb1c0e57baff8dc386204 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 4 Oct 2018 19:39:02 +0200 Subject: [PATCH 304/828] BeginTabItem: Fixed missing parameter name in .h file messing up with cimgui generator. (#2115) --- imgui.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.h b/imgui.h index 4299af319a7a..8b54b65e79d4 100644 --- a/imgui.h +++ b/imgui.h @@ -518,7 +518,7 @@ namespace ImGui // Note: Tabs are automatically created by the docking system. Use this to create tab bars/tabs yourself without docking being involved. IMGUI_API bool BeginTabBar(const char* str_id, ImGuiTabBarFlags flags = 0); // create and append into a TabBar IMGUI_API void EndTabBar(); - IMGUI_API bool BeginTabItem(const char* label, bool* p_open = NULL, ImGuiTabItemFlags = 0);// create a Tab. Returns true if the Tab is selected. + IMGUI_API bool BeginTabItem(const char* label, bool* p_open = NULL, ImGuiTabItemFlags flags = 0);// create a Tab. Returns true if the Tab is selected. IMGUI_API void EndTabItem(); // only call EndTabItem() if BeginTabItem() returns true! IMGUI_API void SetTabItemClosed(const char* tab_or_docked_window_label); // notify TabBar or Docking system of a closed tab/window ahead (useful to reduce visual flicker on reorderable tab bars). For tab-bar: call after BeginTabBar() and before Tab submissions. Otherwise call with a window name. From cb76c086e026834a3223aee9703ab6eec3e8310c Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 5 Oct 2018 16:14:51 +0200 Subject: [PATCH 305/828] Docking: Removed context parameter from internal DockBuilderXXX api at it is expected we transition it to be public facing. (#2109) --- imgui.cpp | 63 +++++++++++++++++++++++++++--------------------- imgui_internal.h | 22 ++++++++--------- 2 files changed, 47 insertions(+), 38 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index c453acad1248..6392e142788e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -9776,8 +9776,9 @@ void ImGui::DockContextOnLoadSettings(ImGuiContext* ctx) void ImGui::DockContextClearNodes(ImGuiContext* ctx, ImGuiID root_id, bool clear_persistent_docking_references) { - DockBuilderRemoveNodeDockedWindows(ctx, root_id, clear_persistent_docking_references); - DockBuilderRemoveNodeChildNodes(ctx, root_id); + IM_ASSERT(ctx == GImGui); + DockBuilderRemoveNodeDockedWindows(root_id, clear_persistent_docking_references); + DockBuilderRemoveNodeChildNodes(root_id); } // This function also acts as a defacto test to make sure we can rebuild from scratch without a glitch @@ -11666,7 +11667,7 @@ void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags doc // It is expected that those functions are all called _before_ the dockspace node submission. //----------------------------------------------------------------------------- -void ImGui::DockBuilderDockWindow(ImGuiContext*, const char* window_name, ImGuiID node_id) +void ImGui::DockBuilderDockWindow(const char* window_name, ImGuiID node_id) { ImGuiID window_id = ImHash(window_name, 0); if (ImGuiWindow* window = FindWindowByID(window_id)) @@ -11684,33 +11685,37 @@ void ImGui::DockBuilderDockWindow(ImGuiContext*, const char* window_name, ImGuiI } } -ImGuiDockNode* ImGui::DockBuilderGetNode(ImGuiContext* ctx, ImGuiID node_id) +ImGuiDockNode* ImGui::DockBuilderGetNode(ImGuiID node_id) { + ImGuiContext* ctx = GImGui; return DockContextFindNodeByID(ctx, node_id); } -void ImGui::DockBuilderAddNode(ImGuiContext* ctx, ImGuiID id, ImVec2 ref_size, ImGuiDockNodeFlags flags) +void ImGui::DockBuilderAddNode(ImGuiID id, ImVec2 ref_size, ImGuiDockNodeFlags flags) { + ImGuiContext* ctx = GImGui; DockSpace(id, ImVec2(0,0), flags | ImGuiDockNodeFlags_KeepAliveOnly); ImGuiDockNode* node = DockContextFindNodeByID(ctx, id); node->SizeRef = node->Size = ref_size; node->LastFrameAlive = -1; } -void ImGui::DockBuilderRemoveNode(ImGuiContext* ctx, ImGuiID node_id) +void ImGui::DockBuilderRemoveNode(ImGuiID node_id) { + ImGuiContext* ctx = GImGui; ImGuiDockNode* node = DockContextFindNodeByID(ctx, node_id); if (node == NULL) return; - DockBuilderRemoveNodeDockedWindows(ctx, node_id, true); - DockBuilderRemoveNodeChildNodes(ctx, node_id); + DockBuilderRemoveNodeDockedWindows(node_id, true); + DockBuilderRemoveNodeChildNodes(node_id); if (node->IsCentralNode && node->ParentNode) node->ParentNode->IsCentralNode = true; DockContextRemoveNode(ctx, node, true); } -void ImGui::DockBuilderRemoveNodeChildNodes(ImGuiContext* ctx, ImGuiID root_id) +void ImGui::DockBuilderRemoveNodeChildNodes(ImGuiID root_id) { + ImGuiContext* ctx = GImGui; ImGuiDockContext* dc = ctx->DockContext; ImGuiDockNode* root_node = root_id ? DockContextFindNodeByID(ctx, root_id) : NULL; @@ -11763,9 +11768,10 @@ void ImGui::DockBuilderRemoveNodeChildNodes(ImGuiContext* ctx, ImGuiID root_id) } } -void ImGui::DockBuilderRemoveNodeDockedWindows(ImGuiContext* ctx, ImGuiID root_id, bool clear_persistent_docking_references) +void ImGui::DockBuilderRemoveNodeDockedWindows(ImGuiID root_id, bool clear_persistent_docking_references) { // Clear references in settings + ImGuiContext* ctx = GImGui; ImGuiContext& g = *ctx; if (clear_persistent_docking_references) { @@ -11797,8 +11803,9 @@ void ImGui::DockBuilderRemoveNodeDockedWindows(ImGuiContext* ctx, ImGuiID root_i } } -ImGuiID ImGui::DockBuilderSplitNode(ImGuiContext* ctx, ImGuiID id, ImGuiDir split_dir, float size_ratio_for_node_at_dir, ImGuiID* out_id_at_dir, ImGuiID* out_id_other) +ImGuiID ImGui::DockBuilderSplitNode(ImGuiID id, ImGuiDir split_dir, float size_ratio_for_node_at_dir, ImGuiID* out_id_at_dir, ImGuiID* out_id_other) { + ImGuiContext* ctx = GImGui; IM_ASSERT(split_dir != ImGuiDir_None); ImGuiDockNode* node = DockContextFindNodeByID(ctx, id); @@ -11829,8 +11836,9 @@ ImGuiID ImGui::DockBuilderSplitNode(ImGuiContext* ctx, ImGuiID id, ImGuiDir spli return id_at_dir; } -static ImGuiDockNode* DockBuilderCopyNodeRec(ImGuiContext* ctx, ImGuiDockNode* src_node, ImGuiID dst_node_id_if_known, ImVector* out_node_remap_pairs) +static ImGuiDockNode* DockBuilderCopyNodeRec(ImGuiDockNode* src_node, ImGuiID dst_node_id_if_known, ImVector* out_node_remap_pairs) { + ImGuiContext* ctx = GImGui; ImGuiDockNode* dst_node = ImGui::DockContextAddNode(ctx, dst_node_id_if_known); dst_node->Flags = src_node->Flags; dst_node->Pos = src_node->Pos; @@ -11846,7 +11854,7 @@ static ImGuiDockNode* DockBuilderCopyNodeRec(ImGuiContext* ctx, ImGuiDockNode* s for (int child_n = 0; child_n < IM_ARRAYSIZE(src_node->ChildNodes); child_n++) if (src_node->ChildNodes[child_n]) { - dst_node->ChildNodes[child_n] = DockBuilderCopyNodeRec(ctx, src_node->ChildNodes[child_n], 0, out_node_remap_pairs); + dst_node->ChildNodes[child_n] = DockBuilderCopyNodeRec(src_node->ChildNodes[child_n], 0, out_node_remap_pairs); dst_node->ChildNodes[child_n]->ParentNode = dst_node; } @@ -11854,8 +11862,9 @@ static ImGuiDockNode* DockBuilderCopyNodeRec(ImGuiContext* ctx, ImGuiDockNode* s return dst_node; } -void ImGui::DockBuilderCopyNode(ImGuiContext* ctx, ImGuiID src_node_id, ImGuiID dst_node_id, ImVector* out_node_remap_pairs) +void ImGui::DockBuilderCopyNode(ImGuiID src_node_id, ImGuiID dst_node_id, ImVector* out_node_remap_pairs) { + ImGuiContext* ctx = GImGui; IM_ASSERT(src_node_id != 0); IM_ASSERT(dst_node_id != 0); IM_ASSERT(out_node_remap_pairs != NULL); @@ -11864,15 +11873,14 @@ void ImGui::DockBuilderCopyNode(ImGuiContext* ctx, ImGuiID src_node_id, ImGuiID IM_ASSERT(src_node != NULL); out_node_remap_pairs->clear(); - DockBuilderRemoveNode(ctx, dst_node_id); - DockBuilderCopyNodeRec(ctx, src_node, dst_node_id, out_node_remap_pairs); + DockBuilderRemoveNode(dst_node_id); + DockBuilderCopyNodeRec(src_node, dst_node_id, out_node_remap_pairs); IM_ASSERT((out_node_remap_pairs->Size % 2) == 0); } -void ImGui::DockBuilderCopyWindowSettings(ImGuiContext* ctx, const char* src_name, const char* dst_name) +void ImGui::DockBuilderCopyWindowSettings(const char* src_name, const char* dst_name) { - (void)ctx; ImGuiWindow* src_window = FindWindowByName(src_name); if (src_window == NULL) return; @@ -11901,7 +11909,7 @@ void ImGui::DockBuilderCopyWindowSettings(ImGuiContext* ctx, const char* src_nam } // FIXME: Will probably want to change this signature, in particular how the window remapping pairs are passed. -void ImGui::DockBuilderCopyDockspace(ImGuiContext* ctx, ImGuiID src_dockspace_id, ImGuiID dst_dockspace_id, ImVector* in_window_remap_pairs) +void ImGui::DockBuilderCopyDockspace(ImGuiID src_dockspace_id, ImGuiID dst_dockspace_id, ImVector* in_window_remap_pairs) { IM_ASSERT(src_dockspace_id != 0); IM_ASSERT(dst_dockspace_id != 0); @@ -11912,7 +11920,7 @@ void ImGui::DockBuilderCopyDockspace(ImGuiContext* ctx, ImGuiID src_dockspace_id // FIXME: When overwriting dst_dockspace_id, windows that aren't part of our dockspace family but that are docked in a same node will be split apart, // whereas we could attempt to at least keep them together in a new, same floating node. ImVector node_remap_pairs; - ImGui::DockBuilderCopyNode(ctx, src_dockspace_id, dst_dockspace_id, &node_remap_pairs); + DockBuilderCopyNode(src_dockspace_id, dst_dockspace_id, &node_remap_pairs); // Attempt to transition all the upcoming windows associated to dst_dockspace_id into the newly created hierarchy of dock nodes // (The windows associated to src_dockspace_id are staying in place) @@ -11942,14 +11950,14 @@ void ImGui::DockBuilderCopyDockspace(ImGuiContext* ctx, ImGuiID src_dockspace_id if (dst_dock_id != 0) { // Docked windows gets redocked into the new node hierarchy. - IMGUI_DEBUG_LOG("Remap window '%s' %08X -> %08X\n", dst_window_name, src_dock_id, dst_dock_id); - ImGui::DockBuilderDockWindow(ctx, dst_window_name, dst_dock_id); + //IMGUI_DEBUG_LOG("Remap window '%s' %08X -> %08X\n", dst_window_name, src_dock_id, dst_dock_id); + DockBuilderDockWindow(dst_window_name, dst_dock_id); } else { // Floating windows gets their settings transferred (regardless of whether the new window already exist or not) // When this is leading to a Copy and not a Move, we would get two overlapping floating windows. Could we possibly dock them together? - ImGui::DockBuilderCopyWindowSettings(ctx, src_window_name, dst_window_name); + DockBuilderCopyWindowSettings(src_window_name, dst_window_name); } } @@ -11959,7 +11967,7 @@ void ImGui::DockBuilderCopyDockspace(ImGuiContext* ctx, ImGuiID src_dockspace_id if (ImGuiID src_dock_id = node_remap_pairs[dock_remap_n]) { ImGuiID dst_dock_id = node_remap_pairs[dock_remap_n + 1]; - ImGuiDockNode* dock_node = ImGui::DockBuilderGetNode(ctx, src_dock_id); + ImGuiDockNode* dock_node = DockBuilderGetNode(src_dock_id); for (int window_n = 0; window_n < dock_node->Windows.Size; window_n++) { ImGuiWindow* window = dock_node->Windows[window_n]; @@ -11967,14 +11975,15 @@ void ImGui::DockBuilderCopyDockspace(ImGuiContext* ctx, ImGuiID src_dockspace_id continue; // Docked windows gets redocked into the new node hierarchy. - IMGUI_DEBUG_LOG("Remap window '%s' %08X -> %08X\n", window->Name, src_dock_id, dst_dock_id); - ImGui::DockBuilderDockWindow(ctx, window->Name, dst_dock_id); + //IMGUI_DEBUG_LOG("Remap window '%s' %08X -> %08X\n", window->Name, src_dock_id, dst_dock_id); + DockBuilderDockWindow(window->Name, dst_dock_id); } } } -void ImGui::DockBuilderFinish(ImGuiContext* ctx, ImGuiID root_id) +void ImGui::DockBuilderFinish(ImGuiID root_id) { + ImGuiContext* ctx = GImGui; //DockContextRebuild(ctx); DockContextBuildAddWindowsToNodes(ctx, root_id); } diff --git a/imgui_internal.h b/imgui_internal.h index e958102ad7f3..a939bd2f8d4f 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1486,17 +1486,17 @@ namespace ImGui IMGUI_API void ShowDockingDebug(); // Docking - Builder function needs to be generally called before the DockSpace() node is submitted. - IMGUI_API void DockBuilderDockWindow(ImGuiContext* ctx, const char* window_name, ImGuiID node_id); - IMGUI_API ImGuiDockNode*DockBuilderGetNode(ImGuiContext* ctx, ImGuiID node_id); - IMGUI_API void DockBuilderAddNode(ImGuiContext* ctx, ImGuiID node_id, ImVec2 ref_size, ImGuiDockNodeFlags flags = 0); - IMGUI_API void DockBuilderRemoveNode(ImGuiContext* ctx, ImGuiID node_id); // Remove node and all its child, undock all windows - IMGUI_API void DockBuilderRemoveNodeDockedWindows(ImGuiContext* ctx, ImGuiID node_id, bool clear_persistent_docking_references = true); - IMGUI_API void DockBuilderRemoveNodeChildNodes(ImGuiContext* ctx, ImGuiID node_id); // Remove all split/hierarchy. All remaining docked windows will be re-docked to the root. - IMGUI_API ImGuiID DockBuilderSplitNode(ImGuiContext* ctx, ImGuiID node_id, ImGuiDir split_dir, float size_ratio_for_node_at_dir, ImGuiID* out_id_dir, ImGuiID* out_id_other); - IMGUI_API void DockBuilderCopyDockspace(ImGuiContext* ctx, ImGuiID src_dockspace_id, ImGuiID dst_dockspace_id, ImVector* in_window_remap_pairs); - IMGUI_API void DockBuilderCopyNode(ImGuiContext* ctx, ImGuiID src_node_id, ImGuiID dst_node_id, ImVector* out_node_remap_pairs); - IMGUI_API void DockBuilderCopyWindowSettings(ImGuiContext* ctx, const char* src_name, const char* dst_name); - IMGUI_API void DockBuilderFinish(ImGuiContext* ctx, ImGuiID node_id); + IMGUI_API void DockBuilderDockWindow(const char* window_name, ImGuiID node_id); + IMGUI_API ImGuiDockNode*DockBuilderGetNode(ImGuiID node_id); + IMGUI_API void DockBuilderAddNode(ImGuiID node_id, ImVec2 ref_size, ImGuiDockNodeFlags flags = 0); + IMGUI_API void DockBuilderRemoveNode(ImGuiID node_id); // Remove node and all its child, undock all windows + IMGUI_API void DockBuilderRemoveNodeDockedWindows(ImGuiID node_id, bool clear_persistent_docking_references = true); + IMGUI_API void DockBuilderRemoveNodeChildNodes(ImGuiID node_id); // Remove all split/hierarchy. All remaining docked windows will be re-docked to the root. + IMGUI_API ImGuiID DockBuilderSplitNode(ImGuiID node_id, ImGuiDir split_dir, float size_ratio_for_node_at_dir, ImGuiID* out_id_dir, ImGuiID* out_id_other); + IMGUI_API void DockBuilderCopyDockspace(ImGuiID src_dockspace_id, ImGuiID dst_dockspace_id, ImVector* in_window_remap_pairs); + IMGUI_API void DockBuilderCopyNode(ImGuiID src_node_id, ImGuiID dst_node_id, ImVector* out_node_remap_pairs); + IMGUI_API void DockBuilderCopyWindowSettings(const char* src_name, const char* dst_name); + IMGUI_API void DockBuilderFinish(ImGuiID node_id); // Drag and Drop IMGUI_API bool BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id); From 84507cc7445e13f35caccc2dd348079cab39889f Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 8 Oct 2018 12:26:33 +0200 Subject: [PATCH 306/828] Docking: Kept ImGuiDockNodeFlags_PassthruDockspace and removed ImGuiDockNodeFlags_PassthruInEmptyNodes, ImGuiDockNodeFlags_RenderWindowBg. Doesn't include ImGuiDockNodeFlags_NoDockingInCentralNode. (#2109) --- docs/TODO.txt | 4 ++-- imgui.cpp | 11 +++++------ imgui.h | 4 +--- imgui_demo.cpp | 9 +++------ imgui_internal.h | 6 +++--- 5 files changed, 14 insertions(+), 20 deletions(-) diff --git a/docs/TODO.txt b/docs/TODO.txt index 5b1f18e475f2..e8941ba5be76 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -124,11 +124,10 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - splitter/separator: formalize the splitter idiom into an official api (we want to handle n-way split) (#319) - dock: A~ Unreal style document system (requires low-level controls of dockspace serialization fork/copy/delete). this is mostly working but the DockBuilderXXX api are not exposed/finished. - - dock: A- implicit, invisible per-viewport dockspace to dock to. - dock: B: when docking outer, perform size locking on neighbors nodes the same way we do it with splitters, so other nodes are not resized. - dock: B~ central node resizing behavior incorrect. - dock: B~ central node ID retrieval API? - - dock: B- debug full rebuild loses viewport of floating dock nodes. + - dock: B- full rebuild (which is a debug option) loses viewport of floating dock nodes. - dock: B- dock node inside its own viewports creates 1 temporary viewport per window on startup before ditching them (doesn't affect the user nor request platform windows to be created, but unnecessary) - dock: B- resize sibling locking behavior may be less desirable if we merged same-axis sibling in a same node level? - dock: B- single visible node part of a hidden split hierarchy (OnlyNodeWithWindows != NULL) should show a normal title bar (not a tab bar) @@ -147,6 +146,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - dock: B- dockspace: flag to lock the dock tree and/or sizes - dock: B- reintroduce collapsing a floating dock node. also collapsing a docked dock node! - dock: B- allow dragging a non-floating dock node by clicking on the title-bar-looking section (not just the collapse/menu button) + - dock: B- option to remember undocked window size? (instead of keeping their docked size) (relate to #2104) - dock: C- nav: CTRL+TAB highlighting tabs shows the mismatch between focus-stack and tab-order (not visible in VS because it doesn't highlight the tabs) - dock: C- after a dock/undock, the Scrollbar Status update in Begin() should use an updated e.g. size_y_for_scrollbars to avoid a 1 frame scrollbar flicker. diff --git a/imgui.cpp b/imgui.cpp index 6392e142788e..99c054b8c87c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10197,7 +10197,6 @@ void ImGui::DockContextProcessUndockWindow(ImGuiContext* ctx, ImGuiWindow* windo void ImGui::DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node) { - (void)ctx; IM_ASSERT(node->IsLeafNode()); IM_ASSERT(node->Windows.Size >= 1); @@ -10695,9 +10694,9 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) if (g.NavWindow && g.NavWindow->RootWindowDockStop->DockNode && g.NavWindow->RootWindowDockStop->ParentWindow == host_window) node->LastFocusedNodeID = g.NavWindow->RootWindowDockStop->DockNode->ID; - // We need to draw a background if requested by ImGuiDockNodeFlags_RenderWindowBg, but we will only know the correct pos/size after + // We need to draw a background if requested by ImGuiDockNodeFlags_PassthruDockspace, but we will only know the correct pos/size after // processing the resizing splitters. So we are using the DrawList channel splitting facility to submit drawing primitives out of order! - const bool render_dockspace_bg = node->IsRootNode() && host_window && (node->Flags & ImGuiDockNodeFlags_RenderWindowBg) != 0; + const bool render_dockspace_bg = node->IsRootNode() && host_window && (node->Flags & ImGuiDockNodeFlags_PassthruDockspace) != 0; if (render_dockspace_bg) { host_window->DrawList->ChannelsSplit(2); @@ -10705,7 +10704,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) } // Register a hit-test hole in the window unless we are currently dragging a window that is compatible our dockspace - bool central_node_hole = node->IsRootNode() && host_window && (node->Flags & ImGuiDockNodeFlags_PassthruInEmptyNodes) != 0 && central_node != NULL && central_node->IsEmpty(); + bool central_node_hole = node->IsRootNode() && host_window && (node->Flags & ImGuiDockNodeFlags_PassthruDockspace) != 0 && central_node != NULL && central_node->IsEmpty(); bool central_node_hole_register_hit_test_hole = central_node_hole; if (central_node_hole) if (const ImGuiPayload* payload = ImGui::GetDragDropPayload()) @@ -10732,10 +10731,10 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) } // Draw empty node background (currently can only be the Central Node) - if (host_window && node->IsEmpty() && node->IsVisible && !(node->Flags & ImGuiDockNodeFlags_PassthruInEmptyNodes)) + if (host_window && node->IsEmpty() && node->IsVisible && !(node->Flags & ImGuiDockNodeFlags_PassthruDockspace)) host_window->DrawList->AddRectFilled(node->Pos, node->Pos + node->Size, GetColorU32(ImGuiCol_DockingEmptyBg)); - // Draw whole dockspace background if ImGuiDockNodeFlags_RenderWindowBg if set. + // Draw whole dockspace background if ImGuiDockNodeFlags_PassthruDockspace if set. if (render_dockspace_bg && node->IsVisible) { host_window->DrawList->ChannelsSetCurrent(0); diff --git a/imgui.h b/imgui.h index 8b54b65e79d4..8a0d9e4f18f7 100644 --- a/imgui.h +++ b/imgui.h @@ -801,9 +801,7 @@ enum ImGuiDockNodeFlags_ //ImGuiDockNodeFlags_NoCentralNode = 1 << 2, // Disable Central Node (the node which can stay empty) //ImGuiDockNodeFlags_NoOuterBorder = 1 << 3, // Disable outer border on a DockSpace() node. ImGuiDockNodeFlags_NoDockingInCentralNode = 1 << 4, // Disable docking inside the Central Node, which will be always kept empty. - ImGuiDockNodeFlags_PassthruInEmptyNodes = 1 << 5, // When Central Node is empty: let inputs pass-through + won't display a DockingEmptyBg background. - ImGuiDockNodeFlags_RenderWindowBg = 1 << 6, // DockSpace() will render a ImGuiCol_WindowBg background covering everything excepted the Central Node (when empty). Meaning the host window should properly use SetNextWindowBgAlpha(0.0f) + ImGuiDockNodeFlags_NoOuterBorder prior to Begin() when using this. - ImGuiDockNodeFlags_PassthruDockspace = ImGuiDockNodeFlags_NoDockingInCentralNode | ImGuiDockNodeFlags_RenderWindowBg | ImGuiDockNodeFlags_PassthruInEmptyNodes + ImGuiDockNodeFlags_PassthruDockspace = 1 << 5 // 1) DockSpace() will render a ImGuiCol_WindowBg background covering everything excepted the Central Node when empty. Meaning the host window should probably use SetNextWindowBgAlpha(0.0f) prior to Begin() when using this. 2) When Central Node is empty: let inputs pass-through + won't display a DockingEmptyBg background. }; // Flags for ImGui::IsWindowFocused() diff --git a/imgui_demo.cpp b/imgui_demo.cpp index b8aac46bd8ca..31ecb08b8c11 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3733,8 +3733,8 @@ void ShowExampleAppDockSpace(bool* p_open) window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus; } - // When using ImGuiDockNodeFlags_RenderWindowBg or ImGuiDockNodeFlags_InvisibleDockspace, DockSpace() will render our background and handle the pass-thru hole, so we ask Begin() to not render a background. - if (opt_flags & ImGuiDockNodeFlags_RenderWindowBg) + // When using ImGuiDockNodeFlags_PassthruDockspace, DockSpace() will render our background and handle the pass-thru hole, so we ask Begin() to not render a background. + if (opt_flags & ImGuiDockNodeFlags_PassthruDockspace) ImGui::SetNextWindowBgAlpha(0.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); @@ -3766,10 +3766,7 @@ void ShowExampleAppDockSpace(bool* p_open) if (ImGui::MenuItem("Flag: NoSplit", "", (opt_flags & ImGuiDockNodeFlags_NoSplit) != 0)) opt_flags ^= ImGuiDockNodeFlags_NoSplit; if (ImGui::MenuItem("Flag: NoDockingInCentralNode", "", (opt_flags & ImGuiDockNodeFlags_NoDockingInCentralNode) != 0)) opt_flags ^= ImGuiDockNodeFlags_NoDockingInCentralNode; - if (ImGui::MenuItem("Flag: PassthruInEmptyNodes", "", (opt_flags & ImGuiDockNodeFlags_PassthruInEmptyNodes) != 0)) opt_flags ^= ImGuiDockNodeFlags_PassthruInEmptyNodes; - if (ImGui::MenuItem("Flag: RenderWindowBg", "", (opt_flags & ImGuiDockNodeFlags_RenderWindowBg) != 0)) opt_flags ^= ImGuiDockNodeFlags_RenderWindowBg; - if (ImGui::MenuItem("Flag: PassthruDockspace (all 3 above)", "", (opt_flags & ImGuiDockNodeFlags_PassthruDockspace) == ImGuiDockNodeFlags_PassthruDockspace)) - opt_flags = (opt_flags & ~ImGuiDockNodeFlags_PassthruDockspace) | ((opt_flags & ImGuiDockNodeFlags_PassthruDockspace) == ImGuiDockNodeFlags_PassthruDockspace) ? 0 : ImGuiDockNodeFlags_PassthruDockspace; + if (ImGui::MenuItem("Flag: PassthruDockspace", "", (opt_flags & ImGuiDockNodeFlags_PassthruDockspace) != 0)) opt_flags ^= ImGuiDockNodeFlags_PassthruDockspace; ImGui::Separator(); if (ImGui::MenuItem("Close DockSpace", NULL, false, p_open != NULL)) *p_open = false; diff --git a/imgui_internal.h b/imgui_internal.h index a939bd2f8d4f..c4639de4ab8b 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1487,11 +1487,11 @@ namespace ImGui // Docking - Builder function needs to be generally called before the DockSpace() node is submitted. IMGUI_API void DockBuilderDockWindow(const char* window_name, ImGuiID node_id); - IMGUI_API ImGuiDockNode*DockBuilderGetNode(ImGuiID node_id); + IMGUI_API ImGuiDockNode*DockBuilderGetNode(ImGuiID node_id); // Warning: DO NOT HOLD ON ImGuiDockNode* pointer, will be invalided by any split/merge/remove operation. IMGUI_API void DockBuilderAddNode(ImGuiID node_id, ImVec2 ref_size, ImGuiDockNodeFlags flags = 0); - IMGUI_API void DockBuilderRemoveNode(ImGuiID node_id); // Remove node and all its child, undock all windows + IMGUI_API void DockBuilderRemoveNode(ImGuiID node_id); // Remove node and all its child, undock all windows IMGUI_API void DockBuilderRemoveNodeDockedWindows(ImGuiID node_id, bool clear_persistent_docking_references = true); - IMGUI_API void DockBuilderRemoveNodeChildNodes(ImGuiID node_id); // Remove all split/hierarchy. All remaining docked windows will be re-docked to the root. + IMGUI_API void DockBuilderRemoveNodeChildNodes(ImGuiID node_id); // Remove all split/hierarchy. All remaining docked windows will be re-docked to the root. IMGUI_API ImGuiID DockBuilderSplitNode(ImGuiID node_id, ImGuiDir split_dir, float size_ratio_for_node_at_dir, ImGuiID* out_id_dir, ImGuiID* out_id_other); IMGUI_API void DockBuilderCopyDockspace(ImGuiID src_dockspace_id, ImGuiID dst_dockspace_id, ImVector* in_window_remap_pairs); IMGUI_API void DockBuilderCopyNode(ImGuiID src_node_id, ImGuiID dst_node_id, ImVector* out_node_remap_pairs); From 7b3433c68b0e7ca341802b13ca23bb2445d31adc Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 9 Oct 2018 14:19:39 +0200 Subject: [PATCH 307/828] Viewport: Added ImGuiConfigFlags_ViewportsDecoration to re-enable platform decoration (#1542) --- docs/TODO.txt | 1 + imgui.cpp | 37 ++++++++++++++++++++++--------------- imgui.h | 18 ++++++++++-------- imgui_demo.cpp | 4 +++- 4 files changed, 36 insertions(+), 24 deletions(-) diff --git a/docs/TODO.txt b/docs/TODO.txt index e8941ba5be76..a872ef29e7b8 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -305,6 +305,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - examples: move ImGui::NewFrame() out of the backend _NewFrame() ? - viewport: make it possible to have no main/hosting viewport + - viewport: with platform decoration enabled, platform may force constraint (e.g. minimum size) - viewport: use getfocus/setfocus api to synchronize imgui<>platform focus better (e.g imgui-side ctrl-tab can focus os window, OS initial setup and alt-tab can focus imgui window etc.) - viewport: store per-viewport/monitor DPI in .ini file so an application reload or main window changing DPI on reload can be properly patched for. - viewport: vulkan renderer implementation. diff --git a/imgui.cpp b/imgui.cpp index 99c054b8c87c..70460c023d0a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3237,10 +3237,13 @@ void ImGui::NewFrame() { if ((g.IO.BackendFlags & ImGuiBackendFlags_PlatformHasViewports) && (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasViewports)) { - IM_ASSERT((g.FrameCount == 0 || g.FrameCount == g.FrameCountPlatformEnded) && "Forgot to call UpdatePlatformWindows() at the end of the previous frame?"); + IM_ASSERT((g.FrameCount == 0 || g.FrameCount == g.FrameCountPlatformEnded) && "Forgot to call UpdatePlatformWindows() in main loop after EndFrame()?"); IM_ASSERT(g.PlatformIO.Platform_CreateWindow != NULL && "Platform init didn't install handlers?"); IM_ASSERT(g.PlatformIO.Platform_DestroyWindow != NULL && "Platform init didn't install handlers?"); IM_ASSERT(g.PlatformIO.Platform_GetWindowPos != NULL && "Platform init didn't install handlers?"); + IM_ASSERT(g.PlatformIO.Platform_SetWindowPos != NULL && "Platform init didn't install handlers?"); + IM_ASSERT(g.PlatformIO.Platform_GetWindowSize != NULL && "Platform init didn't install handlers?"); + IM_ASSERT(g.PlatformIO.Platform_SetWindowSize != NULL && "Platform init didn't install handlers?"); IM_ASSERT((g.Viewports[0]->PlatformUserData != NULL || g.Viewports[0]->PlatformHandle != NULL) && "Platform init didn't setup main viewport."); #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS IM_ASSERT(g.IO.RenderDrawListsFn == NULL); // Call ImGui::Render() then pass ImGui::GetDrawData() yourself to your render function! @@ -5175,7 +5178,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) { // Late create viewport, based on the assumption that with our calculations, the DPI will be known ahead (same as the DPI of the selection done in UpdateSelectWindowViewport) //ImGuiViewport* old_viewport = window->Viewport; - ImGuiViewportFlags viewport_flags = ImGuiViewportFlags_NoDecoration | ImGuiViewportFlags_NoFocusOnAppearing | ((window->Flags & ImGuiWindowFlags_NoInputs) ? ImGuiViewportFlags_NoInputs : 0); + ImGuiViewportFlags viewport_flags = ImGuiViewportFlags_NoFocusOnAppearing | ((window->Flags & ImGuiWindowFlags_NoInputs) ? ImGuiViewportFlags_NoInputs : 0); window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, viewport_flags); // FIXME-DPI @@ -7416,6 +7419,7 @@ static ImGuiViewportP* FindViewportHoveredFromPlatformWindowStack(const ImVec2 m return best_candidate; } +// Called in NewFrame() static void ImGui::UpdateViewports() { ImGuiContext& g = *GImGui; @@ -7641,7 +7645,7 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) { window->Viewport = FindViewportByID(window->ViewportId); if (window->Viewport == NULL && window->ViewportPos.x != FLT_MAX && window->ViewportPos.y != FLT_MAX) - window->Viewport = AddUpdateViewport(window, window->ID, window->ViewportPos, window->Size, ImGuiViewportFlags_NoDecoration); + window->Viewport = AddUpdateViewport(window, window->ID, window->ViewportPos, window->Size, ImGuiViewportFlags_None); } } @@ -7671,11 +7675,11 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) bool leave_host_viewport = has_viewport && !own_viewport && !window->Viewport->GetRect().Contains(window->Rect()); bool move_from_own_viewport = has_viewport && own_viewport && !(window->Viewport->Flags & ImGuiViewportFlags_NoInputs); if (!has_viewport || leave_host_viewport || move_from_own_viewport) - window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoDecoration | ImGuiViewportFlags_NoFocusOnAppearing | ImGuiViewportFlags_NoInputs); + window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoFocusOnAppearing | ImGuiViewportFlags_NoInputs); } else if (GetWindowAlwaysWantOwnViewport(window)) { - window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoDecoration); + window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_None); } // Mark window as allowed to protrude outside of its viewport and into the current monitor @@ -7705,7 +7709,7 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) else if (!UpdateTryMergeWindowIntoHostViewport(window, g.Viewports[0])) // Merge? { // New viewport - window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoDecoration | ImGuiViewportFlags_NoFocusOnAppearing); + window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoFocusOnAppearing); } } else if ((flags & ImGuiWindowFlags_DockNodeHost) && (window->Appearing)) @@ -7723,16 +7727,15 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) // Update flags window->ViewportOwned = (window == window->Viewport->Window); - if (window->ViewportOwned) - window->Viewport->Flags |= ImGuiViewportFlags_NoDecoration; // If the OS window has a title bar, hide our imgui title bar - if (window->ViewportOwned && !(window->Viewport->Flags & ImGuiViewportFlags_NoDecoration)) - window->Flags |= ImGuiWindowFlags_NoTitleBar; + //if (window->ViewportOwned && !(window->Viewport->Flags & ImGuiViewportFlags_NoDecoration)) + // window->Flags |= ImGuiWindowFlags_NoTitleBar; window->ViewportId = window->Viewport->ID; } +// Called by imgui at the end of the main loop, after EndFrame() void ImGui::UpdatePlatformWindows() { ImGuiContext& g = *GImGui; @@ -7766,13 +7769,17 @@ void ImGui::UpdatePlatformWindows() if (viewport->Size.x <= 0 || viewport->Size.y <= 0) continue; - // Update viewport flags + // Update common viewport flags for owned viewports if (viewport->Window != NULL) { - bool topmost = (viewport->Window->Flags & ImGuiWindowFlags_Tooltip) != 0; - bool no_task_bar_icon = (g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsNoTaskBarIcons) != 0 || (viewport->Window->Flags & (ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) != 0; - viewport->Flags = topmost ? (viewport->Flags | ImGuiViewportFlags_TopMost) : (viewport->Flags & ~ImGuiViewportFlags_TopMost); - viewport->Flags = no_task_bar_icon ? (viewport->Flags | ImGuiViewportFlags_NoTaskBarIcon) : (viewport->Flags & ~ImGuiViewportFlags_NoTaskBarIcon); + ImGuiViewportFlags flags = viewport->Flags & ~(ImGuiViewportFlags_TopMost | ImGuiViewportFlags_NoTaskBarIcon | ImGuiViewportFlags_NoDecoration); + if (viewport->Window->Flags & ImGuiWindowFlags_Tooltip) + flags |= ImGuiViewportFlags_TopMost; + if ((g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsNoTaskBarIcon) != 0 || (viewport->Window->Flags & (ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) != 0) + flags |= ImGuiViewportFlags_NoTaskBarIcon; + if ((g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsDecoration) == 0 || (viewport->Window->Flags & (ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) != 0) + flags |= ImGuiViewportFlags_NoDecoration; + viewport->Flags = flags; } // Create window diff --git a/imgui.h b/imgui.h index 8a0d9e4f18f7..aa262738929a 100644 --- a/imgui.h +++ b/imgui.h @@ -954,10 +954,11 @@ enum ImGuiConfigFlags_ // [BETA] Viewports ImGuiConfigFlags_ViewportsEnable = 1 << 10, // Viewport enable flags (require both ImGuiConfigFlags_PlatformHasViewports + ImGuiConfigFlags_RendererHasViewports set by the respective back-ends) - ImGuiConfigFlags_ViewportsNoTaskBarIcons= 1 << 11, // Disable task bars icons for all secondary viewports (will set ImGuiViewportFlags_NoTaskBarIcon on them) + ImGuiConfigFlags_ViewportsNoTaskBarIcon = 1 << 11, // Disable task bars icons for all secondary viewports (will set ImGuiViewportFlags_NoTaskBarIcon on them) ImGuiConfigFlags_ViewportsNoMerge = 1 << 12, // All floating windows will always create their own viewport and platform window. - ImGuiConfigFlags_DpiEnableScaleViewports= 1 << 13, // FIXME-DPI: Reposition and resize imgui windows when the DpiScale of a viewport changed (mostly useful for the main viewport hosting other window). Note that resizing the main window itself is up to your application. - ImGuiConfigFlags_DpiEnableScaleFonts = 1 << 14, // FIXME-DPI: Request bitmap-scaled fonts to match DpiScale. This is a very low-quality workaround. The correct way to handle DPI is _currently_ to replace the atlas and/or fonts in the Platform_OnChangedViewport callback, but this is all early work in progress. + ImGuiConfigFlags_ViewportsDecoration = 1 << 13, // FIXME [Broken] Enable platform decoration for all secondary viewports (will not set ImGuiViewportFlags_NoDecoration on them). This currently doesn't behave well in Windows because 1) By default the new window animation get in the way of our transitions, 2) It enable a minimum window size which tends to breaks resizing. You can workaround the later by setting style.WindowMinSize to a bigger value. + ImGuiConfigFlags_DpiEnableScaleViewports= 1 << 14, // FIXME-DPI: Reposition and resize imgui windows when the DpiScale of a viewport changed (mostly useful for the main viewport hosting other window). Note that resizing the main window itself is up to your application. + ImGuiConfigFlags_DpiEnableScaleFonts = 1 << 15, // FIXME-DPI: Request bitmap-scaled fonts to match DpiScale. This is a very low-quality workaround. The correct way to handle DPI is _currently_ to replace the atlas and/or fonts in the Platform_OnChangedViewport callback, but this is all early work in progress. // User storage (to allow your back-end/engine to communicate to code that may be shared between multiple projects. Those flags are not used by core ImGui) ImGuiConfigFlags_IsSRGB = 1 << 20, // Application is SRGB-aware. @@ -1219,7 +1220,7 @@ struct ImGuiIO // Miscellaneous configuration options bool MouseDrawCursor; // = false // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). Cannot be easily renamed to 'io.ConfigXXX' because this is frequently used by back-end implementations. - bool ConfigDockingWithShift; // = true // Enable docking with holding Shift key (reduce visual noise, allows dropping in wider space) + bool ConfigDockingWithShift; // = false // Enable docking with holding Shift key (reduce visual noise, allows dropping in wider space) bool ConfigMacOSXBehaviors; // = defined(__APPLE__) // OS X style: Text editing cursor movement using Alt instead of Ctrl, Shortcuts using Cmd/Super instead of Ctrl, Line/Text Start and End using Cmd+Arrows instead of Home/End, Double click selects by word instead of selecting whole text, Multi-selection in lists uses Cmd/Super instead of Ctrl (was called io.OptMacOSXBehaviors prior to 1.63) bool ConfigInputTextCursorBlink; // = true // Set to false to disable blinking cursor, for users who consider it distracting. (was called: io.OptCursorBlink prior to 1.63) bool ConfigResizeWindowsFromEdges; // = true // [BETA] Enable resizing of windows from their edges and from the lower-left corner. This requires (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) because it needs mouse cursor feedback. (This used to be the ImGuiWindowFlags_ResizeFromAnySide flag) @@ -2157,7 +2158,8 @@ struct ImGuiPlatformIO // Flags stored in ImGuiViewport::Flags, giving indications to the platform back-ends enum ImGuiViewportFlags_ { - ImGuiViewportFlags_NoDecoration = 1 << 0, // Platform Window: Disable platform title bar, borders, etc. + ImGuiViewportFlags_None = 0, + ImGuiViewportFlags_NoDecoration = 1 << 0, // Platform Window: Disable platform decorations: title bar, borders, etc. ImGuiViewportFlags_NoFocusOnAppearing = 1 << 1, // Platform Window: Don't take focus when created. ImGuiViewportFlags_NoInputs = 1 << 2, // Platform Window: Make mouse pass through so we can drag this window while peaking behind it. ImGuiViewportFlags_NoTaskBarIcon = 1 << 3, // Platform Window: Disable platform task bar icon (for popups, menus, or all windows if ImGuiConfigFlags_ViewportsNoTaskBarIcons if set) @@ -2177,9 +2179,9 @@ struct ImGuiViewport void* PlatformUserData; // void* to hold custom data structure for the platform (e.g. windowing info, render context) void* PlatformHandle; // void* for FindViewportByPlatformHandle(). (e.g. suggested to use natural platform handle such as HWND, GlfwWindow*, SDL_Window*) - bool PlatformRequestClose; // Platform window requested closure - bool PlatformRequestMove; // Platform window requested move (e.g. window was moved by the OS / host window manager) - bool PlatformRequestResize; // Platform window requested resize (e.g. window was resize by the OS / host window manager) + bool PlatformRequestClose; // Platform window requested closure (e.g. window was moved by the OS / host window manager, e.g. pressing ALT-F4) + bool PlatformRequestMove; // Platform window requested move (e.g. window was moved by the OS / host window manager, authoritative position will be OS window position) + bool PlatformRequestResize; // Platform window requested resize (e.g. window was resized by the OS / host window manager, authoritative size will be OS window size) void* RendererUserData; // void* to hold custom data structure for the renderer (e.g. swap chain, frame-buffers etc.) ImGuiViewport() { ID = 0; Flags = 0; DpiScale = 0.0f; DrawData = NULL; PlatformUserData = PlatformHandle = NULL; PlatformRequestClose = PlatformRequestMove = PlatformRequestResize = false; RendererUserData = NULL; } diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 31ecb08b8c11..3bc639bb82ed 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -338,8 +338,10 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::CheckboxFlags("io.ConfigFlags: ViewportsEnable", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_ViewportsEnable); ImGui::SameLine(); ShowHelpMarker("Toggling this at runtime is normally unsupported (it will offset your windows)."); - ImGui::CheckboxFlags("io.ConfigFlags: ViewportsNoTaskBarIcons", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_ViewportsNoTaskBarIcons); + ImGui::CheckboxFlags("io.ConfigFlags: ViewportsNoTaskBarIcon", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_ViewportsNoTaskBarIcon); ImGui::SameLine(); ShowHelpMarker("Toggling this at runtime is normally unsupported (most platform back-ends won't refresh the task bar icon state right away)."); + ImGui::CheckboxFlags("io.ConfigFlags: ViewportsDecoration", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_ViewportsDecoration); + ImGui::SameLine(); ShowHelpMarker("Toggling this at runtime is normally unsupported (most platform back-ends won't refresh the decoration right away)."); ImGui::CheckboxFlags("io.ConfigFlags: ViewportsNoMerge", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_ViewportsNoMerge); ImGui::SameLine(); ShowHelpMarker("All floating windows will always create their own viewport and platform window."); From 3a7828de6b49b8ad7cdb8787fade5ab48277e8c7 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 9 Oct 2018 14:19:39 +0200 Subject: [PATCH 308/828] Viewport: Added ImGuiConfigFlags_ViewportsDecoration to re-enable platform decoration (#1542) --- docs/TODO.txt | 1 + imgui.cpp | 35 +++++++++++++++++++++-------------- imgui.h | 16 +++++++++------- imgui_demo.cpp | 4 +++- 4 files changed, 34 insertions(+), 22 deletions(-) diff --git a/docs/TODO.txt b/docs/TODO.txt index 49706570fcd3..9b2893d31e32 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -281,6 +281,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - examples: move ImGui::NewFrame() out of the backend _NewFrame() ? - viewport: make it possible to have no main/hosting viewport + - viewport: with platform decoration enabled, platform may force constraint (e.g. minimum size) - viewport: use getfocus/setfocus api to synchronize imgui<>platform focus better (e.g imgui-side ctrl-tab can focus os window, OS initial setup and alt-tab can focus imgui window etc.) - viewport: store per-viewport/monitor DPI in .ini file so an application reload or main window changing DPI on reload can be properly patched for. - viewport: vulkan renderer implementation. diff --git a/imgui.cpp b/imgui.cpp index 8b35381faeea..0dfe9795f28d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3211,10 +3211,13 @@ void ImGui::NewFrame() { if ((g.IO.BackendFlags & ImGuiBackendFlags_PlatformHasViewports) && (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasViewports)) { - IM_ASSERT((g.FrameCount == 0 || g.FrameCount == g.FrameCountPlatformEnded) && "Forgot to call UpdatePlatformWindows() at the end of the previous frame?"); + IM_ASSERT((g.FrameCount == 0 || g.FrameCount == g.FrameCountPlatformEnded) && "Forgot to call UpdatePlatformWindows() in main loop after EndFrame()?"); IM_ASSERT(g.PlatformIO.Platform_CreateWindow != NULL && "Platform init didn't install handlers?"); IM_ASSERT(g.PlatformIO.Platform_DestroyWindow != NULL && "Platform init didn't install handlers?"); IM_ASSERT(g.PlatformIO.Platform_GetWindowPos != NULL && "Platform init didn't install handlers?"); + IM_ASSERT(g.PlatformIO.Platform_SetWindowPos != NULL && "Platform init didn't install handlers?"); + IM_ASSERT(g.PlatformIO.Platform_GetWindowSize != NULL && "Platform init didn't install handlers?"); + IM_ASSERT(g.PlatformIO.Platform_SetWindowSize != NULL && "Platform init didn't install handlers?"); IM_ASSERT((g.Viewports[0]->PlatformUserData != NULL || g.Viewports[0]->PlatformHandle != NULL) && "Platform init didn't setup main viewport."); #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS IM_ASSERT(g.IO.RenderDrawListsFn == NULL); // Call ImGui::Render() then pass ImGui::GetDrawData() yourself to your render function! @@ -5028,7 +5031,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) { // Late create viewport, based on the assumption that with our calculations, the DPI will be known ahead (same as the DPI of the selection done in UpdateSelectWindowViewport) //ImGuiViewport* old_viewport = window->Viewport; - ImGuiViewportFlags viewport_flags = ImGuiViewportFlags_NoDecoration | ImGuiViewportFlags_NoFocusOnAppearing | ((window->Flags & ImGuiWindowFlags_NoInputs) ? ImGuiViewportFlags_NoInputs : 0); + ImGuiViewportFlags viewport_flags = ImGuiViewportFlags_NoFocusOnAppearing | ((window->Flags & ImGuiWindowFlags_NoInputs) ? ImGuiViewportFlags_NoInputs : 0); window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, viewport_flags); // FIXME-DPI @@ -7191,6 +7194,7 @@ static ImGuiViewportP* FindViewportHoveredFromPlatformWindowStack(const ImVec2 m return best_candidate; } +// Called in NewFrame() static void ImGui::UpdateViewports() { ImGuiContext& g = *GImGui; @@ -7413,7 +7417,7 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) { window->Viewport = FindViewportByID(window->ViewportId); if (window->Viewport == NULL && window->ViewportPos.x != FLT_MAX && window->ViewportPos.y != FLT_MAX) - window->Viewport = AddUpdateViewport(window, window->ID, window->ViewportPos, window->Size, ImGuiViewportFlags_NoDecoration); + window->Viewport = AddUpdateViewport(window, window->ID, window->ViewportPos, window->Size, ImGuiViewportFlags_None); } } @@ -7443,11 +7447,11 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) bool leave_host_viewport = has_viewport && !own_viewport && !window->Viewport->GetRect().Contains(window->Rect()); bool move_from_own_viewport = has_viewport && own_viewport && !(window->Viewport->Flags & ImGuiViewportFlags_NoInputs); if (!has_viewport || leave_host_viewport || move_from_own_viewport) - window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoDecoration | ImGuiViewportFlags_NoFocusOnAppearing | ImGuiViewportFlags_NoInputs); + window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoFocusOnAppearing | ImGuiViewportFlags_NoInputs); } else if (GetWindowAlwaysWantOwnViewport(window)) { - window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoDecoration); + window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_None); } // Mark window as allowed to protrude outside of its viewport and into the current monitor @@ -7472,16 +7476,15 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) // Update flags window->ViewportOwned = (window == window->Viewport->Window); - if (window->ViewportOwned) - window->Viewport->Flags |= ImGuiViewportFlags_NoDecoration; // If the OS window has a title bar, hide our imgui title bar - if (window->ViewportOwned && !(window->Viewport->Flags & ImGuiViewportFlags_NoDecoration)) - window->Flags |= ImGuiWindowFlags_NoTitleBar; + //if (window->ViewportOwned && !(window->Viewport->Flags & ImGuiViewportFlags_NoDecoration)) + // window->Flags |= ImGuiWindowFlags_NoTitleBar; window->ViewportId = window->Viewport->ID; } +// Called by imgui at the end of the main loop, after EndFrame() void ImGui::UpdatePlatformWindows() { ImGuiContext& g = *GImGui; @@ -7515,13 +7518,17 @@ void ImGui::UpdatePlatformWindows() if (viewport->Size.x <= 0 || viewport->Size.y <= 0) continue; - // Update viewport flags + // Update common viewport flags for owned viewports if (viewport->Window != NULL) { - bool topmost = (viewport->Window->Flags & ImGuiWindowFlags_Tooltip) != 0; - bool no_task_bar_icon = (g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsNoTaskBarIcons) != 0 || (viewport->Window->Flags & (ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) != 0; - viewport->Flags = topmost ? (viewport->Flags | ImGuiViewportFlags_TopMost) : (viewport->Flags & ~ImGuiViewportFlags_TopMost); - viewport->Flags = no_task_bar_icon ? (viewport->Flags | ImGuiViewportFlags_NoTaskBarIcon) : (viewport->Flags & ~ImGuiViewportFlags_NoTaskBarIcon); + ImGuiViewportFlags flags = viewport->Flags & ~(ImGuiViewportFlags_TopMost | ImGuiViewportFlags_NoTaskBarIcon | ImGuiViewportFlags_NoDecoration); + if (viewport->Window->Flags & ImGuiWindowFlags_Tooltip) + flags |= ImGuiViewportFlags_TopMost; + if ((g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsNoTaskBarIcon) != 0 || (viewport->Window->Flags & (ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) != 0) + flags |= ImGuiViewportFlags_NoTaskBarIcon; + if ((g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsDecoration) == 0 || (viewport->Window->Flags & (ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) != 0) + flags |= ImGuiViewportFlags_NoDecoration; + viewport->Flags = flags; } // Create window diff --git a/imgui.h b/imgui.h index 2534007340be..34b5246621b4 100644 --- a/imgui.h +++ b/imgui.h @@ -886,10 +886,11 @@ enum ImGuiConfigFlags_ // [BETA] Viewports ImGuiConfigFlags_ViewportsEnable = 1 << 10, // Viewport enable flags (require both ImGuiConfigFlags_PlatformHasViewports + ImGuiConfigFlags_RendererHasViewports set by the respective back-ends) - ImGuiConfigFlags_ViewportsNoTaskBarIcons= 1 << 11, // Disable task bars icons for all secondary viewports (will set ImGuiViewportFlags_NoTaskBarIcon on them) + ImGuiConfigFlags_ViewportsNoTaskBarIcon = 1 << 11, // Disable task bars icons for all secondary viewports (will set ImGuiViewportFlags_NoTaskBarIcon on them) ImGuiConfigFlags_ViewportsNoMerge = 1 << 12, // All floating windows will always create their own viewport and platform window. - ImGuiConfigFlags_DpiEnableScaleViewports= 1 << 13, // FIXME-DPI: Reposition and resize imgui windows when the DpiScale of a viewport changed (mostly useful for the main viewport hosting other window). Note that resizing the main window itself is up to your application. - ImGuiConfigFlags_DpiEnableScaleFonts = 1 << 14, // FIXME-DPI: Request bitmap-scaled fonts to match DpiScale. This is a very low-quality workaround. The correct way to handle DPI is _currently_ to replace the atlas and/or fonts in the Platform_OnChangedViewport callback, but this is all early work in progress. + ImGuiConfigFlags_ViewportsDecoration = 1 << 13, // FIXME [Broken] Enable platform decoration for all secondary viewports (will not set ImGuiViewportFlags_NoDecoration on them). This currently doesn't behave well in Windows because 1) By default the new window animation get in the way of our transitions, 2) It enable a minimum window size which tends to breaks resizing. You can workaround the later by setting style.WindowMinSize to a bigger value. + ImGuiConfigFlags_DpiEnableScaleViewports= 1 << 14, // FIXME-DPI: Reposition and resize imgui windows when the DpiScale of a viewport changed (mostly useful for the main viewport hosting other window). Note that resizing the main window itself is up to your application. + ImGuiConfigFlags_DpiEnableScaleFonts = 1 << 15, // FIXME-DPI: Request bitmap-scaled fonts to match DpiScale. This is a very low-quality workaround. The correct way to handle DPI is _currently_ to replace the atlas and/or fonts in the Platform_OnChangedViewport callback, but this is all early work in progress. // User storage (to allow your back-end/engine to communicate to code that may be shared between multiple projects. Those flags are not used by core ImGui) ImGuiConfigFlags_IsSRGB = 1 << 20, // Application is SRGB-aware. @@ -2068,7 +2069,8 @@ struct ImGuiPlatformIO // Flags stored in ImGuiViewport::Flags, giving indications to the platform back-ends enum ImGuiViewportFlags_ { - ImGuiViewportFlags_NoDecoration = 1 << 0, // Platform Window: Disable platform title bar, borders, etc. + ImGuiViewportFlags_None = 0, + ImGuiViewportFlags_NoDecoration = 1 << 0, // Platform Window: Disable platform decorations: title bar, borders, etc. ImGuiViewportFlags_NoFocusOnAppearing = 1 << 1, // Platform Window: Don't take focus when created. ImGuiViewportFlags_NoInputs = 1 << 2, // Platform Window: Make mouse pass through so we can drag this window while peaking behind it. ImGuiViewportFlags_NoTaskBarIcon = 1 << 3, // Platform Window: Disable platform task bar icon (for popups, menus, or all windows if ImGuiConfigFlags_ViewportsNoTaskBarIcons if set) @@ -2088,9 +2090,9 @@ struct ImGuiViewport void* PlatformUserData; // void* to hold custom data structure for the platform (e.g. windowing info, render context) void* PlatformHandle; // void* for FindViewportByPlatformHandle(). (e.g. suggested to use natural platform handle such as HWND, GlfwWindow*, SDL_Window*) - bool PlatformRequestClose; // Platform window requested closure - bool PlatformRequestMove; // Platform window requested move (e.g. window was moved by the OS / host window manager) - bool PlatformRequestResize; // Platform window requested resize (e.g. window was resize by the OS / host window manager) + bool PlatformRequestClose; // Platform window requested closure (e.g. window was moved by the OS / host window manager, e.g. pressing ALT-F4) + bool PlatformRequestMove; // Platform window requested move (e.g. window was moved by the OS / host window manager, authoritative position will be OS window position) + bool PlatformRequestResize; // Platform window requested resize (e.g. window was resized by the OS / host window manager, authoritative size will be OS window size) void* RendererUserData; // void* to hold custom data structure for the renderer (e.g. swap chain, frame-buffers etc.) ImGuiViewport() { ID = 0; Flags = 0; DpiScale = 0.0f; DrawData = NULL; PlatformUserData = PlatformHandle = NULL; PlatformRequestClose = PlatformRequestMove = PlatformRequestResize = false; RendererUserData = NULL; } diff --git a/imgui_demo.cpp b/imgui_demo.cpp index ad343e631003..c6d328707846 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -312,8 +312,10 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::SameLine(); ShowHelpMarker("Instruct back-end to not alter mouse cursor shape and visibility."); ImGui::CheckboxFlags("io.ConfigFlags: ViewportsEnable", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_ViewportsEnable); ImGui::SameLine(); ShowHelpMarker("Toggling this at runtime is normally unsupported (it will offset your windows)."); - ImGui::CheckboxFlags("io.ConfigFlags: ViewportsNoTaskBarIcons", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_ViewportsNoTaskBarIcons); + ImGui::CheckboxFlags("io.ConfigFlags: ViewportsNoTaskBarIcon", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_ViewportsNoTaskBarIcon); ImGui::SameLine(); ShowHelpMarker("Toggling this at runtime is normally unsupported (most platform back-ends won't refresh the task bar icon state right away)."); + ImGui::CheckboxFlags("io.ConfigFlags: ViewportsDecoration", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_ViewportsDecoration); + ImGui::SameLine(); ShowHelpMarker("Toggling this at runtime is normally unsupported (most platform back-ends won't refresh the decoration right away)."); ImGui::CheckboxFlags("io.ConfigFlags: ViewportsNoMerge", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_ViewportsNoMerge); ImGui::SameLine(); ShowHelpMarker("All floating windows will always create their own viewport and platform window."); ImGui::Checkbox("io.ConfigInputTextCursorBlink", &io.ConfigInputTextCursorBlink); From 15ec78e9d8347ff3e731375181b225e0a5e9318d Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 11 Oct 2018 21:31:42 +0200 Subject: [PATCH 309/828] Internal: Moved stored window name update code to the first_begin_of_the_frame block. This will be useful in the Docking branch. --- imgui.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 0dfe9795f28d..2b6ef904fa47 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4766,15 +4766,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window = CreateNewWindow(name, size_on_first_use, flags); } - // Update stored window name when it changes (which can only happen with the "###" operator). - // Only if it is meant to be displayed to the end user in a different place than the title bar (which already always display the 'name' parameter) - bool window_title_visible_elsewhere = (window->Viewport && window->Viewport->Window == window); - if (!window_just_created && window_title_visible_elsewhere && strcmp(name, window->Name) != 0) - { - IM_DELETE(window->Name); - window->Name = ImStrdup(name); - } - // Automatically disable manual moving/resizing when NoInputs is set if (flags & ImGuiWindowFlags_NoInputs) flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize; @@ -4886,6 +4877,15 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->LastFrameActive = current_frame; window->IDStack.resize(1); + // Update stored window name when it changes (which can only happen with the "###" operator). + // The title bar always display the 'name' parameter, so we only update storage if the title is displayed to the end-user in a different place. + bool window_title_visible_elsewhere = (window->Viewport && window->Viewport->Window == window); + if (!window_just_created && window_title_visible_elsewhere && strcmp(name, window->Name) != 0) + { + IM_DELETE(window->Name); + window->Name = ImStrdup(name); + } + // UPDATE CONTENTS SIZE, UPDATE HIDDEN STATUS // Update contents size from last frame for auto-fitting (or use explicit size) From 33994bbfa1550947944f3069e1796dd772e29983 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 12 Oct 2018 12:56:56 +0200 Subject: [PATCH 310/828] Docking: Fixed Modal window from being dockable. --- imgui.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 73a40840c081..e32c23964f58 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7103,7 +7103,8 @@ bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags) g.NextWindowData.Clear(); // We behave like Begin() and need to consume those values return false; } - return BeginPopupEx(g.CurrentWindow->GetID(str_id), flags|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_NoDocking); + flags |= ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoDocking; + return BeginPopupEx(g.CurrentWindow->GetID(str_id), flags); } bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags) @@ -7122,7 +7123,8 @@ bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags fla if (g.NextWindowData.PosCond == 0) SetNextWindowPos(window->Viewport->GetCenter(), ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); - bool is_open = Begin(name, p_open, flags | ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings); + flags |= flags | ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoDocking; + const bool is_open = Begin(name, p_open, flags); if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display) { EndPopup(); From 2eaf5b03dfe8c92b6808e22e53d14f2c4512c51d Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 12 Oct 2018 13:29:13 +0200 Subject: [PATCH 311/828] Fixes crash introduced in previous commit 9cf94d5. --- imgui_draw.cpp | 8 ++++---- imgui_widgets.cpp | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 4ecb22581214..62699775320f 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -2669,9 +2669,8 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col if (y + line_height < clip_rect.y && !word_wrap_enabled) while (y + line_height < clip_rect.y && s < text_end) { - s = (const char*)memchr(s, '\n', text_end - s) + 1; - if (s == NULL) - s = text_end; + s = (const char*)memchr(s, '\n', text_end - s); + s = s ? s + 1 : text_end; y += line_height; } @@ -2683,7 +2682,8 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col float y_end = y; while (y_end < clip_rect.w && s_end < text_end) { - s_end = (const char*)memchr(s_end, '\n', text_end - s_end) + 1; + s_end = (const char*)memchr(s_end, '\n', text_end - s_end); + s = s ? s + 1 : text_end; y_end += line_height; } text_end = s_end; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 9ba1fd0c34a0..ab3d3d189475 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -3714,7 +3714,8 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 break; if (rect_pos.y < clip_rect.y) { - p = (const ImWchar*)wmemchr((const wchar_t*)p, '\n', text_selected_end - p) + 1; + p = (const ImWchar*)wmemchr((const wchar_t*)p, '\n', text_selected_end - p); + p = p ? p + 1 : text_selected_end; } else { From 8b956216b75cdc7b746d7593193b44caaa5a3708 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 15 Oct 2018 17:19:52 +0200 Subject: [PATCH 312/828] Demo: Testing return value of BeginTabBar() for consistency. --- docs/TODO.txt | 3 +- imgui_demo.cpp | 109 ++++++++++++++++++++++++++----------------------- 2 files changed, 59 insertions(+), 53 deletions(-) diff --git a/docs/TODO.txt b/docs/TODO.txt index a872ef29e7b8..daa347897266 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -150,7 +150,8 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - dock: C- nav: CTRL+TAB highlighting tabs shows the mismatch between focus-stack and tab-order (not visible in VS because it doesn't highlight the tabs) - dock: C- after a dock/undock, the Scrollbar Status update in Begin() should use an updated e.g. size_y_for_scrollbars to avoid a 1 frame scrollbar flicker. - - tabs: re-ordering, close buttons, context menu, persistent order (#261, #351) + - tabs: make EndTabBar fail if users doesn't respect BeginTabBar return value, for consistency/future-proofing. + - tabs: persistent order/focus in BeginTabBar() api (#261, #351) - ext: stl-ish friendly extension (imgui_stl.h) that has wrapper for std::string, std::vector etc. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index e3cf62117cc6..fc448bbf1caa 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1721,23 +1721,25 @@ void ImGui::ShowDemoWindow(bool* p_open) if (ImGui::TreeNode("Basic")) { ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_None; - ImGui::BeginTabBar("MyTabBar", tab_bar_flags); - if (ImGui::BeginTabItem("Avocado")) + if (ImGui::BeginTabBar("MyTabBar", tab_bar_flags)) { - ImGui::Text("This is the Avocado tab!\nblah blah blah blah blah"); - ImGui::EndTabItem(); - } - if (ImGui::BeginTabItem("Broccoli")) - { - ImGui::Text("This is the Broccoli tab!\nblah blah blah blah blah"); - ImGui::EndTabItem(); - } - if (ImGui::BeginTabItem("Cucumber")) - { - ImGui::Text("This is the Cucumber tab!\nblah blah blah blah blah"); - ImGui::EndTabItem(); + if (ImGui::BeginTabItem("Avocado")) + { + ImGui::Text("This is the Avocado tab!\nblah blah blah blah blah"); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("Broccoli")) + { + ImGui::Text("This is the Broccoli tab!\nblah blah blah blah blah"); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("Cucumber")) + { + ImGui::Text("This is the Cucumber tab!\nblah blah blah blah blah"); + ImGui::EndTabItem(); + } + ImGui::EndTabBar(); } - ImGui::EndTabBar(); ImGui::Separator(); ImGui::TreePop(); } @@ -1766,16 +1768,18 @@ void ImGui::ShowDemoWindow(bool* p_open) } // Passing a bool* to BeginTabItem() is similar to passing one to Begin(): the underlying bool will be set to false when the tab is closed. - ImGui::BeginTabBar("MyTabBar", tab_bar_flags); - for (int n = 0; n < IM_ARRAYSIZE(opened); n++) - if (opened[n] && ImGui::BeginTabItem(names[n], &opened[n])) - { - ImGui::Text("This is the %s tab!", names[n]); - if (n & 1) - ImGui::Text("I am an odd tab."); - ImGui::EndTabItem(); - } - ImGui::EndTabBar(); + if (ImGui::BeginTabBar("MyTabBar", tab_bar_flags)) + { + for (int n = 0; n < IM_ARRAYSIZE(opened); n++) + if (opened[n] && ImGui::BeginTabItem(names[n], &opened[n])) + { + ImGui::Text("This is the %s tab!", names[n]); + if (n & 1) + ImGui::Text("I am an odd tab."); + ImGui::EndTabItem(); + } + ImGui::EndTabBar(); + } ImGui::Separator(); ImGui::TreePop(); } @@ -3952,41 +3956,42 @@ void ShowExampleAppDocuments(bool* p_open) if (opt_target == Target_Tab) { ImGuiTabBarFlags tab_bar_flags = (opt_fitting_flags) | (opt_reorderable ? ImGuiTabBarFlags_Reorderable : 0); - ImGui::BeginTabBar("##tabs", tab_bar_flags); + if (ImGui::BeginTabBar("##tabs", tab_bar_flags)) + { + if (opt_reorderable) + NotifyOfDocumentsClosedElsewhere(app); - if (opt_reorderable) - NotifyOfDocumentsClosedElsewhere(app); + // [DEBUG] Stress tests + //if ((ImGui::GetFrameCount() % 30) == 0) docs[1].Open ^= 1; // [DEBUG] Automatically show/hide a tab. Test various interactions e.g. dragging with this on. + //if (ImGui::GetIO().KeyCtrl) ImGui::SetTabItemSelected(docs[1].Name); // [DEBUG] Test SetTabItemSelected(), probably not very useful as-is anyway.. - // [DEBUG] Stress tests - //if ((ImGui::GetFrameCount() % 30) == 0) docs[1].Open ^= 1; // [DEBUG] Automatically show/hide a tab. Test various interactions e.g. dragging with this on. - //if (ImGui::GetIO().KeyCtrl) ImGui::SetTabItemSelected(docs[1].Name); // [DEBUG] Test SetTabItemSelected(), probably not very useful as-is anyway.. + // Submit Tabs + for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++) + { + MyDocument* doc = &app.Documents[doc_n]; + if (!doc->Open) + continue; - // Submit Tabs - for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++) - { - MyDocument* doc = &app.Documents[doc_n]; - if (!doc->Open) - continue; + ImGuiTabItemFlags tab_flags = (doc->Dirty ? ImGuiTabItemFlags_UnsavedDocument : 0); + bool visible = ImGui::BeginTabItem(doc->Name, &doc->Open, tab_flags); - ImGuiTabItemFlags tab_flags = (doc->Dirty ? ImGuiTabItemFlags_UnsavedDocument : 0); - bool visible = ImGui::BeginTabItem(doc->Name, &doc->Open, tab_flags); + // Cancel attempt to close when unsaved add to save queue so we can display a popup. + if (!doc->Open && doc->Dirty) + { + doc->Open = true; + doc->DoQueueClose(); + } - // Cancel attempt to close when unsaved add to save queue so we can display a popup. - if (!doc->Open && doc->Dirty) - { - doc->Open = true; - doc->DoQueueClose(); + MyDocument::DisplayContextMenu(doc); + if (visible) + { + MyDocument::DisplayContents(doc); + ImGui::EndTabItem(); + } } - MyDocument::DisplayContextMenu(doc); - if (visible) - { - MyDocument::DisplayContents(doc); - ImGui::EndTabItem(); - } + ImGui::EndTabBar(); } - - ImGui::EndTabBar(); } else if (opt_target == Target_Window) { From 69db792bf0d28ab066d7dfe0053a82a9b9bc9728 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 15 Oct 2018 17:38:36 +0200 Subject: [PATCH 313/828] Viewport: Exposed GetOverlayDrawList(ImGuiViewport* viewport) in public API. (#1542, #1660, #1878, etc.) --- imgui.cpp | 3 ++- imgui.h | 3 ++- imgui_internal.h | 1 - 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 1577fc0aff72..38a0bd41c2aa 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2919,10 +2919,11 @@ int ImGui::GetFrameCount() return GImGui->FrameCount; } -ImDrawList* ImGui::GetOverlayDrawList(ImGuiViewportP* viewport) +ImDrawList* ImGui::GetOverlayDrawList(ImGuiViewport* viewport_public) { // Create the draw list on demand, because it is not frequently used for all viewports ImGuiContext& g = *GImGui; + ImGuiViewportP* viewport = (ImGuiViewportP*)viewport_public; if (viewport->OverlayDrawList == NULL) { viewport->OverlayDrawList = IM_NEW(ImDrawList)(&g.DrawListSharedData); diff --git a/imgui.h b/imgui.h index b6e87cd65871..23b5d98dd7c2 100644 --- a/imgui.h +++ b/imgui.h @@ -558,7 +558,8 @@ namespace ImGui IMGUI_API bool IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max); // test if rectangle (in screen space) is visible / not clipped. to perform coarse clipping on user's side. IMGUI_API double GetTime(); IMGUI_API int GetFrameCount(); - IMGUI_API ImDrawList* GetOverlayDrawList(); // this draw list will be the last rendered. it covers the entire current viewport. useful to quickly draw overlays shapes/text + IMGUI_API ImDrawList* GetOverlayDrawList(); // get overlay draw list for the viewport associated to the current window. this draw list will be the last rendered. useful to quickly draw overlays shapes/text. + IMGUI_API ImDrawList* GetOverlayDrawList(ImGuiViewport* viewport); // get overlay draw list for the given viewport. IMGUI_API ImDrawListSharedData* GetDrawListSharedData(); // you may use this when creating your own ImDrawList instances IMGUI_API const char* GetStyleColorName(ImGuiCol idx); IMGUI_API void SetStateStorage(ImGuiStorage* storage); // replace current window storage with our own (if you want to manipulate it yourself, typically clear subsection of it) diff --git a/imgui_internal.h b/imgui_internal.h index 90c33a0a9043..ebf36baf159f 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1189,7 +1189,6 @@ namespace ImGui IMGUI_API void SetCurrentFont(ImFont* font); inline ImFont* GetDefaultFont() { ImGuiContext& g = *GImGui; return g.IO.FontDefault ? g.IO.FontDefault : g.IO.Fonts->Fonts[0]; } - IMGUI_API ImDrawList* GetOverlayDrawList(ImGuiViewportP* viewport); inline ImDrawList* GetOverlayDrawList(ImGuiWindow* window) { return GetOverlayDrawList(window->Viewport); } // Init From 5f79a28a0789d094bbfce07c986397210d8f865e Mon Sep 17 00:00:00 2001 From: Brandon <35241930+eclbtownsend@users.noreply.github.com> Date: Thu, 18 Oct 2018 01:48:18 -0700 Subject: [PATCH 314/828] Examples: SDL: Fixed usage of SDL_GetDisplayDPI (#2137, #1676) --- examples/imgui_impl_sdl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/imgui_impl_sdl.cpp b/examples/imgui_impl_sdl.cpp index f1da247625cc..2917a421e952 100644 --- a/examples/imgui_impl_sdl.cpp +++ b/examples/imgui_impl_sdl.cpp @@ -523,7 +523,7 @@ static void ImGui_ImplSDL2_UpdateMonitors() #endif #if SDL_HAS_PER_MONITOR_DPI float dpi = 0.0f; - if (SDL_GetDisplayDPI(n, &dpi, NULL, NULL)) + if (!SDL_GetDisplayDPI(n, &dpi, NULL, NULL)) monitor.DpiScale = dpi / 96.0f; #endif platform_io.Monitors.push_back(monitor); From 745fda081e32ed1c77ada78325db335d155a852e Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 23 Oct 2018 18:26:35 +0200 Subject: [PATCH 315/828] Viewport, Docking: Fixed incorrectly setting the ImGuiViewportFlags_NoInputs flag, affecting split docking node. (#2109) --- imgui.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 33fab7490efd..be15055199bd 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5213,7 +5213,9 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) { // Late create viewport, based on the assumption that with our calculations, the DPI will be known ahead (same as the DPI of the selection done in UpdateSelectWindowViewport) //ImGuiViewport* old_viewport = window->Viewport; - ImGuiViewportFlags viewport_flags = ImGuiViewportFlags_NoFocusOnAppearing | ((window->Flags & ImGuiWindowFlags_NoInputs) ? ImGuiViewportFlags_NoInputs : 0); + ImGuiViewportFlags viewport_flags = ImGuiViewportFlags_NoFocusOnAppearing; + if ((window->Flags & ImGuiWindowFlags_NoMouseInputs) && (window->Flags & ImGuiWindowFlags_NoNavInputs)) + viewport_flags |= ImGuiViewportFlags_NoInputs; window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, viewport_flags); // FIXME-DPI From f270c81c49cae4f2c69f3cce77be9b7be4c42924 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 23 Oct 2018 18:26:35 +0200 Subject: [PATCH 316/828] Viewport, Docking: Fixed incorrectly setting the ImGuiViewportFlags_NoInputs flag, affecting split docking node. (#2109) --- imgui.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 79a3851a96e8..25764a187b6c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5066,7 +5066,9 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) { // Late create viewport, based on the assumption that with our calculations, the DPI will be known ahead (same as the DPI of the selection done in UpdateSelectWindowViewport) //ImGuiViewport* old_viewport = window->Viewport; - ImGuiViewportFlags viewport_flags = ImGuiViewportFlags_NoFocusOnAppearing | ((window->Flags & ImGuiWindowFlags_NoInputs) ? ImGuiViewportFlags_NoInputs : 0); + ImGuiViewportFlags viewport_flags = ImGuiViewportFlags_NoFocusOnAppearing; + if ((window->Flags & ImGuiWindowFlags_NoMouseInputs) && (window->Flags & ImGuiWindowFlags_NoNavInputs)) + viewport_flags |= ImGuiViewportFlags_NoInputs; window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, viewport_flags); // FIXME-DPI From 56ba60ee68417c44b3fc3835868c80acb5604e2f Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 24 Oct 2018 12:25:58 +0200 Subject: [PATCH 317/828] Docking: Fixed not saving .ini file correct if DockingEnable flag is cleared. (#2109) --- imgui.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index be15055199bd..f0c38d6b9fb2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -12359,9 +12359,13 @@ static void DockSettingsHandler_DockNodeToSettings(ImGuiDockContext* dc, ImGuiDo static void ImGui::DockSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf) { + ImGuiContext& g = *ctx; + ImGuiDockContext* dc = g.DockContext; + if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable)) + return; + // Gather settings data // (unlike our windows settings, because nodes are always built we can do a full rewrite of the SettingsNode buffer) - ImGuiDockContext* dc = ctx->DockContext; dc->SettingsNodes.resize(0); dc->SettingsNodes.reserve(dc->Nodes.Data.Size); for (int n = 0; n < dc->Nodes.Data.Size; n++) @@ -12774,8 +12778,6 @@ static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSetting // Gather data from windows that were active during this session // (if a window wasn't opened in this session we preserve its settings) ImGuiContext& g = *imgui_ctx; - if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable)) - return; for (int i = 0; i != g.Windows.Size; i++) { ImGuiWindow* window = g.Windows[i]; From c547b2e8ee7d236a143acfe8b25bdc34a99e3ebe Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 25 Oct 2018 15:34:53 +0200 Subject: [PATCH 318/828] Added DockSpaceOverViewport() call, not sure about this because of the menu bar limitation. (#2109) --- imgui.cpp | 35 +++++++++++++++++++++++++++++++++++ imgui.h | 1 + imgui_demo.cpp | 2 +- 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index f0c38d6b9fb2..5bb9c74fe000 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11711,6 +11711,41 @@ void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags doc End(); } +// Tips: Use with ImGuiDockNodeFlags_PassthruDockspace! +// The limitation with this call is that your window won't have a menu bar. +// Even though we could pass window flags, it would also require the user to be able to call BeginMenuBar() somehow meaning we can't Begin/End in a single function. +// So if you want a menu bar you need to repeat this code manually ourselves. As with advanced other Docking API, we may change this function signature. +ImGuiID ImGui::DockSpaceOverViewport(ImGuiViewport* viewport, ImGuiDockNodeFlags dockspace_flags, const ImGuiDockFamily* dock_family) +{ + if (viewport == NULL) + viewport = GetMainViewport(); + + SetNextWindowPos(viewport->Pos); + SetNextWindowSize(viewport->Size); + SetNextWindowViewport(viewport->ID); + + ImGuiWindowFlags host_window_flags = 0; + host_window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDocking; + host_window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus; + if (dockspace_flags & ImGuiDockNodeFlags_PassthruDockspace) + host_window_flags |= ImGuiWindowFlags_NoBackground; + + char label[32]; + ImFormatString(label, IM_ARRAYSIZE(label), "DockspaceViewport_%08X", viewport->ID); + + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); + ImGui::Begin(label, NULL, host_window_flags); + ImGui::PopStyleVar(3); + + ImGuiID dockspace_id = ImGui::GetID("Dockspace"); + ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags, dock_family); + ImGui::End(); + + return dockspace_id; +} + //----------------------------------------------------------------------------- // Docking: Builder Functions //----------------------------------------------------------------------------- diff --git a/imgui.h b/imgui.h index 5ef4b41eae9e..10f3f1e2685e 100644 --- a/imgui.h +++ b/imgui.h @@ -528,6 +528,7 @@ namespace ImGui // To dock windows: hold SHIFT anywhere while moving windows (if io.ConfigDockingWithShift == true) or drag windows from their title bar (if io.ConfigDockingWithShift = false) // Use DockSpace() to create an explicit dock node _within_ an existing window. See Docking demo for details. IMGUI_API void DockSpace(ImGuiID id, const ImVec2& size = ImVec2(0, 0), ImGuiDockNodeFlags flags = 0, const ImGuiDockFamily* dock_family = NULL); + IMGUI_API ImGuiID DockSpaceOverViewport(ImGuiViewport* viewport = NULL, ImGuiDockNodeFlags dockspace_flags = 0, const ImGuiDockFamily* dock_family = NULL); IMGUI_API void SetNextWindowDockId(ImGuiID dock_id, ImGuiCond cond = 0); // set next window dock id (FIXME-DOCK) IMGUI_API void SetNextWindowDockFamily(const ImGuiDockFamily* dock_family); // set next window user type (docking filters by same user_type) IMGUI_API ImGuiID GetWindowDockId(); diff --git a/imgui_demo.cpp b/imgui_demo.cpp index ac3016833888..9bc3dc89f939 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3768,7 +3768,7 @@ void ShowExampleAppDockSpace(bool* p_open) // When using ImGuiDockNodeFlags_PassthruDockspace, DockSpace() will render our background and handle the pass-thru hole, so we ask Begin() to not render a background. if (opt_flags & ImGuiDockNodeFlags_PassthruDockspace) - ImGui::SetNextWindowBgAlpha(0.0f); + window_flags |= ImGuiWindowFlags_NoBackground; ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); ImGui::Begin("DockSpace Demo", p_open, window_flags); From 1a6eea012d0f306ba182d4a41277977e0ae2f713 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 25 Oct 2018 17:32:11 +0200 Subject: [PATCH 319/828] Docking: Tweaked tooltip on tabs so they don't show immediately when holding and releasing a tab (using HoveredIdNotActiveTimer) + Minor refactor + Removed legacy .ini parsing. --- imgui.cpp | 40 ++++++++++++++++++++-------------------- imgui_widgets.cpp | 6 +++--- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 5bb9c74fe000..1b6b12bfb724 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10755,7 +10755,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) host_window->DrawList->ChannelsSetCurrent(1); } - // Register a hit-test hole in the window unless we are currently dragging a window that is compatible our dockspace + // Register a hit-test hole in the window unless we are currently dragging a window that is compatible with our dockspace bool central_node_hole = node->IsRootNode() && host_window && (node->Flags & ImGuiDockNodeFlags_PassthruDockspace) != 0 && central_node != NULL && central_node->IsEmpty(); bool central_node_hole_register_hit_test_hole = central_node_hole; if (central_node_hole) @@ -11011,11 +11011,14 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w focus_tab_id = tab_bar->WantFocusTabId; // When clicking on the title bar outside of tabs, we still focus the selected tab for that node - if (g.HoveredWindow == host_window && g.HoveredId == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && IsMouseClicked(0)) + if (g.HoveredWindow == host_window && g.HoveredId == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max)) { - focus_tab_id = tab_bar->SelectedTabId; - if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, focus_tab_id)) - StartMouseMovingWindow(tab->Window); + if (IsMouseClicked(0)) + { + focus_tab_id = tab_bar->SelectedTabId; + if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, focus_tab_id)) + StartMouseMovingWindow(tab->Window); + } } // Forward focus from host node to selected window @@ -11224,8 +11227,8 @@ static bool ImGui::DockNodePreviewDockCalc(ImGuiWindow* host_window, ImGuiDockNo { ImGuiDir split_dir = data->SplitDir; ImGuiAxis split_axis = (split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y; - ImVec2 pos_old = data->FutureNode.Pos, pos_new; - ImVec2 size_old = data->FutureNode.Size, size_new; + ImVec2 pos_new, pos_old = data->FutureNode.Pos; + ImVec2 size_new, size_old = data->FutureNode.Size; DockNodeCalcSplitRects(pos_old, size_old, pos_new, size_new, split_dir, root_payload->Size); // Calculate split ratio so we can pass it down the docking request @@ -11510,8 +11513,6 @@ void ImGui::DockNodeTreeUpdateSplitter(ImGuiDockNode* node) bb.Max[axis ^ 1] += child_1->Size[axis ^ 1]; //if (g.IO.KeyCtrl) GetOverlayDrawList(g.CurrentWindow->Viewport)->AddRect(bb.Min, bb.Max, IM_COL32(255,0,255,255)); - float w1 = child_0->Size[axis]; - float w2 = child_1->Size[axis]; //bb.Min[axis] += 1; // Display a little inward so highlight doesn't connect with nearby tabs on the neighbor node. //bb.Max[axis] -= 1; PushID(node->ID); @@ -11546,15 +11547,17 @@ void ImGui::DockNodeTreeUpdateSplitter(ImGuiDockNode* node) } // Use a short delay before highlighting the splitter (and changing the mouse cursor) in order for regular mouse movement to not highlight many splitters + float cur_size_0 = child_0->Size[axis]; + float cur_size_1 = child_1->Size[axis]; float min_size_0 = resize_limits[0] - child_0->Pos[axis]; float min_size_1 = child_1->Pos[axis] + child_1->Size[axis] - resize_limits[1]; - if (SplitterBehavior(bb, GetID("##Splitter"), axis, &w1, &w2, min_size_0, min_size_1, RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS, RESIZE_WINDOWS_FROM_EDGES_FEEDBACK_TIMER)) + if (SplitterBehavior(bb, GetID("##Splitter"), axis, &cur_size_0, &cur_size_1, min_size_0, min_size_1, RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS, RESIZE_WINDOWS_FROM_EDGES_FEEDBACK_TIMER)) { if (touching_nodes[0].Size > 0 && touching_nodes[1].Size > 0) { - child_0->Size[axis] = child_0->SizeRef[axis] = w1; - child_1->Pos[axis] -= w2 - child_1->Size[axis]; - child_1->Size[axis] = child_1->SizeRef[axis] = w2; + child_0->Size[axis] = child_0->SizeRef[axis] = cur_size_0; + child_1->Pos[axis] -= cur_size_1 - child_1->Size[axis]; + child_1->Size[axis] = child_1->SizeRef[axis] = cur_size_1; // Lock the size of every node that is a sibling of the node we are touching // This might be less desirable if we can merge sibling of a same axis into the same parental level. @@ -11617,7 +11620,7 @@ ImGuiDockNode* ImGui::DockNodeTreeFindNodeByPos(ImGuiDockNode* node, ImVec2 pos) } //----------------------------------------------------------------------------- -// Docking: Public Functions (SetWindowDock, DockSpace) +// Docking: Public Functions (SetWindowDock, DockSpace, DockSpaceOverViewport) //----------------------------------------------------------------------------- void ImGui::SetWindowDock(ImGuiWindow* window, ImGuiID dock_id, ImGuiCond cond) @@ -12170,6 +12173,7 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) if (dock_node->Flags & ImGuiDockNodeFlags_KeepAliveOnly) return; + // When the window is selected we mark it as visible. if (dock_node->TabBar && dock_node->TabBar->VisibleTabId == window->ID) window->DockTabIsVisible = true; @@ -12359,9 +12363,6 @@ static void ImGui::DockSettingsHandler_ReadLine(ImGuiContext* ctx, ImGuiSettings if (sscanf(line, " SizeRef=%i,%i%n", &x, &y, &r) == 2) { line += r; node.SizeRef = ImVec2ih((short)x, (short)y); } } if (sscanf(line, " Split=%c%n", &c, &r) == 1) { line += r; if (c == 'X') node.SplitAxis = ImGuiAxis_X; else if (c == 'Y') node.SplitAxis = ImGuiAxis_Y; } -#if 1 // FIXME-DOCK FIXME-LEGACY - if (sscanf(line, " DocRoot=%d%n", &x, &r) == 1) { line += r; node.IsCentralNode = (x != 0); } -#endif if (sscanf(line, " CentralNode=%d%n", &x, &r) == 1) { line += r; node.IsCentralNode = (x != 0); } if (sscanf(line, " SelectedTab=0x%08X%n", &node.SelectedTabID,&r) == 1) { line += r; } ImGuiDockContext* dc = ctx->DockContext; @@ -13286,9 +13287,8 @@ void ImGui::ShowDockingDebug() { IM_ASSERT(node->ChildNodes[0] == NULL || node->ChildNodes[0]->ParentNode == node); IM_ASSERT(node->ChildNodes[1] == NULL || node->ChildNodes[1]->ParentNode == node); - ImGui::BulletText("Pos (%.0f,%.0f), Size (%.0f, %.0f), LastExplicit (%.0f, %.0f)", - node->Pos.x, node->Pos.y, node->Size.x, node->Size.y, - node->SizeRef.x, node->SizeRef.y); + ImGui::BulletText("Pos (%.0f,%.0f), Size (%.0f, %.0f) Ref (%.0f, %.0f)", + node->Pos.x, node->Pos.y, node->Size.x, node->Size.y, node->SizeRef.x, node->SizeRef.y); ImGui::BulletText("Flags %02X%s%s%s%s", node->Flags, node->IsDockSpace ? ", IsDockSpace" : "", node->IsCentralNode ? ", IsCentralNode" : "", (GImGui->FrameCount - node->LastFrameAlive < 2) ? ", IsAlive" : "", (GImGui->FrameCount - node->LastFrameActive < 2) ? ", IsActive" : ""); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 122fdda15e35..725c7916ad25 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6489,10 +6489,10 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, } #if 0 - if (hovered && g.HoveredIdTimer > 0.40f && bb.GetWidth() < tab->WidthContents) + if (hovered && g.HoveredIdNotActiveTimer > 0.50f && bb.GetWidth() < tab->WidthContents) { // Enlarge tab display when hovering - bb.Max.x = bb.Min.x + (float)(int)ImLerp(bb.GetWidth(), tab->WidthContents, ImSaturate((g.HoveredIdTimer - 0.40f) * 6.0f)); + bb.Max.x = bb.Min.x + (float)(int)ImLerp(bb.GetWidth(), tab->WidthContents, ImSaturate((g.HoveredIdNotActiveTimer - 0.40f) * 6.0f)); display_draw_list = GetOverlayDrawList(window); TabItemRenderBackground(display_draw_list, bb, flags, GetColorU32(ImGuiCol_TitleBgActive)); } @@ -6527,7 +6527,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, window->DC.CursorPos = backup_main_cursor_pos; // Tooltip (FIXME: Won't work over the close button because ItemOverlap systems messes up with HoveredIdTimer) - if (g.HoveredId == id && !held && g.HoveredIdTimer > 0.50f) + if (g.HoveredId == id && !held && g.HoveredIdNotActiveTimer > 0.50f) SetTooltip("%.*s", (int)(FindRenderedTextEnd(label) - label), label); return tab_contents_visible; From 9ee86f22b71069a405800141115f775bcfb2767f Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 5 Nov 2018 17:37:06 +0100 Subject: [PATCH 320/828] Examples: Win32: Handle UTf-8 in platform window title setting. (#2164) --- examples/imgui_impl_win32.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index ea5a25d57ad4..fb7537946957 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -538,9 +538,14 @@ static bool ImGui_ImplWin32_GetWindowFocus(ImGuiViewport* viewport) static void ImGui_ImplWin32_SetWindowTitle(ImGuiViewport* viewport, const char* title) { + // ::SetWindowTextA() doesn't properly handle UTF-8 so we explicitely convert our string. ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData; IM_ASSERT(data->Hwnd != 0); - ::SetWindowTextA(data->Hwnd, title); + int n = ::MultiByteToWideChar(CP_UTF8, 0, title, -1, NULL, 0); + ImVector title_w; + title_w.resize(n); + ::MultiByteToWideChar(CP_UTF8, 0, title, -1, title_w.Data, n); + ::SetWindowTextW(data->Hwnd, title_w.Data); } static void ImGui_ImplWin32_SetWindowAlpha(ImGuiViewport* viewport, float alpha) From 5ea08394f5d85fbdbcf82ad4cdb13c92fe9f858d Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 6 Nov 2018 09:46:43 +0100 Subject: [PATCH 321/828] Examples: SDL: Tweaked Windows instructions and batch files. (#2175) --- examples/example_sdl_opengl2/README.md | 7 +++++-- examples/example_sdl_opengl2/build_win32.bat | 9 +++++++-- examples/example_sdl_opengl3/README.md | 7 +++++-- examples/example_sdl_opengl3/build_win32.bat | 9 +++++++-- 4 files changed, 24 insertions(+), 8 deletions(-) diff --git a/examples/example_sdl_opengl2/README.md b/examples/example_sdl_opengl2/README.md index dfbfa7b6e4ce..60a5e377fddc 100644 --- a/examples/example_sdl_opengl2/README.md +++ b/examples/example_sdl_opengl2/README.md @@ -4,8 +4,11 @@ - On Windows with Visual Studio's CLI ``` -set SDL2DIR=path_to_your_sdl2_folder -cl /Zi /MD /I %SDL2DIR%\include /I .. /I ..\.. main.cpp ..\imgui_impl_sdl.cpp ..\imgui_impl_opengl2.cpp ..\..\imgui*.cpp /link /LIBPATH:%SDL2DIR%\lib SDL2.lib SDL2main.lib opengl32.lib /subsystem:console +set SDL2_DIR=path_to_your_sdl2_folder +cl /Zi /MD /I.. /I..\.. /I%SDL2_DIR%\include main.cpp ..\imgui_impl_sdl.cpp ..\imgui_impl_opengl2.cpp ..\..\imgui*.cpp /FeDebug/example_sdl_opengl2.exe /FoDebug/ /link /libpath:%SDL2_DIR%\lib\x86 SDL2.lib SDL2main.lib opengl32.lib /subsystem:console +# ^^ include paths ^^ source files ^^ output exe ^^ output dir ^^ libraries +# or for 64-bit: +cl /Zi /MD /I.. /I..\.. /I%SDL2_DIR%\include main.cpp ..\imgui_impl_sdl.cpp ..\imgui_impl_opengl2.cpp ..\..\imgui*.cpp /FeDebug/example_sdl_opengl2.exe /FoDebug/ /link /libpath:%SDL2_DIR%\lib\x64 SDL2.lib SDL2main.lib opengl32.lib /subsystem:console ``` - On Linux and similar Unixes diff --git a/examples/example_sdl_opengl2/build_win32.bat b/examples/example_sdl_opengl2/build_win32.bat index bc2eb3b2d6fc..97692d9e483c 100644 --- a/examples/example_sdl_opengl2/build_win32.bat +++ b/examples/example_sdl_opengl2/build_win32.bat @@ -1,3 +1,8 @@ @REM Build for Visual Studio compiler. Run your copy of vcvars32.bat or vcvarsall.bat to setup command-line compiler. -mkdir Debug -cl /nologo /Zi /MD /I .. /I ..\.. /I ..\libs\gl3w /I %SDL2_DIR%\include *.cpp ..\imgui_impl_opengl2.cpp ..\imgui_impl_sdl.cpp ..\..\imgui*.cpp ..\libs\gl3w\GL\gl3w.c /FeDebug/example_sdl_opengl2.exe /FoDebug/ /link /libpath:%SDL2_DIR%\lib\x86 SDL2.lib SDL2main.lib opengl32.lib /subsystem:console +set OUT_DIR=Debug +set OUT_EXE=example_sdl_opengl2.exe +set INCLUDES=/I.. /I..\.. /I%SDL2_DIR%\include +set SOURCES=main.cpp ..\imgui_impl_sdl.cpp ..\imgui_impl_opengl2.cpp ..\..\imgui*.cpp +set LIBS=/libpath:%SDL2_DIR%\lib\x86 SDL2.lib SDL2main.lib opengl32.lib +mkdir %OUT_DIR% +cl /nologo /Zi /MD %INCLUDES% %SOURCES% /Fe%OUT_DIR%/%OUT_DIR%.exe /Fo%OUT_DIR%/ /link %LIBS% /subsystem:console diff --git a/examples/example_sdl_opengl3/README.md b/examples/example_sdl_opengl3/README.md index f029ce71af70..ec21fb78aac0 100644 --- a/examples/example_sdl_opengl3/README.md +++ b/examples/example_sdl_opengl3/README.md @@ -4,8 +4,11 @@ - On Windows with Visual Studio's CLI ``` -set SDL2DIR=path_to_your_sdl2_folder -cl /Zi /MD /I .. /I ..\.. /I ..\libs\gl3w /I %SDL2DIR%\include main.cpp ..\imgui_impl_sdl.cpp ..\imgui_impl_opengl3.cpp ..\..\imgui*.cpp ..\libs\gl3w\GL\gl3w.c /link /libpath:%SDL2DIR%\lib\x86 SDL2.lib SDL2main.lib opengl32.lib /subsystem:console +set SDL2_DIR=path_to_your_sdl2_folder +cl /Zi /MD /I.. /I..\.. /I%SDL2_DIR%\include /I..\libs\gl3w main.cpp ..\imgui_impl_sdl.cpp ..\imgui_impl_opengl3.cpp ..\..\imgui*.cpp ..\libs\gl3w\GL\gl3w.c /FeDebug/example_sdl_opengl3.exe /FoDebug/ /link /libpath:%SDL2_DIR%\lib\x86 SDL2.lib SDL2main.lib opengl32.lib /subsystem:console +# ^^ include paths ^^ source files ^^ output exe ^^ output dir ^^ libraries +# or for 64-bit: +cl /Zi /MD /I.. /I..\.. /I%SDL2_DIR%\include /I..\libs\gl3w main.cpp ..\imgui_impl_sdl.cpp ..\imgui_impl_opengl3.cpp ..\..\imgui*.cpp ..\libs\gl3w\GL\gl3w.c /FeDebug/example_sdl_opengl3.exe /FoDebug/ /link /libpath:%SDL2_DIR%\lib\x64 SDL2.lib SDL2main.lib opengl32.lib /subsystem:console ``` - On Linux and similar Unixes diff --git a/examples/example_sdl_opengl3/build_win32.bat b/examples/example_sdl_opengl3/build_win32.bat index d2cfa67fa80d..f263c4b3431c 100644 --- a/examples/example_sdl_opengl3/build_win32.bat +++ b/examples/example_sdl_opengl3/build_win32.bat @@ -1,3 +1,8 @@ @REM Build for Visual Studio compiler. Run your copy of vcvars32.bat or vcvarsall.bat to setup command-line compiler. -mkdir Debug -cl /nologo /Zi /MD /I .. /I ..\.. /I ..\libs\gl3w /I %SDL2_DIR%\include *.cpp ..\imgui_impl_opengl3.cpp ..\imgui_impl_sdl.cpp ..\..\imgui*.cpp ..\libs\gl3w\GL\gl3w.c /FeDebug/example_sdl_opengl3.exe /FoDebug/ /link /libpath:%SDL2_DIR%\lib\x86 SDL2.lib SDL2main.lib opengl32.lib /subsystem:console +set OUT_DIR=Debug +set OUT_EXE=example_sdl_opengl3.exe +set INCLUDES=/I.. /I..\.. /I%SDL2_DIR%\include /I..\libs\gl3w +set SOURCES=main.cpp ..\imgui_impl_sdl.cpp ..\imgui_impl_opengl3.cpp ..\..\imgui*.cpp ..\libs\gl3w\GL\gl3w.c +set LIBS=/libpath:%SDL2_DIR%\lib\x86 SDL2.lib SDL2main.lib opengl32.lib +mkdir %OUT_DIR% +cl /nologo /Zi /MD %INCLUDES% %SOURCES% /Fe%OUT_DIR%/%OUT_EXE%.exe /Fo%OUT_DIR%/ /link %LIBS% /subsystem:console From 797f2044cdf0978321842ae8b3d1250f79cfbc39 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 6 Nov 2018 09:51:09 +0100 Subject: [PATCH 322/828] Viewport: Fixed SDL+OpenGL2 example to work with multi-viewports. (#2175) --- examples/example_sdl_opengl2/main.cpp | 9 +++++++++ imgui.cpp | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/examples/example_sdl_opengl2/main.cpp b/examples/example_sdl_opengl2/main.cpp index d6c2a1eb59cc..9acb3e07f207 100644 --- a/examples/example_sdl_opengl2/main.cpp +++ b/examples/example_sdl_opengl2/main.cpp @@ -135,6 +135,15 @@ int main(int, char**) glClear(GL_COLOR_BUFFER_BIT); //glUseProgram(0); // You may want this if using this code in an OpenGL 3+ context where shaders may be bound ImGui_ImplOpenGL2_RenderDrawData(ImGui::GetDrawData()); + + // Update and Render additional Platform Windows + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); + } + + SDL_GL_MakeCurrent(window, gl_context); SDL_GL_SwapWindow(window); } diff --git a/imgui.cpp b/imgui.cpp index 1ae0d0519855..80c8baad4e2e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3247,7 +3247,7 @@ void ImGui::NewFrame() { if ((g.IO.BackendFlags & ImGuiBackendFlags_PlatformHasViewports) && (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasViewports)) { - IM_ASSERT((g.FrameCount == 0 || g.FrameCount == g.FrameCountPlatformEnded) && "Forgot to call UpdatePlatformWindows() in main loop after EndFrame()?"); + IM_ASSERT((g.FrameCount == 0 || g.FrameCount == g.FrameCountPlatformEnded) && "Forgot to call UpdatePlatformWindows() in main loop after EndFrame()? Check examples/ applications for reference."); IM_ASSERT(g.PlatformIO.Platform_CreateWindow != NULL && "Platform init didn't install handlers?"); IM_ASSERT(g.PlatformIO.Platform_DestroyWindow != NULL && "Platform init didn't install handlers?"); IM_ASSERT(g.PlatformIO.Platform_GetWindowPos != NULL && "Platform init didn't install handlers?"); From 1546153ca25704bebf4dab13d399f60f6707f430 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 13 Nov 2018 19:31:10 +0100 Subject: [PATCH 323/828] Docking: Fixed a regression where clicking a Tab wouldn't immediately set NavWindow. Which led to breaking the NavSaveLastChildNavWindow/NavRestoreLastChildNavWindow logic. (#2109) --- imgui.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index fb8500afc47c..4c30095d5cba 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11018,10 +11018,6 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w } } - // When clicked on a tab we requested focus to the docked child - if (tab_bar->WantFocusTabId) - focus_tab_id = tab_bar->WantFocusTabId; - // When clicking on the title bar outside of tabs, we still focus the selected tab for that node if (g.HoveredWindow == host_window && g.HoveredId == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max)) { @@ -11037,6 +11033,11 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w if (is_focused && g.NavWindow == host_window && !g.NavWindowingTarget) focus_tab_id = tab_bar->SelectedTabId; + // When clicked on a tab we requested focus to the docked child + // This overrides the value set by "forward focus from host node to selected window". + if (tab_bar->WantFocusTabId) + focus_tab_id = tab_bar->WantFocusTabId; + // Apply navigation focus if (focus_tab_id != 0) if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, focus_tab_id)) From ae34241f8b782204c9209914a2eced8087199a30 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 13 Nov 2018 22:00:12 +0100 Subject: [PATCH 324/828] Viewport: BeginMainMenuBar(): explicitly set viewport to avoid creating new one when ImGuiConfigFlags_ViewportsNoMerge is set + misc shallow changes. --- imgui.cpp | 9 +++------ imgui_widgets.cpp | 6 ++++-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 4c30095d5cba..60da1d7b855e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3027,7 +3027,8 @@ void ImGui::UpdateMouseMovingWindow() { // Try to merge the window back into the main viewport. // This works because MouseViewport should be != MovingWindow->Viewport on release (as per code in UpdateViewports) - UpdateTryMergeWindowIntoHostViewport(moving_window, g.MouseViewport); + if (g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + UpdateTryMergeWindowIntoHostViewport(moving_window, g.MouseViewport); // Restore the mouse viewport so that we don't hover the viewport _under_ the moved window during the frame we released the mouse button. if (!IsDragDropPayloadBeingAccepted()) @@ -5220,7 +5221,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->Pos = FindBestWindowPosForPopup(window); if (window->ViewportAllowPlatformMonitorExtend >= 0 && !window->ViewportOwned) - { if (!window->Viewport->GetRect().Contains(window->Rect())) { // Late create viewport, based on the assumption that with our calculations, the DPI will be known ahead (same as the DPI of the selection done in UpdateSelectWindowViewport) @@ -5236,7 +5236,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->FontDpiScale = (g.IO.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleFonts) ? window->Viewport->DpiScale : 1.0f; SetCurrentWindow(window); } - } // Synchronize viewport --> window in case the platform window has been moved or resized from the OS/WM if (window->ViewportOwned) @@ -7423,8 +7422,6 @@ static bool ImGui::GetWindowAlwaysWantOwnViewport(ImGuiWindow* window) static bool ImGui::UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* viewport) { ImGuiContext& g = *GImGui; - if (!(g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) - return false; if (!(viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) || window->Viewport == viewport) return false; if (!viewport->GetRect().Contains(window->Rect())) @@ -7740,10 +7737,10 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) // Mark window as allowed to protrude outside of its viewport and into the current monitor // We need to take account of the possibility that mouse may become invalid. - const bool use_mouse_ref = (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow); if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) { ImVec2 mouse_ref = (flags & ImGuiWindowFlags_Tooltip) ? g.IO.MousePos : g.CurrentPopupStack.back().OpenMousePos; + bool use_mouse_ref = (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow); bool mouse_valid = IsMousePosValid(&mouse_ref); if ((window->Appearing || (flags & ImGuiWindowFlags_Tooltip)) && (!use_mouse_ref || mouse_valid)) window->ViewportAllowPlatformMonitorExtend = FindPlatformMonitorForPos((use_mouse_ref && mouse_valid) ? mouse_ref : NavCalcPreferredRefPos()); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 7be32725df4b..1c594eae5146 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -5464,9 +5464,11 @@ float ImGuiMenuColumns::CalcExtraSpace(float avail_w) bool ImGui::BeginMainMenuBar() { ImGuiContext& g = *GImGui; + ImGuiViewport* viewport = g.Viewports[0]; g.NextWindowData.MenuBarOffsetMinVal = ImVec2(g.Style.DisplaySafeAreaPadding.x, ImMax(g.Style.DisplaySafeAreaPadding.y - g.Style.FramePadding.y, 0.0f)); - SetNextWindowPos(g.Viewports[0]->Pos); - SetNextWindowSize(ImVec2(g.Viewports[0]->Size.x, g.NextWindowData.MenuBarOffsetMinVal.y + g.FontBaseSize + g.Style.FramePadding.y)); + SetNextWindowPos(viewport->Pos); + SetNextWindowSize(ImVec2(viewport->Size.x, g.NextWindowData.MenuBarOffsetMinVal.y + g.FontBaseSize + g.Style.FramePadding.y)); + SetNextWindowViewport(viewport->ID); // Enforce viewport so we don't create our onw viewport when ImGuiConfigFlags_ViewportsNoMerge is set. PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0,0)); ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_MenuBar; From f2d577c33f2f20869fc5d3f507e6b3109395829d Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 13 Nov 2018 22:00:12 +0100 Subject: [PATCH 325/828] Viewport: BeginMainMenuBar(): explicitly set viewport to avoid creating new one when ImGuiConfigFlags_ViewportsNoMerge is set + misc shallow changes. --- imgui.cpp | 9 +++------ imgui_widgets.cpp | 6 ++++-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 80c8baad4e2e..43b5291760b5 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3001,7 +3001,8 @@ void ImGui::UpdateMouseMovingWindow() { // Try to merge the window back into the main viewport. // This works because MouseViewport should be != MovingWindow->Viewport on release (as per code in UpdateViewports) - UpdateTryMergeWindowIntoHostViewport(moving_window, g.MouseViewport); + if (g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + UpdateTryMergeWindowIntoHostViewport(moving_window, g.MouseViewport); // Restore the mouse viewport so that we don't hover the viewport _under_ the moved window during the frame we released the mouse button. if (!IsDragDropPayloadBeingAccepted()) @@ -5073,7 +5074,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->Pos = FindBestWindowPosForPopup(window); if (window->ViewportAllowPlatformMonitorExtend >= 0 && !window->ViewportOwned) - { if (!window->Viewport->GetRect().Contains(window->Rect())) { // Late create viewport, based on the assumption that with our calculations, the DPI will be known ahead (same as the DPI of the selection done in UpdateSelectWindowViewport) @@ -5089,7 +5089,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->FontDpiScale = (g.IO.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleFonts) ? window->Viewport->DpiScale : 1.0f; SetCurrentWindow(window); } - } // Synchronize viewport --> window in case the platform window has been moved or resized from the OS/WM if (window->ViewportOwned) @@ -7197,8 +7196,6 @@ static bool ImGui::GetWindowAlwaysWantOwnViewport(ImGuiWindow* window) static void ImGui::UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* viewport) { ImGuiContext& g = *GImGui; - if (!(g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) - return; if (!(viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) || window->Viewport == viewport) return; if (!viewport->GetRect().Contains(window->Rect())) @@ -7510,10 +7507,10 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) // Mark window as allowed to protrude outside of its viewport and into the current monitor // We need to take account of the possibility that mouse may become invalid. - const bool use_mouse_ref = (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow); if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) { ImVec2 mouse_ref = (flags & ImGuiWindowFlags_Tooltip) ? g.IO.MousePos : g.CurrentPopupStack.back().OpenMousePos; + bool use_mouse_ref = (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow); bool mouse_valid = IsMousePosValid(&mouse_ref); if ((window->Appearing || (flags & ImGuiWindowFlags_Tooltip)) && (!use_mouse_ref || mouse_valid)) window->ViewportAllowPlatformMonitorExtend = FindPlatformMonitorForPos((use_mouse_ref && mouse_valid) ? mouse_ref : NavCalcPreferredRefPos()); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 75c15ea14d26..18e740203e85 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -5439,9 +5439,11 @@ float ImGuiMenuColumns::CalcExtraSpace(float avail_w) bool ImGui::BeginMainMenuBar() { ImGuiContext& g = *GImGui; + ImGuiViewport* viewport = g.Viewports[0]; g.NextWindowData.MenuBarOffsetMinVal = ImVec2(g.Style.DisplaySafeAreaPadding.x, ImMax(g.Style.DisplaySafeAreaPadding.y - g.Style.FramePadding.y, 0.0f)); - SetNextWindowPos(g.Viewports[0]->Pos); - SetNextWindowSize(ImVec2(g.Viewports[0]->Size.x, g.NextWindowData.MenuBarOffsetMinVal.y + g.FontBaseSize + g.Style.FramePadding.y)); + SetNextWindowPos(viewport->Pos); + SetNextWindowSize(ImVec2(viewport->Size.x, g.NextWindowData.MenuBarOffsetMinVal.y + g.FontBaseSize + g.Style.FramePadding.y)); + SetNextWindowViewport(viewport->ID); // Enforce viewport so we don't create our onw viewport when ImGuiConfigFlags_ViewportsNoMerge is set. PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0,0)); ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_MenuBar; From d9c54826bbf7170901e68010be3b8efaa6030605 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 22 Nov 2018 17:27:35 +0100 Subject: [PATCH 326/828] Viewport: Stop relying on viewport->CreatedPlatformWindow to create Platform_DestroyWindow and Renderer_DestroyWindow. Explicitly require Platform_GetWindowFocus() to require supporting empty data. --- imgui.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 767a7bbdb3cb..298bdcc65553 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3459,7 +3459,6 @@ void ImGui::Initialize(ImGuiContext* context) ImGuiViewportP* viewport = IM_NEW(ImGuiViewportP)(); viewport->ID = IMGUI_VIEWPORT_DEFAULT_ID; viewport->Idx = 0; - viewport->CreatedPlatformWindow = true; // Set this flag so DestroyPlatformWindows() gives a chance for backend to receive DestroyWindow calls for the main viewport. g.Viewports.push_back(viewport); g.PlatformIO.MainViewport = g.Viewports[0]; // Make it accessible in public-facing GetPlatformIO() immediately (before the first call to EndFrame) g.PlatformIO.Viewports.push_back(g.Viewports[0]); @@ -7645,11 +7644,12 @@ void ImGui::UpdatePlatformWindows() } // Update our implicit z-order knowledge of platform windows, which is used when the back-end cannot provide io.MouseHoveredViewport. + // When setting Platform_GetWindowFocus, it is expected that the platform back-end can handle calls without crashing if it doesn't have data stored. if (g.PlatformIO.Platform_GetWindowFocus != NULL) { ImGuiViewportP* focused_viewport = NULL; for (int i = 0; i < g.Viewports.Size && focused_viewport == NULL; i++) - if (g.Viewports[i]->PlatformUserData != NULL || g.Viewports[i]->PlatformHandle != NULL || g.Viewports[i]->CreatedPlatformWindow) + if (g.Viewports[i]->PlatformUserData != NULL || g.Viewports[i]->PlatformHandle != NULL) if (g.PlatformIO.Platform_GetWindowFocus(g.Viewports[i])) focused_viewport = g.Viewports[i]; if (focused_viewport && g.PlatformLastFocusedViewport != focused_viewport->ID) @@ -7732,9 +7732,9 @@ void ImGui::RenderPlatformWindowsDefault(void* platform_render_arg, void* render void ImGui::DestroyPlatformWindow(ImGuiViewportP* viewport) { ImGuiContext& g = *GImGui; - if (viewport->CreatedPlatformWindow && g.PlatformIO.Renderer_DestroyWindow) + if (g.PlatformIO.Renderer_DestroyWindow) g.PlatformIO.Renderer_DestroyWindow(viewport); - if (viewport->CreatedPlatformWindow && g.PlatformIO.Platform_DestroyWindow) + if (g.PlatformIO.Platform_DestroyWindow) g.PlatformIO.Platform_DestroyWindow(viewport); IM_ASSERT(viewport->RendererUserData == NULL); IM_ASSERT(viewport->PlatformUserData == NULL); @@ -7745,15 +7745,15 @@ void ImGui::DestroyPlatformWindow(ImGuiViewportP* viewport) void ImGui::DestroyPlatformWindows() { - // We call the destroy window on the main viewport (index 0) to give a chance to the back-end to clear any data - // have stored in e.g. PlatformUserData, RendererUserData. It can be convenient for the platform back-end code to - // store something in the main viewport, in order for e.g. the mouse handling code to work in a more generic manner. + // We call the destroy window on every viewport (including the main viewport, index 0) to give a chance to the back-end + // to clear any data they may have stored in e.g. PlatformUserData, RendererUserData. + // It is convenient for the platform back-end code to store something in the main viewport, in order for e.g. the mouse handling + // code to operator a consistent manner. // It is expected that the back-end can handle calls to Renderer_DestroyWindow/Platform_DestroyWindow without // crashing if it doesn't have data stored. ImGuiContext& g = *GImGui; for (int i = 0; i < g.Viewports.Size; i++) - if (g.Viewports[i]->CreatedPlatformWindow) - DestroyPlatformWindow(g.Viewports[i]); + DestroyPlatformWindow(g.Viewports[i]); } //----------------------------------------------------------------------------- From 510f0e505c8014f67335643d3ca929be0e865bbe Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 22 Nov 2018 20:13:55 +0100 Subject: [PATCH 327/828] Viewport: UpdateTryMergeWindowIntoHostViewport() calls BringWindowToDisplayFront() - possible now that BringWindowToFocusFront() is a different function. --- imgui.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 5bb44ab6a0e8..c082d0c7359c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7435,6 +7435,8 @@ static bool ImGui::UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImG if (g.Windows[n]->Viewport == old_viewport) SetWindowViewport(g.Windows[n], viewport); SetWindowViewport(window, viewport); + BringWindowToDisplayFront(window); + return true; } @@ -7748,7 +7750,7 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) } else if (window->Viewport && window != window->Viewport->Window && window->Viewport->Window && !(flags & ImGuiWindowFlags_ChildWindow)) { - // When called from Begin() we don't have access to a proper version of the Hidden flag yet. + // When called from Begin() we don't have access to a proper version of the Hidden flag yet, so we replicate this code. const bool will_be_visible = (window->DockIsActive && !window->DockTabIsVisible) ? false : true; if ((window->Flags & ImGuiWindowFlags_DockNodeHost) && window->Viewport->LastFrameActive < g.FrameCount && will_be_visible) { From 760c1d95b984f2b4bc2db6b7487a7e67674f1af5 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 22 Nov 2018 21:13:07 +0100 Subject: [PATCH 328/828] Viewport: Merging/Extracting to/from main host viewport is now performed based on current state rather than triggers. Windows which don't fit in host window are now consistently in their own viewport. Toward simplifying the UpdateSelectWindowViewport() function. Probably broke something.. (#1542) --- imgui.cpp | 67 ++++++++++++++++-------------------------------- imgui_internal.h | 2 -- 2 files changed, 22 insertions(+), 47 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index c082d0c7359c..84a51565aada 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2387,7 +2387,6 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) ScrollbarSizes = ImVec2(0.0f, 0.0f); ScrollbarX = ScrollbarY = false; ViewportOwned = false; - ViewportTryMerge = ViewportTrySplit = false; Active = WasActive = false; WriteAccessed = false; Collapsed = false; @@ -4878,10 +4877,6 @@ static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au if (size_target.x != FLT_MAX && (size_target.x != window->SizeFull.x || size_target.y != window->SizeFull.y)) { window->SizeFull = size_target; - if (window->ViewportOwned) - window->ViewportTryMerge = true; - else - window->ViewportTrySplit = true; MarkIniSettingsDirty(window); } if (pos_target.x != FLT_MAX) @@ -5224,10 +5219,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) { // Late create viewport, based on the assumption that with our calculations, the DPI will be known ahead (same as the DPI of the selection done in UpdateSelectWindowViewport) //ImGuiViewport* old_viewport = window->Viewport; - ImGuiViewportFlags viewport_flags = ImGuiViewportFlags_NoFocusOnAppearing; - if ((window->Flags & ImGuiWindowFlags_NoMouseInputs) && (window->Flags & ImGuiWindowFlags_NoNavInputs)) - viewport_flags |= ImGuiViewportFlags_NoInputs; - window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, viewport_flags); + window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoFocusOnAppearing); // FIXME-DPI //IM_ASSERT(old_viewport->DpiScale == window->Viewport->DpiScale); // FIXME-DPI: Something went wrong @@ -5240,15 +5232,9 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if (window->ViewportOwned) { if (window->Viewport->PlatformRequestMove) - { window->Pos = window->Viewport->Pos; - window->ViewportTryMerge = true; - } if (window->Viewport->PlatformRequestResize) - { window->Size = window->SizeFull = window->Viewport->Size; - window->ViewportTryMerge = true; - } // We also tell the back-end that clearing the platform window won't be necessary, as our window is filling the viewport and we have disabled BgAlpha window->Viewport->Flags |= ImGuiViewportFlags_NoRendererClear; @@ -5269,7 +5255,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) { // Fallback for "lost" window (e.g. a monitor disconnected): we move the window back over the main viewport SetWindowPos(window, g.Viewports[0]->Pos + style.DisplayWindowPadding, ImGuiCond_Always); - window->ViewportTryMerge = true; } else { @@ -7610,11 +7595,22 @@ static void ImGui::UpdateViewports() IM_ASSERT(g.MouseViewport != NULL); } +// FIXME: We should ideally refactor the system to call this everyframe (we currently don't) ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const ImVec2& pos, const ImVec2& size, ImGuiViewportFlags flags) { ImGuiContext& g = *GImGui; IM_ASSERT(id != 0); + if (window != NULL) + { + if (g.MovingWindow && g.MovingWindow->RootWindow == window) + flags |= ImGuiViewportFlags_NoInputs | ImGuiViewportFlags_NoFocusOnAppearing; + if ((window->Flags & ImGuiWindowFlags_NoMouseInputs) && (window->Flags & ImGuiWindowFlags_NoNavInputs)) + flags |= ImGuiViewportFlags_NoInputs; + if (window->Flags & ImGuiWindowFlags_NoFocusOnAppearing) + flags |= ImGuiViewportFlags_NoFocusOnAppearing; + } + ImGuiViewportP* viewport = (ImGuiViewportP*)FindViewportByID(id); if (viewport) { @@ -7633,9 +7629,6 @@ ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const g.Viewports.push_back(viewport); //IMGUI_DEBUG_LOG("Add Viewport %08X (%s)\n", id, window->Name); - if (window && (window->Flags & ImGuiWindowFlags_NoFocusOnAppearing)) - flags |= ImGuiViewportFlags_NoFocusOnAppearing; - // We normally setup for all viewports in NewFrame() but here need to handle the mid-frame creation of a new viewport. // We need to extend the fullscreen clip rect so the OverlayDrawList clip is correct for that the first frame g.DrawListSharedData.ClipRectFullscreen.z = ImMax(g.DrawListSharedData.ClipRectFullscreen.z, viewport->Pos.x + viewport->Size.x); @@ -7673,12 +7666,9 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) return; } - // Merge into host viewports (after moving, resizing) - if (window->ViewportOwned && window->ViewportTryMerge && g.ActiveId == 0) - { + // Merge into host viewport + if (window->ViewportOwned && g.ActiveId == 0) UpdateTryMergeWindowIntoHostViewport(window, g.Viewports[0]); - window->ViewportTryMerge = false; - } window->ViewportOwned = false; // Appearing popups reset their viewport so they can inherit again @@ -7718,23 +7708,15 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) { window->Viewport = g.MouseViewport; } - else if (g.MovingWindow && g.MovingWindow->RootWindow == window && IsMousePosValid()) - { - // Transition to our own viewport when leaving our host boundaries + set the NoInputs flag (will be cleared in UpdateMouseMovingWindow() when releasing mouse) - // If we are already in our own viewport, if need to set the NoInputs flag. - // If we have no viewport (which happens when detaching a docked node) immediately create one. - // We test for 'window->Viewport->Window == window' instead of 'window->ViewportOwned' because ViewportOwned is not valid during this function. - bool has_viewport = (window->Viewport != NULL); - bool own_viewport = has_viewport && (window->Viewport->Window == window); - bool leave_host_viewport = has_viewport && !own_viewport && !window->Viewport->GetRect().Contains(window->Rect()); - bool move_from_own_viewport = has_viewport && own_viewport && !(window->Viewport->Flags & ImGuiViewportFlags_NoInputs); - if (!has_viewport || leave_host_viewport || move_from_own_viewport) - window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoFocusOnAppearing | ImGuiViewportFlags_NoInputs); - } else if (GetWindowAlwaysWantOwnViewport(window)) { window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_None); } + else if (g.MovingWindow && g.MovingWindow->RootWindow == window && IsMousePosValid()) + { + if (window->Viewport != NULL && window->Viewport->Window == window) + window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_None); + } // Mark window as allowed to protrude outside of its viewport and into the current monitor // We need to take account of the possibility that mouse may become invalid. @@ -7766,19 +7748,14 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoFocusOnAppearing); } } - else if ((flags & ImGuiWindowFlags_DockNodeHost) && (window->Appearing)) - { - // Mark so the dock host can be on its own viewport - window->ViewportAllowPlatformMonitorExtend = FindPlatformMonitorForRect(window->Rect()); - } - if (window->ViewportTrySplit && window->ViewportAllowPlatformMonitorExtend < 0) - window->ViewportAllowPlatformMonitorExtend = window->Viewport->PlatformMonitor; - window->ViewportTrySplit = false; // Fallback to default viewport if (window->Viewport == NULL) window->Viewport = main_viewport; + if (window->ViewportAllowPlatformMonitorExtend < 0) + window->ViewportAllowPlatformMonitorExtend = window->Viewport->PlatformMonitor; + // Update flags window->ViewportOwned = (window == window->Viewport->Window); diff --git a/imgui_internal.h b/imgui_internal.h index d54f38846fda..682c91efb361 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1195,8 +1195,6 @@ struct IMGUI_API ImGuiWindow ImVec2 ScrollbarSizes; // Size taken by scrollbars on each axis bool ScrollbarX, ScrollbarY; bool ViewportOwned; - bool ViewportTryMerge; // Request attempt to merge into a host viewport and destroy our owned viewport - bool ViewportTrySplit; // Request attempt to split out of a host viewport and create our owned viewport bool Active; // Set to true on Begin(), unless Collapsed bool WasActive; bool WriteAccessed; // Set to true when any widget access the current window From 1f78e08427f458e4ee5f215a15810b1f61050622 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 22 Nov 2018 20:13:55 +0100 Subject: [PATCH 329/828] Viewport: UpdateTryMergeWindowIntoHostViewport() calls BringWindowToDisplayFront() - possible now that BringWindowToFocusFront() is a different function. --- imgui.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/imgui.cpp b/imgui.cpp index 298bdcc65553..acac22686808 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7214,6 +7214,7 @@ static void ImGui::UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImG if (g.Windows[n]->Viewport == old_viewport) SetWindowViewport(g.Windows[n], viewport); SetWindowViewport(window, viewport); + BringWindowToDisplayFront(window); } // Scale all windows (position, size). Use when e.g. changing DPI. (This is a lossy operation!) From cae4d020dc41ec7bc20c8528852e4540787d3026 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 22 Nov 2018 21:13:07 +0100 Subject: [PATCH 330/828] Viewport: Merging/Extracting to/from main host viewport is now performed based on current state rather than triggers. Windows which don't fit in host window are now consistently in their own viewport. Toward simplifying the UpdateSelectWindowViewport() function. Probably broke something.. (#1542) --- imgui.cpp | 62 +++++++++++++++++------------------------------- imgui_internal.h | 2 -- 2 files changed, 22 insertions(+), 42 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index acac22686808..5d29849d7f7a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2369,7 +2369,6 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) ScrollbarSizes = ImVec2(0.0f, 0.0f); ScrollbarX = ScrollbarY = false; ViewportOwned = false; - ViewportTryMerge = ViewportTrySplit = false; Active = WasActive = false; WriteAccessed = false; Collapsed = false; @@ -4762,10 +4761,6 @@ static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au if (size_target.x != FLT_MAX && (size_target.x != window->SizeFull.x || size_target.y != window->SizeFull.y)) { window->SizeFull = size_target; - if (window->ViewportOwned) - window->ViewportTryMerge = true; - else - window->ViewportTrySplit = true; MarkIniSettingsDirty(window); } if (pos_target.x != FLT_MAX) @@ -5082,10 +5077,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) { // Late create viewport, based on the assumption that with our calculations, the DPI will be known ahead (same as the DPI of the selection done in UpdateSelectWindowViewport) //ImGuiViewport* old_viewport = window->Viewport; - ImGuiViewportFlags viewport_flags = ImGuiViewportFlags_NoFocusOnAppearing; - if ((window->Flags & ImGuiWindowFlags_NoMouseInputs) && (window->Flags & ImGuiWindowFlags_NoNavInputs)) - viewport_flags |= ImGuiViewportFlags_NoInputs; - window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, viewport_flags); + window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoFocusOnAppearing); // FIXME-DPI //IM_ASSERT(old_viewport->DpiScale == window->Viewport->DpiScale); // FIXME-DPI: Something went wrong @@ -5098,15 +5090,9 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if (window->ViewportOwned) { if (window->Viewport->PlatformRequestMove) - { window->Pos = window->Viewport->Pos; - window->ViewportTryMerge = true; - } if (window->Viewport->PlatformRequestResize) - { window->Size = window->SizeFull = window->Viewport->Size; - window->ViewportTryMerge = true; - } // We also tell the back-end that clearing the platform window won't be necessary, as our window is filling the viewport and we have disabled BgAlpha window->Viewport->Flags |= ImGuiViewportFlags_NoRendererClear; @@ -5127,7 +5113,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) { // Fallback for "lost" window (e.g. a monitor disconnected): we move the window back over the main viewport SetWindowPos(window, g.Viewports[0]->Pos + style.DisplayWindowPadding, ImGuiCond_Always); - window->ViewportTryMerge = true; } else { @@ -7385,11 +7370,22 @@ static void ImGui::UpdateViewports() IM_ASSERT(g.MouseViewport != NULL); } +// FIXME: We should ideally refactor the system to call this everyframe (we currently don't) ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const ImVec2& pos, const ImVec2& size, ImGuiViewportFlags flags) { ImGuiContext& g = *GImGui; IM_ASSERT(id != 0); + if (window != NULL) + { + if (g.MovingWindow && g.MovingWindow->RootWindow == window) + flags |= ImGuiViewportFlags_NoInputs | ImGuiViewportFlags_NoFocusOnAppearing; + if ((window->Flags & ImGuiWindowFlags_NoMouseInputs) && (window->Flags & ImGuiWindowFlags_NoNavInputs)) + flags |= ImGuiViewportFlags_NoInputs; + if (window->Flags & ImGuiWindowFlags_NoFocusOnAppearing) + flags |= ImGuiViewportFlags_NoFocusOnAppearing; + } + ImGuiViewportP* viewport = (ImGuiViewportP*)FindViewportByID(id); if (viewport) { @@ -7407,9 +7403,6 @@ ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const viewport->PlatformMonitor = FindPlatformMonitorForRect(viewport->GetRect()); g.Viewports.push_back(viewport); - if (window && (window->Flags & ImGuiWindowFlags_NoFocusOnAppearing)) - flags |= ImGuiViewportFlags_NoFocusOnAppearing; - // We normally setup for all viewports in NewFrame() but here need to handle the mid-frame creation of a new viewport. // We need to extend the fullscreen clip rect so the OverlayDrawList clip is correct for that the first frame g.DrawListSharedData.ClipRectFullscreen.z = ImMax(g.DrawListSharedData.ClipRectFullscreen.z, viewport->Pos.x + viewport->Size.x); @@ -7447,12 +7440,9 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) return; } - // Merge into host viewports (after moving, resizing) - if (window->ViewportOwned && window->ViewportTryMerge && g.ActiveId == 0) - { + // Merge into host viewport + if (window->ViewportOwned && g.ActiveId == 0) UpdateTryMergeWindowIntoHostViewport(window, g.Viewports[0]); - window->ViewportTryMerge = false; - } window->ViewportOwned = false; // Appearing popups reset their viewport so they can inherit again @@ -7492,23 +7482,15 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) { window->Viewport = g.MouseViewport; } - else if (g.MovingWindow && g.MovingWindow->RootWindow == window && IsMousePosValid()) - { - // Transition to our own viewport when leaving our host boundaries + set the NoInputs flag (will be cleared in UpdateMouseMovingWindow() when releasing mouse) - // If we are already in our own viewport, if need to set the NoInputs flag. - // If we have no viewport (which happens when detaching a docked node) immediately create one. - // We test for 'window->Viewport->Window == window' instead of 'window->ViewportOwned' because ViewportOwned is not valid during this function. - bool has_viewport = (window->Viewport != NULL); - bool own_viewport = has_viewport && (window->Viewport->Window == window); - bool leave_host_viewport = has_viewport && !own_viewport && !window->Viewport->GetRect().Contains(window->Rect()); - bool move_from_own_viewport = has_viewport && own_viewport && !(window->Viewport->Flags & ImGuiViewportFlags_NoInputs); - if (!has_viewport || leave_host_viewport || move_from_own_viewport) - window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoFocusOnAppearing | ImGuiViewportFlags_NoInputs); - } else if (GetWindowAlwaysWantOwnViewport(window)) { window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_None); } + else if (g.MovingWindow && g.MovingWindow->RootWindow == window && IsMousePosValid()) + { + if (window->Viewport != NULL && window->Viewport->Window == window) + window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_None); + } // Mark window as allowed to protrude outside of its viewport and into the current monitor // We need to take account of the possibility that mouse may become invalid. @@ -7522,14 +7504,14 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) else window->ViewportAllowPlatformMonitorExtend = window->Viewport->PlatformMonitor; } - if (window->ViewportTrySplit && window->ViewportAllowPlatformMonitorExtend < 0) - window->ViewportAllowPlatformMonitorExtend = window->Viewport->PlatformMonitor; - window->ViewportTrySplit = false; // Fallback to default viewport if (window->Viewport == NULL) window->Viewport = main_viewport; + if (window->ViewportAllowPlatformMonitorExtend < 0) + window->ViewportAllowPlatformMonitorExtend = window->Viewport->PlatformMonitor; + // Update flags window->ViewportOwned = (window == window->Viewport->Window); diff --git a/imgui_internal.h b/imgui_internal.h index 2bebf9a086de..e84669c35ded 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1074,8 +1074,6 @@ struct IMGUI_API ImGuiWindow ImVec2 ScrollbarSizes; // Size taken by scrollbars on each axis bool ScrollbarX, ScrollbarY; bool ViewportOwned; - bool ViewportTryMerge; // Request attempt to merge into a host viewport and destroy our owned viewport - bool ViewportTrySplit; // Request attempt to split out of a host viewport and create our owned viewport bool Active; // Set to true on Begin(), unless Collapsed bool WasActive; bool WriteAccessed; // Set to true when any widget access the current window From 862781b1950b9b55170ea7fbf3a90c2453afad33 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 23 Nov 2018 15:50:33 +0100 Subject: [PATCH 331/828] Viewport: Avoid unnecessary reapplying platform pos/size every frame. (#2205) + Fix GLFW ImGui_ImplGlfw_CreateWindow() from not applying position immediately. Clear LastNameHash properly (bug already fixed in Docking branch) --- examples/imgui_impl_glfw.cpp | 1 + imgui.cpp | 17 ++++++++++------- imgui_internal.h | 6 ++++-- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index d357d5defb99..c062c7041f05 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -409,6 +409,7 @@ static void ImGui_ImplGlfw_CreateWindow(ImGuiViewport* viewport) data->Window = glfwCreateWindow((int)viewport->Size.x, (int)viewport->Size.y, "No Title Yet", NULL, share_window); data->WindowOwned = true; viewport->PlatformHandle = (void*)data->Window; + glfwSetWindowPos(data->Window, (int)viewport->Pos.x, (int)viewport->Pos.y); // Install callbacks for secondary viewports glfwSetMouseButtonCallback(data->Window, ImGui_ImplGlfw_MouseButtonCallback); diff --git a/imgui.cpp b/imgui.cpp index 5d29849d7f7a..3f94cd025519 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7280,9 +7280,9 @@ static void ImGui::UpdateViewports() // Apply Position and Size (from Platform Window to ImGui) if requested. // We do it early in the frame instead of waiting for UpdatePlatformWindows() to avoid a frame of lag when moving/resizing using OS facilities. if (viewport->PlatformRequestMove) - viewport->Pos = g.PlatformIO.Platform_GetWindowPos(viewport); + viewport->Pos = viewport->LastPlatformPos = g.PlatformIO.Platform_GetWindowPos(viewport); if (viewport->PlatformRequestResize) - viewport->Size = g.PlatformIO.Platform_GetWindowSize(viewport); + viewport->Size = viewport->LastPlatformSize = g.PlatformIO.Platform_GetWindowSize(viewport); // Translate imgui windows when a Host Viewport has been moved ImVec2 delta = viewport->Pos - viewport->LastPos; @@ -7576,18 +7576,21 @@ void ImGui::UpdatePlatformWindows() g.PlatformIO.Platform_CreateWindow(viewport); if (g.PlatformIO.Renderer_CreateWindow != NULL) g.PlatformIO.Renderer_CreateWindow(viewport); - viewport->RendererLastSize = viewport->Size; + viewport->LastNameHash = 0; + viewport->LastPlatformPos = viewport->LastPlatformSize = ImVec2(FLT_MAX, FLT_MAX); // By clearing those we'll enforce a call to Platform_SetWindowPos/Platform_SetWindowSize before Platform_ShowWindow + viewport->LastRendererSize = viewport->Size; viewport->CreatedPlatformWindow = true; } // Apply Position and Size (from ImGui to Platform/Renderer back-ends) - if (!viewport->PlatformRequestMove) + if ((viewport->LastPlatformPos.x != viewport->Pos.x || viewport->LastPlatformPos.y != viewport->Pos.y) && !viewport->PlatformRequestMove) g.PlatformIO.Platform_SetWindowPos(viewport, viewport->Pos); - if (!viewport->PlatformRequestResize) + if ((viewport->LastPlatformSize.x != viewport->Size.x || viewport->LastPlatformSize.y != viewport->Size.y) && !viewport->PlatformRequestResize) g.PlatformIO.Platform_SetWindowSize(viewport, viewport->Size); - if (g.PlatformIO.Renderer_SetWindowSize && (viewport->RendererLastSize.x != viewport->Size.x || viewport->RendererLastSize.y != viewport->Size.y)) + if ((viewport->LastRendererSize.x != viewport->Size.x || viewport->LastRendererSize.y != viewport->Size.y) && g.PlatformIO.Renderer_SetWindowSize) g.PlatformIO.Renderer_SetWindowSize(viewport, viewport->Size); - viewport->RendererLastSize = viewport->Size; + viewport->LastPlatformPos = viewport->Pos; + viewport->LastPlatformSize = viewport->LastRendererSize = viewport->Size; // Update title bar (if it changed) if (ImGuiWindow* window_for_title = viewport->Window) diff --git a/imgui_internal.h b/imgui_internal.h index e84669c35ded..83a4c326f167 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -611,9 +611,11 @@ struct ImGuiViewportP : public ImGuiViewport ImDrawList* OverlayDrawList; // For convenience, a draw list we can render to that's always rendered last (we use it to draw software mouse cursor when io.MouseDrawCursor is set) ImDrawData DrawDataP; ImDrawDataBuilder DrawDataBuilder; - ImVec2 RendererLastSize; + ImVec2 LastPlatformPos; + ImVec2 LastPlatformSize; + ImVec2 LastRendererSize; - ImGuiViewportP() { Idx = -1; LastFrameActive = LastFrameOverlayDrawList = LastFrontMostStampCount = -1; LastNameHash = 0; CreatedPlatformWindow = false; Alpha = LastAlpha = 1.0f; PlatformMonitor = INT_MIN; Window = NULL; OverlayDrawList = NULL; RendererLastSize = ImVec2(-1.0f,-1.0f); } + ImGuiViewportP() { Idx = -1; LastFrameActive = LastFrameOverlayDrawList = LastFrontMostStampCount = -1; LastNameHash = 0; CreatedPlatformWindow = false; Alpha = LastAlpha = 1.0f; PlatformMonitor = INT_MIN; Window = NULL; OverlayDrawList = NULL; LastPlatformPos = LastPlatformSize = LastRendererSize = ImVec2(FLT_MAX, FLT_MAX); } ~ImGuiViewportP() { if (OverlayDrawList) IM_DELETE(OverlayDrawList); } ImRect GetRect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } ImVec2 GetCenter() const{ return ImVec2(Pos.x + Size.x * 0.5f, Pos.y + Size.y * 0.5f); } From d8ab2c1ac9e40ebb09e962a9cffd719c5ca2b244 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 23 Nov 2018 16:18:30 +0100 Subject: [PATCH 332/828] Viewport: Added support for minimized window which caused problem when ImGuiBackendFlags_HasMouseHoveredViewport was not supported. (#1542) + todo --- docs/TODO.txt | 6 +++++- examples/imgui_impl_glfw.cpp | 7 +++++++ examples/imgui_impl_sdl.cpp | 8 ++++++++ examples/imgui_impl_win32.cpp | 8 ++++++++ imgui.cpp | 35 +++++++++++++++++++++-------------- imgui.h | 1 + imgui_internal.h | 3 ++- 7 files changed, 52 insertions(+), 16 deletions(-) diff --git a/docs/TODO.txt b/docs/TODO.txt index 9b2893d31e32..ffc0dbe05a3f 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -281,14 +281,18 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - examples: move ImGui::NewFrame() out of the backend _NewFrame() ? - viewport: make it possible to have no main/hosting viewport + - viewport: We set ImGuiViewportFlags_NoFocusOnAppearing in a way that is required for GLFW/SDL binding, but could be handled better without + on a custom e.g. Win32 bindings. It prevents newly dragged-out viewports from taking the focus, which makes ALT+F4 more ambiguous. + - viewport: not focusing newly undocked viewport means clicking back on previous one doesn't bring OS window to front. - viewport: with platform decoration enabled, platform may force constraint (e.g. minimum size) - viewport: use getfocus/setfocus api to synchronize imgui<>platform focus better (e.g imgui-side ctrl-tab can focus os window, OS initial setup and alt-tab can focus imgui window etc.) - viewport: store per-viewport/monitor DPI in .ini file so an application reload or main window changing DPI on reload can be properly patched for. - - viewport: vulkan renderer implementation. + - viewport: implicit Debug window can hog a zombie viewport (harmless, noisy?) - viewport: need to clarify how to use GetMousePos() from a user point of view. - platform: glfw: no support for ImGuiBackendFlags_HasMouseHoveredViewport. - platform: sdl: no support for ImGuiBackendFlags_HasMouseHoveredViewport. maybe we could use SDL_GetMouseFocus() / SDL_WINDOW_MOUSE_FOCUS if imgui could fallback on its heuristic when NoInputs is set - platform: sdl: no refresh of monitor/display (SDL doesn't seem to have an event for it). + - platform: sdl: multi-viewport + minimized window seems to break mouse wheel events (at least under Win32). - inputs: we need an explicit flag about whether the imgui window is focused, to be able to distinguish focused key releases vs alt-tabbing all release behaviors. - inputs: rework IO system to be able to pass actual ordered/timestamped events. use an event queue? (~#335, #71) diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index c062c7041f05..f1cc1cfcb75d 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -548,6 +548,12 @@ static bool ImGui_ImplGlfw_GetWindowFocus(ImGuiViewport* viewport) return glfwGetWindowAttrib(data->Window, GLFW_FOCUSED) != 0; } +static bool ImGui_ImplGlfw_GetWindowMinimized(ImGuiViewport* viewport) +{ + ImGuiViewportDataGlfw* data = (ImGuiViewportDataGlfw*)viewport->PlatformUserData; + return glfwGetWindowAttrib(data->Window, GLFW_ICONIFIED) != 0; +} + #if GLFW_HAS_WINDOW_ALPHA static void ImGui_ImplGlfw_SetWindowAlpha(ImGuiViewport* viewport, float alpha) { @@ -668,6 +674,7 @@ static void ImGui_ImplGlfw_InitPlatformInterface() platform_io.Platform_GetWindowSize = ImGui_ImplGlfw_GetWindowSize; platform_io.Platform_SetWindowFocus = ImGui_ImplGlfw_SetWindowFocus; platform_io.Platform_GetWindowFocus = ImGui_ImplGlfw_GetWindowFocus; + platform_io.Platform_GetWindowMinimized = ImGui_ImplGlfw_GetWindowMinimized; platform_io.Platform_SetWindowTitle = ImGui_ImplGlfw_SetWindowTitle; platform_io.Platform_RenderWindow = ImGui_ImplGlfw_RenderWindow; platform_io.Platform_SwapBuffers = ImGui_ImplGlfw_SwapBuffers; diff --git a/examples/imgui_impl_sdl.cpp b/examples/imgui_impl_sdl.cpp index 2917a421e952..b0fb5c6b2307 100644 --- a/examples/imgui_impl_sdl.cpp +++ b/examples/imgui_impl_sdl.cpp @@ -10,6 +10,7 @@ // Missing features: // [ ] Platform: SDL2 handling of IME under Windows appears to be broken and it explicitly disable the regular Windows IME. You can restore Windows IME by compiling SDL with SDL_DISABLE_WINDOWS_IME. // [ ] Platform: Gamepad support (need to use SDL_GameController API to fill the io.NavInputs[] value when ImGuiConfigFlags_NavEnableGamepad is set). +// [ ] Platform: Multi-viewport + Minimized windows seems to break mouse wheel events (at least under Windows). // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp. @@ -472,6 +473,12 @@ static bool ImGui_ImplSDL2_GetWindowFocus(ImGuiViewport* viewport) return (SDL_GetWindowFlags(data->Window) & SDL_WINDOW_INPUT_FOCUS) != 0; } +static bool ImGui_ImplSDL2_GetWindowMinimized(ImGuiViewport* viewport) +{ + ImGuiViewportDataSDL2* data = (ImGuiViewportDataSDL2*)viewport->PlatformUserData; + return (SDL_GetWindowFlags(data->Window) & SDL_WINDOW_MINIMIZED) != 0; +} + static void ImGui_ImplSDL2_RenderWindow(ImGuiViewport* viewport, void*) { ImGuiViewportDataSDL2* data = (ImGuiViewportDataSDL2*)viewport->PlatformUserData; @@ -543,6 +550,7 @@ static void ImGui_ImplSDL2_InitPlatformInterface(SDL_Window* window, void* sdl_g platform_io.Platform_GetWindowSize = ImGui_ImplSDL2_GetWindowSize; platform_io.Platform_SetWindowFocus = ImGui_ImplSDL2_SetWindowFocus; platform_io.Platform_GetWindowFocus = ImGui_ImplSDL2_GetWindowFocus; + platform_io.Platform_GetWindowMinimized = ImGui_ImplSDL2_GetWindowMinimized; platform_io.Platform_SetWindowTitle = ImGui_ImplSDL2_SetWindowTitle; platform_io.Platform_RenderWindow = ImGui_ImplSDL2_RenderWindow; platform_io.Platform_SwapBuffers = ImGui_ImplSDL2_SwapBuffers; diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index fb7537946957..b040bd97428a 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -536,6 +536,13 @@ static bool ImGui_ImplWin32_GetWindowFocus(ImGuiViewport* viewport) return ::GetActiveWindow() == data->Hwnd; } +static bool ImGui_ImplWin32_GetWindowMinimized(ImGuiViewport* viewport) +{ + ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData; + IM_ASSERT(data->Hwnd != 0); + return ::IsIconic(data->Hwnd) != 0; +} + static void ImGui_ImplWin32_SetWindowTitle(ImGuiViewport* viewport, const char* title) { // ::SetWindowTextA() doesn't properly handle UTF-8 so we explicitely convert our string. @@ -683,6 +690,7 @@ static void ImGui_ImplWin32_InitPlatformInterface() platform_io.Platform_GetWindowSize = ImGui_ImplWin32_GetWindowSize; platform_io.Platform_SetWindowFocus = ImGui_ImplWin32_SetWindowFocus; platform_io.Platform_GetWindowFocus = ImGui_ImplWin32_GetWindowFocus; + platform_io.Platform_GetWindowMinimized = ImGui_ImplWin32_GetWindowMinimized; platform_io.Platform_SetWindowTitle = ImGui_ImplWin32_SetWindowTitle; platform_io.Platform_SetWindowAlpha = ImGui_ImplWin32_SetWindowAlpha; platform_io.Platform_GetWindowDpiScale = ImGui_ImplWin32_GetWindowDpiScale; diff --git a/imgui.cpp b/imgui.cpp index 3f94cd025519..77e7cdd535cc 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7185,7 +7185,7 @@ static bool ImGui::GetWindowAlwaysWantOwnViewport(ImGuiWindow* window) static void ImGui::UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* viewport) { ImGuiContext& g = *GImGui; - if (!(viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) || window->Viewport == viewport) + if (!(viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) || window->Viewport == viewport || viewport->PlatformIsMinimized) return; if (!viewport->GetRect().Contains(window->Rect())) return; @@ -7228,7 +7228,7 @@ static ImGuiViewportP* FindViewportHoveredFromPlatformWindowStack(const ImVec2 m for (int n = 0; n < g.Viewports.Size; n++) { ImGuiViewportP* viewport = g.Viewports[n]; - if (!(viewport->Flags & ImGuiViewportFlags_NoInputs) && viewport->GetRect().Contains(mouse_platform_pos)) + if (!(viewport->Flags & ImGuiViewportFlags_NoInputs) && !viewport->PlatformIsMinimized && viewport->GetRect().Contains(mouse_platform_pos)) if (best_candidate == NULL || best_candidate->LastFrontMostStampCount < viewport->LastFrontMostStampCount) best_candidate = viewport; } @@ -7277,12 +7277,18 @@ static void ImGui::UpdateViewports() if ((g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) { + if (g.PlatformIO.Platform_GetWindowMinimized && (n == 0 || viewport->CreatedPlatformWindow)) + viewport->PlatformIsMinimized = g.PlatformIO.Platform_GetWindowMinimized(viewport); + // Apply Position and Size (from Platform Window to ImGui) if requested. // We do it early in the frame instead of waiting for UpdatePlatformWindows() to avoid a frame of lag when moving/resizing using OS facilities. - if (viewport->PlatformRequestMove) - viewport->Pos = viewport->LastPlatformPos = g.PlatformIO.Platform_GetWindowPos(viewport); - if (viewport->PlatformRequestResize) - viewport->Size = viewport->LastPlatformSize = g.PlatformIO.Platform_GetWindowSize(viewport); + if (!viewport->PlatformIsMinimized) + { + if (viewport->PlatformRequestMove) + viewport->Pos = viewport->LastPlatformPos = g.PlatformIO.Platform_GetWindowPos(viewport); + if (viewport->PlatformRequestResize) + viewport->Size = viewport->LastPlatformSize = g.PlatformIO.Platform_GetWindowSize(viewport); + } // Translate imgui windows when a Host Viewport has been moved ImVec2 delta = viewport->Pos - viewport->LastPos; @@ -9866,7 +9872,8 @@ static void RenderViewportThumbnail(ImDrawList* draw_list, ImGuiViewportP* viewp ImVec2 scale = bb.GetSize() / viewport->Size; ImVec2 off = bb.Min - viewport->Pos * scale; - window->DrawList->AddRectFilled(bb.Min, bb.Max, ImGui::GetColorU32(ImGuiCol_Border, 0.40f)); + float alpha_mul = viewport->PlatformIsMinimized ? 0.30f : 1.00f; + window->DrawList->AddRectFilled(bb.Min, bb.Max, ImGui::GetColorU32(ImGuiCol_Border, alpha_mul * 0.40f)); for (int i = 0; i != g.Windows.Size; i++) { ImGuiWindow* thumb_window = g.Windows[i]; @@ -9884,12 +9891,12 @@ static void RenderViewportThumbnail(ImDrawList* draw_list, ImGuiViewportP* viewp thumb_r_scaled.ClipWithFull(bb); title_r_scaled.ClipWithFull(bb); const bool window_is_focused = (g.NavWindow && thumb_window->RootWindowForTitleBarHighlight == g.NavWindow->RootWindowForTitleBarHighlight); - window->DrawList->AddRectFilled(thumb_r_scaled.Min, thumb_r_scaled.Max, ImGui::GetColorU32(ImGuiCol_WindowBg)); - window->DrawList->AddRectFilled(title_r_scaled.Min, title_r_scaled.Max, ImGui::GetColorU32(window_is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg)); - window->DrawList->AddRect(thumb_r_scaled.Min, thumb_r_scaled.Max, ImGui::GetColorU32(ImGuiCol_Border)); - window->DrawList->AddText(g.Font, g.FontSize * 1.0f, title_r_scaled.Min, ImGui::GetColorU32(ImGuiCol_Text), thumb_window->Name, ImGui::FindRenderedTextEnd(thumb_window->Name)); + window->DrawList->AddRectFilled(thumb_r_scaled.Min, thumb_r_scaled.Max, ImGui::GetColorU32(ImGuiCol_WindowBg, alpha_mul)); + window->DrawList->AddRectFilled(title_r_scaled.Min, title_r_scaled.Max, ImGui::GetColorU32(window_is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg, alpha_mul)); + window->DrawList->AddRect(thumb_r_scaled.Min, thumb_r_scaled.Max, ImGui::GetColorU32(ImGuiCol_Border, alpha_mul)); + window->DrawList->AddText(g.Font, g.FontSize * 1.0f, title_r_scaled.Min, ImGui::GetColorU32(ImGuiCol_Text, alpha_mul), thumb_window->Name, ImGui::FindRenderedTextEnd(thumb_window->Name)); } - draw_list->AddRect(bb.Min, bb.Max, ImGui::GetColorU32(ImGuiCol_Border)); + draw_list->AddRect(bb.Min, bb.Max, ImGui::GetColorU32(ImGuiCol_Border, alpha_mul)); } void ImGui::ShowViewportThumbnails() @@ -10069,10 +10076,10 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGuiWindowFlags flags = viewport->Flags; ImGui::BulletText("Pos: (%.0f,%.0f), Size: (%.0f,%.0f), Monitor: %d, DpiScale: %.0f%%", viewport->Pos.x, viewport->Pos.y, viewport->Size.x, viewport->Size.y, viewport->PlatformMonitor, viewport->DpiScale * 100.0f); if (viewport->Idx > 0) { ImGui::SameLine(); if (ImGui::SmallButton("Reset Pos")) { viewport->Pos = ImVec2(200,200); if (viewport->Window) viewport->Window->Pos = ImVec2(200,200); } } - ImGui::BulletText("Flags: 0x%04X =%s%s%s%s%s", viewport->Flags, + ImGui::BulletText("Flags: 0x%04X =%s%s%s%s%s%s", viewport->Flags, (flags & ImGuiViewportFlags_CanHostOtherWindows) ? " CanHostOtherWindows" : "", (flags & ImGuiViewportFlags_NoDecoration) ? " NoDecoration" : "", (flags & ImGuiViewportFlags_NoFocusOnAppearing) ? " NoFocusOnAppearing" : "", (flags & ImGuiViewportFlags_NoInputs) ? " NoInputs" : "", - (flags & ImGuiViewportFlags_NoRendererClear) ? " NoRendererClear" : ""); + (flags & ImGuiViewportFlags_NoRendererClear) ? " NoRendererClear" : "", viewport->PlatformIsMinimized ? ", PlatformIsMinimized" : ""); for (int layer_i = 0; layer_i < IM_ARRAYSIZE(viewport->DrawDataBuilder.Layers); layer_i++) for (int draw_list_i = 0; draw_list_i < viewport->DrawDataBuilder.Layers[layer_i].Size; draw_list_i++) Funcs::NodeDrawList(NULL, viewport, viewport->DrawDataBuilder.Layers[layer_i][draw_list_i], "DrawList"); diff --git a/imgui.h b/imgui.h index b4062708893c..bee7411bd395 100644 --- a/imgui.h +++ b/imgui.h @@ -2047,6 +2047,7 @@ struct ImGuiPlatformIO ImVec2 (*Platform_GetWindowSize)(ImGuiViewport* vp); void (*Platform_SetWindowFocus)(ImGuiViewport* vp); // Move window to front and set input focus bool (*Platform_GetWindowFocus)(ImGuiViewport* vp); + bool (*Platform_GetWindowMinimized)(ImGuiViewport* vp); void (*Platform_SetWindowTitle)(ImGuiViewport* vp, const char* title); void (*Platform_SetWindowAlpha)(ImGuiViewport* vp, float alpha); // (Optional) Setup window transparency void (*Platform_RenderWindow)(ImGuiViewport* vp, void* render_arg); // (Optional) Setup for render (platform side) diff --git a/imgui_internal.h b/imgui_internal.h index 83a4c326f167..4f623c250196 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -607,6 +607,7 @@ struct ImGuiViewportP : public ImGuiViewport float Alpha; // Window opacity (when dragging dockable windows/viewports we make them transparent) float LastAlpha; int PlatformMonitor; + bool PlatformIsMinimized; ImGuiWindow* Window; ImDrawList* OverlayDrawList; // For convenience, a draw list we can render to that's always rendered last (we use it to draw software mouse cursor when io.MouseDrawCursor is set) ImDrawData DrawDataP; @@ -615,7 +616,7 @@ struct ImGuiViewportP : public ImGuiViewport ImVec2 LastPlatformSize; ImVec2 LastRendererSize; - ImGuiViewportP() { Idx = -1; LastFrameActive = LastFrameOverlayDrawList = LastFrontMostStampCount = -1; LastNameHash = 0; CreatedPlatformWindow = false; Alpha = LastAlpha = 1.0f; PlatformMonitor = INT_MIN; Window = NULL; OverlayDrawList = NULL; LastPlatformPos = LastPlatformSize = LastRendererSize = ImVec2(FLT_MAX, FLT_MAX); } + ImGuiViewportP() { Idx = -1; LastFrameActive = LastFrameOverlayDrawList = LastFrontMostStampCount = -1; LastNameHash = 0; CreatedPlatformWindow = false; Alpha = LastAlpha = 1.0f; PlatformMonitor = INT_MIN; PlatformIsMinimized = false; Window = NULL; OverlayDrawList = NULL; LastPlatformPos = LastPlatformSize = LastRendererSize = ImVec2(FLT_MAX, FLT_MAX); } ~ImGuiViewportP() { if (OverlayDrawList) IM_DELETE(OverlayDrawList); } ImRect GetRect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } ImVec2 GetCenter() const{ return ImVec2(Pos.x + Size.x * 0.5f, Pos.y + Size.y * 0.5f); } From 131de7ab62186544677a5a02a9b4a2fe960ade3d Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 23 Nov 2018 18:42:16 +0100 Subject: [PATCH 333/828] Docking: Added ImGuiConfigFlags_DockingNoSplit flag. (#2109) --- imgui.cpp | 18 ++++++++++++++++-- imgui.h | 1 + imgui_demo.cpp | 2 ++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 84a51565aada..960c8c253930 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -9857,6 +9857,16 @@ void ImGui::DockContextNewFrameUpdateUndocking(ImGuiContext* ctx) DockContextClearNodes(ctx, 0, true); return; } + if (g.IO.ConfigFlags & ImGuiConfigFlags_DockingNoSplit) + { + for (int n = 0; n < dc->Nodes.Data.Size; n++) + if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p) + if (node->IsRootNode() && node->IsSplitNode()) + { + DockBuilderRemoveNodeChildNodes(node->ID); + //dc->WantFullRebuild = true; + } + } #if 0 if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_C))) @@ -11195,7 +11205,7 @@ static bool ImGui::DockNodePreviewDockCalc(ImGuiWindow* host_window, ImGuiDockNo data->IsCenterAvailable = false; data->IsSidesAvailable = true; - if (host_node && (host_node->Flags & ImGuiDockNodeFlags_NoSplit)) + if ((host_node && (host_node->Flags & ImGuiDockNodeFlags_NoSplit)) || (g.IO.ConfigFlags & ImGuiConfigFlags_DockingNoSplit)) data->IsSidesAvailable = false; if (!is_outer_docking && host_node && host_node->ParentNode == NULL && host_node->IsCentralNode) data->IsSidesAvailable = false; @@ -11335,7 +11345,7 @@ static void ImGui::DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDock } // Stop after ImGuiDir_None - if (host_node && (host_node->Flags & ImGuiDockNodeFlags_NoSplit)) + if ((host_node && (host_node->Flags & ImGuiDockNodeFlags_NoSplit)) || (g.IO.ConfigFlags & ImGuiConfigFlags_DockingNoSplit)) return; } } @@ -11833,6 +11843,10 @@ void ImGui::DockBuilderRemoveNodeChildNodes(ImGuiID root_id) } } + // DockNodeMoveWindows->DockNodeAddWindow will normally set those when reaching two windows (which is only adequate during interactive merge) + // Make sure we don't lose our current pos/size. (FIXME-DOCK: Consider tidying up that code in DockNodeAddWindow instead) + root_node->InitFromFirstWindowPosSize = false; + // Apply to settings for (int settings_n = 0; settings_n < ctx->SettingsWindows.Size; settings_n++) if (ImGuiID window_settings_dock_id = ctx->SettingsWindows[settings_n].DockId) diff --git a/imgui.h b/imgui.h index d3c35c71215f..6721b0ac5b6f 100644 --- a/imgui.h +++ b/imgui.h @@ -959,6 +959,7 @@ enum ImGuiConfigFlags_ // [BETA] Docking ImGuiConfigFlags_DockingEnable = 1 << 6, // Docking enable flags. Use SHIFT to dock window into another (or without SHIFT if io.ConfigDockingWithShift = false). + ImGuiConfigFlags_DockingNoSplit = 1 << 7, // Simplified docking mode: disable window splitting, so docking is limited to merging multiple windows together into tab-bars. // [BETA] Viewports ImGuiConfigFlags_ViewportsEnable = 1 << 10, // Viewport enable flags (require both ImGuiConfigFlags_PlatformHasViewports + ImGuiConfigFlags_RendererHasViewports set by the respective back-ends) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 3b6b2f0bf4c4..a02336784c22 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -347,6 +347,8 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::CheckboxFlags("io.ConfigFlags: DockingEnable", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_DockingEnable); ImGui::SameLine(); ShowHelpMarker("Use SHIFT to dock window into another (or without SHIFT if io.ConfigDockingWithShift == false)"); + ImGui::CheckboxFlags("io.ConfigFlags: DockingNoSplit", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_DockingNoSplit); + ImGui::SameLine(); ShowHelpMarker("Simplified docking mode: disable window splitting, so docking is limited to merging multiple windows together into tab-bars."); ImGui::CheckboxFlags("io.ConfigFlags: ViewportsEnable", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_ViewportsEnable); ImGui::SameLine(); ShowHelpMarker("Toggling this at runtime is normally unsupported (it will offset your windows)."); From 379733aba12279fedc7790b87a6b27e1f68b258d Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 26 Nov 2018 14:53:04 +0100 Subject: [PATCH 334/828] Viewport: Fix viewport regression with protruding child window creating their own viewport. (fixes cae4d02, 760c1d95) (#1542) --- imgui.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 77e7cdd535cc..2afa1d457b6a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7498,6 +7498,10 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_None); } + // Fallback to default viewport + if (window->Viewport == NULL) + window->Viewport = main_viewport; + // Mark window as allowed to protrude outside of its viewport and into the current monitor // We need to take account of the possibility that mouse may become invalid. if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) @@ -7507,15 +7511,8 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) bool mouse_valid = IsMousePosValid(&mouse_ref); if ((window->Appearing || (flags & ImGuiWindowFlags_Tooltip)) && (!use_mouse_ref || mouse_valid)) window->ViewportAllowPlatformMonitorExtend = FindPlatformMonitorForPos((use_mouse_ref && mouse_valid) ? mouse_ref : NavCalcPreferredRefPos()); - else - window->ViewportAllowPlatformMonitorExtend = window->Viewport->PlatformMonitor; } - - // Fallback to default viewport - if (window->Viewport == NULL) - window->Viewport = main_viewport; - - if (window->ViewportAllowPlatformMonitorExtend < 0) + if (window->ViewportAllowPlatformMonitorExtend < 0 && (flags & ImGuiWindowFlags_ChildWindow) == 0) window->ViewportAllowPlatformMonitorExtend = window->Viewport->PlatformMonitor; // Update flags From c93e92671a796d5957ee81ff1efdc1627b1356d6 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 26 Nov 2018 16:55:40 +0100 Subject: [PATCH 335/828] Viewport: Fixes moving child menu viewport (fix 379733a). --- imgui.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 7b672a2dd356..ddacebe421bf 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7503,16 +7503,22 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) window->Viewport = main_viewport; // Mark window as allowed to protrude outside of its viewport and into the current monitor - // We need to take account of the possibility that mouse may become invalid. if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) { + // We need to take account of the possibility that mouse may become invalid. + // Popups/Tooltip always set ViewportAllowPlatformMonitorExtend so GetWindowAllowedExtentRect() will return full monitor bounds. ImVec2 mouse_ref = (flags & ImGuiWindowFlags_Tooltip) ? g.IO.MousePos : g.CurrentPopupStack.back().OpenMousePos; bool use_mouse_ref = (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow); bool mouse_valid = IsMousePosValid(&mouse_ref); if ((window->Appearing || (flags & ImGuiWindowFlags_Tooltip)) && (!use_mouse_ref || mouse_valid)) window->ViewportAllowPlatformMonitorExtend = FindPlatformMonitorForPos((use_mouse_ref && mouse_valid) ? mouse_ref : NavCalcPreferredRefPos()); + else + window->ViewportAllowPlatformMonitorExtend = window->Viewport->PlatformMonitor; } - if (window->ViewportAllowPlatformMonitorExtend < 0 && (flags & ImGuiWindowFlags_ChildWindow) == 0) + + // Regular (non-child, non-popup) windows by default are also allowed to protrude + // Child windows are kept contained within their parent. + else if (window->ViewportAllowPlatformMonitorExtend < 0 && (flags & ImGuiWindowFlags_ChildWindow) == 0) window->ViewportAllowPlatformMonitorExtend = window->Viewport->PlatformMonitor; // Update flags From 1e7b50aeae5ff4f6d49a5c55607ddc4ea88d1f09 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 26 Nov 2018 16:55:40 +0100 Subject: [PATCH 336/828] Viewport: Fixes moving child menu viewport (fix 379733a). --- imgui.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index c9bdf27433a6..c2683c9d2ebc 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7728,14 +7728,17 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) window->Viewport = main_viewport; // Mark window as allowed to protrude outside of its viewport and into the current monitor - // We need to take account of the possibility that mouse may become invalid. if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) { + // We need to take account of the possibility that mouse may become invalid. + // Popups/Tooltip always set ViewportAllowPlatformMonitorExtend so GetWindowAllowedExtentRect() will return full monitor bounds. ImVec2 mouse_ref = (flags & ImGuiWindowFlags_Tooltip) ? g.IO.MousePos : g.CurrentPopupStack.back().OpenMousePos; bool use_mouse_ref = (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow); bool mouse_valid = IsMousePosValid(&mouse_ref); if ((window->Appearing || (flags & ImGuiWindowFlags_Tooltip)) && (!use_mouse_ref || mouse_valid)) window->ViewportAllowPlatformMonitorExtend = FindPlatformMonitorForPos((use_mouse_ref && mouse_valid) ? mouse_ref : NavCalcPreferredRefPos()); + else + window->ViewportAllowPlatformMonitorExtend = window->Viewport->PlatformMonitor; } else if (window->Viewport && window != window->Viewport->Window && window->Viewport->Window && !(flags & ImGuiWindowFlags_ChildWindow)) { @@ -7755,8 +7758,9 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoFocusOnAppearing); } } - - if (window->ViewportAllowPlatformMonitorExtend < 0 && (flags & ImGuiWindowFlags_ChildWindow) == 0) + // Regular (non-child, non-popup) windows by default are also allowed to protrude + // Child windows are kept contained within their parent. + else if (window->ViewportAllowPlatformMonitorExtend < 0 && (flags & ImGuiWindowFlags_ChildWindow) == 0) window->ViewportAllowPlatformMonitorExtend = window->Viewport->PlatformMonitor; // Update flags From d23c69d319776522b6895faeb2ae397093b107f2 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 26 Nov 2018 17:25:06 +0100 Subject: [PATCH 337/828] Docking: Added context menu option to hide the tab bar of single-window dock nodes, similar to Unreal. (#2109) --- docs/TODO.txt | 4 +- imgui.cpp | 107 +++++++++++++++++++++++++++++++++++++---------- imgui_draw.cpp | 6 +-- imgui_internal.h | 30 ++++++------- 4 files changed, 107 insertions(+), 40 deletions(-) diff --git a/docs/TODO.txt b/docs/TODO.txt index 80b459314fe5..f6972fab64b8 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -127,7 +127,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - dock: B: when docking outer, perform size locking on neighbors nodes the same way we do it with splitters, so other nodes are not resized. - dock: B~ central node resizing behavior incorrect. - dock: B~ central node ID retrieval API? - - dock: B- full rebuild (which is a debug option) loses viewport of floating dock nodes. + - dock: B: changing title font/style per-window is not supported as dock nodes are created in NewFrame. - dock: B- dock node inside its own viewports creates 1 temporary viewport per window on startup before ditching them (doesn't affect the user nor request platform windows to be created, but unnecessary) - dock: B- resize sibling locking behavior may be less desirable if we merged same-axis sibling in a same node level? - dock: B- single visible node part of a hidden split hierarchy (OnlyNodeWithWindows != NULL) should show a normal title bar (not a tab bar) @@ -143,7 +143,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - dock: B- tab bar: make selected tab always shows its full title? - dock: B- tab bar: the order/focus restoring code could be part of TabBar and not DockNode? (#8) - dock: B- nav: design interactions so nav controls can dock/undock - - dock: B- dockspace: flag to lock the dock tree and/or sizes + - dock: B- dockspace: flag to lock the dock tree and/or sizes (ImGuiDockFlags_Locked?) - dock: B- reintroduce collapsing a floating dock node. also collapsing a docked dock node! - dock: B- allow dragging a non-floating dock node by clicking on the title-bar-looking section (not just the collapse/menu button) - dock: B- option to remember undocked window size? (instead of keeping their docked size) (relate to #2104) diff --git a/imgui.cpp b/imgui.cpp index c2683c9d2ebc..4febd9a5e831 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5396,6 +5396,20 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border), style.FrameBorderSize); } + // Docking: Unhide tab bar + if (window->DockNode && window->DockNode->IsHiddenTabBar) + { + float unhide_sz_draw = ImFloor(g.FontSize * 0.65f); + float unhide_sz_hit = ImFloor(g.FontSize * 0.55f); + ImVec2 p = window->DockNode->Pos; + ImRect r(p, p + ImVec2(unhide_sz_hit, unhide_sz_hit)); + bool hovered, held; + if (ButtonBehavior(r, window->GetID("#UNHIDE"), &hovered, &held, ImGuiButtonFlags_FlattenChildren)) + window->DockNode->WantHiddenTabBarToggle = true; + const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); + window->DrawList->AddTriangleFilled(p, p + ImVec2(unhide_sz_draw, 0.0f), p + ImVec2(0.0f, unhide_sz_draw), col); + } + // Scrollbars if (window->ScrollbarX) Scrollbar(ImGuiLayoutType_Horizontal); @@ -9719,10 +9733,11 @@ struct ImGuiDockNodeSettings char Depth; char IsDockSpace; char IsCentralNode; + char IsHiddenTabBar; ImVec2ih Pos; ImVec2ih Size; ImVec2ih SizeRef; - ImGuiDockNodeSettings() { ID = ParentID = SelectedTabID = 0; SplitAxis = ImGuiAxis_None; Depth = 0; IsDockSpace = IsCentralNode = 0; } + ImGuiDockNodeSettings() { ID = ParentID = SelectedTabID = 0; SplitAxis = ImGuiAxis_None; Depth = 0; IsDockSpace = IsCentralNode = IsHiddenTabBar = 0; } }; struct ImGuiDockContext @@ -10067,6 +10082,7 @@ static void ImGui::DockContextBuildNodesFromSettings(ImGuiContext* ctx, ImGuiDoc node->SplitAxis = node_settings->SplitAxis; node->IsDockSpace = node_settings->IsDockSpace != 0; node->IsCentralNode = node_settings->IsCentralNode != 0; + node->IsHiddenTabBar = node_settings->IsHiddenTabBar != 0; // Bind host window immediately if it already exist (in case of a rebuild) // This is useful as the RootWindowForTitleBarHighlight links necessary to highlight the currently focused node requires node->HostWindow to be set. @@ -10194,10 +10210,12 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) if (target_node) { inheritor_node->IsCentralNode = target_node->IsCentralNode; + inheritor_node->IsHiddenTabBar = target_node->IsHiddenTabBar; target_node->IsCentralNode = false; } target_node = new_node; } + target_node->IsHiddenTabBar = false; if (target_node != payload_node) { @@ -10318,7 +10336,8 @@ ImGuiDockNode::ImGuiDockNode(ImGuiID id) WantCloseTabID = 0; InitFromFirstWindowPosSize = InitFromFirstWindowViewport = false; IsVisible = true; - IsDockSpace = IsCentralNode = HasCloseButton = HasCollapseButton = WantCloseAll = WantLockSizeOnce = WantMouseMove = false; + IsDockSpace = IsCentralNode = IsHiddenTabBar = HasCloseButton = HasCollapseButton = false; + WantCloseAll = WantLockSizeOnce = WantMouseMove = WantHiddenTabBarToggle = false; } ImGuiDockNode::~ImGuiDockNode() @@ -10597,6 +10616,13 @@ static void ImGui::DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* nod window_n--; } + // Apply toggles at a single point of the frame (here!) + if (node->Windows.Size > 1) + node->IsHiddenTabBar = false; + else if (node->WantHiddenTabBarToggle) + node->IsHiddenTabBar ^= 1; + node->WantHiddenTabBarToggle = false; + DockNodeUpdateVisibleFlag(node); } @@ -10876,6 +10902,30 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w node->WantCloseAll = false; node->WantCloseTabID = 0; + // Decide if we should use a focused title bar color + bool is_focused = false; + ImGuiDockNode* root_node = DockNodeGetRootNode(node); + if (g.NavWindowingTarget) + is_focused = (g.NavWindowingTarget->DockNode == node); + else if (g.NavWindow && g.NavWindow->RootWindowForTitleBarHighlight == host_window->RootWindow && root_node->LastFocusedNodeID == node->ID) + is_focused = true; + + // Hidden tab bar will show a triangle on the upper-left (in Begin) + if (node->IsHiddenTabBar) + { + node->VisibleWindow = (node->Windows.Size > 0) ? node->Windows[0] : NULL; + + // Notify root of visible window (used to display title in OS task bar) + if (node->VisibleWindow) + { + if (is_focused || root_node->VisibleWindow == NULL) + root_node->VisibleWindow = node->VisibleWindow; + if (node->TabBar) + node->TabBar->VisibleTabId = node->VisibleWindow->ID; + } + return; + } + // Move ourselves to the Menu layer (so we can be accessed by tapping Alt) + undo SkipItems flag in order to draw over the title bar even if the window is collapsed bool backup_skip_item = host_window->SkipItems; if (!node->IsDockSpace) @@ -10891,14 +10941,6 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w if (tab_bar == NULL) tab_bar = node->TabBar = IM_NEW(ImGuiTabBar)(); - // Decide if we should use a focused title bar color - bool is_focused = false; - ImGuiDockNode* root_node = DockNodeGetRootNode(node); - if (g.NavWindowingTarget) - is_focused = (g.NavWindowingTarget->DockNode == node); - else if (g.NavWindow && g.NavWindow->RootWindowForTitleBarHighlight == host_window->RootWindow && root_node->LastFocusedNodeID == node->ID) - is_focused = true; - // Collapse button changes shape and display a list if (IsPopupOpen("#TabListMenu")) { @@ -10906,14 +10948,22 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w if (BeginPopup("#TabListMenu")) { is_focused = true; - for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++) + if (tab_bar->Tabs.Size == 1) { - ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; - IM_ASSERT(tab->Window != NULL); - if (Selectable(tab->Window->Name, tab->ID == tab_bar->SelectedTabId)) - tab_bar->NextSelectedTabId = tab_bar->WantFocusTabId = tab->ID; - SameLine(); - Text(" "); + if (MenuItem("Hide tab bar", NULL, node->IsHiddenTabBar)) + node->WantHiddenTabBarToggle = true; + } + else + { + for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++) + { + ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; + IM_ASSERT(tab->Window != NULL); + if (Selectable(tab->Window->Name, tab->ID == tab_bar->SelectedTabId)) + tab_bar->NextSelectedTabId = tab_bar->WantFocusTabId = tab->ID; + SameLine(); + Text(" "); + } } EndPopup(); } @@ -11301,9 +11351,16 @@ static void ImGui::DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDock ImRect tab_bar_rect = DockNodeCalcTabBarRect(&data->FutureNode); ImVec2 tab_pos = tab_bar_rect.Min; if (host_node && host_node->TabBar) - tab_pos.x += host_node->TabBar->OffsetMax + g.Style.ItemInnerSpacing.x; // We don't use OffsetNewTab because when using non-persistent-order tab bar it is incremented with each Tab submission. + { + if (!host_node->IsHiddenTabBar) + tab_pos.x += host_node->TabBar->OffsetMax + g.Style.ItemInnerSpacing.x; // We don't use OffsetNewTab because when using non-persistent-order tab bar it is incremented with each Tab submission. + else + tab_pos.x += g.Style.ItemInnerSpacing.x + TabItemCalcSize(host_node->Windows[0]->Name, host_node->Windows[0]->HasCloseButton).x; + } else if (!(host_window->Flags & ImGuiWindowFlags_DockNodeHost)) + { tab_pos.x += g.Style.ItemInnerSpacing.x + TabItemCalcSize(host_window->Name, host_window->HasCloseButton).x; // Account for slight offset which will be added when changing from title bar to tab bar + } // Draw tab shape/label preview (payload may be a loose window or a host window carrying multiple tabbed windows) if (root_payload->DockNodeAsHost) @@ -11421,6 +11478,7 @@ void ImGui::DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImG parent_node->InitFromFirstWindowPosSize = parent_node->InitFromFirstWindowViewport = false; parent_node->VisibleWindow = merge_lead_child->VisibleWindow; parent_node->IsCentralNode = (child_0 && child_0->IsCentralNode) || (child_1 && child_1->IsCentralNode); + parent_node->IsHiddenTabBar = merge_lead_child->IsHiddenTabBar; parent_node->SizeRef = backup_last_explicit_size; if (child_0) @@ -12215,7 +12273,10 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) // Update window flag IM_ASSERT((window->Flags & ImGuiWindowFlags_ChildWindow) == 0); window->Flags |= ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_NoResize; - window->Flags &= ~ImGuiWindowFlags_NoTitleBar; // Clear the NoTitleBar flag in case the user set it: confusingly enough we need a title bar height so we are correctly offset, but it won't be displayed! + if (dock_node->IsHiddenTabBar) + window->Flags |= ImGuiWindowFlags_NoTitleBar; + else + window->Flags &= ~ImGuiWindowFlags_NoTitleBar; // Clear the NoTitleBar flag in case the user set it: confusingly enough we need a title bar height so we are correctly offset, but it won't be displayed! // Save new dock order only if the tab bar is active if (dock_node->TabBar) @@ -12391,6 +12452,7 @@ static void ImGui::DockSettingsHandler_ReadLine(ImGuiContext* ctx, ImGuiSettings } if (sscanf(line, " Split=%c%n", &c, &r) == 1) { line += r; if (c == 'X') node.SplitAxis = ImGuiAxis_X; else if (c == 'Y') node.SplitAxis = ImGuiAxis_Y; } if (sscanf(line, " CentralNode=%d%n", &x, &r) == 1) { line += r; node.IsCentralNode = (x != 0); } + if (sscanf(line, " HiddenTabBar=%d%n", &x, &r) == 1) { line += r; node.IsHiddenTabBar = (x != 0); } if (sscanf(line, " SelectedTab=0x%08X%n", &node.SelectedTabID,&r) == 1) { line += r; } ImGuiDockContext* dc = ctx->DockContext; if (node.ParentID != 0) @@ -12410,6 +12472,7 @@ static void DockSettingsHandler_DockNodeToSettings(ImGuiDockContext* dc, ImGuiDo node_settings.Depth = (char)depth; node_settings.IsDockSpace = (char)node->IsDockSpace; node_settings.IsCentralNode = (char)node->IsCentralNode; + node_settings.IsHiddenTabBar = (char)node->IsHiddenTabBar; node_settings.Pos = ImVec2ih((short)node->Pos.x, (short)node->Pos.y); node_settings.Size = ImVec2ih((short)node->Size.x, (short)node->Size.y); node_settings.SizeRef = ImVec2ih((short)node->SizeRef.x, (short)node->SizeRef.y); @@ -12457,7 +12520,9 @@ static void ImGui::DockSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettings if (node_settings->SplitAxis != ImGuiAxis_None) buf->appendf(" Split=%c", (node_settings->SplitAxis == ImGuiAxis_X) ? 'X' : 'Y'); if (node_settings->IsCentralNode) - buf->appendf(" CentralNode=%d", node_settings->IsCentralNode); + buf->appendf(" CentralNode=1"); + if (node_settings->IsHiddenTabBar) + buf->appendf(" HiddenTabBar=1"); if (node_settings->SelectedTabID) buf->appendf(" SelectedTab=0x%08X", node_settings->SelectedTabID); @@ -13317,7 +13382,7 @@ void ImGui::ShowDockingDebug() IM_ASSERT(node->ChildNodes[1] == NULL || node->ChildNodes[1]->ParentNode == node); ImGui::BulletText("Pos (%.0f,%.0f), Size (%.0f, %.0f) Ref (%.0f, %.0f)", node->Pos.x, node->Pos.y, node->Size.x, node->Size.y, node->SizeRef.x, node->SizeRef.y); - ImGui::BulletText("Flags %02X%s%s%s%s", + ImGui::BulletText("Flags 0x%02X%s%s%s%s", node->Flags, node->IsDockSpace ? ", IsDockSpace" : "", node->IsCentralNode ? ", IsCentralNode" : "", (GImGui->FrameCount - node->LastFrameAlive < 2) ? ", IsAlive" : "", (GImGui->FrameCount - node->LastFrameActive < 2) ? ", IsActive" : ""); if (node->ChildNodes[0]) diff --git a/imgui_draw.cpp b/imgui_draw.cpp index b1c16bcb1fdb..e966fcf6abb0 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -211,7 +211,7 @@ void ImGui::StyleColorsDark(ImGuiStyle* dst) colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f); colors[ImGuiCol_TabHovered] = colors[ImGuiCol_HeaderHovered]; colors[ImGuiCol_DockingPreview] = colors[ImGuiCol_HeaderActive] * ImVec4(1.0f, 1.0f, 1.0f, 0.7f); - colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f);; + colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f); colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f); colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f); colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); @@ -268,7 +268,7 @@ void ImGui::StyleColorsClassic(ImGuiStyle* dst) colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f); colors[ImGuiCol_TabHovered] = colors[ImGuiCol_HeaderHovered]; colors[ImGuiCol_DockingPreview] = colors[ImGuiCol_Header] * ImVec4(1.0f, 1.0f, 1.0f, 0.7f); - colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f);; + colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f); colors[ImGuiCol_PlotLines] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); colors[ImGuiCol_PlotLinesHovered] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); @@ -326,7 +326,7 @@ void ImGui::StyleColorsLight(ImGuiStyle* dst) colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f); colors[ImGuiCol_TabHovered] = colors[ImGuiCol_HeaderHovered]; colors[ImGuiCol_DockingPreview] = colors[ImGuiCol_Header] * ImVec4(1.0f, 1.0f, 1.0f, 0.7f); - colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f);; + colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f); colors[ImGuiCol_PlotLines] = ImVec4(0.39f, 0.39f, 0.39f, 1.00f); colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f); colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); diff --git a/imgui_internal.h b/imgui_internal.h index 74a58135d0ce..55d59072d60c 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -769,22 +769,24 @@ struct ImGuiDockNode ImGuiWindow* HostWindow; ImGuiWindow* VisibleWindow; - ImGuiDockNode* OnlyNodeWithWindows; // [Root node only] Set when there is a single visible node within the hierarchy - int LastFrameAlive; // Last frame number the node was updated or kept alive explicitly with DockSpace() + ImGuiDockNodeFlags_KeepAliveOnly - int LastFrameActive; // Last frame number the node was updated. - ImGuiID LastFocusedNodeID; // [Root node only] Which of our child node (any ancestor in the hierarchy) was last focused. - ImGuiID SelectedTabID; // [Tab node only] Which of our tab is selected. - ImGuiID WantCloseTabID; // [Tab node only] Set when closing a specific tab. + ImGuiDockNode* OnlyNodeWithWindows; // [Root node only] Set when there is a single visible node within the hierarchy + int LastFrameAlive; // Last frame number the node was updated or kept alive explicitly with DockSpace() + ImGuiDockNodeFlags_KeepAliveOnly + int LastFrameActive; // Last frame number the node was updated. + ImGuiID LastFocusedNodeID; // [Root node only] Which of our child node (any ancestor in the hierarchy) was last focused. + ImGuiID SelectedTabID; // [Tab node only] Which of our tab is selected. + ImGuiID WantCloseTabID; // [Tab node only] Set when closing a specific tab. bool InitFromFirstWindowPosSize :1; bool InitFromFirstWindowViewport :1; - bool IsVisible :1; // Set to false when the node is hidden (usually disabled as it has no active window) - bool IsDockSpace :1; // Root node was created by a DockSpace() call. - bool IsCentralNode :1; - bool HasCloseButton :1; - bool HasCollapseButton :1; - bool WantCloseAll :1; // Set when closing all tabs at once. - bool WantLockSizeOnce :1; - bool WantMouseMove :1; // After a node extraction we need to transition toward moving the newly created host window + bool IsVisible :1; // Set to false when the node is hidden (usually disabled as it has no active window) + bool IsDockSpace :1; // Root node was created by a DockSpace() call. + bool IsCentralNode :1; + bool IsHiddenTabBar :1; + bool HasCloseButton :1; + bool HasCollapseButton :1; + bool WantCloseAll :1; // Set when closing all tabs at once. + bool WantLockSizeOnce :1; + bool WantMouseMove :1; // After a node extraction we need to transition toward moving the newly created host window + bool WantHiddenTabBarToggle :1; ImGuiDockNode(ImGuiID id); ~ImGuiDockNode(); From 66cfbbff5e0ae04f7a182678e09692a18adafe6f Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 26 Nov 2018 17:39:38 +0100 Subject: [PATCH 338/828] Docking: Fixed double-overlay when dragging window over one of the (four cardinal) outer drop boxes. (#2109) --- imgui.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/imgui.cpp b/imgui.cpp index 4febd9a5e831..521a0fe8ead2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -12354,6 +12354,8 @@ void ImGui::BeginAsDockableDragDropTarget(ImGuiWindow* window) if (DockNodePreviewDockCalc(window, root_node, payload_window, &split_outer, is_explicit_target, true)) split_data = &split_outer; DockNodePreviewDockCalc(window, target_node, payload_window, &split_inner, is_explicit_target, false); + if (split_data == &split_outer) + split_inner.IsDropAllowed = false; // Draw inner then outer, so that previewed tab (in inner data) will be behind the outer drop boxes DockNodePreviewDockRender(window, target_node, payload_window, &split_inner); From 4a8efd7e687ee7fe69e467675293ffd564c2fcb5 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 26 Nov 2018 17:57:01 +0100 Subject: [PATCH 339/828] Docking: Hidden tab-bar triangle reflects focus but using Button colors. (#2109) --- imgui.cpp | 10 +++++++--- imgui_internal.h | 1 + 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 521a0fe8ead2..522a0909e2d8 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5399,14 +5399,15 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Docking: Unhide tab bar if (window->DockNode && window->DockNode->IsHiddenTabBar) { - float unhide_sz_draw = ImFloor(g.FontSize * 0.65f); + float unhide_sz_draw = ImFloor(g.FontSize * 0.70f); float unhide_sz_hit = ImFloor(g.FontSize * 0.55f); ImVec2 p = window->DockNode->Pos; ImRect r(p, p + ImVec2(unhide_sz_hit, unhide_sz_hit)); bool hovered, held; if (ButtonBehavior(r, window->GetID("#UNHIDE"), &hovered, &held, ImGuiButtonFlags_FlattenChildren)) window->DockNode->WantHiddenTabBarToggle = true; - const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); + // FIXME-DOCK: Ideally we'd use ImGuiCol_TitleBgActive/ImGuiCol_TitleBg here, but neither is guaranteed to be visible enough at this sort of size.. + ImU32 col = GetColorU32(((held && hovered) || (window->DockNode->IsFocused && !hovered)) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); window->DrawList->AddTriangleFilled(p, p + ImVec2(unhide_sz_draw, 0.0f), p + ImVec2(0.0f, unhide_sz_draw), col); } @@ -10336,7 +10337,7 @@ ImGuiDockNode::ImGuiDockNode(ImGuiID id) WantCloseTabID = 0; InitFromFirstWindowPosSize = InitFromFirstWindowViewport = false; IsVisible = true; - IsDockSpace = IsCentralNode = IsHiddenTabBar = HasCloseButton = HasCollapseButton = false; + IsFocused = IsDockSpace = IsCentralNode = IsHiddenTabBar = HasCloseButton = HasCollapseButton = false; WantCloseAll = WantLockSizeOnce = WantMouseMove = WantHiddenTabBarToggle = false; } @@ -10856,6 +10857,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) { node->WantCloseAll = false; node->WantCloseTabID = 0; + node->IsFocused = false; if (node->Windows.Size > 0) node->SelectedTabID = node->Windows[0]->ID; } @@ -10914,6 +10916,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w if (node->IsHiddenTabBar) { node->VisibleWindow = (node->Windows.Size > 0) ? node->Windows[0] : NULL; + node->IsFocused = is_focused; // Notify root of visible window (used to display title in OS task bar) if (node->VisibleWindow) @@ -10972,6 +10975,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w ImGuiID focus_tab_id = 0; // Title bar + node->IsFocused = is_focused; ImRect title_bar_rect = ImRect(node->Pos, node->Pos + ImVec2(node->Size.x, g.FontSize + style.FramePadding.y * 2.0f)); ImU32 title_bar_col = GetColorU32(host_window->Collapsed ? ImGuiCol_TitleBgCollapsed : is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg); host_window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, host_window->WindowRounding, ImDrawCornerFlags_Top); diff --git a/imgui_internal.h b/imgui_internal.h index 55d59072d60c..42f86248e171 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -778,6 +778,7 @@ struct ImGuiDockNode bool InitFromFirstWindowPosSize :1; bool InitFromFirstWindowViewport :1; bool IsVisible :1; // Set to false when the node is hidden (usually disabled as it has no active window) + bool IsFocused :1; bool IsDockSpace :1; // Root node was created by a DockSpace() call. bool IsCentralNode :1; bool IsHiddenTabBar :1; From 12a1e7d04ecc758c4e44515d1887b964e795d652 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 26 Nov 2018 19:20:37 +0100 Subject: [PATCH 340/828] Viewport: Comment to suggest making WindowBg opaque when viewports are enabled. --- examples/example_glfw_opengl3/main.cpp | 5 ++++- examples/example_sdl_opengl3/main.cpp | 4 ++++ examples/example_win32_directx11/main.cpp | 5 ++++- imgui.h | 1 + 4 files changed, 13 insertions(+), 2 deletions(-) diff --git a/examples/example_glfw_opengl3/main.cpp b/examples/example_glfw_opengl3/main.cpp index 325c46b87e19..93dcead54a06 100644 --- a/examples/example_glfw_opengl3/main.cpp +++ b/examples/example_glfw_opengl3/main.cpp @@ -97,10 +97,13 @@ int main(int, char**) ImGui_ImplOpenGL3_Init(glsl_version); // Setup Style - ImGui::GetStyle().WindowRounding = 0.0f; ImGui::StyleColorsDark(); //ImGui::StyleColorsClassic(); + ImGuiStyle& style = ImGui::GetStyle(); + style.WindowRounding = 0.0f; // When viewports are enabled it is preferable to disable WinodwRounding + style.Colors[ImGuiCol_WindowBg].w = 1.0f; // When viewports are enabled it is preferable to disable WindowBg alpha + // Load Fonts // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. diff --git a/examples/example_sdl_opengl3/main.cpp b/examples/example_sdl_opengl3/main.cpp index 2a9ccec99ad0..93a49a10b179 100644 --- a/examples/example_sdl_opengl3/main.cpp +++ b/examples/example_sdl_opengl3/main.cpp @@ -91,6 +91,10 @@ int main(int, char**) ImGui::StyleColorsDark(); //ImGui::StyleColorsClassic(); + ImGuiStyle& style = ImGui::GetStyle(); + style.WindowRounding = 0.0f; // When viewports are enabled it is preferable to disable WinodwRounding + style.Colors[ImGuiCol_WindowBg].w = 1.0f; // When viewports are enabled it is preferable to disable WindowBg alpha + // Load Fonts // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. diff --git a/examples/example_win32_directx11/main.cpp b/examples/example_win32_directx11/main.cpp index 93cb137fbd63..6346885d1e3c 100644 --- a/examples/example_win32_directx11/main.cpp +++ b/examples/example_win32_directx11/main.cpp @@ -145,10 +145,13 @@ int main(int, char**) ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext); // Setup Style - ImGui::GetStyle().WindowRounding = 0.0f; ImGui::StyleColorsDark(); //ImGui::StyleColorsClassic(); + ImGuiStyle& style = ImGui::GetStyle(); + style.WindowRounding = 0.0f; // When viewports are enabled it is preferable to disable WinodwRounding + style.Colors[ImGuiCol_WindowBg].w = 1.0f; // When viewports are enabled it is preferable to disable WindowBg alpha + // Load Fonts // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. diff --git a/imgui.h b/imgui.h index 594f57208454..a2ee80fe6d75 100644 --- a/imgui.h +++ b/imgui.h @@ -891,6 +891,7 @@ enum ImGuiConfigFlags_ ImGuiConfigFlags_NoMouseCursorChange = 1 << 5, // Instruct back-end to not alter mouse cursor shape and visibility. Use if the back-end cursor changes are interfering with yours and you don't want to use SetMouseCursor() to change mouse cursor. You may want to honor requests from imgui by reading GetMouseCursor() yourself instead. // [BETA] Viewports + // When using viewports it is recommended that your default value for ImGuiCol_WindowBg is opaque (Alpha=1.0) so transition to a viewport won't be noticeable. ImGuiConfigFlags_ViewportsEnable = 1 << 10, // Viewport enable flags (require both ImGuiConfigFlags_PlatformHasViewports + ImGuiConfigFlags_RendererHasViewports set by the respective back-ends) ImGuiConfigFlags_ViewportsNoTaskBarIcon = 1 << 11, // Disable task bars icons for all secondary viewports (will set ImGuiViewportFlags_NoTaskBarIcon on them) ImGuiConfigFlags_ViewportsNoMerge = 1 << 12, // All floating windows will always create their own viewport and platform window. From 1a0d2578a1776366b124227172c894f8d6ec99e2 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 26 Nov 2018 21:35:44 +0100 Subject: [PATCH 341/828] Viewport: Merging fixes + relying on multiple viewport overlaps. Follow-up to previous attempts are reworking the split/merge mechanisms. (#1542) --- imgui.cpp | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index ddacebe421bf..a7b9faf2ae88 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -969,7 +969,7 @@ const ImGuiID IMGUI_VIEWPORT_DEFAULT_ID = 0x11111111; // Using an arbi static ImGuiViewportP* AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const ImVec2& platform_pos, const ImVec2& size, ImGuiViewportFlags flags); static void UpdateViewports(); static void UpdateSelectWindowViewport(ImGuiWindow* window); -static void UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* host_viewport); +static bool UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* host_viewport); static void SetCurrentViewport(ImGuiWindow* window, ImGuiViewportP* viewport); static bool GetWindowAlwaysWantOwnViewport(ImGuiWindow* window); static int FindPlatformMonitorForPos(const ImVec2& pos); @@ -7182,15 +7182,25 @@ static bool ImGui::GetWindowAlwaysWantOwnViewport(ImGuiWindow* window) return false; } -static void ImGui::UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* viewport) +static bool ImGui::UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* viewport) { ImGuiContext& g = *GImGui; if (!(viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) || window->Viewport == viewport || viewport->PlatformIsMinimized) - return; + return false; if (!viewport->GetRect().Contains(window->Rect())) - return; + return false; if (GetWindowAlwaysWantOwnViewport(window)) - return; + return false; + + for (int n = 0; n < g.Windows.Size; n++) + { + ImGuiWindow* window_behind = g.Windows[n]; + if (window_behind == window) + break; + if (window_behind->WasActive && window_behind->ViewportOwned && !(window_behind->Flags & ImGuiWindowFlags_ChildWindow)) + if (window_behind->Viewport->GetRect().Overlaps(window->Rect())) + return false; + } // Move to the existing viewport, Move child/hosted windows as well (FIXME-OPT: iterate child) ImGuiViewportP* old_viewport = window->Viewport; @@ -7200,6 +7210,8 @@ static void ImGui::UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImG SetWindowViewport(g.Windows[n], viewport); SetWindowViewport(window, viewport); BringWindowToDisplayFront(window); + + return true; } // Scale all windows (position, size). Use when e.g. changing DPI. (This is a lossy operation!) @@ -7262,7 +7274,10 @@ static void ImGui::UpdateViewports() // Clear references to this viewport in windows (window->ViewportId becomes the master data) for (int window_n = 0; window_n < g.Windows.Size; window_n++) if (g.Windows[window_n]->Viewport == viewport) + { g.Windows[window_n]->Viewport = NULL; + g.Windows[window_n]->ViewportOwned = false; + } if (viewport == g.MouseLastHoveredViewport) g.MouseLastHoveredViewport = NULL; g.Viewports.erase(g.Viewports.Data + n); @@ -7447,8 +7462,9 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) } // Merge into host viewport + bool try_to_merge_into_host_viewport = false; if (window->ViewportOwned && g.ActiveId == 0) - UpdateTryMergeWindowIntoHostViewport(window, g.Viewports[0]); + try_to_merge_into_host_viewport = true; window->ViewportOwned = false; // Appearing popups reset their viewport so they can inherit again @@ -7479,7 +7495,7 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) window->Viewport = FindViewportByID(g.NextWindowData.ViewportId); window->ViewportId = g.NextWindowData.ViewportId; // Store ID even if Viewport isn't resolved yet. } - else if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_ChildMenu)) + else if ((flags & ImGuiWindowFlags_ChildWindow) || (flags & ImGuiWindowFlags_ChildMenu)) { // Always inherit viewport from parent window window->Viewport = window->ParentWindow->Viewport; @@ -7497,6 +7513,11 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) if (window->Viewport != NULL && window->Viewport->Window == window) window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_None); } + else + { + if (try_to_merge_into_host_viewport) + UpdateTryMergeWindowIntoHostViewport(window, g.Viewports[0]); + } // Fallback to default viewport if (window->Viewport == NULL) From 962dcb466d95610ce43ee487ee2c175c253e11d2 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 26 Nov 2018 21:52:40 +0100 Subject: [PATCH 342/828] Docking: Added ImGuiDockNodeFlags_NoResize. (#2109) --- imgui.cpp | 134 ++++++++++++++++++++++++++----------------------- imgui.h | 9 ++-- imgui_demo.cpp | 7 +-- 3 files changed, 80 insertions(+), 70 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index ed32c3423e25..f88f2a2c378b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11618,82 +11618,90 @@ void ImGui::DockNodeTreeUpdateSplitter(ImGuiDockNode* node) bb.Max[axis ^ 1] += child_1->Size[axis ^ 1]; //if (g.IO.KeyCtrl) GetOverlayDrawList(g.CurrentWindow->Viewport)->AddRect(bb.Min, bb.Max, IM_COL32(255,0,255,255)); - //bb.Min[axis] += 1; // Display a little inward so highlight doesn't connect with nearby tabs on the neighbor node. - //bb.Max[axis] -= 1; - PushID(node->ID); - - // Gather list of nodes that are touching the splitter line. Find resizing limits based on those nodes. - ImVector touching_nodes[2]; - float min_size = g.Style.WindowMinSize[axis]; - float resize_limits[2]; - resize_limits[0] = node->ChildNodes[0]->Pos[axis] + min_size; - resize_limits[1] = node->ChildNodes[1]->Pos[axis] + node->ChildNodes[1]->Size[axis] - min_size; - - ImGuiID splitter_id = GetID("##Splitter"); - if (g.ActiveId == splitter_id) - { - // Only process when splitter is active - DockNodeTreeUpdateSplitterFindTouchingNode(child_0, axis, 1, &touching_nodes[0]); - DockNodeTreeUpdateSplitterFindTouchingNode(child_1, axis, 0, &touching_nodes[1]); - for (int touching_node_n = 0; touching_node_n < touching_nodes[0].Size; touching_node_n++) - resize_limits[0] = ImMax(resize_limits[0], touching_nodes[0][touching_node_n]->Rect().Min[axis] + min_size); - for (int touching_node_n = 0; touching_node_n < touching_nodes[1].Size; touching_node_n++) - resize_limits[1] = ImMin(resize_limits[1], touching_nodes[1][touching_node_n]->Rect().Max[axis] - min_size); - - /* - // [DEBUG] Render limits - ImDrawList* draw_list = node->HostWindow ? GetOverlayDrawList(node->HostWindow) : GetOverlayDrawList((ImGuiViewportP*)GetMainViewport()); - for (int n = 0; n < 2; n++) - if (axis == ImGuiAxis_X) - draw_list->AddLine(ImVec2(resize_limits[n], node->ChildNodes[n]->Pos.y), ImVec2(resize_limits[n], node->ChildNodes[n]->Pos.y + node->ChildNodes[n]->Size.y), IM_COL32(255, 0, 255, 255), 3.0f); - else - draw_list->AddLine(ImVec2(node->ChildNodes[n]->Pos.x, resize_limits[n]), ImVec2(node->ChildNodes[n]->Pos.x + node->ChildNodes[n]->Size.x, resize_limits[n]), IM_COL32(255, 0, 255, 255), 3.0f); - */ + if (node->Flags & ImGuiDockNodeFlags_NoResize) + { + ImGuiWindow* window = g.CurrentWindow; + window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_Separator), g.Style.FrameRounding); } - - // Use a short delay before highlighting the splitter (and changing the mouse cursor) in order for regular mouse movement to not highlight many splitters - float cur_size_0 = child_0->Size[axis]; - float cur_size_1 = child_1->Size[axis]; - float min_size_0 = resize_limits[0] - child_0->Pos[axis]; - float min_size_1 = child_1->Pos[axis] + child_1->Size[axis] - resize_limits[1]; - if (SplitterBehavior(bb, GetID("##Splitter"), axis, &cur_size_0, &cur_size_1, min_size_0, min_size_1, RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS, RESIZE_WINDOWS_FROM_EDGES_FEEDBACK_TIMER)) + else { - if (touching_nodes[0].Size > 0 && touching_nodes[1].Size > 0) + //bb.Min[axis] += 1; // Display a little inward so highlight doesn't connect with nearby tabs on the neighbor node. + //bb.Max[axis] -= 1; + PushID(node->ID); + + // Gather list of nodes that are touching the splitter line. Find resizing limits based on those nodes. + ImVector touching_nodes[2]; + float min_size = g.Style.WindowMinSize[axis]; + float resize_limits[2]; + resize_limits[0] = node->ChildNodes[0]->Pos[axis] + min_size; + resize_limits[1] = node->ChildNodes[1]->Pos[axis] + node->ChildNodes[1]->Size[axis] - min_size; + + ImGuiID splitter_id = GetID("##Splitter"); + if (g.ActiveId == splitter_id) + { + // Only process when splitter is active + DockNodeTreeUpdateSplitterFindTouchingNode(child_0, axis, 1, &touching_nodes[0]); + DockNodeTreeUpdateSplitterFindTouchingNode(child_1, axis, 0, &touching_nodes[1]); + for (int touching_node_n = 0; touching_node_n < touching_nodes[0].Size; touching_node_n++) + resize_limits[0] = ImMax(resize_limits[0], touching_nodes[0][touching_node_n]->Rect().Min[axis] + min_size); + for (int touching_node_n = 0; touching_node_n < touching_nodes[1].Size; touching_node_n++) + resize_limits[1] = ImMin(resize_limits[1], touching_nodes[1][touching_node_n]->Rect().Max[axis] - min_size); + + /* + // [DEBUG] Render limits + ImDrawList* draw_list = node->HostWindow ? GetOverlayDrawList(node->HostWindow) : GetOverlayDrawList((ImGuiViewportP*)GetMainViewport()); + for (int n = 0; n < 2; n++) + if (axis == ImGuiAxis_X) + draw_list->AddLine(ImVec2(resize_limits[n], node->ChildNodes[n]->Pos.y), ImVec2(resize_limits[n], node->ChildNodes[n]->Pos.y + node->ChildNodes[n]->Size.y), IM_COL32(255, 0, 255, 255), 3.0f); + else + draw_list->AddLine(ImVec2(node->ChildNodes[n]->Pos.x, resize_limits[n]), ImVec2(node->ChildNodes[n]->Pos.x + node->ChildNodes[n]->Size.x, resize_limits[n]), IM_COL32(255, 0, 255, 255), 3.0f); + */ + } + + // Use a short delay before highlighting the splitter (and changing the mouse cursor) in order for regular mouse movement to not highlight many splitters + float cur_size_0 = child_0->Size[axis]; + float cur_size_1 = child_1->Size[axis]; + float min_size_0 = resize_limits[0] - child_0->Pos[axis]; + float min_size_1 = child_1->Pos[axis] + child_1->Size[axis] - resize_limits[1]; + if (SplitterBehavior(bb, GetID("##Splitter"), axis, &cur_size_0, &cur_size_1, min_size_0, min_size_1, RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS, RESIZE_WINDOWS_FROM_EDGES_FEEDBACK_TIMER)) { - child_0->Size[axis] = child_0->SizeRef[axis] = cur_size_0; - child_1->Pos[axis] -= cur_size_1 - child_1->Size[axis]; - child_1->Size[axis] = child_1->SizeRef[axis] = cur_size_1; + if (touching_nodes[0].Size > 0 && touching_nodes[1].Size > 0) + { + child_0->Size[axis] = child_0->SizeRef[axis] = cur_size_0; + child_1->Pos[axis] -= cur_size_1 - child_1->Size[axis]; + child_1->Size[axis] = child_1->SizeRef[axis] = cur_size_1; - // Lock the size of every node that is a sibling of the node we are touching - // This might be less desirable if we can merge sibling of a same axis into the same parental level. + // Lock the size of every node that is a sibling of the node we are touching + // This might be less desirable if we can merge sibling of a same axis into the same parental level. #if 1 - for (int side_n = 0; side_n < 2; side_n++) - for (int touching_node_n = 0; touching_node_n < touching_nodes[side_n].Size; touching_node_n++) - { - ImGuiDockNode* touching_node = touching_nodes[side_n][touching_node_n]; - //ImDrawList* draw_list = node->HostWindow ? GetOverlayDrawList(node->HostWindow) : GetOverlayDrawList((ImGuiViewportP*)GetMainViewport()); - //draw_list->AddRect(touching_node->Pos, touching_node->Pos + touching_node->Size, IM_COL32(255, 128, 0, 255)); - while (touching_node->ParentNode != node) + for (int side_n = 0; side_n < 2; side_n++) + for (int touching_node_n = 0; touching_node_n < touching_nodes[side_n].Size; touching_node_n++) { - if (touching_node->ParentNode->SplitAxis == axis) + ImGuiDockNode* touching_node = touching_nodes[side_n][touching_node_n]; + //ImDrawList* draw_list = node->HostWindow ? GetOverlayDrawList(node->HostWindow) : GetOverlayDrawList((ImGuiViewportP*)GetMainViewport()); + //draw_list->AddRect(touching_node->Pos, touching_node->Pos + touching_node->Size, IM_COL32(255, 128, 0, 255)); + while (touching_node->ParentNode != node) { - // Mark other node so its size will be preserved during the upcoming call to DockNodeTreeUpdatePosSize(). - ImGuiDockNode* node_to_preserve = touching_node->ParentNode->ChildNodes[side_n]; - node_to_preserve->WantLockSizeOnce = true; - //draw_list->AddRect(touching_node->Pos, touching_node->Rect().Max, IM_COL32(255, 0, 0, 255)); - //draw_list->AddRectFilled(node_to_preserve->Pos, node_to_preserve->Rect().Max, IM_COL32(0, 255, 0, 100)); + if (touching_node->ParentNode->SplitAxis == axis) + { + // Mark other node so its size will be preserved during the upcoming call to DockNodeTreeUpdatePosSize(). + ImGuiDockNode* node_to_preserve = touching_node->ParentNode->ChildNodes[side_n]; + node_to_preserve->WantLockSizeOnce = true; + //draw_list->AddRect(touching_node->Pos, touching_node->Rect().Max, IM_COL32(255, 0, 0, 255)); + //draw_list->AddRectFilled(node_to_preserve->Pos, node_to_preserve->Rect().Max, IM_COL32(0, 255, 0, 100)); + } + touching_node = touching_node->ParentNode; } - touching_node = touching_node->ParentNode; } - } #endif - DockNodeTreeUpdatePosSize(child_0, child_0->Pos, child_0->Size); - DockNodeTreeUpdatePosSize(child_1, child_1->Pos, child_1->Size); - MarkIniSettingsDirty(); + DockNodeTreeUpdatePosSize(child_0, child_0->Pos, child_0->Size); + DockNodeTreeUpdatePosSize(child_1, child_1->Pos, child_1->Size); + MarkIniSettingsDirty(); + } } + PopID(); } - PopID(); } if (child_0->IsVisible) diff --git a/imgui.h b/imgui.h index 6dc4553b5ccb..0096bfc08fb7 100644 --- a/imgui.h +++ b/imgui.h @@ -800,16 +800,17 @@ enum ImGuiTabItemFlags_ ImGuiTabItemFlags_NoPushId = 1 << 3 // Don't call PushID(tab->ID)/PopID() on BeginTabItem()/EndTabItem() }; -// Flags for ImGui::DockSpace() +// Flags for ImGui::DockSpace(), inherited by child nodes. enum ImGuiDockNodeFlags_ { ImGuiDockNodeFlags_None = 0, ImGuiDockNodeFlags_KeepAliveOnly = 1 << 0, // Don't display the dockspace node but keep it alive. Windows docked into this dockspace node won't be undocked. ImGuiDockNodeFlags_NoSplit = 1 << 1, // Disable splitting the node into smaller nodes. Useful e.g. when embedding dockspaces into a main root one (the root one may have splitting disabled to reduce confusion) //ImGuiDockNodeFlags_NoCentralNode = 1 << 2, // Disable Central Node (the node which can stay empty) - //ImGuiDockNodeFlags_NoOuterBorder = 1 << 3, // Disable outer border on a DockSpace() node. - ImGuiDockNodeFlags_NoDockingInCentralNode = 1 << 4, // Disable docking inside the Central Node, which will be always kept empty. - ImGuiDockNodeFlags_PassthruDockspace = 1 << 5 // 1) DockSpace() will render a ImGuiCol_WindowBg background covering everything excepted the Central Node when empty. Meaning the host window should probably use SetNextWindowBgAlpha(0.0f) prior to Begin() when using this. 2) When Central Node is empty: let inputs pass-through + won't display a DockingEmptyBg background. + ImGuiDockNodeFlags_NoDockingInCentralNode = 1 << 3, // Disable docking inside the Central Node, which will be always kept empty. + //ImGuiDockNodeFlags_NoLayoutChanges = 1 << 4, // Disable adding/removing nodes interactively. Useful with programatically setup dockspaces. + ImGuiDockNodeFlags_NoResize = 1 << 5, // Disable resizing child nodes using the splitter/separators. Useful with programatically setup dockspaces. + ImGuiDockNodeFlags_PassthruDockspace = 1 << 6 // 1) DockSpace() will render a ImGuiCol_WindowBg background covering everything excepted the Central Node when empty. Meaning the host window should probably use SetNextWindowBgAlpha(0.0f) prior to Begin() when using this. 2) When Central Node is empty: let inputs pass-through + won't display a DockingEmptyBg background. }; // Flags for ImGui::IsWindowFocused() diff --git a/imgui_demo.cpp b/imgui_demo.cpp index a2c5a85f5487..a9f5cd80f25e 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3831,9 +3831,10 @@ void ShowExampleAppDockSpace(bool* p_open) // which we can't undo at the moment without finer window depth/z control. //ImGui::MenuItem("Fullscreen", NULL, &opt_fullscreen_persistant); - if (ImGui::MenuItem("Flag: NoSplit", "", (opt_flags & ImGuiDockNodeFlags_NoSplit) != 0)) opt_flags ^= ImGuiDockNodeFlags_NoSplit; - if (ImGui::MenuItem("Flag: NoDockingInCentralNode", "", (opt_flags & ImGuiDockNodeFlags_NoDockingInCentralNode) != 0)) opt_flags ^= ImGuiDockNodeFlags_NoDockingInCentralNode; - if (ImGui::MenuItem("Flag: PassthruDockspace", "", (opt_flags & ImGuiDockNodeFlags_PassthruDockspace) != 0)) opt_flags ^= ImGuiDockNodeFlags_PassthruDockspace; + if (ImGui::MenuItem("Flag: NoSplit", "", (opt_flags & ImGuiDockNodeFlags_NoSplit) != 0)) opt_flags ^= ImGuiDockNodeFlags_NoSplit; + if (ImGui::MenuItem("Flag: NoDockingInCentralNode", "", (opt_flags & ImGuiDockNodeFlags_NoDockingInCentralNode) != 0)) opt_flags ^= ImGuiDockNodeFlags_NoDockingInCentralNode; + if (ImGui::MenuItem("Flag: NoResize", "", (opt_flags & ImGuiDockNodeFlags_NoResize) != 0)) opt_flags ^= ImGuiDockNodeFlags_NoResize; + if (ImGui::MenuItem("Flag: PassthruDockspace", "", (opt_flags & ImGuiDockNodeFlags_PassthruDockspace) != 0)) opt_flags ^= ImGuiDockNodeFlags_PassthruDockspace; ImGui::Separator(); if (ImGui::MenuItem("Close DockSpace", NULL, false, p_open != NULL)) *p_open = false; From 3f51c831def4c99ce704bbea4bfc62844ff7b0be Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 26 Nov 2018 22:18:55 +0100 Subject: [PATCH 343/828] Docking: Added internal DockBuilderGetCentralNode(). Fixed being unable to undock whole node from dock button in DockSpace with a central node. (#2109) --- imgui.cpp | 11 ++++++----- imgui_internal.h | 4 +++- imgui_widgets.cpp | 10 +++++++++- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index f88f2a2c378b..5c60c4d94108 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10350,7 +10350,7 @@ ImGuiDockNode::ImGuiDockNode(ImGuiID id) SplitAxis = ImGuiAxis_None; HostWindow = VisibleWindow = NULL; - OnlyNodeWithWindows = NULL; + CentralNode = OnlyNodeWithWindows = NULL; LastFrameAlive = LastFrameActive = -1; LastFocusedNodeID = 0; SelectedTabID = 0; @@ -10674,7 +10674,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) IM_ASSERT(node->LastFrameActive != g.FrameCount); node->LastFrameAlive = g.FrameCount; - ImGuiDockNode* central_node = NULL; + node->CentralNode = node->OnlyNodeWithWindows = NULL; if (node->IsRootNode()) { DockNodeUpdateVisibleFlagAndInactiveChilds(node); @@ -10682,10 +10682,10 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) // Find if there's only a single visible window in the hierarchy (in which case we need to display a regular title bar -> FIXME-DOCK: that last part is not done yet!) ImGuiDockNodeUpdateScanResults results; DockNodeUpdateScanRec(node, &results); - node->OnlyNodeWithWindows = (results.CountNodesWithWindows == 1 ? results.FirstNodeWithWindows : NULL); + node->CentralNode = results.CentralNode; + node->OnlyNodeWithWindows = (results.CountNodesWithWindows == 1) ? results.FirstNodeWithWindows : NULL; if (node->LastFocusedNodeID == 0 && results.FirstNodeWithWindows != NULL) node->LastFocusedNodeID = results.FirstNodeWithWindows->ID; - central_node = results.CentralNode; // Copy the dock family from of our window so it can be used for proper dock filtering. // When node has mixed windows, prioritize the family with the most constraint (CompatibleWithNeutral = false) as the reference to copy. @@ -10825,7 +10825,8 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) } // Register a hit-test hole in the window unless we are currently dragging a window that is compatible with our dockspace - bool central_node_hole = node->IsRootNode() && host_window && (node->Flags & ImGuiDockNodeFlags_PassthruDockspace) != 0 && central_node != NULL && central_node->IsEmpty(); + const ImGuiDockNode* central_node = node->CentralNode; + const bool central_node_hole = node->IsRootNode() && host_window && (node->Flags & ImGuiDockNodeFlags_PassthruDockspace) != 0 && central_node != NULL && central_node->IsEmpty(); bool central_node_hole_register_hit_test_hole = central_node_hole; if (central_node_hole) if (const ImGuiPayload* payload = ImGui::GetDragDropPayload()) diff --git a/imgui_internal.h b/imgui_internal.h index 42f86248e171..679279494b22 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -769,7 +769,8 @@ struct ImGuiDockNode ImGuiWindow* HostWindow; ImGuiWindow* VisibleWindow; - ImGuiDockNode* OnlyNodeWithWindows; // [Root node only] Set when there is a single visible node within the hierarchy + ImGuiDockNode* CentralNode; // [Root node only] Pointer to central node. + ImGuiDockNode* OnlyNodeWithWindows; // [Root node only] Set when there is a single visible node within the hierarchy. int LastFrameAlive; // Last frame number the node was updated or kept alive explicitly with DockSpace() + ImGuiDockNodeFlags_KeepAliveOnly int LastFrameActive; // Last frame number the node was updated. ImGuiID LastFocusedNodeID; // [Root node only] Which of our child node (any ancestor in the hierarchy) was last focused. @@ -1505,6 +1506,7 @@ namespace ImGui // Docking - Builder function needs to be generally called before the DockSpace() node is submitted. IMGUI_API void DockBuilderDockWindow(const char* window_name, ImGuiID node_id); IMGUI_API ImGuiDockNode*DockBuilderGetNode(ImGuiID node_id); // Warning: DO NOT HOLD ON ImGuiDockNode* pointer, will be invalided by any split/merge/remove operation. + inline ImGuiDockNode* DockBuilderGetCentralNode(ImGuiID node_id) { ImGuiDockNode* node = DockBuilderGetNode(node_id); if (!node) return NULL; return DockNodeGetRootNode(node)->CentralNode; } IMGUI_API void DockBuilderAddNode(ImGuiID node_id, ImVec2 ref_size, ImGuiDockNodeFlags flags = 0); IMGUI_API void DockBuilderRemoveNode(ImGuiID node_id); // Remove node and all its child, undock all windows IMGUI_API void DockBuilderRemoveNodeDockedWindows(ImGuiID node_id, bool clear_persistent_docking_references = true); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index d9d3e34f6093..a638eb689fa9 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -671,6 +671,7 @@ bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos, float radius) return pressed; } +// The Collapse button also functions as a Dock Menu button. bool ImGui::CollapseButton(ImGuiID id, const ImVec2& pos, ImGuiDockNode* dock_node) { ImGuiContext& g = *GImGui; @@ -695,7 +696,14 @@ bool ImGui::CollapseButton(ImGuiID id, const ImVec2& pos, ImGuiDockNode* dock_no // Switch to moving the window after mouse is moved beyond the initial drag threshold if (IsItemActive() && IsMouseDragging(0)) { - if (dock_node != NULL && DockNodeGetRootNode(dock_node)->OnlyNodeWithWindows != dock_node) + bool can_extract_dock_node = false; + if (dock_node != NULL) + { + ImGuiDockNode* root_node = DockNodeGetRootNode(dock_node); + if (root_node->OnlyNodeWithWindows != dock_node || (root_node->CentralNode != NULL)) + can_extract_dock_node = true; + } + if (can_extract_dock_node) { float threshold_base = g.FontSize; float threshold_x = (threshold_base * 2.2f); From 4575354bc038dbd955fca55a1313f94dc9aa9b26 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 27 Nov 2018 12:05:30 +0100 Subject: [PATCH 344/828] Docking: Comments + maiintain LastFrameFocused per node + using bitfiield for docking bools. --- docs/TODO.txt | 2 +- imgui.cpp | 38 +++++++++++++++++++++++--------------- imgui_internal.h | 21 +++++++++++---------- 3 files changed, 35 insertions(+), 26 deletions(-) diff --git a/docs/TODO.txt b/docs/TODO.txt index f6972fab64b8..4db4199ad0d9 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -133,7 +133,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - dock: B- single visible node part of a hidden split hierarchy (OnlyNodeWithWindows != NULL) should show a normal title bar (not a tab bar) - dock: B~ SetNextWindowDock() calls (with conditional) -> defer everything to DockContextUpdate (repro: Documents->[X]Windows->Dock 1 elsewhere->Click Redock All - dock: B~ tidy up tab list popup buttons features (available with manual tab-bar, see ImGuiTabBarFlags_NoTabListPopupButton code, not used by docking nodes) - - dock: B- DockSpace() border issues + - dock: B- SetNextWindowDockId(0) with a second Begin() in the frame will asserts - dock: B- inconsistent clipping/border 1-pixel issue (#2) - dock: B- fix/disable auto-resize grip on split host nodes (~#2) - dock: B- SetNextWindowFocus() doesn't seem to apply if the window is hidden this frame, need repro (#4) diff --git a/imgui.cpp b/imgui.cpp index 5c60c4d94108..03385094d159 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10351,7 +10351,7 @@ ImGuiDockNode::ImGuiDockNode(ImGuiID id) HostWindow = VisibleWindow = NULL; CentralNode = OnlyNodeWithWindows = NULL; - LastFrameAlive = LastFrameActive = -1; + LastFrameAlive = LastFrameActive = LastFrameFocused = -1; LastFocusedNodeID = 0; SelectedTabID = 0; WantCloseTabID = 0; @@ -10565,10 +10565,10 @@ static void ImGui::DockNodeHideHostWindow(ImGuiDockNode* node) struct ImGuiDockNodeUpdateScanResults { - ImGuiDockNode* CentralNode; - ImGuiDockNode* FirstNodeWithWindows; - int CountNodesWithWindows; - ImGuiDockFamily DockFamilyForMerges; + ImGuiDockNode* CentralNode; + ImGuiDockNode* FirstNodeWithWindows; + int CountNodesWithWindows; + //ImGuiDockFamily DockFamilyForMerges; ImGuiDockNodeUpdateScanResults() { CentralNode = FirstNodeWithWindows = NULL; CountNodesWithWindows = 0; } }; @@ -10594,6 +10594,8 @@ static void DockNodeUpdateScanRec(ImGuiDockNode* node, ImGuiDockNodeUpdateScanRe DockNodeUpdateScanRec(node->ChildNodes[1], results); } +// - Remove inactive windows/nodes. +// - Update visibility flag. static void ImGui::DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* node) { ImGuiContext& g = *GImGui; @@ -10679,7 +10681,9 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) { DockNodeUpdateVisibleFlagAndInactiveChilds(node); - // Find if there's only a single visible window in the hierarchy (in which case we need to display a regular title bar -> FIXME-DOCK: that last part is not done yet!) + // FIXME-DOCK: Merge this scan into the one above. + // - Setup central node pointers + // - Find if there's only a single visible window in the hierarchy (in which case we need to display a regular title bar -> FIXME-DOCK: that last part is not done yet!) ImGuiDockNodeUpdateScanResults results; DockNodeUpdateScanRec(node, &results); node->CentralNode = results.CentralNode; @@ -10756,7 +10760,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) node->HasCollapseButton = (node->Windows.Size > 0); for (int window_n = 0; window_n < node->Windows.Size; window_n++) { - // FIXME: Setting DockIsActive here means that for single active window in a leaf node, DockIsActive will be cleared until the next Begin() call. + // FIXME-DOCK: Setting DockIsActive here means that for single active window in a leaf node, DockIsActive will be cleared until the next Begin() call. ImGuiWindow* window = node->Windows[window_n]; window->DockIsActive = (node->Windows.Size > 1); node->HasCloseButton |= window->HasCloseButton; @@ -10938,6 +10942,8 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w { node->VisibleWindow = (node->Windows.Size > 0) ? node->Windows[0] : NULL; node->IsFocused = is_focused; + if (is_focused) + node->LastFrameFocused = g.FrameCount; // Notify root of visible window (used to display title in OS task bar) if (node->VisibleWindow) @@ -10997,6 +11003,8 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w // Title bar node->IsFocused = is_focused; + if (is_focused) + node->LastFrameFocused = g.FrameCount; ImRect title_bar_rect = ImRect(node->Pos, node->Pos + ImVec2(node->Size.x, g.FontSize + style.FramePadding.y * 2.0f)); ImU32 title_bar_col = GetColorU32(host_window->Collapsed ? ImGuiCol_TitleBgCollapsed : is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg); host_window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, host_window->WindowRounding, ImDrawCornerFlags_Top); @@ -11850,15 +11858,15 @@ ImGuiID ImGui::DockSpaceOverViewport(ImGuiViewport* viewport, ImGuiDockNodeFlags char label[32]; ImFormatString(label, IM_ARRAYSIZE(label), "DockspaceViewport_%08X", viewport->ID); - ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); - ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); - ImGui::Begin(label, NULL, host_window_flags); - ImGui::PopStyleVar(3); + PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); + PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); + Begin(label, NULL, host_window_flags); + PopStyleVar(3); - ImGuiID dockspace_id = ImGui::GetID("Dockspace"); - ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags, dock_family); - ImGui::End(); + ImGuiID dockspace_id = GetID("Dockspace"); + DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags, dock_family); + End(); return dockspace_id; } diff --git a/imgui_internal.h b/imgui_internal.h index 679279494b22..d2c3a2a0e9a1 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -752,19 +752,19 @@ struct ImGuiTabBarSortItem float Width; }; -// sizeof() 108~144 +// sizeof() 116~160 struct ImGuiDockNode { ImGuiID ID; ImGuiDockNodeFlags Flags; ImGuiDockNode* ParentNode; - ImGuiDockNode* ChildNodes[2]; // [Split node only] Child nodes (left/right or top/bottom). Consider switching to an array. - ImVector Windows; // Note: unordered list! Iterate TabBar->Tabs for user-order. + ImGuiDockNode* ChildNodes[2]; // [Split node only] Child nodes (left/right or top/bottom). Consider switching to an array. + ImVector Windows; // Note: unordered list! Iterate TabBar->Tabs for user-order. ImGuiTabBar* TabBar; - ImVec2 Pos; // Current position - ImVec2 Size; // Current size - ImVec2 SizeRef; // [Split node only] Last explicitly written-to size (overridden when using a splitter affecting the node), used to calculate Size. - int SplitAxis; // [Split node only] Split axis (X or Y) + ImVec2 Pos; // Current position + ImVec2 Size; // Current size + ImVec2 SizeRef; // [Split node only] Last explicitly written-to size (overridden when using a splitter affecting the node), used to calculate Size. + int SplitAxis; // [Split node only] Split axis (X or Y) ImGuiDockFamily DockFamily; ImGuiWindow* HostWindow; @@ -773,6 +773,7 @@ struct ImGuiDockNode ImGuiDockNode* OnlyNodeWithWindows; // [Root node only] Set when there is a single visible node within the hierarchy. int LastFrameAlive; // Last frame number the node was updated or kept alive explicitly with DockSpace() + ImGuiDockNodeFlags_KeepAliveOnly int LastFrameActive; // Last frame number the node was updated. + int LastFrameFocused; // Last frame number the node was focused. ImGuiID LastFocusedNodeID; // [Root node only] Which of our child node (any ancestor in the hierarchy) was last focused. ImGuiID SelectedTabID; // [Tab node only] Which of our tab is selected. ImGuiID WantCloseTabID; // [Tab node only] Set when closing a specific tab. @@ -1274,9 +1275,9 @@ struct IMGUI_API ImGuiWindow ImGuiItemStatusFlags DockTabItemStatusFlags; ImRect DockTabItemRect; short DockOrder; // Order of the last time the window was visible within its DockNode. This is used to reorder windows that are reappearing on the same frame. Same value between windows that were active and windows that were none are possible. - bool DockIsActive; // =~ (DockNode != NULL) && (DockNode->Windows.Size > 1) - bool DockTabIsVisible; // Is the window visible this frame? =~ is the corresponding tab selected? - bool DockTabWantClose; + bool DockIsActive :1; // =~ (DockNode != NULL) && (DockNode->Windows.Size > 1) + bool DockTabIsVisible :1; // Is the window visible this frame? =~ is the corresponding tab selected? + bool DockTabWantClose :1; public: ImGuiWindow(ImGuiContext* context, const char* name); From fc16e546128e45e89a4dac192b18aa7f48d35c2f Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 27 Nov 2018 13:35:05 +0100 Subject: [PATCH 345/828] Docking: Maintain CentralNode. When SetNextWindowDockID() on a now split node id, we dig to find the central node or the last focused node. (#2109) --- imgui.cpp | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 03385094d159..295a3197d2c4 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11752,9 +11752,25 @@ void ImGui::SetWindowDock(ImGuiWindow* window, ImGuiID dock_id, ImGuiCond cond) return; window->SetWindowDockAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing); - // Set if (window->DockId == dock_id) return; + + // If the user attempt to set a dock id that is a split node, we'll dig within to find a suitable docking spot + ImGuiContext* ctx = GImGui; + if (ImGuiDockNode* new_node = DockContextFindNodeByID(ctx, dock_id)) + if (new_node->IsSplitNode()) + { + // Policy: Find central node or latest focused node. We first move back to our root node. + new_node = DockNodeGetRootNode(new_node); + if (new_node->CentralNode) + dock_id = new_node->CentralNode->ID; + else + dock_id = new_node->LastFocusedNodeID; + } + + if (window->DockId == dock_id) + return; + if (window->DockNode) DockNodeRemoveWindow(window->DockNode, window, 0); window->DockId = dock_id; @@ -12228,15 +12244,18 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) if (window->DockId != 0 && dock_node == NULL) { dock_node = DockContextFindNodeByID(ctx, window->DockId); - if (dock_node == NULL) - dock_node = DockContextAddNode(ctx, window->DockId); - - if (dock_node->IsSplitNode()) + + // We should not be docking into a split node (SetWindowDock should avoid this) + if (dock_node && dock_node->IsSplitNode()) { DockContextProcessUndockWindow(ctx, window); return; } + // Create node + if (dock_node == NULL) + dock_node = DockContextAddNode(ctx, window->DockId); + DockNodeAddWindow(dock_node, window, true); IM_ASSERT(dock_node == window->DockNode); From e138a5fcb51377e10450a16362e8eae266be39ea Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 27 Nov 2018 17:40:25 +0100 Subject: [PATCH 346/828] Docking: Fixed debug Rebuild operation. --- imgui.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 295a3197d2c4..eecff101a4a0 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11970,7 +11970,8 @@ void ImGui::DockBuilderRemoveNodeChildNodes(ImGuiID root_id) // DockNodeMoveWindows->DockNodeAddWindow will normally set those when reaching two windows (which is only adequate during interactive merge) // Make sure we don't lose our current pos/size. (FIXME-DOCK: Consider tidying up that code in DockNodeAddWindow instead) - root_node->InitFromFirstWindowPosSize = false; + if (root_node) + root_node->InitFromFirstWindowPosSize = false; // Apply to settings for (int settings_n = 0; settings_n < ctx->SettingsWindows.Size; settings_n++) From c08b4b46f4c4e142cfb46defcd41a5e941c40296 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 27 Nov 2018 19:45:37 +0100 Subject: [PATCH 347/828] Viewport: Better support for toggling ImGuiConfigFlags_ViewportsEnable. (#2196) --- imgui.cpp | 33 +++++++++++++++++---------------- imgui_demo.cpp | 1 - imgui_internal.h | 2 ++ 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index a7b9faf2ae88..a142608b072a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3000,7 +3000,7 @@ void ImGui::UpdateMouseMovingWindow() { // Try to merge the window back into the main viewport. // This works because MouseViewport should be != MovingWindow->Viewport on release (as per code in UpdateViewports) - if (g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + if (g.ConfigFlagsForFrame & ImGuiConfigFlags_ViewportsEnable) UpdateTryMergeWindowIntoHostViewport(moving_window, g.MouseViewport); // Restore the mouse viewport so that we don't hover the viewport _under_ the moved window during the frame we released the mouse button. @@ -3303,6 +3303,7 @@ void ImGui::NewFrame() g.FrameCount += 1; g.TooltipOverrideCount = 0; g.WindowsActiveCount = 0; + g.ConfigFlagsForFrame = g.IO.ConfigFlags; UpdateViewports(); @@ -3778,6 +3779,7 @@ void ImGui::EndFrame() for (int i = 0; i < g.Viewports.Size; i++) { ImGuiViewportP* viewport = g.Viewports[i]; + viewport->LastPos = viewport->Pos; if (viewport->LastFrameActive < g.FrameCount || viewport->Size.x <= 0.0f || viewport->Size.y <= 0.0f) continue; if (viewport->Window && !IsWindowActiveAndVisible(viewport->Window)) @@ -7175,7 +7177,7 @@ static bool ImGui::GetWindowAlwaysWantOwnViewport(ImGuiWindow* window) { // Tooltips and menus are not automatically forced into their own viewport when the NoMerge flag is set, however the multiplication of viewports makes them more likely to protude and create their own. ImGuiContext& g = *GImGui; - if ((g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsNoMerge) && (g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) + if ((g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsNoMerge) && (g.ConfigFlagsForFrame & ImGuiConfigFlags_ViewportsEnable)) //if (window->DockStatus == ImGuiDockStatus_Floating) if ((window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip)) == 0) return true; @@ -7257,7 +7259,7 @@ static void ImGui::UpdateViewports() ImGuiViewportP* main_viewport = g.Viewports[0]; IM_ASSERT(main_viewport->ID == IMGUI_VIEWPORT_DEFAULT_ID); ImVec2 main_viewport_platform_pos = ImVec2(0.0f, 0.0f); - if ((g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) + if ((g.ConfigFlagsForFrame & ImGuiConfigFlags_ViewportsEnable)) main_viewport_platform_pos = g.PlatformIO.Platform_GetWindowPos(main_viewport); AddUpdateViewport(NULL, IMGUI_VIEWPORT_DEFAULT_ID, main_viewport_platform_pos, g.IO.DisplaySize, ImGuiViewportFlags_CanHostOtherWindows); @@ -7290,7 +7292,7 @@ static void ImGui::UpdateViewports() continue; } - if ((g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) + if ((g.ConfigFlagsForFrame & ImGuiConfigFlags_ViewportsEnable)) { if (g.PlatformIO.Platform_GetWindowMinimized && (n == 0 || viewport->CreatedPlatformWindow)) viewport->PlatformIsMinimized = g.PlatformIO.Platform_GetWindowMinimized(viewport); @@ -7305,17 +7307,18 @@ static void ImGui::UpdateViewports() viewport->Size = viewport->LastPlatformSize = g.PlatformIO.Platform_GetWindowSize(viewport); } - // Translate imgui windows when a Host Viewport has been moved - ImVec2 delta = viewport->Pos - viewport->LastPos; - if ((viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) && (delta.x != 0.0f || delta.y != 0.0f)) - for (int window_n = 0; window_n < g.Windows.Size; window_n++) - if (g.Windows[window_n]->Viewport == viewport) - TranslateWindow(g.Windows[window_n], delta); - // Update monitor (we'll use this info to clamp windows and save windows lost in a removed monitor) viewport->PlatformMonitor = FindPlatformMonitorForRect(viewport->GetRect()); } + // Translate imgui windows when a Host Viewport has been moved + // (This additionally keeps windows at the same place when ImGuiConfigFlags_ViewportsEnable is toggled!) + ImVec2 viewport_delta = viewport->Pos - viewport->LastPos; + if ((viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) && (viewport_delta.x != 0.0f || viewport_delta.y != 0.0f)) + for (int window_n = 0; window_n < g.Windows.Size; window_n++) + if (g.Windows[window_n]->Viewport == viewport || (g.ConfigFlagsForFrame & ImGuiConfigFlags_ViewportsEnable) == 0) + TranslateWindow(g.Windows[window_n], viewport_delta); + // Update DPI scale float new_dpi_scale; if (g.PlatformIO.Platform_GetWindowDpiScale) @@ -7339,7 +7342,7 @@ static void ImGui::UpdateViewports() viewport->DpiScale = new_dpi_scale; } - if (!(g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) + if (!(g.ConfigFlagsForFrame & ImGuiConfigFlags_ViewportsEnable)) { g.MouseViewport = main_viewport; return; @@ -7455,7 +7458,7 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) // Restore main viewport if multi-viewport is not supported by the back-end ImGuiViewportP* main_viewport = g.Viewports[0]; - if (!(g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) + if (!(g.ConfigFlagsForFrame & ImGuiConfigFlags_ViewportsEnable)) { SetWindowViewport(window, main_viewport); return; @@ -7559,8 +7562,7 @@ void ImGui::UpdatePlatformWindows() IM_ASSERT(g.FrameCountEnded == g.FrameCount && "Forgot to call Render() or EndFrame() before UpdatePlatformWindows()?"); IM_ASSERT(g.FrameCountPlatformEnded < g.FrameCount); g.FrameCountPlatformEnded = g.FrameCount; - g.Viewports[0]->LastPos = g.Viewports[0]->Pos; - if (!(g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) + if (!(g.ConfigFlagsForFrame & ImGuiConfigFlags_ViewportsEnable)) return; // Create/resize/destroy platform windows to match each active viewport. @@ -7568,7 +7570,6 @@ void ImGui::UpdatePlatformWindows() for (int i = 1; i < g.Viewports.Size; i++) { ImGuiViewportP* viewport = g.Viewports[i]; - viewport->LastPos = viewport->Pos; // Destroy platform window if the viewport hasn't been submitted or if it is hosting a hidden window (the implicit Debug window will be registered its viewport then be disabled) bool destroy_platform_window = false; diff --git a/imgui_demo.cpp b/imgui_demo.cpp index bb7ac8fd127f..828fe39115db 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -323,7 +323,6 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::CheckboxFlags("io.ConfigFlags: NoMouseCursorChange", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_NoMouseCursorChange); ImGui::SameLine(); ShowHelpMarker("Instruct back-end to not alter mouse cursor shape and visibility."); ImGui::CheckboxFlags("io.ConfigFlags: ViewportsEnable", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_ViewportsEnable); - ImGui::SameLine(); ShowHelpMarker("Toggling this at runtime is normally unsupported (it will offset your windows)."); ImGui::CheckboxFlags("io.ConfigFlags: ViewportsNoTaskBarIcon", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_ViewportsNoTaskBarIcon); ImGui::SameLine(); ShowHelpMarker("Toggling this at runtime is normally unsupported (most platform back-ends won't refresh the task bar icon state right away)."); ImGui::CheckboxFlags("io.ConfigFlags: ViewportsDecoration", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_ViewportsDecoration); diff --git a/imgui_internal.h b/imgui_internal.h index c1896b2a26b1..78b8bab68bd4 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -711,6 +711,7 @@ struct ImGuiContext ImGuiIO IO; ImGuiPlatformIO PlatformIO; ImGuiStyle Style; + ImGuiConfigFlags ConfigFlagsForFrame; // = g.IO.ConfigFlags at the time of NewFrame() ImFont* Font; // (Shortcut) == FontStack.empty() ? IO.Font : FontStack.back() float FontSize; // (Shortcut) == FontBaseSize * g.CurrentWindow->FontWindowScale == window->FontSize(). Text height for current window. float FontBaseSize; // (Shortcut) == IO.FontGlobalScale * Font->Scale * Font->FontSize. Base text height. @@ -876,6 +877,7 @@ struct ImGuiContext { Initialized = false; FrameScopeActive = false; + ConfigFlagsForFrame = ImGuiConfigFlags_None; Font = NULL; FontSize = FontBaseSize = 0.0f; FontAtlasOwnedByContext = shared_font_atlas ? false : true; From 4cadb57c462f585ff761853a667199e449d383b2 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 27 Nov 2018 19:53:33 +0100 Subject: [PATCH 348/828] Viewport: Comments. --- imgui.h | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/imgui.h b/imgui.h index 64d81c6a5ee4..f77943a6f3e0 100644 --- a/imgui.h +++ b/imgui.h @@ -20,6 +20,7 @@ Index of this file: // Helpers (ImVector, ImGuiOnceUponAFrame, ImGuiTextFilter, ImGuiTextBuffer, ImGuiStorage, ImGuiListClipper, ImColor) // Draw List API (ImDrawCmd, ImDrawIdx, ImDrawVert, ImDrawChannel, ImDrawListFlags, ImDrawList, ImDrawData) // Font API (ImFontConfig, ImFontGlyph, ImFontAtlasFlags, ImFontAtlas, ImFont) +// Platform interface for multi-viewport support (ImGuiPlatformMonitor, ImGuiPlatformIO, ImGuiViewport) */ @@ -2066,7 +2067,7 @@ struct ImFont //----------------------------------------------------------------------------- // (Optional) Represent the bounds of each connected monitor/display -// Dear ImGui only uses this to clamp the position of popups and tooltips so they don't straddle multiple monitors +// Dear ImGui only uses this to clamp the position of popups and tooltips so they don't straddle multiple monitors. struct ImGuiPlatformMonitor { ImVec2 MainPos, MainSize; // Coordinates of the area displayed on this monitor (Min = upper left, Max = bottom right) @@ -2075,12 +2076,16 @@ struct ImGuiPlatformMonitor ImGuiPlatformMonitor() { MainPos = MainSize = WorkPos = WorkSize = ImVec2(0,0); DpiScale = 1.0f; } }; -// (Optional) Setup required only if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) is enabled. Access via ImGui::GetPlatformIO(). -// This is designed so we can mix and match two imgui_impl_xxxx files, one for the Platform (~window handling), one for Renderer. -// Custom engine back-ends will often provide both Platform and Renderer interfaces and thus may not need to use all functions. -// Platform functions are typically called before their Renderer counterpart, apart from Destroy which are called the other way. -// RenderPlatformWindowsDefault() basically iterate secondary viewports and call Platform+Renderer's RenderWindow then Platform+Renderer's SwapBuffers, -// You may skip using RenderPlatformWindowsDefault() and call your draw/swap functions yourself if you need specific behavior for your multi-window rendering. +// (Optional) Setup required only if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) is enabled. +// Access via ImGui::GetPlatformIO(). This is designed so we can mix and match two imgui_impl_xxxx files, one for +// the Platform (~window handling), one for Renderer. Custom engine back-ends will often provide both Platform +// and Renderer interfaces and thus may not need to use all functions. +// Platform functions are typically called before their Renderer counterpart, +// apart from Destroy which are called the other way. +// RenderPlatformWindowsDefault() is that helper that iterate secondary viewports and call, in this order: +// Platform_RenderWindow(), Renderer_RenderWindow(), Platform_SwapBuffers(), Renderer_SwapBuffers() +// You may skip using RenderPlatformWindowsDefault() and call your draw/swap functions yourself if you need +// specific behavior for your multi-window rendering. struct ImGuiPlatformIO { //------------------------------------------------------------------ @@ -2124,11 +2129,10 @@ struct ImGuiPlatformIO // List of viewports (the list is updated by calling ImGui::EndFrame or ImGui::Render) ImGuiViewport* MainViewport; // Guaranteed to be == Viewports[0] ImVector Viewports; // Main viewports, followed by all secondary viewports. - ImGuiPlatformIO() { memset(this, 0, sizeof(*this)); } // Zero clear }; -// Flags stored in ImGuiViewport::Flags, giving indications to the platform back-ends +// Flags stored in ImGuiViewport::Flags, giving indications to the platform back-ends. enum ImGuiViewportFlags_ { ImGuiViewportFlags_None = 0, @@ -2150,7 +2154,7 @@ struct ImGuiViewport float DpiScale; // 1.0f = 96 DPI = No extra scale ImDrawData* DrawData; // The ImDrawData corresponding to this viewport. Valid after Render() and until the next call to NewFrame(). - void* PlatformUserData; // void* to hold custom data structure for the platform (e.g. windowing info, render context) + void* PlatformUserData; // void* to hold custom data structure for the OS / platform (e.g. windowing info, render context) void* PlatformHandle; // void* for FindViewportByPlatformHandle(). (e.g. suggested to use natural platform handle such as HWND, GlfwWindow*, SDL_Window*) bool PlatformRequestClose; // Platform window requested closure (e.g. window was moved by the OS / host window manager, e.g. pressing ALT-F4) bool PlatformRequestMove; // Platform window requested move (e.g. window was moved by the OS / host window manager, authoritative position will be OS window position) From 19c4fa8dd5671a751275c7389deffa029a61b294 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 28 Nov 2018 15:41:17 +0100 Subject: [PATCH 349/828] Examples: DX12: Fixed compilation of imgui_impl_dx12.cpp in viewport branch. Multi-viewport is still not functional. --- examples/example_win32_directx12/main.cpp | 4 ++-- examples/imgui_impl_dx12.cpp | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/examples/example_win32_directx12/main.cpp b/examples/example_win32_directx12/main.cpp index 70e9ea937130..e8ceae3d33e5 100644 --- a/examples/example_win32_directx12/main.cpp +++ b/examples/example_win32_directx12/main.cpp @@ -38,9 +38,9 @@ static D3D12_CPU_DESCRIPTOR_HANDLE g_mainRenderTargetDescriptor[NUM_BACK_BUFFER void CreateRenderTarget() { - ID3D12Resource* pBackBuffer; for (UINT i = 0; i < NUM_BACK_BUFFERS; i++) { + ID3D12Resource* pBackBuffer = NULL; g_pSwapChain->GetBuffer(i, IID_PPV_ARGS(&pBackBuffer)); g_pd3dDevice->CreateRenderTargetView(pBackBuffer, NULL, g_mainRenderTargetDescriptor[i]); g_mainRenderTargetResource[i] = pBackBuffer; @@ -415,7 +415,7 @@ int main(int, char**) if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { ImGui::UpdatePlatformWindows(); - ImGui::RenderPlatformWindowsDefault(); + ImGui::RenderPlatformWindowsDefault(NULL, (void*)g_pd3dCommandList); } g_pSwapChain->Present(1, 0); // Present with vsync diff --git a/examples/imgui_impl_dx12.cpp b/examples/imgui_impl_dx12.cpp index 821b14991e56..eca42f6502ab 100644 --- a/examples/imgui_impl_dx12.cpp +++ b/examples/imgui_impl_dx12.cpp @@ -731,18 +731,22 @@ static void ImGui_ImplDX12_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) */ } -static void ImGui_ImplDX12_RenderWindow(ImGuiViewport* viewport, void*) +// arg = ID3D12GraphicsCommandList* +static void ImGui_ImplDX12_RenderWindow(ImGuiViewport* viewport, void* renderer_arg) { ImGuiViewportDataDx12* data = (ImGuiViewportDataDx12*)viewport->RendererUserData; IM_ASSERT(0); (void)data; + + ID3D12GraphicsCommandList* command_list = (ID3D12GraphicsCommandList*)renderer_arg; + /* ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); g_pd3dDeviceContext->OMSetRenderTargets(1, &data->RTView, NULL); if (!(viewport->Flags & ImGuiViewportFlags_NoRendererClear)) g_pd3dDeviceContext->ClearRenderTargetView(data->RTView, (float*)&clear_color); */ - ImGui_ImplDX12_RenderDrawData(viewport->DrawData); + ImGui_ImplDX12_RenderDrawData(viewport->DrawData, command_list); } static void ImGui_ImplDX12_SwapBuffers(ImGuiViewport* viewport, void*) From 01f940dc9a71985f0da92eb43266fe4c1309c59b Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 28 Nov 2018 22:50:37 +0100 Subject: [PATCH 350/828] Viewport: imgui_impl_sdl2: Added support for PlatformIO Platform_SetWindowAlpha. --- examples/imgui_impl_sdl.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/examples/imgui_impl_sdl.cpp b/examples/imgui_impl_sdl.cpp index af02914217b1..ea5886cc80eb 100644 --- a/examples/imgui_impl_sdl.cpp +++ b/examples/imgui_impl_sdl.cpp @@ -46,7 +46,7 @@ #include #define SDL_HAS_WARP_MOUSE_GLOBAL SDL_VERSION_ATLEAST(2,0,4) #define SDL_HAS_CAPTURE_MOUSE SDL_VERSION_ATLEAST(2,0,4) -#define SDL_HAS_WINDOW_OPACITY SDL_VERSION_ATLEAST(2,0,5) +#define SDL_HAS_WINDOW_ALPHA SDL_VERSION_ATLEAST(2,0,5) #define SDL_HAS_ALWAYS_ON_TOP SDL_VERSION_ATLEAST(2,0,5) #define SDL_HAS_USABLE_DISPLAY_BOUNDS SDL_VERSION_ATLEAST(2,0,5) #define SDL_HAS_PER_MONITOR_DPI SDL_VERSION_ATLEAST(2,0,4) @@ -463,6 +463,14 @@ static void ImGui_ImplSDL2_SetWindowTitle(ImGuiViewport* viewport, const char* t SDL_SetWindowTitle(data->Window, title); } +#if SDL_HAS_WINDOW_ALPHA +static void ImGui_ImplSDL2_SetWindowAlpha(ImGuiViewport* viewport, float alpha) +{ + ImGuiViewportDataSDL2* data = (ImGuiViewportDataSDL2*)viewport->PlatformUserData; + SDL_SetWindowOpacity(data->Window, alpha); +} +#endif + static void ImGui_ImplSDL2_SetWindowFocus(ImGuiViewport* viewport) { ImGuiViewportDataSDL2* data = (ImGuiViewportDataSDL2*)viewport->PlatformUserData; @@ -556,6 +564,9 @@ static void ImGui_ImplSDL2_InitPlatformInterface(SDL_Window* window, void* sdl_g platform_io.Platform_SetWindowTitle = ImGui_ImplSDL2_SetWindowTitle; platform_io.Platform_RenderWindow = ImGui_ImplSDL2_RenderWindow; platform_io.Platform_SwapBuffers = ImGui_ImplSDL2_SwapBuffers; +#if SDL_HAS_WINDOW_ALPHA + platform_io.Platform_SetWindowAlpha = ImGui_ImplSDL2_SetWindowAlpha; +#endif #if SDL_HAS_VULKAN platform_io.Platform_CreateVkSurface = ImGui_ImplSDL2_CreateVkSurface; #endif From 19d17ed274df95e1cf3373ffda0cd07d2f9c31ea Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 29 Nov 2018 15:56:51 +0100 Subject: [PATCH 351/828] Docking: Added io.ConfigDockingTransparentPayload option (to use with ImGuiConfigFlags_ViewportsNoMerge) --- imgui.cpp | 47 ++++++++++++++++++++++++++++++++++++++--------- imgui.h | 11 ++++++----- imgui_internal.h | 1 + 3 files changed, 45 insertions(+), 14 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index a43fc5ed7a4b..2b51fa589ef9 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -920,6 +920,9 @@ static const float NAV_WINDOWING_LIST_APPEAR_DELAY = 0.15f; // Time static const float RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS = 4.0f; // Extend outside and inside windows. Affect FindHoveredWindow(). static const float RESIZE_WINDOWS_FROM_EDGES_FEEDBACK_TIMER = 0.04f; // Reduce visual noise by only highlighting the border after a certain time. +// Docking +static const float DOCKING_TRANSPARENT_PAYLOAD_ALPHA = 0.50f; // For use with io.ConfigDockingTransparentPayload. Apply to Viewport _or_ WindowBg in host viewport. + //------------------------------------------------------------------------- // [SECTION] FORWARD DECLARATIONS //------------------------------------------------------------------------- @@ -1114,6 +1117,7 @@ ImGuiIO::ImGuiIO() // Miscellaneous configuration options ConfigDockingWithShift = false; + ConfigDockingTransparentPayload = false; #ifdef __APPLE__ ConfigMacOSXBehaviors = true; // Set Mac OS X style defaults based on __APPLE__ compile time flag #else @@ -3281,6 +3285,8 @@ void ImGui::NewFrame() IM_ASSERT(g.PlatformIO.Platform_GetWindowSize != NULL && "Platform init didn't install handlers?"); IM_ASSERT(g.PlatformIO.Platform_SetWindowSize != NULL && "Platform init didn't install handlers?"); IM_ASSERT((g.Viewports[0]->PlatformUserData != NULL || g.Viewports[0]->PlatformHandle != NULL) && "Platform init didn't setup main viewport."); + if (g.IO.ConfigDockingTransparentPayload && (g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable)) + IM_ASSERT(g.PlatformIO.Platform_SetWindowAlpha != NULL && "Platform_SetWindowAlpha handler is required to use io.ConfigDockingTransparent!"); #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS IM_ASSERT(g.IO.RenderDrawListsFn == NULL); // Call ImGui::Render() then pass ImGui::GetDrawData() yourself to your render function! #endif @@ -5367,13 +5373,29 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Window background if (!(flags & ImGuiWindowFlags_NoBackground)) { + bool is_docking_transparent_payload = false; + if (g.DragDropActive && (g.FrameCount - g.DragDropAcceptFrameCount) <= 1 && g.IO.ConfigDockingTransparentPayload) + if (g.DragDropPayload.IsDataType(IMGUI_PAYLOAD_TYPE_WINDOW) && *(ImGuiWindow**)g.DragDropPayload.Data == window) + is_docking_transparent_payload = true; + ImU32 bg_col = GetColorU32(GetWindowBgColorIdxFromFlags(flags)); - if (g.NextWindowData.BgAlphaCond != 0) - bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(g.NextWindowData.BgAlphaVal) << IM_COL32_A_SHIFT); if (window->ViewportOwned) { - //window->Viewport->Alpha = ((bg_col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT) / 255.0f; + // No alpha bg_col = (bg_col | IM_COL32_A_MASK); + if (is_docking_transparent_payload) + window->Viewport->Alpha *= DOCKING_TRANSPARENT_PAYLOAD_ALPHA; + } + else + { + // Adjust alpha. For docking + float alpha = 1.0f; + if (g.NextWindowData.BgAlphaCond != 0) + alpha = g.NextWindowData.BgAlphaVal; + if (is_docking_transparent_payload) + alpha *= DOCKING_TRANSPARENT_PAYLOAD_ALPHA; + if (alpha != 1.0f) + bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(alpha) << IM_COL32_A_SHIFT); } window->DrawList->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight()), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Bot); } @@ -7534,7 +7556,7 @@ static void ImGui::UpdateViewports() if (g.PlatformIO.Platform_GetWindowMinimized && (n == 0 || viewport->CreatedPlatformWindow)) viewport->PlatformIsMinimized = g.PlatformIO.Platform_GetWindowMinimized(viewport); - // Apply Position and Size (from Platform Window to ImGui) if requested. + // Update Position and Size (from Platform Window to ImGui) if requested. // We do it early in the frame instead of waiting for UpdatePlatformWindows() to avoid a frame of lag when moving/resizing using OS facilities. if (!viewport->PlatformIsMinimized) { @@ -7548,6 +7570,9 @@ static void ImGui::UpdateViewports() viewport->PlatformMonitor = FindPlatformMonitorForRect(viewport->GetRect()); } + // Reset alpha every frame. Users of transparency (docking) needs to request a lower alpha back. + viewport->Alpha = 1.0f; + // Translate imgui windows when a Host Viewport has been moved // (This additionally keeps windows at the same place when ImGuiConfigFlags_ViewportsEnable is toggled!) ImVec2 viewport_delta = viewport->Pos - viewport->LastPos; @@ -11352,19 +11377,23 @@ static void ImGui::DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDock { ImGuiContext& g = *GImGui; + // With this option, we only display the preview on the target viewport, and the payload viewport is made transparent. + // To compensate for the single layer obstructed by the payload, we'll increase the alpha of the preview nodes. + const bool is_transparent_payload = g.IO.ConfigDockingTransparentPayload; + // In case the two windows involved are on different viewports, we will draw the overlay on each of them. int overlay_draw_lists_count = 0; ImDrawList* overlay_draw_lists[2]; overlay_draw_lists[overlay_draw_lists_count++] = GetOverlayDrawList(host_window->Viewport); - if (host_window->Viewport != root_payload->Viewport) + if (host_window->Viewport != root_payload->Viewport && !is_transparent_payload) overlay_draw_lists[overlay_draw_lists_count++] = GetOverlayDrawList(root_payload->Viewport); // Draw main preview rectangle const ImU32 overlay_col_tabs = GetColorU32(ImGuiCol_TabActive); - const ImU32 overlay_col_main = GetColorU32(ImGuiCol_DockingPreview, 0.40f); - const ImU32 overlay_col_drop = GetColorU32(ImGuiCol_DockingPreview, 0.70f); - const ImU32 overlay_col_drop_hovered = GetColorU32(ImGuiCol_DockingPreview); - const ImU32 overlay_col_lines = GetColorU32(ImGuiCol_NavWindowingHighlight, 0.60f); + const ImU32 overlay_col_main = GetColorU32(ImGuiCol_DockingPreview, is_transparent_payload ? 0.60f : 0.40f); + const ImU32 overlay_col_drop = GetColorU32(ImGuiCol_DockingPreview, is_transparent_payload ? 0.90f : 0.70f); + const ImU32 overlay_col_drop_hovered = GetColorU32(ImGuiCol_DockingPreview, is_transparent_payload ? 1.20f : 1.00f); + const ImU32 overlay_col_lines = GetColorU32(ImGuiCol_NavWindowingHighlight, is_transparent_payload ? 0.80f : 0.60f); // Display area preview const bool can_preview_tabs = (root_payload->DockNodeAsHost == NULL || root_payload->DockNodeAsHost->Windows.Size > 0); diff --git a/imgui.h b/imgui.h index 50ffa88fe2d6..9984049aba1e 100644 --- a/imgui.h +++ b/imgui.h @@ -1275,11 +1275,12 @@ struct ImGuiIO ImVec2 DisplayFramebufferScale; // = (1.0f,1.0f) // For retina display or other situations where window coordinates are different from framebuffer coordinates. User storage only, presently not used by ImGui. // Miscellaneous configuration options - bool MouseDrawCursor; // = false // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). Cannot be easily renamed to 'io.ConfigXXX' because this is frequently used by back-end implementations. - bool ConfigDockingWithShift; // = false // Enable docking with holding Shift key (reduce visual noise, allows dropping in wider space) - bool ConfigMacOSXBehaviors; // = defined(__APPLE__) // OS X style: Text editing cursor movement using Alt instead of Ctrl, Shortcuts using Cmd/Super instead of Ctrl, Line/Text Start and End using Cmd+Arrows instead of Home/End, Double click selects by word instead of selecting whole text, Multi-selection in lists uses Cmd/Super instead of Ctrl (was called io.OptMacOSXBehaviors prior to 1.63) - bool ConfigInputTextCursorBlink; // = true // Set to false to disable blinking cursor, for users who consider it distracting. (was called: io.OptCursorBlink prior to 1.63) - bool ConfigResizeWindowsFromEdges; // = true // [BETA] Enable resizing of windows from their edges and from the lower-left corner. This requires (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) because it needs mouse cursor feedback. (This used to be the ImGuiWindowFlags_ResizeFromAnySide flag) + bool MouseDrawCursor; // = false // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). Cannot be easily renamed to 'io.ConfigXXX' because this is frequently used by back-end implementations. + bool ConfigDockingWithShift; // = false // Enable docking with holding Shift key (reduce visual noise, allows dropping in wider space) + bool ConfigDockingTransparentPayload; // = false // [BETA] Make window or viewport transparent when docking and only display docking boxes on the target viewport. Useful if rendering of multiple viewport can be synced. Best used with ImGuiConfigFlags_ViewportsNoMerge. + bool ConfigMacOSXBehaviors; // = defined(__APPLE__) // OS X style: Text editing cursor movement using Alt instead of Ctrl, Shortcuts using Cmd/Super instead of Ctrl, Line/Text Start and End using Cmd+Arrows instead of Home/End, Double click selects by word instead of selecting whole text, Multi-selection in lists uses Cmd/Super instead of Ctrl (was called io.OptMacOSXBehaviors prior to 1.63) + bool ConfigInputTextCursorBlink; // = true // Set to false to disable blinking cursor, for users who consider it distracting. (was called: io.OptCursorBlink prior to 1.63) + bool ConfigResizeWindowsFromEdges; // = true // [BETA] Enable resizing of windows from their edges and from the lower-left corner. This requires (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) because it needs mouse cursor feedback. (This used to be the ImGuiWindowFlags_ResizeFromAnySide flag) //------------------------------------------------------------------ // Settings (User Functions) diff --git a/imgui_internal.h b/imgui_internal.h index 5f746f5e568c..9f4dbe7dde03 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1490,6 +1490,7 @@ namespace ImGui inline bool IsNavInputPressedAnyOfTwo(ImGuiNavInput n1, ImGuiNavInput n2, ImGuiInputReadMode mode) { return (GetNavInputAmount(n1, mode) + GetNavInputAmount(n2, mode)) > 0.0f; } // Docking + // (some functions are only declared in imgui.cpp, see Docking section) IMGUI_API void DockContextInitialize(ImGuiContext* ctx); IMGUI_API void DockContextShutdown(ImGuiContext* ctx); IMGUI_API void DockContextOnLoadSettings(ImGuiContext* ctx); From 36cbe1e5211be295d7cf0ccefd144deb85753cd8 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 29 Nov 2018 21:16:45 +0100 Subject: [PATCH 352/828] Viewport: Misc renaming. --- imgui.cpp | 24 ++++++++++++------------ imgui_internal.h | 6 +++--- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index a3480ef27409..19ddfc1d7379 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7187,7 +7187,7 @@ static bool ImGui::GetWindowAlwaysWantOwnViewport(ImGuiWindow* window) static bool ImGui::UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* viewport) { ImGuiContext& g = *GImGui; - if (!(viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) || window->Viewport == viewport || viewport->PlatformIsMinimized) + if (!(viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) || window->Viewport == viewport || viewport->PlatformWindowMinimized) return false; if (!viewport->GetRect().Contains(window->Rect())) return false; @@ -7242,7 +7242,7 @@ static ImGuiViewportP* FindViewportHoveredFromPlatformWindowStack(const ImVec2 m for (int n = 0; n < g.Viewports.Size; n++) { ImGuiViewportP* viewport = g.Viewports[n]; - if (!(viewport->Flags & ImGuiViewportFlags_NoInputs) && !viewport->PlatformIsMinimized && viewport->GetRect().Contains(mouse_platform_pos)) + if (!(viewport->Flags & ImGuiViewportFlags_NoInputs) && !viewport->PlatformWindowMinimized && viewport->GetRect().Contains(mouse_platform_pos)) if (best_candidate == NULL || best_candidate->LastFrontMostStampCount < viewport->LastFrontMostStampCount) best_candidate = viewport; } @@ -7294,12 +7294,12 @@ static void ImGui::UpdateViewports() if ((g.ConfigFlagsForFrame & ImGuiConfigFlags_ViewportsEnable)) { - if (g.PlatformIO.Platform_GetWindowMinimized && (n == 0 || viewport->CreatedPlatformWindow)) - viewport->PlatformIsMinimized = g.PlatformIO.Platform_GetWindowMinimized(viewport); + if (g.PlatformIO.Platform_GetWindowMinimized && (n == 0 || viewport->PlatformWindowCreated)) + viewport->PlatformWindowMinimized = g.PlatformIO.Platform_GetWindowMinimized(viewport); // Apply Position and Size (from Platform Window to ImGui) if requested. // We do it early in the frame instead of waiting for UpdatePlatformWindows() to avoid a frame of lag when moving/resizing using OS facilities. - if (!viewport->PlatformIsMinimized) + if (!viewport->PlatformWindowMinimized) { if (viewport->PlatformRequestMove) viewport->Pos = viewport->LastPlatformPos = g.PlatformIO.Platform_GetWindowPos(viewport); @@ -7387,14 +7387,14 @@ static void ImGui::UpdateViewports() const bool is_mouse_dragging_with_an_expected_destination = g.DragDropActive; if (is_mouse_dragging_with_an_expected_destination && viewport_hovered == NULL) viewport_hovered = g.MouseLastHoveredViewport; - if (is_mouse_dragging_with_an_expected_destination || g.ActiveId == 0 || !ImGui::IsAnyMouseDown()) + if (is_mouse_dragging_with_an_expected_destination || g.ActiveId == 0 || !IsAnyMouseDown()) if (viewport_hovered != NULL && viewport_hovered != g.MouseViewport && !(viewport_hovered->Flags & ImGuiViewportFlags_NoInputs)) g.MouseViewport = viewport_hovered; IM_ASSERT(g.MouseViewport != NULL); } -// FIXME: We should ideally refactor the system to call this everyframe (we currently don't) +// FIXME: We should ideally refactor the system to call this every frame (we currently don't) ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const ImVec2& pos, const ImVec2& size, ImGuiViewportFlags flags) { ImGuiContext& g = *GImGui; @@ -7601,7 +7601,7 @@ void ImGui::UpdatePlatformWindows() } // Create window - bool is_new_window = (viewport->CreatedPlatformWindow == false); + bool is_new_window = (viewport->PlatformWindowCreated == false); if (is_new_window) { g.PlatformIO.Platform_CreateWindow(viewport); @@ -7610,7 +7610,7 @@ void ImGui::UpdatePlatformWindows() viewport->LastNameHash = 0; viewport->LastPlatformPos = viewport->LastPlatformSize = ImVec2(FLT_MAX, FLT_MAX); // By clearing those we'll enforce a call to Platform_SetWindowPos/Platform_SetWindowSize before Platform_ShowWindow viewport->LastRendererSize = viewport->Size; - viewport->CreatedPlatformWindow = true; + viewport->PlatformWindowCreated = true; } // Apply Position and Size (from ImGui to Platform/Renderer back-ends) @@ -7757,7 +7757,7 @@ void ImGui::DestroyPlatformWindow(ImGuiViewportP* viewport) IM_ASSERT(viewport->PlatformUserData == NULL); viewport->PlatformHandle = NULL; viewport->RendererUserData = viewport->PlatformHandle = NULL; - viewport->CreatedPlatformWindow = false; + viewport->PlatformWindowCreated = false; } void ImGui::DestroyPlatformWindows() @@ -9897,7 +9897,7 @@ static void RenderViewportThumbnail(ImDrawList* draw_list, ImGuiViewportP* viewp ImVec2 scale = bb.GetSize() / viewport->Size; ImVec2 off = bb.Min - viewport->Pos * scale; - float alpha_mul = viewport->PlatformIsMinimized ? 0.30f : 1.00f; + float alpha_mul = viewport->PlatformWindowMinimized ? 0.30f : 1.00f; window->DrawList->AddRectFilled(bb.Min, bb.Max, ImGui::GetColorU32(ImGuiCol_Border, alpha_mul * 0.40f)); for (int i = 0; i != g.Windows.Size; i++) { @@ -10104,7 +10104,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::BulletText("Flags: 0x%04X =%s%s%s%s%s%s", viewport->Flags, (flags & ImGuiViewportFlags_CanHostOtherWindows) ? " CanHostOtherWindows" : "", (flags & ImGuiViewportFlags_NoDecoration) ? " NoDecoration" : "", (flags & ImGuiViewportFlags_NoFocusOnAppearing) ? " NoFocusOnAppearing" : "", (flags & ImGuiViewportFlags_NoInputs) ? " NoInputs" : "", - (flags & ImGuiViewportFlags_NoRendererClear) ? " NoRendererClear" : "", viewport->PlatformIsMinimized ? ", PlatformIsMinimized" : ""); + (flags & ImGuiViewportFlags_NoRendererClear) ? " NoRendererClear" : "", viewport->PlatformWindowMinimized ? ", PlatformWindowMinimized" : ""); for (int layer_i = 0; layer_i < IM_ARRAYSIZE(viewport->DrawDataBuilder.Layers); layer_i++) for (int draw_list_i = 0; draw_list_i < viewport->DrawDataBuilder.Layers[layer_i].Size; draw_list_i++) Funcs::NodeDrawList(NULL, viewport, viewport->DrawDataBuilder.Layers[layer_i][draw_list_i], "DrawList"); diff --git a/imgui_internal.h b/imgui_internal.h index 78b8bab68bd4..65d3a57386cd 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -627,11 +627,11 @@ struct ImGuiViewportP : public ImGuiViewport int LastFrontMostStampCount; // Last stamp number from when a window hosted by this viewport was made front-most (by comparing this value between two viewport we have an implicit viewport z-order ImGuiID LastNameHash; ImVec2 LastPos; - bool CreatedPlatformWindow; float Alpha; // Window opacity (when dragging dockable windows/viewports we make them transparent) float LastAlpha; int PlatformMonitor; - bool PlatformIsMinimized; + bool PlatformWindowCreated; + bool PlatformWindowMinimized; ImGuiWindow* Window; ImDrawList* OverlayDrawList; // For convenience, a draw list we can render to that's always rendered last (we use it to draw software mouse cursor when io.MouseDrawCursor is set) ImDrawData DrawDataP; @@ -640,7 +640,7 @@ struct ImGuiViewportP : public ImGuiViewport ImVec2 LastPlatformSize; ImVec2 LastRendererSize; - ImGuiViewportP() { Idx = -1; LastFrameActive = LastFrameOverlayDrawList = LastFrontMostStampCount = -1; LastNameHash = 0; CreatedPlatformWindow = false; Alpha = LastAlpha = 1.0f; PlatformMonitor = INT_MIN; PlatformIsMinimized = false; Window = NULL; OverlayDrawList = NULL; LastPlatformPos = LastPlatformSize = LastRendererSize = ImVec2(FLT_MAX, FLT_MAX); } + ImGuiViewportP() { Idx = -1; LastFrameActive = LastFrameOverlayDrawList = LastFrontMostStampCount = -1; LastNameHash = 0; Alpha = LastAlpha = 1.0f; PlatformMonitor = INT_MIN; PlatformWindowCreated = PlatformWindowMinimized = false; Window = NULL; OverlayDrawList = NULL; LastPlatformPos = LastPlatformSize = LastRendererSize = ImVec2(FLT_MAX, FLT_MAX); } ~ImGuiViewportP() { if (OverlayDrawList) IM_DELETE(OverlayDrawList); } ImRect GetRect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } ImVec2 GetCenter() const{ return ImVec2(Pos.x + Size.x * 0.5f, Pos.y + Size.y * 0.5f); } From 2fbbcaa339f46c1f87dcd9cb044374a480afba3f Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 29 Nov 2018 21:28:47 +0100 Subject: [PATCH 353/828] Viewport: Avoid calling platform functions when window is not created (apart from Platform_GetWindowDpiScale, documented as such). Main viewport situation is still ambiguous. (#1542) --- imgui.cpp | 19 ++++++++++++------- imgui.h | 4 ++-- imgui_internal.h | 2 +- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 19ddfc1d7379..28e34cec2451 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3681,7 +3681,7 @@ void ImGui::EndFrame() IM_ASSERT(g.FrameScopeActive && "Forgot to call ImGui::NewFrame()"); // Notify OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME) - if (g.PlatformIO.Platform_SetImeInputPos && g.PlatformImePosViewport != NULL && ImLengthSqr(g.PlatformImePos - g.PlatformImeLastPos) > 0.0001f) + if (g.PlatformIO.Platform_SetImeInputPos && ImLengthSqr(g.PlatformImePos - g.PlatformImeLastPos) > 0.0001f && g.PlatformImePosViewport && g.PlatformImePosViewport->PlatformWindowCreated) { g.PlatformIO.Platform_SetImeInputPos(g.PlatformImePosViewport, g.PlatformImePos); g.PlatformImeLastPos = g.PlatformImePos; @@ -7292,14 +7292,15 @@ static void ImGui::UpdateViewports() continue; } + const bool platform_funcs_available = (n == 0 || viewport->PlatformWindowCreated); if ((g.ConfigFlagsForFrame & ImGuiConfigFlags_ViewportsEnable)) { - if (g.PlatformIO.Platform_GetWindowMinimized && (n == 0 || viewport->PlatformWindowCreated)) + if (g.PlatformIO.Platform_GetWindowMinimized && platform_funcs_available) viewport->PlatformWindowMinimized = g.PlatformIO.Platform_GetWindowMinimized(viewport); // Apply Position and Size (from Platform Window to ImGui) if requested. // We do it early in the frame instead of waiting for UpdatePlatformWindows() to avoid a frame of lag when moving/resizing using OS facilities. - if (!viewport->PlatformWindowMinimized) + if (!viewport->PlatformWindowMinimized && platform_funcs_available) { if (viewport->PlatformRequestMove) viewport->Pos = viewport->LastPlatformPos = g.PlatformIO.Platform_GetWindowPos(viewport); @@ -7665,10 +7666,13 @@ void ImGui::UpdatePlatformWindows() if (g.PlatformIO.Platform_GetWindowFocus != NULL) { ImGuiViewportP* focused_viewport = NULL; - for (int i = 0; i < g.Viewports.Size && focused_viewport == NULL; i++) - if (g.Viewports[i]->PlatformUserData != NULL || g.Viewports[i]->PlatformHandle != NULL) - if (g.PlatformIO.Platform_GetWindowFocus(g.Viewports[i])) - focused_viewport = g.Viewports[i]; + for (int n = 0; n < g.Viewports.Size && focused_viewport == NULL; n++) + { + ImGuiViewportP* viewport = g.Viewports[n]; + if (n == 0 || viewport->PlatformWindowCreated) + if (g.PlatformIO.Platform_GetWindowFocus(viewport)) + focused_viewport = viewport; + } if (focused_viewport && g.PlatformLastFocusedViewport != focused_viewport->ID) { if (focused_viewport->LastFrontMostStampCount != g.WindowsFrontMostStampCount) @@ -7758,6 +7762,7 @@ void ImGui::DestroyPlatformWindow(ImGuiViewportP* viewport) viewport->PlatformHandle = NULL; viewport->RendererUserData = viewport->PlatformHandle = NULL; viewport->PlatformWindowCreated = false; + viewport->PlatformRequestClose = viewport->PlatformRequestMove = viewport->PlatformRequestResize = false; } void ImGui::DestroyPlatformWindows() diff --git a/imgui.h b/imgui.h index f77943a6f3e0..1d9cdb763224 100644 --- a/imgui.h +++ b/imgui.h @@ -2107,8 +2107,8 @@ struct ImGuiPlatformIO void (*Platform_SetWindowAlpha)(ImGuiViewport* vp, float alpha); // (Optional) Setup window transparency void (*Platform_RenderWindow)(ImGuiViewport* vp, void* render_arg); // (Optional) Setup for render (platform side) void (*Platform_SwapBuffers)(ImGuiViewport* vp, void* render_arg); // (Optional) Call Present/SwapBuffers (platform side) - float (*Platform_GetWindowDpiScale)(ImGuiViewport* vp); // (Optional) DPI handling: Return DPI scale for this viewport. 1.0f = 96 DPI. (FIXME-DPI) - void (*Platform_OnChangedViewport)(ImGuiViewport* vp); // (Optional) DPI handling: Called during Begin() every time the viewport we are outputting into changes, so back-end has a chance to swap fonts to adjust style. + float (*Platform_GetWindowDpiScale)(ImGuiViewport* vp); // (Optional) [BETA] (FIXME-DPI) DPI handling: Return DPI scale for this viewport. 1.0f = 96 DPI. IMPORTANT: this will be called _before_ the window is created, in which case the implementation is expected to use the viewport->Pos/Size fields to estimate DPI value. + void (*Platform_OnChangedViewport)(ImGuiViewport* vp); // (Optional) [BETA] (FIXME-DPI) DPI handling: Called during Begin() every time the viewport we are outputting into changes, so back-end has a chance to swap fonts to adjust style. void (*Platform_SetImeInputPos)(ImGuiViewport* vp, ImVec2 pos); // (Optional) Set IME (Input Method Editor, e.g. for Asian languages) input position, so text preview appears over the imgui input box. int (*Platform_CreateVkSurface)(ImGuiViewport* vp, ImU64 vk_inst, const void* vk_allocators, ImU64* out_vk_surface); // (Optional) For Renderer to call into Platform code diff --git a/imgui_internal.h b/imgui_internal.h index 65d3a57386cd..aff91eb19d78 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -848,7 +848,7 @@ struct ImGuiContext // Platform support ImVec2 PlatformImePos, PlatformImeLastPos; // Cursor position request & last passed to the OS Input Method Editor - ImGuiViewport* PlatformImePosViewport; + ImGuiViewportP* PlatformImePosViewport; // Settings bool SettingsLoaded; From 1c7be88a1ab9c975a956f971367b91c51745c19d Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 30 Nov 2018 14:13:51 +0100 Subject: [PATCH 354/828] Viewport: Fixed a bug where tooltips on their first frame didn't find a monitor leading to the "recovery" code to revert it to the main viewport for a frame. (#1542) --- docs/TODO.txt | 2 +- imgui.cpp | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/TODO.txt b/docs/TODO.txt index ffc0dbe05a3f..edc053a962f5 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -287,7 +287,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - viewport: with platform decoration enabled, platform may force constraint (e.g. minimum size) - viewport: use getfocus/setfocus api to synchronize imgui<>platform focus better (e.g imgui-side ctrl-tab can focus os window, OS initial setup and alt-tab can focus imgui window etc.) - viewport: store per-viewport/monitor DPI in .ini file so an application reload or main window changing DPI on reload can be properly patched for. - - viewport: implicit Debug window can hog a zombie viewport (harmless, noisy?) + - viewport: implicit Debug window can hog a zombie viewport (harmless, noisy?) > could at least clear out the reference on a per session basis? - viewport: need to clarify how to use GetMousePos() from a user point of view. - platform: glfw: no support for ImGuiBackendFlags_HasMouseHoveredViewport. - platform: sdl: no support for ImGuiBackendFlags_HasMouseHoveredViewport. maybe we could use SDL_GetMouseFocus() / SDL_WINDOW_MOUSE_FOCUS if imgui could fallback on its heuristic when NoInputs is set diff --git a/imgui.cpp b/imgui.cpp index 28e34cec2451..0395eb8a96b2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7699,9 +7699,13 @@ static int ImGui::FindPlatformMonitorForPos(const ImVec2& pos) static int ImGui::FindPlatformMonitorForRect(const ImRect& rect) { ImGuiContext& g = *GImGui; - float surface_threshold = rect.GetWidth() * rect.GetHeight() * 0.5f; + + // Use a minimum threshold of 1.0f so a zero-sized rect will still find its monitor given its position. + // This is necessary for tooltips which always resize down to zero at first. + const float surface_threshold = ImMax(rect.GetWidth() * rect.GetHeight() * 0.5f, 1.0f); int best_monitor_n = -1; float best_monitor_surface = 0.001f; + for (int monitor_n = 0; monitor_n < g.PlatformIO.Monitors.Size && best_monitor_surface < surface_threshold; monitor_n++) { const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[monitor_n]; From f663277591388f766b3c783fe1c17c690d14f16e Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 30 Nov 2018 14:33:43 +0100 Subject: [PATCH 355/828] Merge misc/shallow changes from Docking branch to minimize drift: moved some blocks, added comments. --- imgui.cpp | 81 ++++++++++++++++++++++++++++++------------------ imgui_internal.h | 51 +++++++++++++++++------------- 2 files changed, 80 insertions(+), 52 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 0395eb8a96b2..76a3135fa2ce 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3801,7 +3801,8 @@ void ImGui::EndFrame() AddWindowToSortBuffer(&g.WindowsSortBuffer, window); } - IM_ASSERT(g.Windows.Size == g.WindowsSortBuffer.Size); // we done something wrong + // This usually assert if there is a mismatch between the ImGuiWindowFlags_ChildWindow / ParentWindow values and DC.ChildWindows[] in parents, aka we've done something wrong. + IM_ASSERT(g.Windows.Size == g.WindowsSortBuffer.Size); g.Windows.swap(g.WindowsSortBuffer); g.IO.MetricsActiveWindows = g.WindowsActiveCount; @@ -4824,17 +4825,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) const int current_frame = g.FrameCount; const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame); - if (first_begin_of_the_frame) - { - window->FlagsPreviousFrame = window->Flags; - window->Flags = (ImGuiWindowFlags)flags; - window->BeginOrderWithinParent = 0; - window->BeginOrderWithinContext = g.WindowsActiveCount++; - } - else - { - flags = window->Flags; - } // Update the Appearing flag bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on @@ -4849,6 +4839,20 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if (window->Appearing) SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true); + // Update Flags, LastFrameActive, BeginOrderXXX fields + if (first_begin_of_the_frame) + { + window->FlagsPreviousFrame = window->Flags; + window->Flags = (ImGuiWindowFlags)flags; + window->LastFrameActive = current_frame; + window->BeginOrderWithinParent = 0; + window->BeginOrderWithinContext = g.WindowsActiveCount++; + } + else + { + flags = window->Flags; + } + // Parent window is latched only on the first call to Begin() of the frame, so further append-calls can be done from a different window stack ImGuiWindow* parent_window_in_stack = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back(); ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow; @@ -4923,7 +4927,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->Active = true; window->HasCloseButton = (p_open != NULL); window->ClipRect = ImVec4(-FLT_MAX,-FLT_MAX,+FLT_MAX,+FLT_MAX); - window->LastFrameActive = current_frame; window->IDStack.resize(1); // Update stored window name when it changes (which can only happen with the "###" operator). @@ -5223,21 +5226,27 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if (!(flags & ImGuiWindowFlags_NoBackground)) { ImU32 bg_col = GetColorU32(GetWindowBgColorIdxFromFlags(flags)); - if (g.NextWindowData.BgAlphaCond != 0) - bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(g.NextWindowData.BgAlphaVal) << IM_COL32_A_SHIFT); if (window->ViewportOwned) { - //window->Viewport->Alpha = ((bg_col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT) / 255.0f; + // No alpha bg_col = (bg_col | IM_COL32_A_MASK); } + else + { + // Adjust alpha + if (g.NextWindowData.BgAlphaCond != 0) + bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(g.NextWindowData.BgAlphaVal) << IM_COL32_A_SHIFT); + } window->DrawList->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight()), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Bot); } g.NextWindowData.BgAlphaCond = 0; // Title bar - ImU32 title_bar_col = GetColorU32(window->Collapsed ? ImGuiCol_TitleBgCollapsed : title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg); if (!(flags & ImGuiWindowFlags_NoTitleBar)) + { + ImU32 title_bar_col = GetColorU32(window->Collapsed ? ImGuiCol_TitleBgCollapsed : title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg); window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawCornerFlags_Top); + } // Menu bar if (flags & ImGuiWindowFlags_MenuBar) @@ -5391,7 +5400,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DC.NavLayerCurrentMask >>= 1; window->DC.ItemFlags = item_flags_backup; - // Title text (FIXME: refactor text alignment facilities along with RenderText helpers, this is too much code for what it does.) + // Title bar text (with: horizontal alignment, avoiding collapse/close button) + // FIXME: Refactor text alignment facilities along with RenderText helpers, this is too much code.. ImVec2 text_size = CalcTextSize(name, NULL, true); ImRect text_r = title_bar_rect; float pad_left = (flags & ImGuiWindowFlags_NoCollapse) ? style.FramePadding.x : (style.FramePadding.x + g.FontSize + style.ItemInnerSpacing.x); @@ -5461,7 +5471,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Child window can be out of sight and have "negative" clip windows. // Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar). IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0); - if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y) window->HiddenFramesRegular = 1; @@ -5476,7 +5485,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->HiddenFramesRegular = 1; // Update the Hidden flag - window->Hidden = (window->HiddenFramesRegular > 0) || (window->HiddenFramesForResize); + window->Hidden = (window->HiddenFramesRegular > 0) || (window->HiddenFramesForResize > 0); // Return false if we don't intend to display anything to allow user to perform an early out optimization window->SkipItems = (window->Collapsed || !window->Active || window->Hidden) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && window->HiddenFramesForResize <= 0; @@ -5580,7 +5589,7 @@ void ImGui::FocusWindow(ImGuiWindow* window) g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId g.NavIdIsAlive = false; g.NavLayer = 0; - //printf("[%05d] FocusWindow(\"%s\")\n", g.FrameCount, window ? window->Name : NULL); + //IMGUI_DEBUG_LOG("FocusWindow(\"%s\")\n", window ? window->Name : NULL); } // Passing NULL allow to disable keyboard focus @@ -6912,7 +6921,8 @@ bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags) g.NextWindowData.Clear(); // We behave like Begin() and need to consume those values return false; } - return BeginPopupEx(g.CurrentWindow->GetID(str_id), flags|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings); + flags |= ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings; + return BeginPopupEx(g.CurrentWindow->GetID(str_id), flags); } bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags) @@ -6931,7 +6941,8 @@ bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags fla if (g.NextWindowData.PosCond == 0) SetNextWindowPos(window->Viewport->GetCenter(), ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); - bool is_open = Begin(name, p_open, flags | ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings); + flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings; + const bool is_open = Begin(name, p_open, flags); if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display) { EndPopup(); @@ -7111,7 +7122,7 @@ ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window) } //----------------------------------------------------------------------------- -// [SECTION] VIEWPORTS / PLATFORM WINDOWS +// [SECTION] VIEWPORTS, PLATFORM WINDOWS //----------------------------------------------------------------------------- ImGuiViewport* ImGui::GetMainViewport() @@ -7178,7 +7189,7 @@ static bool ImGui::GetWindowAlwaysWantOwnViewport(ImGuiWindow* window) // Tooltips and menus are not automatically forced into their own viewport when the NoMerge flag is set, however the multiplication of viewports makes them more likely to protude and create their own. ImGuiContext& g = *GImGui; if ((g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsNoMerge) && (g.ConfigFlagsForFrame & ImGuiConfigFlags_ViewportsEnable)) - //if (window->DockStatus == ImGuiDockStatus_Floating) + //if (!window->DockIsActive) if ((window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip)) == 0) return true; return false; @@ -7258,6 +7269,7 @@ static void ImGui::UpdateViewports() // Update main viewport with current platform position and size ImGuiViewportP* main_viewport = g.Viewports[0]; IM_ASSERT(main_viewport->ID == IMGUI_VIEWPORT_DEFAULT_ID); + IM_ASSERT(main_viewport->Window == NULL); ImVec2 main_viewport_platform_pos = ImVec2(0.0f, 0.0f); if ((g.ConfigFlagsForFrame & ImGuiConfigFlags_ViewportsEnable)) main_viewport_platform_pos = g.PlatformIO.Platform_GetWindowPos(main_viewport); @@ -7285,6 +7297,7 @@ static void ImGui::UpdateViewports() g.Viewports.erase(g.Viewports.Data + n); // Destroy + //IMGUI_DEBUG_LOG("Delete Viewport %08X (%s)\n", viewport->ID, viewport->Window ? viewport->Window->Name : "n/a"); DestroyPlatformWindow(viewport); // In most circumstances the platform window will already be destroyed here. IM_ASSERT(g.PlatformIO.Viewports.contains(viewport) == false); IM_DELETE(viewport); @@ -7298,7 +7311,7 @@ static void ImGui::UpdateViewports() if (g.PlatformIO.Platform_GetWindowMinimized && platform_funcs_available) viewport->PlatformWindowMinimized = g.PlatformIO.Platform_GetWindowMinimized(viewport); - // Apply Position and Size (from Platform Window to ImGui) if requested. + // Update Position and Size (from Platform Window to ImGui) if requested. // We do it early in the frame instead of waiting for UpdatePlatformWindows() to avoid a frame of lag when moving/resizing using OS facilities. if (!viewport->PlatformWindowMinimized && platform_funcs_available) { @@ -7312,6 +7325,9 @@ static void ImGui::UpdateViewports() viewport->PlatformMonitor = FindPlatformMonitorForRect(viewport->GetRect()); } + // Reset alpha every frame. Users of transparency will need to request a lower alpha back. + viewport->Alpha = 1.0f; + // Translate imgui windows when a Host Viewport has been moved // (This additionally keeps windows at the same place when ImGuiConfigFlags_ViewportsEnable is toggled!) ImVec2 viewport_delta = viewport->Pos - viewport->LastPos; @@ -7427,6 +7443,7 @@ ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const viewport->Size = size; viewport->PlatformMonitor = FindPlatformMonitorForRect(viewport->GetRect()); g.Viewports.push_back(viewport); + //IMGUI_DEBUG_LOG("Add Viewport %08X (%s)\n", id, window->Name); // We normally setup for all viewports in NewFrame() but here need to handle the mid-frame creation of a new viewport. // We need to extend the fullscreen clip rect so the OverlayDrawList clip is correct for that the first frame @@ -7605,6 +7622,7 @@ void ImGui::UpdatePlatformWindows() bool is_new_window = (viewport->PlatformWindowCreated == false); if (is_new_window) { + //IMGUI_DEBUG_LOG("Create Platform Window %08X (%s)\n", viewport->ID, viewport->Window ? viewport->Window->Name : "n/a"); g.PlatformIO.Platform_CreateWindow(viewport); if (g.PlatformIO.Renderer_CreateWindow != NULL) g.PlatformIO.Renderer_CreateWindow(viewport); @@ -10185,12 +10203,13 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGuiWindow* window = g.Windows[n]; if ((window->Flags & ImGuiWindowFlags_ChildWindow) || !window->WasActive) continue; - char buf[32]; - ImFormatString(buf, IM_ARRAYSIZE(buf), "%d", window->BeginOrderWithinContext); - float font_size = ImGui::GetFontSize() * 2; + + char buf[64] = ""; + char* p = buf; + p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "Order: %d\n", window->BeginOrderWithinContext); ImDrawList* overlay_draw_list = GetOverlayDrawList(window->Viewport); - overlay_draw_list->AddRectFilled(window->Pos, window->Pos + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255)); - overlay_draw_list->AddText(NULL, font_size, window->Pos, IM_COL32(255, 255, 255, 255), buf); + overlay_draw_list->AddRectFilled(window->Pos - ImVec2(1, 1), window->Pos + CalcTextSize(buf) + ImVec2(1, 1), IM_COL32(200, 100, 100, 255)); + overlay_draw_list->AddText(NULL, 0.0f, window->Pos, IM_COL32(255, 255, 255, 255), buf); } } ImGui::End(); diff --git a/imgui_internal.h b/imgui_internal.h index aff91eb19d78..1dc43c9452c5 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -99,6 +99,8 @@ extern IMGUI_API ImGuiContext* GImGui; // Current implicit ImGui context pointe #else #define IM_NEWLINE "\n" #endif + +//#define IMGUI_DEBUG_LOG(FMT,...) printf("[%05d] " FMT, GImGui->FrameCount, __VA_ARGS__) #define IM_STATIC_ASSERT(_COND) typedef char static_assertion_##__line__[(_COND)?1:-1] #define IM_F32_TO_INT8_UNBOUND(_VAL) ((int)((_VAL) * 255.0f + ((_VAL)>=0 ? 0.5f : -0.5f))) // Unsaturated, for display purpose #define IM_F32_TO_INT8_SAT(_VAL) ((int)(ImSaturate(_VAL) * 255.0f + 0.5f)) // Saturated, always output 0..255 @@ -241,14 +243,6 @@ struct IMGUI_API ImPool // Types //----------------------------------------------------------------------------- -// 1D vector (this odd construct is used to facilitate the transition between 1D and 2D and maintenance of some patches) -struct ImVec1 -{ - float x; - ImVec1() { x = 0.0f; } - ImVec1(float _x) { x = _x; } -}; - enum ImGuiButtonFlags_ { ImGuiButtonFlags_None = 0, @@ -307,6 +301,19 @@ enum ImGuiSeparatorFlags_ ImGuiSeparatorFlags_Vertical = 1 << 1 }; +// Transient per-window ItemFlags, reset at the beginning of the frame. For child windows: inherited from parent on first Begin(). +// This is going to be exposed in imgui.h when stabilized enough. +enum ImGuiItemFlags_ +{ + ImGuiItemFlags_NoTabStop = 1 << 0, // false + ImGuiItemFlags_ButtonRepeat = 1 << 1, // false // Button() will return true multiple times based on io.KeyRepeatDelay and io.KeyRepeatRate settings. + ImGuiItemFlags_Disabled = 1 << 2, // false // [BETA] Disable interactions but doesn't affect visuals yet. See github.com/ocornut/imgui/issues/211 + ImGuiItemFlags_NoNav = 1 << 3, // false + ImGuiItemFlags_NoNavDefaultFocus = 1 << 4, // false + ImGuiItemFlags_SelectableDontClosePopup = 1 << 5, // false // MenuItem/Selectable() automatically closes current Popup window + ImGuiItemFlags_Default_ = 0 +}; + // Storage for LastItem data enum ImGuiItemStatusFlags_ { @@ -398,6 +405,14 @@ enum ImGuiPopupPositionPolicy ImGuiPopupPositionPolicy_ComboBox }; +// 1D vector (this odd construct is used to facilitate the transition between 1D and 2D and maintenance of some patches) +struct ImVec1 +{ + float x; + ImVec1() { x = 0.0f; } + ImVec1(float _x) { x = _x; } +}; + // 2D axis aligned bounding-box // NB: we can't rely on ImVec2 math operators being available here struct IMGUI_API ImRect @@ -632,7 +647,7 @@ struct ImGuiViewportP : public ImGuiViewport int PlatformMonitor; bool PlatformWindowCreated; bool PlatformWindowMinimized; - ImGuiWindow* Window; + ImGuiWindow* Window; // Set when the viewport is owned by a window ImDrawList* OverlayDrawList; // For convenience, a draw list we can render to that's always rendered last (we use it to draw software mouse cursor when io.MouseDrawCursor is set) ImDrawData DrawDataP; ImDrawDataBuilder DrawDataBuilder; @@ -702,7 +717,10 @@ struct ImGuiNextWindowData } }; +//----------------------------------------------------------------------------- // Main imgui context +//----------------------------------------------------------------------------- + struct ImGuiContext { bool Initialized; @@ -984,18 +1002,9 @@ struct ImGuiContext } }; -// Transient per-window flags, reset at the beginning of the frame. For child window, inherited from parent on first Begin(). -// This is going to be exposed in imgui.h when stabilized enough. -enum ImGuiItemFlags_ -{ - ImGuiItemFlags_NoTabStop = 1 << 0, // false - ImGuiItemFlags_ButtonRepeat = 1 << 1, // false // Button() will return true multiple times based on io.KeyRepeatDelay and io.KeyRepeatRate settings. - ImGuiItemFlags_Disabled = 1 << 2, // false // [BETA] Disable interactions but doesn't affect visuals yet. See github.com/ocornut/imgui/issues/211 - ImGuiItemFlags_NoNav = 1 << 3, // false - ImGuiItemFlags_NoNavDefaultFocus = 1 << 4, // false - ImGuiItemFlags_SelectableDontClosePopup = 1 << 5, // false // MenuItem/Selectable() automatically closes current Popup window - ImGuiItemFlags_Default_ = 0 -}; +//----------------------------------------------------------------------------- +// ImGuiWindow +//----------------------------------------------------------------------------- // Transient per-window data, reset at the beginning of the frame. This used to be called ImGuiDrawContext, hence the DC variable name in ImGuiWindow. // FIXME: That's theory, in practice the delimitation between ImGuiWindow and ImGuiWindowTempData is quite tenuous and could be reconsidered. From 4ef06f5aa2d3bbd9ac48ba1fa987d4de78b12ace Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 30 Nov 2018 17:15:03 +0100 Subject: [PATCH 356/828] Added ShowAboutWindow(), About Window now showing various config/build information. --- docs/CHANGELOG.txt | 3 ++ imgui.cpp | 4 +- imgui.h | 3 +- imgui_demo.cpp | 129 +++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 127 insertions(+), 12 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 0f861ad2f91b..3bd9509aae42 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -39,6 +39,9 @@ Other Changes: - When the focused window become inactive don't restore focus to a window with the ImGuiWindowFlags_NoInputs flag. (#2213) [@zzzyap] - Separator: Fixed Separator() outputting an extraneous empty line when captured into clipboard/text/file. +- Demo: Added ShowAboutWindow() call, previously was only accessible from the demo window. +- Demo: ShowAboutWindow() now display various Build/Config Information (compiler, os, etc.) that can easily be copied into bug reports. +- Fixed build issue with osxcross and macOS. (#2218) [@dos1] - Examples: SDL: changed the signature of ImGui_ImplSDL2_ProcessEvent() to use a const SDL_Event*. (#2187) diff --git a/imgui.cpp b/imgui.cpp index dd03af7b6684..a6ca91b11f6a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1096,7 +1096,7 @@ ImGuiIO::ImGuiIO() ConfigInputTextCursorBlink = true; ConfigResizeWindowsFromEdges = false; - // Settings (User Functions) + // Platform Functions GetClipboardTextFn = GetClipboardTextFn_DefaultImpl; // Platform dependent default implementations SetClipboardTextFn = SetClipboardTextFn_DefaultImpl; ClipboardUserData = NULL; @@ -1107,7 +1107,7 @@ ImGuiIO::ImGuiIO() RenderDrawListsFn = NULL; #endif - // Input (NB: we already have memset zero the entire structure) + // Input (NB: we already have memset zero the entire structure!) MousePos = ImVec2(-FLT_MAX, -FLT_MAX); MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX); MouseDragThreshold = 6.0f; diff --git a/imgui.h b/imgui.h index ee29b7c522d5..646e0784607c 100644 --- a/imgui.h +++ b/imgui.h @@ -204,7 +204,8 @@ namespace ImGui // Demo, Debug, Information IMGUI_API void ShowDemoWindow(bool* p_open = NULL); // create demo/test window (previously called ShowTestWindow). demonstrate most ImGui features. call this to learn about the library! try to make it always available in your application! - IMGUI_API void ShowMetricsWindow(bool* p_open = NULL); // create metrics window. display ImGui internals: draw commands (with individual draw calls and vertices), window list, basic internal state, etc. + IMGUI_API void ShowAboutWindow(bool* p_open = NULL); // create about window. display Dear ImGui version, credits and build/system information. + IMGUI_API void ShowMetricsWindow(bool* p_open = NULL); // create metrics window. display Dear ImGui internals: draw commands (with individual draw calls and vertices), window list, basic internal state, etc. IMGUI_API void ShowStyleEditor(ImGuiStyle* ref = NULL); // add style editor block (not a window). you can pass in a reference ImGuiStyle structure to compare to, revert to and save to (else it uses the default style) IMGUI_API bool ShowStyleSelector(const char* label); // add style selector block (not a window), essentially a combo listing the default styles. IMGUI_API void ShowFontSelector(const char* label); // add font selector block (not a window), essentially a combo listing the loaded fonts. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index d9c9020fa782..ea82a55ff025 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -27,6 +27,7 @@ Index of this file: // [SECTION] Forward Declarations, Helpers // [SECTION] Demo Window / ShowDemoWindow() +// [SECTION] About Window / ShowAboutWindow() // [SECTION] Style Editor / ShowStyleEditor() // [SECTION] Example App: Main Menu Bar / ShowExampleAppMainMenuBar() // [SECTION] Example App: Debug Console / ShowExampleAppConsole() @@ -198,15 +199,7 @@ void ImGui::ShowDemoWindow(bool* p_open) if (show_app_metrics) { ImGui::ShowMetricsWindow(&show_app_metrics); } if (show_app_style_editor) { ImGui::Begin("Style Editor", &show_app_style_editor); ImGui::ShowStyleEditor(); ImGui::End(); } - if (show_app_about) - { - ImGui::Begin("About Dear ImGui", &show_app_about, ImGuiWindowFlags_AlwaysAutoResize); - ImGui::Text("Dear ImGui, %s", ImGui::GetVersion()); - ImGui::Separator(); - ImGui::Text("By Omar Cornut and all dear imgui contributors."); - ImGui::Text("Dear ImGui is licensed under the MIT License, see LICENSE for more information."); - ImGui::End(); - } + if (show_app_about) { ShowAboutWindow(&show_app_about); } // Demonstrate the various window flags. Typically you would just use the default! static bool no_titlebar = false; @@ -2456,6 +2449,123 @@ static void ShowDemoWindowMisc() } } +//----------------------------------------------------------------------------- +// [SECTION] About Window / ShowAboutWindow() +// Access from ImGui Demo -> Help -> About +//----------------------------------------------------------------------------- + +void ImGui::ShowAboutWindow(bool* p_open) +{ + ImGui::Begin("About Dear ImGui", p_open, ImGuiWindowFlags_AlwaysAutoResize); + ImGui::Text("Dear ImGui, %s", ImGui::GetVersion()); + ImGui::Separator(); + ImGui::Text("By Omar Cornut and all dear imgui contributors."); + ImGui::Text("Dear ImGui is licensed under the MIT License, see LICENSE for more information."); + + static bool show_config_info = false; + ImGui::Checkbox("Config/Build Information", &show_config_info); + if (show_config_info) + { + ImGuiIO& io = ImGui::GetIO(); + ImGuiStyle& style = ImGui::GetStyle(); + + bool copy_to_clipboard = ImGui::Button("Copy to clipboard"); + ImGui::BeginChildFrame(ImGui::GetID("cfginfos"), ImVec2(0, ImGui::GetTextLineHeightWithSpacing() * 18), ImGuiWindowFlags_NoMove); + if (copy_to_clipboard) + ImGui::LogToClipboard(); + + ImGui::Text("Dear ImGui %s (%d)", IMGUI_VERSION, IMGUI_VERSION_NUM); + ImGui::Separator(); + ImGui::Text("sizeof(size_t): %d, sizeof(ImDrawIdx): %d, sizeof(ImDrawVert): %d", (int)sizeof(size_t), (int)sizeof(ImDrawIdx), (int)sizeof(ImDrawVert)); + ImGui::Text("define: __cplusplus=%d", (int)__cplusplus); +#ifdef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + ImGui::Text("define: IMGUI_DISABLE_OBSOLETE_FUNCTIONS"); +#endif +#ifdef IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS + ImGui::Text("define: IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS"); +#endif +#ifdef IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS + ImGui::Text("define: IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS"); +#endif +#ifdef IMGUI_DISABLE_WIN32_FUNCTIONS + ImGui::Text("define: IMGUI_DISABLE_WIN32_FUNCTIONS"); +#endif +#ifdef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS + ImGui::Text("define: IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS"); +#endif +#ifdef IMGUI_DISABLE_MATH_FUNCTIONS + ImGui::Text("define: IMGUI_DISABLE_MATH_FUNCTIONS"); +#endif +#ifdef IMGUI_DISABLE_DEFAULT_ALLOCATORS + ImGui::Text("define: IMGUI_DISABLE_DEFAULT_ALLOCATORS"); +#endif +#ifdef IMGUI_USE_BGRA_PACKED_COLOR + ImGui::Text("define: IMGUI_USE_BGRA_PACKED_COLOR"); +#endif +#ifdef _WIN32 + ImGui::Text("define: _WIN32"); +#endif +#ifdef _WIN64 + ImGui::Text("define: _WIN64"); +#endif +#ifdef __linux__ + ImGui::Text("define: __linux__"); +#endif +#ifdef __APPLE__ + ImGui::Text("define: __APPLE__"); +#endif +#ifdef _MSC_VER + ImGui::Text("define: _MSC_VER=%d", _MSC_VER); +#endif +#ifdef __MINGW32__ + ImGui::Text("define: __MINGW32__"); +#endif +#ifdef __MINGW64__ + ImGui::Text("define: __MINGW64__"); +#endif +#ifdef __GNUC__ + ImGui::Text("define: __GNUC__=%d", (int)__GNUC__); +#endif +#ifdef __clang_version__ + ImGui::Text("define: __clang_version__=%s", __clang_version__); +#endif + ImGui::Separator(); + ImGui::Text("io.ConfigFlags: 0x%08X", io.ConfigFlags); + if (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) ImGui::Text(" NavEnableKeyboard"); + if (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) ImGui::Text(" NavEnableGamepad"); + if (io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) ImGui::Text(" NavEnableSetMousePos"); + if (io.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard) ImGui::Text(" NavNoCaptureKeyboard"); + if (io.ConfigFlags & ImGuiConfigFlags_NoMouse) ImGui::Text(" NoMouse"); + if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) ImGui::Text(" NoMouseCursorChange"); + if (io.ConfigFlags & ImGuiConfigFlags_IsSRGB) ImGui::Text(" IsSRGB"); + if (io.ConfigFlags & ImGuiConfigFlags_IsTouchScreen) ImGui::Text(" IsTouchScreen"); + if (io.MouseDrawCursor) ImGui::Text(" MouseDrawCursor"); + if (io.ConfigMacOSXBehaviors) ImGui::Text(" ConfigMacOSXBehaviors"); + if (io.ConfigInputTextCursorBlink) ImGui::Text(" ConfigInputTextCursorBlink"); + if (io.ConfigResizeWindowsFromEdges) ImGui::Text(" ConfigResizeWindowsFromEdges"); + ImGui::Text("io.BackendFlags: 0x%08X", io.BackendFlags); + if (io.BackendFlags & ImGuiBackendFlags_HasGamepad) ImGui::Text(" HasGamepad"); + if (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) ImGui::Text(" HasMouseCursors"); + if (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos) ImGui::Text(" HasSetMousePos"); + ImGui::Separator(); + ImGui::Text("io.Fonts: %d fonts, Flags: 0x%08X, TexSize: %d,%d", io.Fonts->Fonts.Size, io.Fonts->Flags, io.Fonts->TexWidth, io.Fonts->TexHeight); + ImGui::Text("io.DisplaySize: %.2f,%.2f", io.DisplaySize.x, io.DisplaySize.y); + ImGui::Separator(); + ImGui::Text("style.WindowPadding: %.2f,%.2f", style.WindowPadding.x, style.WindowPadding.y); + ImGui::Text("style.WindowBorderSize: %.2f", style.WindowBorderSize); + ImGui::Text("style.FramePadding: %.2f,%.2f", style.FramePadding.x, style.FramePadding.y); + ImGui::Text("style.FrameRounding: %.2f", style.FrameRounding); + ImGui::Text("style.FrameBorderSize: %.2f", style.FrameBorderSize); + ImGui::Text("style.ItemSpacing: %.2f,%.2f", style.ItemSpacing.x, style.ItemSpacing.y); + ImGui::Text("style.ItemInnerSpacing: %.2f,%.2f", style.ItemInnerSpacing.x, style.ItemInnerSpacing.y); + + if (copy_to_clipboard) + ImGui::LogFinish(); + ImGui::EndChildFrame(); + } + ImGui::End(); +} + //----------------------------------------------------------------------------- // [SECTION] Style Editor / ShowStyleEditor() //----------------------------------------------------------------------------- @@ -3650,6 +3760,7 @@ static void ShowExampleAppCustomRendering(bool* p_open) // End of Demo code #else +void ImGui::ShowAboutWindow(bool*) {} void ImGui::ShowDemoWindow(bool*) {} void ImGui::ShowUserGuide() {} void ImGui::ShowStyleEditor(ImGuiStyle*) {} From a423f032ee1b74a375c56f76c482073ac53ee0ec Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 30 Nov 2018 18:18:15 +0100 Subject: [PATCH 357/828] About, IO: Added io.BackendPlatformName, io.BackendRendererName for informational/QA purpose. --- .github/issue_template.md | 12 ++++++++---- examples/imgui_impl_allegro5.cpp | 2 ++ examples/imgui_impl_dx10.cpp | 4 ++++ examples/imgui_impl_dx11.cpp | 4 ++++ examples/imgui_impl_dx12.cpp | 4 ++++ examples/imgui_impl_dx9.cpp | 4 ++++ examples/imgui_impl_freeglut.cpp | 3 +++ examples/imgui_impl_glfw.cpp | 2 ++ examples/imgui_impl_marmalade.cpp | 7 +++++-- examples/imgui_impl_metal.mm | 4 ++++ examples/imgui_impl_opengl2.cpp | 3 +++ examples/imgui_impl_opengl3.cpp | 5 +++++ examples/imgui_impl_osx.mm | 2 ++ examples/imgui_impl_sdl.cpp | 2 ++ examples/imgui_impl_vulkan.cpp | 4 ++++ examples/imgui_impl_win32.cpp | 2 ++ imgui.cpp | 1 + imgui.h | 6 +++++- imgui_demo.cpp | 2 ++ 19 files changed, 66 insertions(+), 7 deletions(-) diff --git a/.github/issue_template.md b/.github/issue_template.md index e434a545f0dc..38a595326b6e 100644 --- a/.github/issue_template.md +++ b/.github/issue_template.md @@ -11,15 +11,18 @@ https://discourse.dearimgui.org/c/getting-started ---- +_(you may also go to Demo>About Window, and click "Config/Build Information" to obtain a bunch of detailed information that you can paste here)_ + **Version/Branch of Dear ImGui:** -XXX +Version: XXX +Branch: XXX _(master/viewport/docking/etc.)_ -**Back-end file/Renderer/OS:** _(or specify if you are using a custom engine back-end)_ +**Back-end/Renderer/Compiler/OS** -Back-ends: imgui_impl_XXX.cpp + imgui_impl_XXX.cpp -OS: XXX +Back-ends: imgui_impl_XXX.cpp + imgui_impl_XXX.cpp _(or specify if using a custom engine/back-end)_ Compiler: XXX _(if the question is related to building)_ +Operating System: XXX **My Issue/Question:** _(please provide context)_ @@ -27,6 +30,7 @@ XXX **Standalone, minimal, complete and verifiable example:** _(see CONTRIBUTING.md)_ ``` +// Please do not forget this! ImGui::Begin("Example Bug"); MoreCodeToExplainMyIssue(); ImGui::End(); diff --git a/examples/imgui_impl_allegro5.cpp b/examples/imgui_impl_allegro5.cpp index 9a9c58019c1e..004638e800b1 100644 --- a/examples/imgui_impl_allegro5.cpp +++ b/examples/imgui_impl_allegro5.cpp @@ -15,6 +15,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2018-11-30: Misc: Setting up io.BackendPlatformName/io.BackendRendererName so they can be displayed in the About Window. // 2018-06-13: Platform: Added clipboard support (from Allegro 5.1.12). // 2018-06-13: Renderer: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle. // 2018-06-13: Renderer: Backup/restore transform and clipping rectangle. @@ -231,6 +232,7 @@ bool ImGui_ImplAllegro5_Init(ALLEGRO_DISPLAY* display) // Setup back-end capabilities flags ImGuiIO& io = ImGui::GetIO(); io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) + io.BackendPlatformName = io.BackendRendererName = "imgui_impl_allegro5"; // Create custom vertex declaration. // Unfortunately Allegro doesn't support 32-bits packed colors so we have to convert them to 4 floats. diff --git a/examples/imgui_impl_dx10.cpp b/examples/imgui_impl_dx10.cpp index f8fdef828bb8..f394d61bc6bf 100644 --- a/examples/imgui_impl_dx10.cpp +++ b/examples/imgui_impl_dx10.cpp @@ -10,6 +10,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window. // 2018-07-13: DirectX10: Fixed unreleased resources in Init and Shutdown functions. // 2018-06-08: Misc: Extracted imgui_impl_dx10.cpp/.h away from the old combined DX10+Win32 example. // 2018-06-08: DirectX10: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle. @@ -463,6 +464,9 @@ void ImGui_ImplDX10_InvalidateDeviceObjects() bool ImGui_ImplDX10_Init(ID3D10Device* device) { + ImGuiIO& io = ImGui::GetIO(); + io.BackendRendererName = "imgui_impl_dx10"; + // Get factory from device IDXGIDevice* pDXGIDevice = NULL; IDXGIAdapter* pDXGIAdapter = NULL; diff --git a/examples/imgui_impl_dx11.cpp b/examples/imgui_impl_dx11.cpp index 052f484f5175..eaef797bfa9c 100644 --- a/examples/imgui_impl_dx11.cpp +++ b/examples/imgui_impl_dx11.cpp @@ -10,6 +10,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window. // 2018-08-01: DirectX11: Querying for IDXGIFactory instead of IDXGIFactory1 to increase compatibility. // 2018-07-13: DirectX11: Fixed unreleased resources in Init and Shutdown functions. // 2018-06-08: Misc: Extracted imgui_impl_dx11.cpp/.h away from the old combined DX11+Win32 example. @@ -470,6 +471,9 @@ void ImGui_ImplDX11_InvalidateDeviceObjects() bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_context) { + ImGuiIO& io = ImGui::GetIO(); + io.BackendRendererName = "imgui_impl_dx11"; + // Get factory from device IDXGIDevice* pDXGIDevice = NULL; IDXGIAdapter* pDXGIAdapter = NULL; diff --git a/examples/imgui_impl_dx12.cpp b/examples/imgui_impl_dx12.cpp index a59939e08473..46d0b8f3295d 100644 --- a/examples/imgui_impl_dx12.cpp +++ b/examples/imgui_impl_dx12.cpp @@ -12,6 +12,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window. // 2018-06-12: DirectX12: Moved the ID3D12GraphicsCommandList* parameter from NewFrame() to RenderDrawData(). // 2018-06-08: Misc: Extracted imgui_impl_dx12.cpp/.h away from the old combined DX12+Win32 example. // 2018-06-08: DirectX12: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle (to ease support for future multi-viewport). @@ -586,6 +587,9 @@ void ImGui_ImplDX12_InvalidateDeviceObjects() bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FORMAT rtv_format, D3D12_CPU_DESCRIPTOR_HANDLE font_srv_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE font_srv_gpu_desc_handle) { + ImGuiIO& io = ImGui::GetIO(); + io.BackendRendererName = "imgui_impl_dx12"; + g_pd3dDevice = device; g_RTVFormat = rtv_format; g_hFontSrvCpuDescHandle = font_srv_cpu_desc_handle; diff --git a/examples/imgui_impl_dx9.cpp b/examples/imgui_impl_dx9.cpp index 4aa139d44c2a..5d3287e81254 100644 --- a/examples/imgui_impl_dx9.cpp +++ b/examples/imgui_impl_dx9.cpp @@ -10,6 +10,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window. // 2018-06-08: Misc: Extracted imgui_impl_dx9.cpp/.h away from the old combined DX9+Win32 example. // 2018-06-08: DirectX9: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle. // 2018-05-07: Render: Saving/restoring Transform because they don't seem to be included in the StateBlock. Setting shading mode to Gouraud. @@ -199,6 +200,9 @@ void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data) bool ImGui_ImplDX9_Init(IDirect3DDevice9* device) { + ImGuiIO& io = ImGui::GetIO(); + io.BackendRendererName = "imgui_impl_dx9"; + g_pd3dDevice = device; return true; } diff --git a/examples/imgui_impl_freeglut.cpp b/examples/imgui_impl_freeglut.cpp index 4058876a5bff..2122e307fc7d 100644 --- a/examples/imgui_impl_freeglut.cpp +++ b/examples/imgui_impl_freeglut.cpp @@ -11,6 +11,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window. // 2018-03-22: Added FreeGLUT Platform binding. #include "imgui.h" @@ -26,6 +27,8 @@ static int g_Time = 0; // Current time, in milliseconds bool ImGui_ImplFreeGLUT_Init() { ImGuiIO& io = ImGui::GetIO(); + io.BackendPlatformName ="imgui_impl_freeglut"; + g_Time = 0; // Glut has 1 function for characters and one for "special keys". We map the characters in the 0..255 range and the keys above. diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index a4bcc31a555b..2122c60182fb 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -14,6 +14,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window. // 2018-11-07: Inputs: When installing our GLFW callbacks, we save user's previously installed ones - if any - and chain call them. // 2018-08-01: Inputs: Workaround for Emscripten which doesn't seem to handle focus related calls. // 2018-06-29: Inputs: Added support for the ImGuiMouseCursor_Hand cursor. @@ -130,6 +131,7 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw ImGuiIO& io = ImGui::GetIO(); io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) + io.BackendPlatformName = "imgui_impl_glfw"; // Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array. io.KeyMap[ImGuiKey_Tab] = GLFW_KEY_TAB; diff --git a/examples/imgui_impl_marmalade.cpp b/examples/imgui_impl_marmalade.cpp index d3aa6983964b..45435977b4e7 100644 --- a/examples/imgui_impl_marmalade.cpp +++ b/examples/imgui_impl_marmalade.cpp @@ -10,6 +10,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2018-11-30: Misc: Setting up io.BackendPlatformName/io.BackendRendererName so they can be displayed in the About Window. // 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_Marmalade_RenderDrawData() in the .h file so you can call it yourself. // 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. // 2018-02-06: Inputs: Added mapping for ImGuiKey_Space. @@ -42,7 +43,7 @@ void ImGui_Marmalade_RenderDrawData(ImDrawData* draw_data) draw_data->ScaleClipRects(io.DisplayFramebufferScale); // Render command lists - for(int n = 0; n < draw_data->CmdListsCount; n++) + for (int n = 0; n < draw_data->CmdListsCount; n++) { const ImDrawList* cmd_list = draw_data->CmdLists[n]; const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data; @@ -51,7 +52,7 @@ void ImGui_Marmalade_RenderDrawData(ImDrawData* draw_data) CIwFVec2* pUVStream = IW_GX_ALLOC(CIwFVec2, nVert); CIwColour* pColStream = IW_GX_ALLOC(CIwColour, nVert); - for( int i=0; i < nVert; i++ ) + for (int i = 0; i < nVert; i++) { // TODO: optimize multiplication on gpu using vertex shader/projection matrix. pVertStream[i].x = cmd_list->VtxBuffer[i].pos.x * g_RenderScale.x; @@ -214,6 +215,8 @@ void ImGui_Marmalade_InvalidateDeviceObjects() bool ImGui_Marmalade_Init(bool install_callbacks) { ImGuiIO& io = ImGui::GetIO(); + io.BackendPlatformName = io.BackendRendererName = "imgui_impl_marmalade"; + io.KeyMap[ImGuiKey_Tab] = s3eKeyTab; // Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array. io.KeyMap[ImGuiKey_LeftArrow] = s3eKeyLeft; io.KeyMap[ImGuiKey_RightArrow] = s3eKeyRight; diff --git a/examples/imgui_impl_metal.mm b/examples/imgui_impl_metal.mm index fc6254c0c554..ae960e9fec90 100644 --- a/examples/imgui_impl_metal.mm +++ b/examples/imgui_impl_metal.mm @@ -10,6 +10,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window. // 2018-07-05: Metal: Added new Metal backend implementation. #include "imgui.h" @@ -65,6 +66,9 @@ - (void)renderDrawData:(ImDrawData *)drawData bool ImGui_ImplMetal_Init(id device) { + ImGuiIO& io = ImGui::GetIO(); + io.BackendRendererName = "imgui_impl_metal"; + static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ g_sharedMetalContext = [[MetalContext alloc] init]; diff --git a/examples/imgui_impl_opengl2.cpp b/examples/imgui_impl_opengl2.cpp index 5acf12f1f0cc..7b9d2b80d65e 100644 --- a/examples/imgui_impl_opengl2.cpp +++ b/examples/imgui_impl_opengl2.cpp @@ -18,6 +18,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window. // 2018-08-03: OpenGL: Disabling/restoring GL_LIGHTING and GL_COLOR_MATERIAL to increase compatibility with legacy OpenGL applications. // 2018-06-08: Misc: Extracted imgui_impl_opengl2.cpp/.h away from the old combined GLFW/SDL+OpenGL2 examples. // 2018-06-08: OpenGL: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle. @@ -53,6 +54,8 @@ static GLuint g_FontTexture = 0; // Functions bool ImGui_ImplOpenGL2_Init() { + ImGuiIO& io = ImGui::GetIO(); + io.BackendRendererName = "imgui_impl_opengl2"; return true; } diff --git a/examples/imgui_impl_opengl3.cpp b/examples/imgui_impl_opengl3.cpp index 353d61e9762b..917fa80941d9 100644 --- a/examples/imgui_impl_opengl3.cpp +++ b/examples/imgui_impl_opengl3.cpp @@ -11,6 +11,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window. // 2018-11-13: OpenGL: Support for GL 4.5's glClipControl(GL_UPPER_LEFT). // 2018-08-29: OpenGL: Added support for more OpenGL loaders: glew and glad, with comments indicative that any loader can be used. // 2018-08-09: OpenGL: Default to OpenGL ES 3 on iOS and Android. GLSL version default to "#version 300 ES". @@ -100,6 +101,9 @@ static unsigned int g_VboHandle = 0, g_ElementsHandle = 0; // Functions bool ImGui_ImplOpenGL3_Init(const char* glsl_version) { + ImGuiIO& io = ImGui::GetIO(); + io.BackendRendererName = "imgui_impl_opengl3"; + // Store GLSL version string so we can refer to it later in case we recreate shaders. Note: GLSL version is NOT the same as GL version. Leave this to NULL if unsure. #ifdef USE_GL_ES3 if (glsl_version == NULL) @@ -111,6 +115,7 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version) IM_ASSERT((int)strlen(glsl_version) + 2 < IM_ARRAYSIZE(g_GlslVersionString)); strcpy(g_GlslVersionString, glsl_version); strcat(g_GlslVersionString, "\n"); + return true; } diff --git a/examples/imgui_impl_osx.mm b/examples/imgui_impl_osx.mm index b1dbe7c41232..39f706c88432 100644 --- a/examples/imgui_impl_osx.mm +++ b/examples/imgui_impl_osx.mm @@ -12,6 +12,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window. // 2018-07-07: Initial version. // Data @@ -27,6 +28,7 @@ bool ImGui_ImplOSX_Init() //io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) //io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional) //io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can set io.MouseHoveredViewport correctly (optional, not easy) + io.BackendPlatformName = "imgui_impl_osx"; // Keyboard mapping. ImGui will use those indices to peek into the io.KeyDown[] array. const int offset_for_function_keys = 256 - 0xF700; diff --git a/examples/imgui_impl_sdl.cpp b/examples/imgui_impl_sdl.cpp index 2b6d97e03883..f948ed267118 100644 --- a/examples/imgui_impl_sdl.cpp +++ b/examples/imgui_impl_sdl.cpp @@ -16,6 +16,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window. // 2018-11-14: Changed the signature of ImGui_ImplSDL2_ProcessEvent() to take a 'const SDL_Event*'. // 2018-08-01: Inputs: Workaround for Emscripten which doesn't seem to handle focus related calls. // 2018-06-29: Inputs: Added support for the ImGuiMouseCursor_Hand cursor. @@ -123,6 +124,7 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window) ImGuiIO& io = ImGui::GetIO(); io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) + io.BackendPlatformName = "imgui_impl_sdl" // Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array. io.KeyMap[ImGuiKey_Tab] = SDL_SCANCODE_TAB; diff --git a/examples/imgui_impl_vulkan.cpp b/examples/imgui_impl_vulkan.cpp index 0746e5b710e8..dbfbe5ca7fc5 100644 --- a/examples/imgui_impl_vulkan.cpp +++ b/examples/imgui_impl_vulkan.cpp @@ -13,6 +13,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window. // 2018-08-25: Vulkan: Fixed mishandled VkSurfaceCapabilitiesKHR::maxImageCount=0 case. // 2018-06-22: Inverted the parameters to ImGui_ImplVulkan_RenderDrawData() to be consistent with other bindings. // 2018-06-08: Misc: Extracted imgui_impl_vulkan.cpp/.h away from the old combined GLFW+Vulkan example. @@ -695,6 +696,9 @@ void ImGui_ImplVulkan_InvalidateDeviceObjects() bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info, VkRenderPass render_pass) { + ImGuiIO& io = ImGui::GetIO(); + io.BackendRendererName = "imgui_impl_vulkan"; + IM_ASSERT(info->Instance != VK_NULL_HANDLE); IM_ASSERT(info->PhysicalDevice != VK_NULL_HANDLE); IM_ASSERT(info->Device != VK_NULL_HANDLE); diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index 6cf8d630690b..9bf8a81d88f7 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -18,6 +18,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window. // 2018-06-29: Inputs: Added support for the ImGuiMouseCursor_Hand cursor. // 2018-06-10: Inputs: Fixed handling of mouse wheel messages to support fine position messages (typically sent by track-pads). // 2018-06-08: Misc: Extracted imgui_impl_win32.cpp/.h away from the old combined DX9/DX10/DX11/DX12 examples. @@ -52,6 +53,7 @@ bool ImGui_ImplWin32_Init(void* hwnd) ImGuiIO& io = ImGui::GetIO(); io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) + io.BackendPlatformName = "imgui_impl_win32"; io.ImeWindowHandle = hwnd; // Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array that we will update during the application lifetime. diff --git a/imgui.cpp b/imgui.cpp index a6ca91b11f6a..1387496193dd 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1097,6 +1097,7 @@ ImGuiIO::ImGuiIO() ConfigResizeWindowsFromEdges = false; // Platform Functions + BackendPlatformName = BackendRendererName = NULL; GetClipboardTextFn = GetClipboardTextFn_DefaultImpl; // Platform dependent default implementations SetClipboardTextFn = SetClipboardTextFn_DefaultImpl; ClipboardUserData = NULL; diff --git a/imgui.h b/imgui.h index 646e0784607c..72e01dc69e05 100644 --- a/imgui.h +++ b/imgui.h @@ -1172,6 +1172,10 @@ struct ImGuiIO // (the imgui_impl_xxxx back-end files are setting those up for you) //------------------------------------------------------------------ + // Optional: Platform/Renderer back-end name (informational only! will be displayed in About Window) + const char* BackendPlatformName; + const char* BackendRendererName; + // Optional: Access OS clipboard // (default to use native Win32 clipboard on Windows, otherwise uses a private clipboard. Override to access OS clipboard on other architectures) const char* (*GetClipboardTextFn)(void* user_data); @@ -1181,7 +1185,7 @@ struct ImGuiIO // Optional: Notify OS Input Method Editor of the screen position of your cursor for text input position (e.g. when using Japanese/Chinese IME on Windows) // (default to use native imm32 api on Windows) void (*ImeSetInputScreenPosFn)(int x, int y); - void* ImeWindowHandle; // (Windows) Set this to your HWND to get automatic IME cursor positioning. + void* ImeWindowHandle; // (Windows) Set this to your HWND to get automatic IME cursor positioning. #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS // [OBSOLETE since 1.60+] Rendering function, will be automatically called in Render(). Please call your rendering function yourself now! diff --git a/imgui_demo.cpp b/imgui_demo.cpp index ea82a55ff025..2a62935827eb 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2547,6 +2547,8 @@ void ImGui::ShowAboutWindow(bool* p_open) if (io.BackendFlags & ImGuiBackendFlags_HasGamepad) ImGui::Text(" HasGamepad"); if (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) ImGui::Text(" HasMouseCursors"); if (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos) ImGui::Text(" HasSetMousePos"); + ImGui::Text("io.BackendPlatformName: %s", io.BackendPlatformName ? io.BackendPlatformName : "NULL"); + ImGui::Text("io.BackendRendererName: %s", io.BackendRendererName ? io.BackendRendererName : "NULL"); ImGui::Separator(); ImGui::Text("io.Fonts: %d fonts, Flags: 0x%08X, TexSize: %d,%d", io.Fonts->Fonts.Size, io.Fonts->Flags, io.Fonts->TexWidth, io.Fonts->TexHeight); ImGui::Text("io.DisplaySize: %.2f,%.2f", io.DisplaySize.x, io.DisplaySize.y); From ec49a486c7918206b625c3b4922893f18fcc2f51 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 30 Nov 2018 18:27:04 +0100 Subject: [PATCH 358/828] About: Added build/system info relating to Viewport branch. --- imgui_demo.cpp | 42 +++++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 360a09bb51c7..bd7a2bc72f3c 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2539,25 +2539,37 @@ void ImGui::ShowAboutWindow(bool* p_open) #endif #ifdef __clang_version__ ImGui::Text("define: __clang_version__=%s", __clang_version__); +#endif +#ifdef IMGUI_HAS_VIEWPORT + ImGui::Text("define: IMGUI_HAS_VIEWPORT"); #endif ImGui::Separator(); ImGui::Text("io.ConfigFlags: 0x%08X", io.ConfigFlags); - if (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) ImGui::Text(" NavEnableKeyboard"); - if (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) ImGui::Text(" NavEnableGamepad"); - if (io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) ImGui::Text(" NavEnableSetMousePos"); - if (io.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard) ImGui::Text(" NavNoCaptureKeyboard"); - if (io.ConfigFlags & ImGuiConfigFlags_NoMouse) ImGui::Text(" NoMouse"); - if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) ImGui::Text(" NoMouseCursorChange"); - if (io.ConfigFlags & ImGuiConfigFlags_IsSRGB) ImGui::Text(" IsSRGB"); - if (io.ConfigFlags & ImGuiConfigFlags_IsTouchScreen) ImGui::Text(" IsTouchScreen"); - if (io.MouseDrawCursor) ImGui::Text(" MouseDrawCursor"); - if (io.ConfigMacOSXBehaviors) ImGui::Text(" ConfigMacOSXBehaviors"); - if (io.ConfigInputTextCursorBlink) ImGui::Text(" ConfigInputTextCursorBlink"); - if (io.ConfigResizeWindowsFromEdges) ImGui::Text(" ConfigResizeWindowsFromEdges"); + if (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) ImGui::Text(" NavEnableKeyboard"); + if (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) ImGui::Text(" NavEnableGamepad"); + if (io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) ImGui::Text(" NavEnableSetMousePos"); + if (io.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard) ImGui::Text(" NavNoCaptureKeyboard"); + if (io.ConfigFlags & ImGuiConfigFlags_NoMouse) ImGui::Text(" NoMouse"); + if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) ImGui::Text(" NoMouseCursorChange"); + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) ImGui::Text(" ViewportsEnable"); + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsNoTaskBarIcon) ImGui::Text(" ViewportsNoTaskBarIcon"); + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsNoMerge) ImGui::Text(" ViewportsNoMerge"); + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsDecoration) ImGui::Text(" ViewportsDecoration"); + if (io.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleViewports) ImGui::Text(" DpiEnableScaleViewports"); + if (io.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleFonts) ImGui::Text(" DpiEnableScaleFonts"); + if (io.ConfigFlags & ImGuiConfigFlags_IsSRGB) ImGui::Text(" IsSRGB"); + if (io.ConfigFlags & ImGuiConfigFlags_IsTouchScreen) ImGui::Text(" IsTouchScreen"); + if (io.MouseDrawCursor) ImGui::Text(" MouseDrawCursor"); + if (io.ConfigMacOSXBehaviors) ImGui::Text(" ConfigMacOSXBehaviors"); + if (io.ConfigInputTextCursorBlink) ImGui::Text(" ConfigInputTextCursorBlink"); + if (io.ConfigResizeWindowsFromEdges) ImGui::Text(" ConfigResizeWindowsFromEdges"); ImGui::Text("io.BackendFlags: 0x%08X", io.BackendFlags); - if (io.BackendFlags & ImGuiBackendFlags_HasGamepad) ImGui::Text(" HasGamepad"); - if (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) ImGui::Text(" HasMouseCursors"); - if (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos) ImGui::Text(" HasSetMousePos"); + if (io.BackendFlags & ImGuiBackendFlags_HasGamepad) ImGui::Text(" HasGamepad"); + if (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) ImGui::Text(" HasMouseCursors"); + if (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos) ImGui::Text(" HasSetMousePos"); + if (io.BackendFlags & ImGuiBackendFlags_PlatformHasViewports) ImGui::Text(" PlatformHasViewports"); + if (io.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport)ImGui::Text(" HasMouseHoveredViewport"); + if (io.BackendFlags & ImGuiBackendFlags_RendererHasViewports) ImGui::Text(" RendererHasViewports"); ImGui::Text("io.BackendPlatformName: %s", io.BackendPlatformName ? io.BackendPlatformName : "NULL"); ImGui::Text("io.BackendRendererName: %s", io.BackendRendererName ? io.BackendRendererName : "NULL"); ImGui::Separator(); From 025e00cccc03deaa9bb005cb2045cf09b13357f2 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 30 Nov 2018 18:29:35 +0100 Subject: [PATCH 359/828] About: Added build/system info relating to Docking branch. --- imgui_demo.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 35633680c96b..d5831418e222 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2646,6 +2646,12 @@ void ImGui::ShowAboutWindow(bool* p_open) #endif #ifdef IMGUI_HAS_VIEWPORT ImGui::Text("define: IMGUI_HAS_VIEWPORT"); +#endif +#ifdef IMGUI_HAS_DOCK + ImGui::Text("define: IMGUI_HAS_DOCK"); +#endif +#ifdef IMGUI_HAS_TABS + ImGui::Text("define: IMGUI_HAS_TABS"); #endif ImGui::Separator(); ImGui::Text("io.ConfigFlags: 0x%08X", io.ConfigFlags); @@ -2655,6 +2661,8 @@ void ImGui::ShowAboutWindow(bool* p_open) if (io.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard) ImGui::Text(" NavNoCaptureKeyboard"); if (io.ConfigFlags & ImGuiConfigFlags_NoMouse) ImGui::Text(" NoMouse"); if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) ImGui::Text(" NoMouseCursorChange"); + if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable) ImGui::Text(" DockingEnable"); + if (io.ConfigFlags & ImGuiConfigFlags_DockingNoSplit) ImGui::Text(" DockingNoSplit"); if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) ImGui::Text(" ViewportsEnable"); if (io.ConfigFlags & ImGuiConfigFlags_ViewportsNoTaskBarIcon) ImGui::Text(" ViewportsNoTaskBarIcon"); if (io.ConfigFlags & ImGuiConfigFlags_ViewportsNoMerge) ImGui::Text(" ViewportsNoMerge"); @@ -2664,6 +2672,8 @@ void ImGui::ShowAboutWindow(bool* p_open) if (io.ConfigFlags & ImGuiConfigFlags_IsSRGB) ImGui::Text(" IsSRGB"); if (io.ConfigFlags & ImGuiConfigFlags_IsTouchScreen) ImGui::Text(" IsTouchScreen"); if (io.MouseDrawCursor) ImGui::Text(" MouseDrawCursor"); + if (io.ConfigDockingWithShift) ImGui::Text(" ConfigDockingWithShift"); + if (io.ConfigDockingTransparentPayload) ImGui::Text(" ConfigDockingTransparentPayload"); if (io.ConfigMacOSXBehaviors) ImGui::Text(" ConfigMacOSXBehaviors"); if (io.ConfigInputTextCursorBlink) ImGui::Text(" ConfigInputTextCursorBlink"); if (io.ConfigResizeWindowsFromEdges) ImGui::Text(" ConfigResizeWindowsFromEdges"); From 8289e5f6b4f3b678f568192009b657f3489d9805 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 30 Nov 2018 23:47:23 +0100 Subject: [PATCH 360/828] Fixed a text rendering/clipping bug introduced in 1.66 (on 2018-10-12, commit ede3a3b9) that affect single ImDrawList::AddText() calls with single strings larger than 10k. Text/TextUnformatted() calls were not affected, but e.g. InputText() was. --- docs/CHANGELOG.txt | 2 ++ imgui_demo.cpp | 2 ++ imgui_draw.cpp | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 3c33a94f828a..7e300fbe4b71 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -37,6 +37,8 @@ Breaking Changes: Other Changes: +- Fixed a text rendering/clipping bug introduced in 1.66 (on 2018-10-12, commit ede3a3b9) that affect single ImDrawList::AddText() + calls with single strings larger than 10k. Text/TextUnformatted() calls were not affected, but e.g. InputText() was. [@pdoane] - When the focused window become inactive don't restore focus to a window with the ImGuiWindowFlags_NoInputs flag. (#2213) [@zzzyap] - Separator: Fixed Separator() outputting an extraneous empty line when captured into clipboard/text/file. - Demo: Added ShowAboutWindow() call, previously was only accessible from the demo window. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index bd7a2bc72f3c..a1b7f4ae1416 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -913,6 +913,8 @@ static void ShowDemoWindowWidgets() if (ImGui::TreeNode("Multi-line Text Input")) { + // Note: we are using a fixed-sized buffer for simplicity here. See ImGuiInputTextFlags_CallbackResize + // and the code in misc/cpp/imgui_stdlib.h for how to setup InputText() for dynamically resizing strings. static bool read_only = false; static char text[1024*16] = "/*\n" diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 6c82015ad9b7..1b29a1d99211 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -2662,7 +2662,7 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col while (y_end < clip_rect.w && s_end < text_end) { s_end = (const char*)memchr(s_end, '\n', text_end - s_end); - s = s ? s + 1 : text_end; + s_end = s_end ? s_end + 1 : text_end; y_end += line_height; } text_end = s_end; From 34e18ef7714db98cf53b5959050502b7f2e1c8ee Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 30 Nov 2018 23:47:23 +0100 Subject: [PATCH 361/828] Fixed a text rendering/clipping bug introduced in 1.66 (on 2018-10-12, commit ede3a3b9) that affect single ImDrawList::AddText() calls with single strings larger than 10k. Text/TextUnformatted() calls were not affected, but e.g. InputText() was. --- docs/CHANGELOG.txt | 2 ++ imgui_demo.cpp | 2 ++ imgui_draw.cpp | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 801f194888d8..6835136a5540 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -64,6 +64,8 @@ Breaking Changes: Other Changes: +- Fixed a text rendering/clipping bug introduced in 1.66 (on 2018-10-12, commit ede3a3b9) that affect single ImDrawList::AddText() + calls with single strings larger than 10k. Text/TextUnformatted() calls were not affected, but e.g. InputText() was. [@pdoane] - When the focused window become inactive don't restore focus to a window with the ImGuiWindowFlags_NoInputs flag. (#2213) [@zzzyap] - Separator: Fixed Separator() outputting an extraneous empty line when captured into clipboard/text/file. - Demo: Added ShowAboutWindow() call, previously was only accessible from the demo window. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index d5831418e222..8d6516dcfbe8 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -945,6 +945,8 @@ static void ShowDemoWindowWidgets() if (ImGui::TreeNode("Multi-line Text Input")) { + // Note: we are using a fixed-sized buffer for simplicity here. See ImGuiInputTextFlags_CallbackResize + // and the code in misc/cpp/imgui_stdlib.h for how to setup InputText() for dynamically resizing strings. static bool read_only = false; static char text[1024*16] = "/*\n" diff --git a/imgui_draw.cpp b/imgui_draw.cpp index e966fcf6abb0..868a56cd9aab 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -2683,7 +2683,7 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col while (y_end < clip_rect.w && s_end < text_end) { s_end = (const char*)memchr(s_end, '\n', text_end - s_end); - s = s ? s + 1 : text_end; + s_end = s_end ? s_end + 1 : text_end; y_end += line_height; } text_end = s_end; From 89a530b442d2f989e5ab58e16037c28c990eef9c Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 3 Dec 2018 11:44:08 +0100 Subject: [PATCH 362/828] Somehow VS keeps modifying this GUID so I am going to playing along with it. --- examples/example_sdl_opengl2/example_sdl_opengl2.vcxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/example_sdl_opengl2/example_sdl_opengl2.vcxproj b/examples/example_sdl_opengl2/example_sdl_opengl2.vcxproj index fa6b8d3afec1..af7637ec58de 100644 --- a/examples/example_sdl_opengl2/example_sdl_opengl2.vcxproj +++ b/examples/example_sdl_opengl2/example_sdl_opengl2.vcxproj @@ -19,7 +19,7 @@ - {94E991D0-790A-4DAF-B442-AAADE3233C75} + {2AE17FDE-F7F3-4CAC-ADAB-0710EDA4F741} example_sdl_opengl2 From 3e84f7cd1dc01e86794f3386510d9b6772028e4f Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 3 Dec 2018 18:16:35 +0100 Subject: [PATCH 363/828] Viewport: Fixed a viewport bug which led some popups to merge in underlying host viewport when they should be z-over their parent viewport. --- docs/TODO.txt | 2 +- imgui.cpp | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/docs/TODO.txt b/docs/TODO.txt index edc053a962f5..c8d77637fa4b 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -287,7 +287,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - viewport: with platform decoration enabled, platform may force constraint (e.g. minimum size) - viewport: use getfocus/setfocus api to synchronize imgui<>platform focus better (e.g imgui-side ctrl-tab can focus os window, OS initial setup and alt-tab can focus imgui window etc.) - viewport: store per-viewport/monitor DPI in .ini file so an application reload or main window changing DPI on reload can be properly patched for. - - viewport: implicit Debug window can hog a zombie viewport (harmless, noisy?) > could at least clear out the reference on a per session basis? + - viewport: implicit/fallback Debug window can hog a zombie viewport (harmless, noisy?) > could at least clear out the reference on a per session basis? - viewport: need to clarify how to use GetMousePos() from a user point of view. - platform: glfw: no support for ImGuiBackendFlags_HasMouseHoveredViewport. - platform: sdl: no support for ImGuiBackendFlags_HasMouseHoveredViewport. maybe we could use SDL_GetMouseFocus() / SDL_WINDOW_MOUSE_FOCUS if imgui could fallback on its heuristic when NoInputs is set diff --git a/imgui.cpp b/imgui.cpp index 631046383763..3c4c20faefc1 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7487,10 +7487,6 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) return; } - // Merge into host viewport - bool try_to_merge_into_host_viewport = false; - if (window->ViewportOwned && g.ActiveId == 0) - try_to_merge_into_host_viewport = true; window->ViewportOwned = false; // Appearing popups reset their viewport so they can inherit again @@ -7541,6 +7537,9 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) } else { + // Merge into host viewport? + // We cannot test window->ViewportOwned as it set lower in the function. + bool try_to_merge_into_host_viewport = (window->Viewport && window == window->Viewport->Window && g.ActiveId == 0); if (try_to_merge_into_host_viewport) UpdateTryMergeWindowIntoHostViewport(window, g.Viewports[0]); } @@ -7594,7 +7593,7 @@ void ImGui::UpdatePlatformWindows() { ImGuiViewportP* viewport = g.Viewports[i]; - // Destroy platform window if the viewport hasn't been submitted or if it is hosting a hidden window (the implicit Debug window will be registered its viewport then be disabled) + // Destroy platform window if the viewport hasn't been submitted or if it is hosting a hidden window (the implicit/fallback Debug window will be registered its viewport then be disabled) bool destroy_platform_window = false; destroy_platform_window |= (viewport->LastFrameActive < g.FrameCount - 1); destroy_platform_window |= (viewport->Window && !IsWindowActiveAndVisible(viewport->Window)); From f6b6ad1959b9478806333afd14071850fd61e0ad Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 3 Dec 2018 20:28:32 +0100 Subject: [PATCH 364/828] Viewport: Minor tweaks, better struct layout. --- imgui.cpp | 6 +++--- imgui.h | 4 ++-- imgui_internal.h | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 3c4c20faefc1..7d0351f1c2f0 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7165,7 +7165,7 @@ void ImGui::SetCurrentViewport(ImGuiWindow* current_window, ImGuiViewportP* view /* if (viewport->LastFrameActive < g.FrameCount && viewport->Window != current_window && viewport->Window != NULL && current_window != NULL) { - //printf("[%05d] Window '%s' steal Viewport %08X from Window '%s'\n", g.FrameCount, current_window->Name, viewport->ID, viewport->Window->Name); + //IMGUI_DEBUG_LOG("[%05d] Window '%s' steal Viewport %08X from Window '%s'\n", g.FrameCount, current_window->Name, viewport->ID, viewport->Window->Name); viewport->Window = current_window; viewport->ID = current_window->ID; } @@ -7327,7 +7327,7 @@ static void ImGui::UpdateViewports() } // Update monitor (we'll use this info to clamp windows and save windows lost in a removed monitor) - viewport->PlatformMonitor = FindPlatformMonitorForRect(viewport->GetRect()); + viewport->PlatformMonitor = (short)FindPlatformMonitorForRect(viewport->GetRect()); } // Reset alpha every frame. Users of transparency will need to request a lower alpha back. @@ -7446,7 +7446,7 @@ ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const viewport->Idx = g.Viewports.Size; viewport->Pos = viewport->LastPos = pos; viewport->Size = size; - viewport->PlatformMonitor = FindPlatformMonitorForRect(viewport->GetRect()); + viewport->PlatformMonitor = (short)FindPlatformMonitorForRect(viewport->GetRect()); g.Viewports.push_back(viewport); //IMGUI_DEBUG_LOG("Add Viewport %08X (%s)\n", id, window->Name); diff --git a/imgui.h b/imgui.h index 9db67ebfb71c..ae7bac6e8554 100644 --- a/imgui.h +++ b/imgui.h @@ -2162,14 +2162,14 @@ struct ImGuiViewport float DpiScale; // 1.0f = 96 DPI = No extra scale ImDrawData* DrawData; // The ImDrawData corresponding to this viewport. Valid after Render() and until the next call to NewFrame(). + void* RendererUserData; // void* to hold custom data structure for the renderer (e.g. swap chain, frame-buffers etc.) void* PlatformUserData; // void* to hold custom data structure for the OS / platform (e.g. windowing info, render context) void* PlatformHandle; // void* for FindViewportByPlatformHandle(). (e.g. suggested to use natural platform handle such as HWND, GlfwWindow*, SDL_Window*) bool PlatformRequestClose; // Platform window requested closure (e.g. window was moved by the OS / host window manager, e.g. pressing ALT-F4) bool PlatformRequestMove; // Platform window requested move (e.g. window was moved by the OS / host window manager, authoritative position will be OS window position) bool PlatformRequestResize; // Platform window requested resize (e.g. window was resized by the OS / host window manager, authoritative size will be OS window size) - void* RendererUserData; // void* to hold custom data structure for the renderer (e.g. swap chain, frame-buffers etc.) - ImGuiViewport() { ID = 0; Flags = 0; DpiScale = 0.0f; DrawData = NULL; PlatformUserData = PlatformHandle = NULL; PlatformRequestClose = PlatformRequestMove = PlatformRequestResize = false; RendererUserData = NULL; } + ImGuiViewport() { ID = 0; Flags = 0; DpiScale = 0.0f; DrawData = NULL; RendererUserData = PlatformUserData = PlatformHandle = NULL; PlatformRequestClose = PlatformRequestMove = PlatformRequestResize = false; } ~ImGuiViewport() { IM_ASSERT(PlatformUserData == NULL && RendererUserData == NULL); } }; diff --git a/imgui_internal.h b/imgui_internal.h index 6c158f7026f3..a6ea650a41e1 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -644,7 +644,7 @@ struct ImGuiViewportP : public ImGuiViewport ImVec2 LastPos; float Alpha; // Window opacity (when dragging dockable windows/viewports we make them transparent) float LastAlpha; - int PlatformMonitor; + short PlatformMonitor; bool PlatformWindowCreated; bool PlatformWindowMinimized; ImGuiWindow* Window; // Set when the viewport is owned by a window @@ -655,7 +655,7 @@ struct ImGuiViewportP : public ImGuiViewport ImVec2 LastPlatformSize; ImVec2 LastRendererSize; - ImGuiViewportP() { Idx = -1; LastFrameActive = LastFrameOverlayDrawList = LastFrontMostStampCount = -1; LastNameHash = 0; Alpha = LastAlpha = 1.0f; PlatformMonitor = INT_MIN; PlatformWindowCreated = PlatformWindowMinimized = false; Window = NULL; OverlayDrawList = NULL; LastPlatformPos = LastPlatformSize = LastRendererSize = ImVec2(FLT_MAX, FLT_MAX); } + ImGuiViewportP() { Idx = -1; LastFrameActive = LastFrameOverlayDrawList = LastFrontMostStampCount = -1; LastNameHash = 0; Alpha = LastAlpha = 1.0f; PlatformMonitor = -1; PlatformWindowCreated = PlatformWindowMinimized = false; Window = NULL; OverlayDrawList = NULL; LastPlatformPos = LastPlatformSize = LastRendererSize = ImVec2(FLT_MAX, FLT_MAX); } ~ImGuiViewportP() { if (OverlayDrawList) IM_DELETE(OverlayDrawList); } ImRect GetRect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } ImVec2 GetCenter() const{ return ImVec2(Pos.x + Size.x * 0.5f, Pos.y + Size.y * 0.5f); } From 2da200fc16ac9b2411e547cb887493e661a9c6e1 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 3 Dec 2018 22:53:10 +0100 Subject: [PATCH 365/828] Docking: Fix generalizing the transition from loose windows to dock node so we can switch to single window dock node while transferring viewports (vs creating two viewports and dropping one). + Extracted DockContextGenNodeID out of DockContextAddNode(). --- imgui.cpp | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index e0634995d936..3fd666d86f7b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -9818,6 +9818,7 @@ namespace ImGui { // ImGuiDockContext static ImGuiDockNode* DockContextAddNode(ImGuiContext* ctx, ImGuiID id); + static ImGuiID DockContextGenNodeID(ImGuiContext* ctx); static void DockContextRemoveNode(ImGuiContext* ctx, ImGuiDockNode* node, bool merge_sibling_into_parent_node); static void DockContextQueueDock(ImGuiContext* ctx, ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, float split_ratio, bool split_outer); static void DockContextQueueNotifyRemovedNode(ImGuiContext* ctx, ImGuiDockNode* node); @@ -10008,17 +10009,23 @@ static ImGuiDockNode* ImGui::DockContextFindNodeByID(ImGuiContext* ctx, ImGuiID return (ImGuiDockNode*)ctx->DockContext->Nodes.GetVoidPtr(id); } +static ImGuiID ImGui::DockContextGenNodeID(ImGuiContext* ctx) +{ + // Generate an ID for new node (the exact ID value doesn't matter as long as it is not already used) + // FIXME-OPT: This is suboptimal, even if the node count is small enough not to be a worry. We should poke in ctx->Nodes to find a suitable ID faster. + ImGuiID id = 0x0001; + while (DockContextFindNodeByID(ctx, id) != NULL) + id++; + return id; +} + static ImGuiDockNode* ImGui::DockContextAddNode(ImGuiContext* ctx, ImGuiID id) { // Generate an ID for the new node (the exact ID value doesn't matter as long as it is not already used) and add the first window. if (id == 0) - { - // FIXME-OPT: This is suboptimal, even if the node count is small enough not to be a worry. We could poke in ctx->Nodes to find a suitable ID faster. - id = 0x0001; - while (DockContextFindNodeByID(ctx, id) != NULL) - id++; - } - IM_ASSERT(DockContextFindNodeByID(ctx, id) == NULL); + id = DockContextGenNodeID(ctx); + else + IM_ASSERT(DockContextFindNodeByID(ctx, id) == NULL); ImGuiDockNode* node = IM_NEW(ImGuiDockNode)(id); node->InitFromFirstWindowPosSize = node->InitFromFirstWindowViewport = true; ctx->DockContext->Nodes.SetVoidPtr(node->ID, node); @@ -10436,14 +10443,15 @@ static void ImGui::DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window, b // If 2+ windows appeared on the same frame, creating a new DockNode+TabBar from the second window, // then we need to hide the first one after the fact otherwise it would be visible as a standalone window for one frame. - if (node->Windows.Size == 2 && node->HostWindow == NULL && node->Windows[0]->WasActive == false) + if (node->HostWindow == NULL && node->Windows.Size == 2 && node->Windows[0]->WasActive == false) { node->Windows[0]->Hidden = true; node->Windows[0]->HiddenFramesRegular = 1; } - // When reactivating a node from two loose window, the target window pos/size are authoritative - if (node->Windows.Size == 2 && !node->IsDockSpace) + // When reactivating a node with one or two loose window, the window pos/size/viewport are authoritative over the node storage. + // In particular it is important we init the viewport from the first window so we don't create two viewports and drop one. + if (node->HostWindow == NULL && !node->IsDockSpace && node->IsRootNode()) node->InitFromFirstWindowPosSize = node->InitFromFirstWindowViewport = true; // Add to tab bar if requested @@ -10817,7 +10825,6 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) } else if (node->HostWindow == NULL) { - // Leaf SetNextWindowPos(node->Pos); SetNextWindowSize(node->Size); } From 8bac6d428d147041566c001c3927496957dc2a03 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 3 Dec 2018 23:52:48 +0100 Subject: [PATCH 366/828] Docking: Another attempt at tidying the inconsistent focus / tab bar reordering issues, and toward generalizing single window dock nodes. (#2109) --- imgui.cpp | 33 +++++++++++++++++++++------------ imgui_internal.h | 1 - imgui_widgets.cpp | 10 ++++------ 3 files changed, 25 insertions(+), 19 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 3fd666d86f7b..bbfd48b85714 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10337,7 +10337,7 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) // Update selection immediately if (ImGuiTabBar* tab_bar = target_node->TabBar) - tab_bar->NextSelectedTabId = tab_bar->WantFocusTabId = next_selected_id; + tab_bar->NextSelectedTabId = next_selected_id; MarkIniSettingsDirty(); } @@ -10461,6 +10461,10 @@ static void ImGui::DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window, b { node->TabBar = IM_NEW(ImGuiTabBar)(); node->TabBar->SelectedTabId = node->TabBar->NextSelectedTabId = node->SelectedTabID; + + // Add existing windows + for (int n = 0; n < node->Windows.Size - 1; n++) + TabBarAddTab(node->TabBar, node->Windows[n], ImGuiTabItemFlags_None); } TabBarAddTab(node->TabBar, window, ImGuiTabItemFlags_Unsorted); } @@ -10835,7 +10839,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) char window_label[20]; DockNodeGetHostWindowTitle(node, window_label, IM_ARRAYSIZE(window_label)); ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_DockNodeHost; - //window_flags |= ImGuiWindowFlags_NoFocusOnAppearing; + window_flags |= ImGuiWindowFlags_NoFocusOnAppearing; window_flags |= ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus; window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse; @@ -11018,6 +11022,8 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w if (tab_bar == NULL) tab_bar = node->TabBar = IM_NEW(ImGuiTabBar)(); + ImGuiID focus_tab_id = 0; + // Collapse button changes shape and display a list if (IsPopupOpen("#TabListMenu")) { @@ -11037,7 +11043,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; IM_ASSERT(tab->Window != NULL); if (Selectable(tab->Window->Name, tab->ID == tab_bar->SelectedTabId)) - tab_bar->NextSelectedTabId = tab_bar->WantFocusTabId = tab->ID; + focus_tab_id = tab_bar->NextSelectedTabId = tab->ID; SameLine(); Text(" "); } @@ -11046,8 +11052,6 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w } } - ImGuiID focus_tab_id = 0; - // Title bar node->IsFocused = is_focused; if (is_focused) @@ -11063,11 +11067,16 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w if (IsItemActive()) focus_tab_id = tab_bar->SelectedTabId; - // Submit new tabs + // Submit new tabs and apply NavWindow focus back to the tab bar. They will be added as Unsorted and sorted below based on relative DockOrder value. const int tabs_count_old = tab_bar->Tabs.Size; for (int window_n = 0; window_n < node->Windows.Size; window_n++) - if (TabBarFindTabByID(tab_bar, node->Windows[window_n]->ID) == NULL) - TabBarAddTab(tab_bar, node->Windows[window_n], ImGuiTabItemFlags_Unsorted); + { + ImGuiWindow* window = node->Windows[window_n]; + if (g.NavWindow && g.NavWindow->RootWindowDockStop == window) + tab_bar->SelectedTabId = window->ID; + if (TabBarFindTabByID(tab_bar, window->ID) == NULL) + TabBarAddTab(tab_bar, window, ImGuiTabItemFlags_Unsorted); + } // If multiple tabs are appearing on the same frame, sort them based on their persistent DockOrder value int tabs_unsorted_start = tab_bar->Tabs.Size; @@ -11170,13 +11179,13 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w } // Forward focus from host node to selected window - if (is_focused && g.NavWindow == host_window && !g.NavWindowingTarget) - focus_tab_id = tab_bar->SelectedTabId; + //if (is_focused && g.NavWindow == host_window && !g.NavWindowingTarget) + // focus_tab_id = tab_bar->SelectedTabId; // When clicked on a tab we requested focus to the docked child // This overrides the value set by "forward focus from host node to selected window". - if (tab_bar->WantFocusTabId) - focus_tab_id = tab_bar->WantFocusTabId; + if (tab_bar->NextSelectedTabId) + focus_tab_id = tab_bar->NextSelectedTabId; // Apply navigation focus if (focus_tab_id != 0) diff --git a/imgui_internal.h b/imgui_internal.h index 07b6200248d6..5ab77baa2b79 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1355,7 +1355,6 @@ struct ImGuiTabBar ImGuiID SelectedTabId; // Selected tab ImGuiID NextSelectedTabId; ImGuiID VisibleTabId; // Can occasionally be != SelectedTabId (e.g. when previewing contents for CTRL+TAB preview) - ImGuiID WantFocusTabId; // Request focus for the window associated to this tab. Used and only honored by DockNode (meaningless for standalone tab bars) int CurrFrameVisible; int PrevFrameVisible; ImRect BarRect; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 072ff02149ea..fe9bc6371b50 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -5813,7 +5813,7 @@ namespace ImGui ImGuiTabBar::ImGuiTabBar() { ID = 0; - SelectedTabId = NextSelectedTabId = VisibleTabId = WantFocusTabId = 0; + SelectedTabId = NextSelectedTabId = VisibleTabId = 0; CurrFrameVisible = PrevFrameVisible = -1; OffsetMax = OffsetNextTab = 0.0f; ScrollingAnim = ScrollingTarget = 0.0f; @@ -5960,7 +5960,6 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) // Setup next selected tab ImGuiID scroll_track_selected_tab_id = 0; - tab_bar->WantFocusTabId = 0; if (tab_bar->NextSelectedTabId) { tab_bar->SelectedTabId = tab_bar->NextSelectedTabId; @@ -6157,7 +6156,6 @@ void ImGui::TabBarRemoveTab(ImGuiTabBar* tab_bar, ImGuiID tab_id) if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_id)) tab_bar->Tabs.erase(tab); if (tab_bar->VisibleTabId == tab_id) { tab_bar->VisibleTabId = 0; } - if (tab_bar->WantFocusTabId == tab_id) { tab_bar->WantFocusTabId = 0; } if (tab_bar->SelectedTabId == tab_id) { tab_bar->SelectedTabId = 0; } if (tab_bar->NextSelectedTabId == tab_id) { tab_bar->NextSelectedTabId = 0; } } @@ -6447,8 +6445,8 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags); hovered |= (g.HoveredId == id); - if (pressed || ((flags & ImGuiTabItemFlags_SetSelected) && !tab_contents_visible)) // SetSelected can only be passed on explicit tab bar, so we don't need to set WantFocusTabId - tab_bar->NextSelectedTabId = tab_bar->WantFocusTabId = id; + if (pressed || ((flags & ImGuiTabItemFlags_SetSelected) && !tab_contents_visible)) // SetSelected can only be passed on explicit tab bar + tab_bar->NextSelectedTabId = id; // Allow the close button to overlap unless we are dragging (in which case we don't want any overlapping tabs to be hovered) if (!held) @@ -6531,7 +6529,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, // Select with right mouse button. This is so the common idiom for context menu automatically highlight the current widget. const bool hovered_unblocked = IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup); if (hovered_unblocked && (IsMouseClicked(1) || IsMouseReleased(1))) - tab_bar->NextSelectedTabId = tab_bar->WantFocusTabId = id; + tab_bar->NextSelectedTabId = id; if (tab_bar->Flags & ImGuiTabBarFlags_NoCloseWithMiddleMouseButton) flags |= ImGuiTabItemFlags_NoCloseWithMiddleMouseButton; From 8cac70d8af05e7bcec1d558cc1ea36c33c14c0be Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 4 Dec 2018 13:34:16 +0100 Subject: [PATCH 367/828] Docking: Focus fix (missing line in 8bac6d4) --- imgui.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index e4734d809c5e..086c51514203 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5811,7 +5811,7 @@ void ImGui::FocusWindow(ImGuiWindow* window) // Select in dock node if (window->DockNode && window->DockNode->TabBar) - window->DockNode->TabBar->SelectedTabId = window->ID; + window->DockNode->TabBar->SelectedTabId = window->DockNode->TabBar->NextSelectedTabId = window->ID; // Move the root window to the top of the pile if (window->RootWindow) @@ -12301,6 +12301,8 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) // Bind to our dock node ImGuiDockNode* dock_node = window->DockNode; + if (dock_node != NULL) + IM_ASSERT(window->DockId == dock_node->ID); if (window->DockId != 0 && dock_node == NULL) { dock_node = DockContextFindNodeByID(ctx, window->DockId); From 6644f1ff641ba080df64a4017ff1f358e3ce2215 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 4 Dec 2018 13:36:36 +0100 Subject: [PATCH 368/828] Docking: Added io.ConfigDockingTabBarOnSingleWindows option (mostly made possible by the previous fixes). Note that dock node have regressions compared to current floating window: no collapse, no auto-resize, resize grip under the scrollbar, border issues, general overhead. Will tackle those. --- imgui.cpp | 47 +++++++++++++++++++++++++++++++++-------------- imgui.h | 1 + imgui_demo.cpp | 2 ++ imgui_widgets.cpp | 21 +++++++++++++++------ 4 files changed, 51 insertions(+), 20 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 086c51514203..6a4f49e15bc5 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4935,6 +4935,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Find or create ImGuiWindow* window = FindWindowByName(name); const bool window_just_created = (window == NULL); + const bool window_is_fallback = (g.CurrentWindowStack.Size == 0); if (window_just_created) { ImVec2 size_on_first_use = (g.NextWindowData.SizeCond != 0) ? g.NextWindowData.SizeVal : ImVec2(0.0f, 0.0f); // Any condition flag will do since we are creating a new window here. @@ -4983,10 +4984,15 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) IM_ASSERT(window->DockNode == NULL || window->DockNodeAsHost == NULL); // Cannot be both if (g.NextWindowData.DockCond) SetWindowDock(window, g.NextWindowData.DockId, g.NextWindowData.DockCond); - if (first_begin_of_the_frame && (window->DockId != 0 || window->DockNode != NULL)) + if (first_begin_of_the_frame) { - BeginDocked(window, p_open); - flags = window->Flags; + bool has_dock_node = (window->DockId != 0 || window->DockNode != NULL); + bool new_auto_dock_node = !has_dock_node && g.IO.ConfigDockingTabBarOnSingleWindows && !(flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoDocking)) && !window_is_fallback; + if (has_dock_node || new_auto_dock_node) + { + BeginDocked(window, p_open); + flags = window->Flags; + } } // Parent window is latched only on the first call to Begin() of the frame, so further append-calls can be done from a different window stack @@ -10247,7 +10253,7 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) if (target_node) IM_ASSERT(target_node->LastFrameAlive < g.FrameCount); if (target_node && target_window && target_node == target_window->DockNodeAsHost) - IM_ASSERT(target_node->Windows.Size > 1 || target_node->IsSplitNode() || target_node->IsCentralNode); + IM_ASSERT(target_node->Windows.Size > 0 || target_node->IsSplitNode() || target_node->IsCentralNode); // Create new node and add existing window to it if (target_node == NULL) @@ -10758,7 +10764,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) } // Early out for hidden root dock nodes (when all DockId references are in inactive windows, or there is only 1 floating window holding on the DockId) - if (node->IsRootNode() && node->IsLeafNode() && node->Windows.Size <= 1 && !node->IsDockSpace) + if (node->Windows.Size <= 1 && node->IsRootNode() && node->IsLeafNode() && !node->IsDockSpace && !g.IO.ConfigDockingTabBarOnSingleWindows) { if (node->Windows.Size == 1) { @@ -12288,15 +12294,28 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) ImGuiContext* ctx = GImGui; ImGuiContext& g = *ctx; - // Calling SetNextWindowPos() undock windows by default (by setting PosUndock) - bool want_undock = false; - want_undock |= (window->Flags & ImGuiWindowFlags_NoDocking) != 0; - want_undock |= (g.NextWindowData.PosCond && (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) && g.NextWindowData.PosUndock); - g.NextWindowData.PosUndock = false; - if (want_undock) + const bool auto_dock_node = (g.IO.ConfigDockingTabBarOnSingleWindows) && !(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoDocking)); + + if (auto_dock_node) { - DockContextProcessUndockWindow(ctx, window); - return; + if (window->DockId == 0) + { + IM_ASSERT(window->DockNode == NULL); + window->DockId = DockContextGenNodeID(ctx); + } + } + else + { + // Calling SetNextWindowPos() undock windows by default (by setting PosUndock) + bool want_undock = false; + want_undock |= (window->Flags & ImGuiWindowFlags_NoDocking) != 0; + want_undock |= (g.NextWindowData.PosCond && (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) && g.NextWindowData.PosUndock); + g.NextWindowData.PosUndock = false; + if (want_undock) + { + DockContextProcessUndockWindow(ctx, window); + return; + } } // Bind to our dock node @@ -12337,7 +12356,7 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) // Undock if our dockspace node disappeared // Note how we are testing for LastFrameAlive and NOT LastFrameActive. A DockSpace node can be maintained alive while being inactive with ImGuiDockNodeFlags_KeepAliveOnly. - if (dock_node->LastFrameAlive < g.FrameCount) + if (dock_node->LastFrameAlive < g.FrameCount && !auto_dock_node) { // If the window has been orphaned, transition the docknode to an implicit node processed in DockContextUpdateDocking() ImGuiDockNode* root_node = DockNodeGetRootNode(dock_node); diff --git a/imgui.h b/imgui.h index a36ccf55d92d..8e94caecbcdd 100644 --- a/imgui.h +++ b/imgui.h @@ -1278,6 +1278,7 @@ struct ImGuiIO // Miscellaneous configuration options bool MouseDrawCursor; // = false // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). Cannot be easily renamed to 'io.ConfigXXX' because this is frequently used by back-end implementations. bool ConfigDockingWithShift; // = false // Enable docking with holding Shift key (reduce visual noise, allows dropping in wider space) + bool ConfigDockingTabBarOnSingleWindows;//= false // [BETA] Make every single floating window display within a docking node. bool ConfigDockingTransparentPayload; // = false // [BETA] Make window or viewport transparent when docking and only display docking boxes on the target viewport. Useful if rendering of multiple viewport can be synced. Best used with ImGuiConfigFlags_ViewportsNoMerge. bool ConfigMacOSXBehaviors; // = defined(__APPLE__) // OS X style: Text editing cursor movement using Alt instead of Ctrl, Shortcuts using Cmd/Super instead of Ctrl, Line/Text Start and End using Cmd+Arrows instead of Home/End, Double click selects by word instead of selecting whole text, Multi-selection in lists uses Cmd/Super instead of Ctrl (was called io.OptMacOSXBehaviors prior to 1.63) bool ConfigInputTextCursorBlink; // = true // Set to false to disable blinking cursor, for users who consider it distracting. (was called: io.OptCursorBlink prior to 1.63) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 39bb1ba04b18..6a6505d196d9 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -353,6 +353,8 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::Checkbox("io.ConfigDockingWithShift", &io.ConfigDockingWithShift); ImGui::SameLine(); ShowHelpMarker("Enable docking when holding Shift only (allows to drop in wider space, reduce visual noise)"); + ImGui::Checkbox("io.ConfigDockingTabBarOnSingleWindows", &io.ConfigDockingTabBarOnSingleWindows); + ImGui::SameLine(); ShowHelpMarker("Associate a docking node and tab-bar to sinle floating windows."); ImGui::Checkbox("io.ConfigInputTextCursorBlink", &io.ConfigInputTextCursorBlink); ImGui::SameLine(); ShowHelpMarker("Set to false to disable blinking cursor, for users who consider it distracting"); ImGui::Checkbox("io.ConfigResizeWindowsFromEdges [beta]", &io.ConfigResizeWindowsFromEdges); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 1096941c1525..7a59a9d3e482 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6452,9 +6452,19 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, if (!held) SetItemAllowOverlap(); - // Drag and drop - if (held && !tab_appearing && IsMouseDragging()) + // Drag and drop a single floating window node moves it + // FIXME-DOCK: In theory we shouldn't test for the ConfigDockingNodifySingleWindows flag here. + // When our single window node and OnlyNodeWithWindows are working properly we may remove this check here. + ImGuiDockNode* node = docked_window->DockNode; + const bool single_window_node = node->IsRootNode() && node->Windows.Size == 1 && g.IO.ConfigDockingTabBarOnSingleWindows; + if (held && single_window_node && IsMouseDragging(0, 0.0f)) { + // Move + StartMouseMovingWindow(docked_window); + } + else if (held && !tab_appearing && IsMouseDragging(0)) + { + // Drag and drop // Re-order local or dockable tabs float drag_distance_from_edge_x = 0.0f; if (!g.DragDropActive && ((tab_bar->Flags & ImGuiTabBarFlags_Reorderable) || (flags & ImGuiTabItemFlags_DockedWindow))) @@ -6478,9 +6488,8 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, if (flags & ImGuiTabItemFlags_DockedWindow) { // We use a variable threshold to distinguish dragging tabs within a tab bar and extracting them out of the tab bar - //ImVec2 drag_delta = GetMouseDragDelta(); bool undocking_tab = (g.DragDropActive && g.DragDropPayload.SourceId == id); - if (!undocking_tab && held)// && (drag_delta.x != 0.0f || drag_delta.y != 0.0f)) + if (!undocking_tab && held) { //if (!g.IO.ConfigDockingWithShift || g.IO.KeyShift) { @@ -6499,9 +6508,9 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, } } - // Undock - if (undocking_tab && g.ActiveId == id && IsMouseDragging()) + if (undocking_tab) { + // Undock DockContextQueueUndockWindow(&g, docked_window); g.MovingWindow = docked_window; g.ActiveId = g.MovingWindow->MoveId; From b96b1f2412a9f324e2fb5218268eb18bbd190202 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 4 Dec 2018 16:38:27 +0100 Subject: [PATCH 369/828] Docking: Documenting an issue. Renamed member of ImGuiDockFamily. --- imgui.cpp | 8 ++++---- imgui.h | 6 +++--- imgui_demo.cpp | 2 ++ 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 6a4f49e15bc5..3c9fcff67c55 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11219,11 +11219,11 @@ static bool DockNodeIsDropAllowedOne(ImGuiWindow* payload, ImGuiWindow* host_win ImGuiDockFamily* host_family = host_window->DockNodeAsHost ? &host_window->DockNodeAsHost->DockFamily : &host_window->DockFamily; ImGuiDockFamily* payload_family = &payload->DockFamily; - if (host_family->ID != payload_family->ID) + if (host_family->FamilyId != payload_family->FamilyId) { - if (host_family->ID != 0 && host_family->CompatibleWithFamilyZero && payload_family->ID == 0) + if (host_family->FamilyId != 0 && host_family->CompatibleWithFamilyZero && payload_family->FamilyId == 0) return true; - if (payload_family->ID != 0 && payload_family->CompatibleWithFamilyZero && host_family->ID == 0) + if (payload_family->FamilyId != 0 && payload_family->CompatibleWithFamilyZero && host_family->FamilyId == 0) return true; return false; } @@ -13068,7 +13068,7 @@ static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSetting settings->ViewportPos = window->ViewportPos; IM_ASSERT(window->DockNode == NULL || window->DockNode->ID == window->DockId); settings->DockId = window->DockId; - settings->DockFamilyId = window->DockFamily.ID; + settings->DockFamilyId = window->DockFamily.FamilyId; settings->DockOrder = window->DockOrder; settings->Collapsed = window->Collapsed; } diff --git a/imgui.h b/imgui.h index 8e94caecbcdd..111d1ebadcf9 100644 --- a/imgui.h +++ b/imgui.h @@ -1446,11 +1446,11 @@ struct ImGuiPayload // [BETA] For SetNextWindowDockFamily() and DockSpace() function struct ImGuiDockFamily { - ImGuiID ID; // 0 = unaffiliated + ImGuiID FamilyId; // 0 = unaffiliated bool CompatibleWithFamilyZero; // true = can be docked/merged with an unaffiliated window - ImGuiDockFamily() { ID = 0; CompatibleWithFamilyZero = true; } - ImGuiDockFamily(ImGuiID id, bool compatible_with_family_zero = true) { ID = id; CompatibleWithFamilyZero = compatible_with_family_zero; } + ImGuiDockFamily() { FamilyId = 0; CompatibleWithFamilyZero = true; } + ImGuiDockFamily(ImGuiID family_id, bool compatible_with_family_zero = true) { FamilyId = family_id; CompatibleWithFamilyZero = compatible_with_family_zero; } }; //----------------------------------------------------------------------------- diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 6a6505d196d9..b74720e4f3ca 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1539,6 +1539,8 @@ static void ShowDemoWindowWidgets() ImGui::Checkbox("Hovered/Active tests after Begin() for title bar testing", &test_window); if (test_window) { + // FIXME-DOCK: This window cannot be docked within the ImGui Demo window, this will cause a feedback loop and get them stuck. + // Could we fix this through an ImGuiDockFamily feature? Or an API call to tag our parent as "don't skip items"? ImGui::Begin("Title bar Hovered/Active tests", &test_window); if (ImGui::BeginPopupContextItem()) // <-- This is using IsItemHovered() { From b94f0241f1218d7a4caaa62e7a37e3cb825ea172 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 5 Dec 2018 21:19:42 +0100 Subject: [PATCH 370/828] Docking: Adjusting the docking popup menu position so it tends to stay within the same viewport. --- imgui.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 3c9fcff67c55..d1651cf39e8a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10845,8 +10845,8 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) DockNodeGetHostWindowTitle(node, window_label, IM_ARRAYSIZE(window_label)); ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_DockNodeHost; window_flags |= ImGuiWindowFlags_NoFocusOnAppearing; - window_flags |= ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus; - window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse; + window_flags |= ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus | ImGuiWindowFlags_NoCollapse; + window_flags |= ImGuiWindowFlags_NoTitleBar; PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); Begin(window_label, NULL, window_flags); @@ -11032,7 +11032,8 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w // Collapse button changes shape and display a list if (IsPopupOpen("#TabListMenu")) { - SetNextWindowPos(ImVec2(node->Pos.x - style.FramePadding.x, node->Pos.y + GetFrameHeight())); + // Try to position the menu so it is more likely to stays within the same viewport + SetNextWindowPos(ImVec2(node->Pos.x, node->Pos.y + GetFrameHeight())); if (BeginPopup("#TabListMenu")) { is_focused = true; From e2082a675c209b3135eae4abcc0f4413662965e7 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 5 Dec 2018 23:38:37 +0100 Subject: [PATCH 371/828] Viewport: Fix handling of PlatformRequestResize/PlatformRequestPos. when OS decoration are enabled via ImGuiConfigFlags_ViewportsDecoration . --- imgui.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index d1651cf39e8a..5110b07a1bb0 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7687,8 +7687,10 @@ ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const ImGuiViewportP* viewport = (ImGuiViewportP*)FindViewportByID(id); if (viewport) { - viewport->Pos = pos; - viewport->Size = size; + if (!viewport->PlatformRequestMove) + viewport->Pos = pos; + if (!viewport->PlatformRequestResize) + viewport->Size = size; } else { From ac52d9d44c0d8aeac198cec513a2ff25dda97394 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 5 Dec 2018 23:38:37 +0100 Subject: [PATCH 372/828] Viewport: Fix handling of PlatformRequestResize/PlatformRequestPos. when OS decoration are enabled via ImGuiConfigFlags_ViewportsDecoration . --- imgui.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 149d7828f8d0..0c42cfb1b3dc 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7435,8 +7435,10 @@ ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const ImGuiViewportP* viewport = (ImGuiViewportP*)FindViewportByID(id); if (viewport) { - viewport->Pos = pos; - viewport->Size = size; + if (!viewport->PlatformRequestMove) + viewport->Pos = pos; + if (!viewport->PlatformRequestResize) + viewport->Size = size; } else { From f3a0b17bb8be22c8ae88a6a064c25c1cf13c3c72 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 6 Dec 2018 16:30:10 +0100 Subject: [PATCH 373/828] Viewport: Win32, GLFW, SDL: Clarified back-ends by using global mouse position direction. GLFW: disabled io.MouseHoveredViewport setting under Mac/Linux. (#1542, #2117) + various comments. --- examples/imgui_impl_glfw.cpp | 26 +++++++++++++++--- examples/imgui_impl_sdl.cpp | 22 ++++++++------- examples/imgui_impl_win32.cpp | 51 +++++++++++++++++++++-------------- imgui.h | 2 +- 4 files changed, 67 insertions(+), 34 deletions(-) diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index 2c4c9cb646a1..a327669a51ea 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -141,7 +141,7 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional) -#if GLFW_HAS_GLFW_HOVERED +#if GLFW_HAS_GLFW_HOVERED && defined(_WIN32) io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can set io.MouseHoveredViewport correctly (optional, not easy) #endif io.BackendPlatformName = "imgui_impl_glfw"; @@ -264,14 +264,32 @@ static void ImGui_ImplGlfw_UpdateMousePosAndButtons() { double mouse_x, mouse_y; glfwGetCursorPos(window, &mouse_x, &mouse_y); - io.MousePos = ImVec2((float)mouse_x + viewport->Pos.x, (float)mouse_y + viewport->Pos.y); + if (io.ConfigFlags & 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) + int window_x, window_y; + glfwGetWindowPos(window, &window_x, &window_y); + io.MousePos = ImVec2((float)mouse_x + window_x, (float)mouse_y + window_y); + } + 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.MousePos = ImVec2((float)mouse_x, (float)mouse_y); + } } for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) io.MouseDown[i] |= glfwGetMouseButton(window, i) != 0; } -#if GLFW_HAS_GLFW_HOVERED - io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; + // (Optional) When using multiple viewports: set io.MouseHoveredViewport to the viewport the OS mouse cursor is hovering. + // Important: this information is not easy to provide and many high-level windowing library won't be able to provide it correctly, because + // - This is _ignoring_ viewports with the ImGuiViewportFlags_NoInputs flag (pass-through windows). + // - This is _regardless_ of whether another viewport is focused or being dragged from. + // If ImGuiBackendFlags_HasMouseHoveredViewport is not set by the back-end, imgui will ignore this field and infer the information by relying on the + // rectangles and last focused time of every viewports it knows about. It will be unaware of other windows that may be sitting between or over your windows. + // [GLFW] FIXME: This is currently only correct on Win32. See what we do below with the WM_NCHITTEST, missing an equivalent for other systems. + // See https://github.com/glfw/glfw/issues/1236 if you want to help in making this a GLFW feature. +#if GLFW_HAS_GLFW_HOVERED && defined(_WIN32) if (glfwGetWindowAttrib(window, GLFW_HOVERED) && !(viewport->Flags & ImGuiViewportFlags_NoInputs)) io.MouseHoveredViewport = viewport->ID; #endif diff --git a/examples/imgui_impl_sdl.cpp b/examples/imgui_impl_sdl.cpp index 7ef8dec01f01..3e883550390b 100644 --- a/examples/imgui_impl_sdl.cpp +++ b/examples/imgui_impl_sdl.cpp @@ -264,16 +264,20 @@ static void ImGui_ImplSDL2_UpdateMousePosAndButtons() SDL_Window* focused_window = SDL_GetKeyboardFocus(); if (focused_window) { - // SDL_GetMouseState() gives mouse position seemingly based on the last window entered/focused(?) - // The creation of new windows at runtime and SDL_CaptureMouse both seems to severely mess up with that, so we retrieve that position globally. - int wx, wy; - SDL_GetWindowPosition(focused_window, &wx, &wy); - SDL_GetGlobalMouseState(&mx, &my); - mx -= wx; - my -= wy; + if (io.ConfigFlags & 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) + SDL_GetGlobalMouseState(&mx, &my); + if (ImGui::FindViewportByPlatformHandle((void*)focused_window) != NULL) + io.MousePos = ImVec2((float)mx, (float)my); + } + 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) + if (focused_window == g_Window) + io.MousePos = ImVec2((float)mx, (float)my); + } } - if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle((void*)focused_window)) - io.MousePos = ImVec2(viewport->Pos.x + (float)mx, viewport->Pos.y + (float)my); // We already retrieve global mouse position, SDL_CaptureMouse() also let the OS know e.g. that our imgui drag outside the SDL window boundaries shouldn't trigger the OS window resize cursor // The function is only supported from SDL 2.0.4 (released Jan 2016) diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index 932bc2110028..63c270468f1a 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -135,21 +135,14 @@ static bool ImGui_ImplWin32_UpdateMouseCursor() return true; } -// This code supports multiple OS Windows mapped into different ImGui viewports, -// Because of that, it is a little more complicated than your typical single-viewport binding code. -// A) In Single-viewport mode imgui needs: -// - io.MousePos ............... mouse position, in client window coordinates (what you'd get from GetCursorPos+ScreenToClient() or from WM_MOUSEMOVE) -// io.MousePos is (0,0) when the mouse is on the upper-left corner of the application window. -// B) In Multi-viewport mode imgui needs: (when ImGuiConfigFlags_ViewportsEnable is set) -// - io.MousePos ............... mouse position, in OS absolute coordinates (what you'd get from GetCursorPos(), or from WM_MOUSEMOVE+viewport->Pos). -// io.MousePos is (0,0) when the mouse is on the upper-left of the primary monitor. -// - io.MouseHoveredViewport ... [optional] viewport which mouse is hovering, with _VERY_ specific and strict conditions (Read comments next to io.MouseHoveredViewport. This is _NOT_ easy to provide in many high-level engine because of how we use the ImGuiViewportFlags_NoInputs flag) +// This code supports multi-viewports (multiple OS Windows mapped into different Dear ImGui viewports) +// Because of that, it is a little more complicated than your typical single-viewport binding code! static void ImGui_ImplWin32_UpdateMousePos() { ImGuiIO& io = ImGui::GetIO(); // Set OS mouse position if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user) - // (When multi-viewports are enabled, all imgui positions are same as OS positions.) + // (When multi-viewports are enabled, all imgui positions are same as OS positions) if (io.WantSetMousePos) { POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y }; @@ -161,21 +154,39 @@ static void ImGui_ImplWin32_UpdateMousePos() io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); io.MouseHoveredViewport = 0; - // Set mouse position and viewport - // (Note that ScreenToClient() and adding +viewport->Pos are mutually cancelling each others when we have multi-viewport enabled. In single-viewport mode, viewport->Pos will be zero) - POINT pos; - if (!::GetCursorPos(&pos)) + // Set imgui mouse position + POINT mouse_screen_pos; + if (!::GetCursorPos(&mouse_screen_pos)) return; if (HWND focused_hwnd = ::GetActiveWindow()) - if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle((void*)focused_hwnd)) + { + if (io.ConfigFlags & 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) + // This is the position you can get with GetCursorPos(). In theory adding viewport->Pos is also the reverse operation of doing ScreenToClient(). + if (ImGui::FindViewportByPlatformHandle((void*)focused_hwnd) != NULL) + io.MousePos = ImVec2((float)mouse_screen_pos.x, (float)mouse_screen_pos.y); + } + else { - POINT client_pos = pos; - ::ScreenToClient(focused_hwnd, &client_pos); - io.MousePos = ImVec2(viewport->Pos.x + (float)client_pos.x, viewport->Pos.y + (float)client_pos.y); + // 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.) + // This is the position you can get with GetCursorPos() + ScreenToClient() or from WM_MOUSEMOVE. + if (focused_hwnd == g_hWnd) + { + POINT mouse_client_pos = mouse_screen_pos; + ::ScreenToClient(focused_hwnd, &mouse_client_pos); + io.MousePos = ImVec2((float)mouse_client_pos.x, (float)mouse_client_pos.y); + } } + } - // Our back-end can tell which window is under the mouse cursor (not every back-end can), so pass that info to imgui - if (HWND hovered_hwnd = ::WindowFromPoint(pos)) + // (Optional) When using multiple viewports: set io.MouseHoveredViewport to the viewport the OS mouse cursor is hovering. + // Important: this information is not easy to provide and many high-level windowing library won't be able to provide it correctly, because + // - This is _ignoring_ viewports with the ImGuiViewportFlags_NoInputs flag (pass-through windows). + // - This is _regardless_ of whether another viewport is focused or being dragged from. + // If ImGuiBackendFlags_HasMouseHoveredViewport is not set by the back-end, imgui will ignore this field and infer the information by relying on the + // rectangles and last focused time of every viewports it knows about. It will be unaware of other windows that may be sitting between or over your windows. + if (HWND hovered_hwnd = ::WindowFromPoint(mouse_screen_pos)) if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle((void*)hovered_hwnd)) io.MouseHoveredViewport = viewport->ID; } diff --git a/imgui.h b/imgui.h index 4a6e435b84bf..ca4326e9bcf4 100644 --- a/imgui.h +++ b/imgui.h @@ -1231,7 +1231,7 @@ struct ImGuiIO bool MouseDown[5]; // Mouse buttons: 0=left, 1=right, 2=middle + extras. ImGui itself mostly only uses left button (BeginPopupContext** are using right button). Others buttons allows us to track if the mouse is being used by your application + available to user as a convenience via IsMouse** API. float MouseWheel; // Mouse wheel Vertical: 1 unit scrolls about 5 lines text. float MouseWheelH; // Mouse wheel Horizontal. Most users don't have a mouse with an horizontal wheel, may not be filled by all back-ends. - ImGuiID MouseHoveredViewport; // (Optional) When using multiple viewports: viewport the OS mouse cursor is hovering _IGNORING_ viewports with the ImGuiViewportFlags_NoInputs flag, and _REGARDLESS_ of whether another viewport is focused. Set io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport if you can provide this info. If you don't imgui will use a decent heuristic instead. + ImGuiID MouseHoveredViewport; // (Optional) When using multiple viewports: viewport the OS mouse cursor is hovering _IGNORING_ viewports with the ImGuiViewportFlags_NoInputs flag, and _REGARDLESS_ of whether another viewport is focused. Set io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport if you can provide this info. If you don't imgui will infer the value using the rectangles and last focused time of the viewports it knows about (ignoring other OS windows). bool KeyCtrl; // Keyboard modifier pressed: Control bool KeyShift; // Keyboard modifier pressed: Shift bool KeyAlt; // Keyboard modifier pressed: Alt From 3a5e758ee37582a52360ada72219d5162ddd3268 Mon Sep 17 00:00:00 2001 From: omar Date: Sat, 8 Dec 2018 12:35:15 +0100 Subject: [PATCH 374/828] Tabs: Fixed crash when using TabItem in a regular (non-docking) tab bar. (#2231) --- imgui_widgets.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index b0a0c68fbf78..086550a87f17 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6455,8 +6455,8 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, // Drag and drop a single floating window node moves it // FIXME-DOCK: In theory we shouldn't test for the ConfigDockingNodifySingleWindows flag here. // When our single window node and OnlyNodeWithWindows are working properly we may remove this check here. - ImGuiDockNode* node = docked_window->DockNode; - const bool single_window_node = node->IsRootNode() && node->Windows.Size == 1 && g.IO.ConfigDockingTabBarOnSingleWindows; + ImGuiDockNode* node = docked_window ? docked_window->DockNode : NULL; + const bool single_window_node = node && node->IsRootNode() && node->Windows.Size == 1 && g.IO.ConfigDockingTabBarOnSingleWindows; if (held && single_window_node && IsMouseDragging(0, 0.0f)) { // Move From 2d4018aa893bf93b5b97a7aad5956fb3adbd866d Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 11 Dec 2018 11:03:28 +0100 Subject: [PATCH 375/828] Docking: Fix io.ConfigWindowsMoveFromTitleBarOnly for docking branch. --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 875c83528242..e3456a0a0f59 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3830,7 +3830,7 @@ void ImGui::EndFrame() if (g.HoveredRootWindow != NULL) { StartMouseMovingWindow(g.HoveredWindow); - if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(g.HoveredRootWindow->Flags & ImGuiWindowFlags_NoTitleBar)) + if (g.IO.ConfigWindowsMoveFromTitleBarOnly && (!(g.HoveredRootWindow->Flags & ImGuiWindowFlags_NoTitleBar) || g.HoveredWindow->RootWindowDockStop->DockIsActive)) if (!g.HoveredRootWindow->TitleBarRect().Contains(g.IO.MouseClickedPos[0])) g.MovingWindow = NULL; } From 2886e0b6f5ff45e4f60c0312d936db837d403843 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 11 Dec 2018 13:25:16 +0100 Subject: [PATCH 376/828] Demo: Fix collateral damage of 54a60aa --- imgui_demo.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index d86606dda5af..d5708bcc66db 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3863,6 +3863,7 @@ static void ShowExampleAppCustomRendering(bool* p_open) if (adding_preview) points.pop_back(); } + ImGui::End(); } //----------------------------------------------------------------------------- From e50894c95ebf87b0730be7502e48a1c5648de147 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 13 Dec 2018 19:16:34 +0100 Subject: [PATCH 377/828] Metrics: Fixed crash when viewports are disabled (g.MouseLastHoveredViewport is never set). --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 908e8776fc19..db367eefc9e8 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10263,7 +10263,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover); ImGui::Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL"); ImGui::Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize); - ImGui::Text("MouseViewport: 0x%08X (UserHovered 0x%08X, LastHovered 0x%08X)", g.MouseViewport->ID, g.IO.MouseHoveredViewport, g.MouseLastHoveredViewport->ID); + ImGui::Text("MouseViewport: 0x%08X (UserHovered 0x%08X, LastHovered 0x%08X)", g.MouseViewport->ID, g.IO.MouseHoveredViewport, g.MouseLastHoveredViewport ? g.MouseLastHoveredViewport->ID : 0); ImGui::TreePop(); } From 5d20da1b3622864eeb06b726cfb27cc2e19c486d Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 14 Dec 2018 11:59:44 +0100 Subject: [PATCH 378/828] Viewport, DPI: Now using DpiScale from the ImGuiPlatformMonitor array instead of calling Platform_GetWindowDpiScale() before the platform window creation. Might even tentatively see if things work out without Platform_GetWindowDpiScale. (#1676) --- examples/imgui_impl_win32.cpp | 20 ++++---------------- examples/imgui_impl_win32.h | 7 +++---- imgui.cpp | 10 ++++++---- imgui.h | 8 ++++---- 4 files changed, 17 insertions(+), 28 deletions(-) diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index 63c270468f1a..cf88c8232202 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -366,7 +366,7 @@ void ImGui_ImplWin32_EnableDpiAwareness() float ImGui_ImplWin32_GetDpiScaleForMonitor(void* monitor) { UINT xdpi = 96, ydpi = 96; - if (IsWindows8Point1OrGreater()) + if (::IsWindows8Point1OrGreater()) { static HINSTANCE shcore_dll = ::LoadLibraryA("shcore.dll"); // Reference counted per-process if (PFN_GetDpiForMonitor GetDpiForMonitorFn = (PFN_GetDpiForMonitor)::GetProcAddress(shcore_dll, "GetDpiForMonitor")) @@ -389,13 +389,6 @@ float ImGui_ImplWin32_GetDpiScaleForHwnd(void* hwnd) return ImGui_ImplWin32_GetDpiScaleForMonitor(monitor); } -float ImGui_ImplWin32_GetDpiScaleForRect(int x1, int y1, int x2, int y2) -{ - RECT viewport_rect = { (LONG)x1, (LONG)y1, (LONG)x2, (LONG)y2 }; - HMONITOR monitor = ::MonitorFromRect(&viewport_rect, MONITOR_DEFAULTTONEAREST); - return ImGui_ImplWin32_GetDpiScaleForMonitor(monitor); -} - //-------------------------------------------------------------------------------------------------------- // IME (Input Method Editor) basic support for e.g. Asian language users //-------------------------------------------------------------------------------------------------------- @@ -589,13 +582,8 @@ static void ImGui_ImplWin32_SetWindowAlpha(ImGuiViewport* viewport, float alpha) static float ImGui_ImplWin32_GetWindowDpiScale(ImGuiViewport* viewport) { ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData; - if (data && data->Hwnd) - return ImGui_ImplWin32_GetDpiScaleForHwnd(data->Hwnd); - - // The first frame a viewport is created we don't have a window yet - return ImGui_ImplWin32_GetDpiScaleForRect( - (int)(viewport->Pos.x), (int)(viewport->Pos.y), - (int)(viewport->Pos.x + viewport->Size.x), (int)(viewport->Pos.y + viewport->Size.y)); + IM_ASSERT(data->Hwnd != 0); + return ImGui_ImplWin32_GetDpiScaleForHwnd(data->Hwnd); } // FIXME-DPI: Testing DPI related ideas @@ -706,7 +694,7 @@ static void ImGui_ImplWin32_InitPlatformInterface() platform_io.Platform_GetWindowMinimized = ImGui_ImplWin32_GetWindowMinimized; platform_io.Platform_SetWindowTitle = ImGui_ImplWin32_SetWindowTitle; platform_io.Platform_SetWindowAlpha = ImGui_ImplWin32_SetWindowAlpha; - platform_io.Platform_GetWindowDpiScale = ImGui_ImplWin32_GetWindowDpiScale; + platform_io.Platform_GetWindowDpiScale = ImGui_ImplWin32_GetWindowDpiScale; // FIXME-DPI platform_io.Platform_OnChangedViewport = ImGui_ImplWin32_OnChangedViewport; // FIXME-DPI #if HAS_WIN32_IME platform_io.Platform_SetImeInputPos = ImGui_ImplWin32_SetImeInputPos; diff --git a/examples/imgui_impl_win32.h b/examples/imgui_impl_win32.h index 5f16315283e2..b7c9d81955c4 100644 --- a/examples/imgui_impl_win32.h +++ b/examples/imgui_impl_win32.h @@ -16,10 +16,9 @@ IMGUI_IMPL_API void ImGui_ImplWin32_Shutdown(); IMGUI_IMPL_API void ImGui_ImplWin32_NewFrame(); // DPI-related helpers (which run and compile without requiring 8.1 or 10, neither Windows version, neither associated SDK) -IMGUI_API void ImGui_ImplWin32_EnableDpiAwareness(); -IMGUI_API float ImGui_ImplWin32_GetDpiScaleForHwnd(void* hwnd); // HWND hwnd -IMGUI_API float ImGui_ImplWin32_GetDpiScaleForMonitor(void* monitor); // HMONITOR monitor -IMGUI_API float ImGui_ImplWin32_GetDpiScaleForRect(int x1, int y1, int x2, int y2); +IMGUI_IMPL_API void ImGui_ImplWin32_EnableDpiAwareness(); +IMGUI_IMPL_API float ImGui_ImplWin32_GetDpiScaleForHwnd(void* hwnd); // HWND hwnd +IMGUI_IMPL_API float ImGui_ImplWin32_GetDpiScaleForMonitor(void* monitor); // HMONITOR monitor // Handler for Win32 messages, update mouse/keyboard data. // You may or not need this for your implementation, but it can serve as reference for handling inputs. diff --git a/imgui.cpp b/imgui.cpp index a7db3b7d2b48..d499d1d81acd 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7376,8 +7376,10 @@ static void ImGui::UpdateViewports() // Update DPI scale float new_dpi_scale; - if (g.PlatformIO.Platform_GetWindowDpiScale) + if (g.PlatformIO.Platform_GetWindowDpiScale && platform_funcs_available) new_dpi_scale = g.PlatformIO.Platform_GetWindowDpiScale(viewport); + else if (viewport->PlatformMonitor != -1) + new_dpi_scale = g.PlatformIO.Monitors[viewport->PlatformMonitor].DpiScale; else new_dpi_scale = (viewport->DpiScale != 0.0f) ? viewport->DpiScale : 1.0f; if (viewport->DpiScale != 0.0f && new_dpi_scale != viewport->DpiScale) @@ -7490,10 +7492,10 @@ ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const g.DrawListSharedData.ClipRectFullscreen.z = ImMax(g.DrawListSharedData.ClipRectFullscreen.z, viewport->Pos.x + viewport->Size.x); g.DrawListSharedData.ClipRectFullscreen.w = ImMax(g.DrawListSharedData.ClipRectFullscreen.w, viewport->Pos.y + viewport->Size.y); - // Request an initial DpiScale before the OS platform window creation + // Store initial DpiScale before the OS platform window creation, based on expected monitor data. // This is so we can select an appropriate font size on the first frame of our window lifetime - if (g.PlatformIO.Platform_GetWindowDpiScale) - viewport->DpiScale = g.PlatformIO.Platform_GetWindowDpiScale(viewport); + if (viewport->PlatformMonitor != -1) + viewport->DpiScale = g.PlatformIO.Monitors[viewport->PlatformMonitor].DpiScale; } viewport->Window = window; diff --git a/imgui.h b/imgui.h index fc687fbc46ff..f70b465b2a84 100644 --- a/imgui.h +++ b/imgui.h @@ -2124,13 +2124,13 @@ struct ImFont // - if you are new to dear imgui and trying to integrate it into your engine, you should probably ignore this for now. //----------------------------------------------------------------------------- -// (Optional) Represent the bounds of each connected monitor/display -// Dear ImGui only uses this to clamp the position of popups and tooltips so they don't straddle multiple monitors. +// (Optional) Represent the bounds of each connected monitor/display and their DPI +// This is used for: multiple DPI support + clamping the position of popups and tooltips so they don't straddle multiple monitors. struct ImGuiPlatformMonitor { ImVec2 MainPos, MainSize; // Coordinates of the area displayed on this monitor (Min = upper left, Max = bottom right) ImVec2 WorkPos, WorkSize; // (Optional) Coordinates without task bars / side bars / menu bars. imgui uses this to avoid positioning popups/tooltips inside this region. - float DpiScale; + float DpiScale; // 1.0f = 96 DPI ImGuiPlatformMonitor() { MainPos = MainSize = WorkPos = WorkSize = ImVec2(0,0); DpiScale = 1.0f; } }; @@ -2165,7 +2165,7 @@ struct ImGuiPlatformIO void (*Platform_SetWindowAlpha)(ImGuiViewport* vp, float alpha); // (Optional) Setup window transparency void (*Platform_RenderWindow)(ImGuiViewport* vp, void* render_arg); // (Optional) Setup for render (platform side) void (*Platform_SwapBuffers)(ImGuiViewport* vp, void* render_arg); // (Optional) Call Present/SwapBuffers (platform side) - float (*Platform_GetWindowDpiScale)(ImGuiViewport* vp); // (Optional) [BETA] (FIXME-DPI) DPI handling: Return DPI scale for this viewport. 1.0f = 96 DPI. IMPORTANT: this will be called _before_ the window is created, in which case the implementation is expected to use the viewport->Pos/Size fields to estimate DPI value. + float (*Platform_GetWindowDpiScale)(ImGuiViewport* vp); // (Optional) [BETA] (FIXME-DPI) DPI handling: Return DPI scale for this viewport. 1.0f = 96 DPI. void (*Platform_OnChangedViewport)(ImGuiViewport* vp); // (Optional) [BETA] (FIXME-DPI) DPI handling: Called during Begin() every time the viewport we are outputting into changes, so back-end has a chance to swap fonts to adjust style. void (*Platform_SetImeInputPos)(ImGuiViewport* vp, ImVec2 pos); // (Optional) Set IME (Input Method Editor, e.g. for Asian languages) input position, so text preview appears over the imgui input box. int (*Platform_CreateVkSurface)(ImGuiViewport* vp, ImU64 vk_inst, const void* vk_allocators, ImU64* out_vk_surface); // (Optional) For Renderer to call into Platform code From a0e5bb95329e33519f0f3c993326d3a3df38aef1 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 20 Dec 2018 12:15:15 +0100 Subject: [PATCH 379/828] Viewport: Corrected/clarified comments. Moved RenderPlatformWindowsDefault() next to UpdatePlatformWindow(). Removed unnecessary flag check. --- imgui.cpp | 101 +++++++++++++++++++++++++++--------------------------- imgui.h | 15 ++++---- 2 files changed, 59 insertions(+), 57 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index fc2db6798b91..e0cce5b048c2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7630,7 +7630,8 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) window->ViewportId = window->Viewport->ID; } -// Called by imgui at the end of the main loop, after EndFrame() +// Called by user at the end of the main loop, after EndFrame() +// This will handle the creation/update of all OS windows via function defined in the ImGuiPlatformIO api. void ImGui::UpdatePlatformWindows() { ImGuiContext& g = *GImGui; @@ -7658,34 +7659,34 @@ void ImGui::UpdatePlatformWindows() if (viewport->LastFrameActive < g.FrameCount) continue; - // New windows that appears directly in a new viewport won't always have a size on their frame + // New windows that appears directly in a new viewport won't always have a size on their first frame if (viewport->Size.x <= 0 || viewport->Size.y <= 0) continue; // Update common viewport flags for owned viewports - if (viewport->Window != NULL) + if (ImGuiWindow* window = viewport->Window) { ImGuiViewportFlags flags = viewport->Flags & ~(ImGuiViewportFlags_TopMost | ImGuiViewportFlags_NoTaskBarIcon | ImGuiViewportFlags_NoDecoration); - if (viewport->Window->Flags & ImGuiWindowFlags_Tooltip) + if (window->Flags & ImGuiWindowFlags_Tooltip) flags |= ImGuiViewportFlags_TopMost; - if ((g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsNoTaskBarIcon) != 0 || (viewport->Window->Flags & (ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) != 0) + if ((g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsNoTaskBarIcon) != 0 || (window->Flags & (ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) != 0) flags |= ImGuiViewportFlags_NoTaskBarIcon; - if ((g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsDecoration) == 0 || (viewport->Window->Flags & (ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) != 0) + if ((g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsDecoration) == 0 || (window->Flags & (ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) != 0) flags |= ImGuiViewportFlags_NoDecoration; viewport->Flags = flags; } // Create window - bool is_new_window = (viewport->PlatformWindowCreated == false); - if (is_new_window) + bool is_new_platform_window = (viewport->PlatformWindowCreated == false); + if (is_new_platform_window) { //IMGUI_DEBUG_LOG("Create Platform Window %08X (%s)\n", viewport->ID, viewport->Window ? viewport->Window->Name : "n/a"); g.PlatformIO.Platform_CreateWindow(viewport); if (g.PlatformIO.Renderer_CreateWindow != NULL) g.PlatformIO.Renderer_CreateWindow(viewport); viewport->LastNameHash = 0; - viewport->LastPlatformPos = viewport->LastPlatformSize = ImVec2(FLT_MAX, FLT_MAX); // By clearing those we'll enforce a call to Platform_SetWindowPos/Platform_SetWindowSize before Platform_ShowWindow - viewport->LastRendererSize = viewport->Size; + viewport->LastPlatformPos = viewport->LastPlatformSize = ImVec2(FLT_MAX, FLT_MAX); // By clearing those we'll enforce a call to Platform_SetWindowPos/Size below, before Platform_ShowWindow (FIXME: Is that necessary?) + viewport->LastRendererSize = viewport->Size; // We don't need to call Renderer_SetWindowSize() as it is expected Renderer_CreateWindow() already did it. viewport->PlatformWindowCreated = true; } @@ -7708,29 +7709,32 @@ void ImGui::UpdatePlatformWindows() if (viewport->LastNameHash != title_hash) { char title_end_backup_c = *title_end; - *title_end = 0; // Cut existing buffer short instead of doing an alloc/free + *title_end = 0; // Cut existing buffer short instead of doing an alloc/free, no small gain. g.PlatformIO.Platform_SetWindowTitle(viewport, title_begin); *title_end = title_end_backup_c; viewport->LastNameHash = title_hash; } } - // Update alpha + // Update alpha (if it changed) if (viewport->LastAlpha != viewport->Alpha && g.PlatformIO.Platform_SetWindowAlpha) g.PlatformIO.Platform_SetWindowAlpha(viewport, viewport->Alpha); viewport->LastAlpha = viewport->Alpha; - // Show window. On startup ensure platform window don't get focus - if (is_new_window) + if (is_new_platform_window) { - if (g.FrameCount < 3) // Give a few frames for the application to stabilize (nested contents may lead to viewport being created a few frames late) + // On startup ensure new platform window don't steal focus (give it a few frames, as nested contents may lead to viewport being created a few frames late) + if (g.FrameCount < 3) viewport->Flags |= ImGuiViewportFlags_NoFocusOnAppearing; + + // Show window g.PlatformIO.Platform_ShowWindow(viewport); - } - // Even without focus, we assume the window becomes front-most. This is used by our platform z-order heuristic when io.MouseHoveredViewport is not available. - if (is_new_window && viewport->LastFrontMostStampCount != g.WindowsFrontMostStampCount) - viewport->LastFrontMostStampCount = ++g.WindowsFrontMostStampCount; + // Even without focus, we assume the window becomes front-most. + // This is useful for our platform z-order heuristic when io.MouseHoveredViewport is not available. + if (viewport->LastFrontMostStampCount != g.WindowsFrontMostStampCount) + viewport->LastFrontMostStampCount = ++g.WindowsFrontMostStampCount; + } // Clear request flags viewport->PlatformRequestClose = viewport->PlatformRequestMove = viewport->PlatformRequestResize = false; @@ -7757,6 +7761,34 @@ void ImGui::UpdatePlatformWindows() } } +// This is a default/basic function for performing the rendering/swap of multiple Platform Windows. +// Custom renderers may prefer to not call this function at all, and instead iterate the publicly exposed platform data and handle rendering/sync themselves. +// The Render/Swap functions stored in ImGuiPlatformIO are merely here to allow for this helper to exist, but you can do it yourself: +// +// ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); +// for (int i = 1; i < platform_io.Viewports.Size; i++) +// MyRenderFunction(platform_io.Viewports[i], my_args); +// for (int i = 1; i < platform_io.Viewports.Size; i++) +// MySwapBufferFunction(platform_io.Viewports[i], my_args); +// +void ImGui::RenderPlatformWindowsDefault(void* platform_render_arg, void* renderer_render_arg) +{ + // Skip the main viewport (index 0), which is always fully handled by the application! + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + for (int i = 1; i < platform_io.Viewports.Size; i++) + { + ImGuiViewport* viewport = platform_io.Viewports[i]; + if (platform_io.Platform_RenderWindow) platform_io.Platform_RenderWindow(viewport, platform_render_arg); + if (platform_io.Renderer_RenderWindow) platform_io.Renderer_RenderWindow(viewport, renderer_render_arg); + } + for (int i = 1; i < platform_io.Viewports.Size; i++) + { + ImGuiViewport* viewport = platform_io.Viewports[i]; + if (platform_io.Platform_SwapBuffers) platform_io.Platform_SwapBuffers(viewport, platform_render_arg); + if (platform_io.Renderer_SwapBuffers) platform_io.Renderer_SwapBuffers(viewport, renderer_render_arg); + } +} + static int ImGui::FindPlatformMonitorForPos(const ImVec2& pos) { ImGuiContext& g = *GImGui; @@ -7798,37 +7830,6 @@ static int ImGui::FindPlatformMonitorForRect(const ImRect& rect) return best_monitor_n; } -// This is a default/basic function for performing the rendering/swap of multiple platform windows. -// Custom renderers may prefer to not call this function at all, and instead iterate the publicly exposed platform data and handle rendering/sync themselves. -// The Render/Swap functions stored in ImGuiPlatformIO are merely here to allow for this helper to exist, but you can do it yourself: -// -// ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); -// for (int i = 1; i < platform_io.Viewports.Size; i++) -// MyRenderFunction(platform_io.Viewports[i], my_args); -// for (int i = 1; i < platform_io.Viewports.Size; i++) -// MySwapBufferFunction(platform_io.Viewports[i], my_args); -// -void ImGui::RenderPlatformWindowsDefault(void* platform_render_arg, void* renderer_render_arg) -{ - if (!(ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) - return; - - // Skip the main viewport (index 0), which is always fully handled by the application! - ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); - for (int i = 1; i < platform_io.Viewports.Size; i++) - { - ImGuiViewport* viewport = platform_io.Viewports[i]; - if (platform_io.Platform_RenderWindow) platform_io.Platform_RenderWindow(viewport, platform_render_arg); - if (platform_io.Renderer_RenderWindow) platform_io.Renderer_RenderWindow(viewport, renderer_render_arg); - } - for (int i = 1; i < platform_io.Viewports.Size; i++) - { - ImGuiViewport* viewport = platform_io.Viewports[i]; - if (platform_io.Platform_SwapBuffers) platform_io.Platform_SwapBuffers(viewport, platform_render_arg); - if (platform_io.Renderer_SwapBuffers) platform_io.Renderer_SwapBuffers(viewport, renderer_render_arg); - } -} - void ImGui::DestroyPlatformWindow(ImGuiViewportP* viewport) { ImGuiContext& g = *GImGui; diff --git a/imgui.h b/imgui.h index 3601dada8386..65d481e7b26d 100644 --- a/imgui.h +++ b/imgui.h @@ -2143,13 +2143,13 @@ struct ImGuiPlatformMonitor }; // (Optional) Setup required only if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) is enabled. -// Access via ImGui::GetPlatformIO(). This is designed so we can mix and match two imgui_impl_xxxx files, one for -// the Platform (~window handling), one for Renderer. Custom engine back-ends will often provide both Platform -// and Renderer interfaces and thus may not need to use all functions. +// Access via ImGui::GetPlatformIO(). This is designed so we can mix and match two imgui_impl_xxxx files, +// one for the Platform (~window handling), one for Renderer. Custom engine back-ends will often provide +// both Platform and Renderer interfaces and so may not need to use all functions. // Platform functions are typically called before their Renderer counterpart, // apart from Destroy which are called the other way. // RenderPlatformWindowsDefault() is that helper that iterate secondary viewports and call, in this order: -// Platform_RenderWindow(), Renderer_RenderWindow(), Platform_SwapBuffers(), Renderer_SwapBuffers() +// Platform_RenderWindow(), Renderer_RenderWindow(), Platform_SwapBuffers(), Renderer_SwapBuffers() // You may skip using RenderPlatformWindowsDefault() and call your draw/swap functions yourself if you need // specific behavior for your multi-window rendering. struct ImGuiPlatformIO @@ -2159,6 +2159,7 @@ struct ImGuiPlatformIO //------------------------------------------------------------------ // (Optional) Platform functions (e.g. Win32, GLFW, SDL2) + // Most of them are called by ImGui::UpdatePlatformWindows() and ImGui::RenderPlatformWindowsDefault(). void (*Platform_CreateWindow)(ImGuiViewport* vp); // Create a new platform window for the given viewport void (*Platform_DestroyWindow)(ImGuiViewport* vp); void (*Platform_ShowWindow)(ImGuiViewport* vp); // Newly created windows are initially hidden so SetWindowPos/Size/Title can be called on them first @@ -2171,7 +2172,7 @@ struct ImGuiPlatformIO bool (*Platform_GetWindowMinimized)(ImGuiViewport* vp); void (*Platform_SetWindowTitle)(ImGuiViewport* vp, const char* title); void (*Platform_SetWindowAlpha)(ImGuiViewport* vp, float alpha); // (Optional) Setup window transparency - void (*Platform_RenderWindow)(ImGuiViewport* vp, void* render_arg); // (Optional) Setup for render (platform side) + void (*Platform_RenderWindow)(ImGuiViewport* vp, void* render_arg); // (Optional) Setup for render void (*Platform_SwapBuffers)(ImGuiViewport* vp, void* render_arg); // (Optional) Call Present/SwapBuffers (platform side) float (*Platform_GetWindowDpiScale)(ImGuiViewport* vp); // (Optional) [BETA] (FIXME-DPI) DPI handling: Return DPI scale for this viewport. 1.0f = 96 DPI. void (*Platform_OnChangedViewport)(ImGuiViewport* vp); // (Optional) [BETA] (FIXME-DPI) DPI handling: Called during Begin() every time the viewport we are outputting into changes, so back-end has a chance to swap fonts to adjust style. @@ -2213,8 +2214,8 @@ enum ImGuiViewportFlags_ // The viewports created and managed by imgui. The role of the platform back-end is to create the platform/OS windows corresponding to each viewport. struct ImGuiViewport { - ImGuiID ID; - ImGuiViewportFlags Flags; + ImGuiID ID; // Unique identifier for the viewport + ImGuiViewportFlags Flags; // See ImGuiViewportFlags_ ImVec2 Pos; // Position of viewport both in imgui space and in OS desktop/native space ImVec2 Size; // Size of viewport in pixel float DpiScale; // 1.0f = 96 DPI = No extra scale From 8fc19d21941789a7a3ca7d32ab0c36ef6b696042 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 20 Dec 2018 16:56:29 +0100 Subject: [PATCH 380/828] Removed IMGUI_HAS_TABS from Docking branch, it's not defined anywhere anymore. --- imgui.h | 1 - 1 file changed, 1 deletion(-) diff --git a/imgui.h b/imgui.h index 5256549294dc..c379203a5d18 100644 --- a/imgui.h +++ b/imgui.h @@ -50,7 +50,6 @@ Index of this file: #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert)) #define IMGUI_HAS_VIEWPORT 1 // Viewport WIP branch #define IMGUI_HAS_DOCK 1 // Docking WIP branch -#define IMGUI_HAS_TABS 1 // Docking WIP branch // Define attributes of all API symbols declarations (e.g. for DLL under Windows) // IMGUI_API is used for core imgui functions, IMGUI_IMPL_API is used for the default bindings files (imgui_impl_xxx.h) From 5794c0491ad8f11beaea512e25d40714cec760f4 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 20 Dec 2018 19:19:33 +0100 Subject: [PATCH 381/828] Docking: Fix an edge case failing to dock into an explicit dockspace which only have inactive nodes (because all the windows are inactive). (#2246, #2109) --- imgui.cpp | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 476c1d259aaf..503ea65f5418 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -9916,6 +9916,7 @@ namespace ImGui static void DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 size); static void DockNodeTreeUpdateSplitter(ImGuiDockNode* node); static ImGuiDockNode* DockNodeTreeFindNodeByPos(ImGuiDockNode* node, ImVec2 pos); + static ImGuiDockNode* DockNodeTreeFindFallbackLeafNode(ImGuiDockNode* node); // Settings static void DockSettingsMoveDockReferencesInInactiveWindow(ImGuiID old_dock_id, ImGuiID new_dock_id); @@ -11399,11 +11400,20 @@ static bool ImGui::DockNodePreviewDockCalc(ImGuiWindow* host_window, ImGuiDockNo ImGuiContext& g = *GImGui; IM_ASSERT(g.CurrentWindow == host_window); // Because we rely on font size to calculate tab sizes + // There is an edge case when docking into a dockspace which only has inactive nodes. + // In this case DockNodeTreeFindNodeByPos() will have selected a leaf node which is inactive. + // Because the inactive leaf node doesn't have proper pos/size yet, we'll use the root node as reference. + ImGuiDockNode* ref_node_for_rect = (host_node && !host_node->IsVisible) ? DockNodeGetRootNode(host_node) : host_node; + if (ref_node_for_rect) + IM_ASSERT(ref_node_for_rect->IsVisible); + + // Build a tentative future node (reuse same structure because it is practical) data->FutureNode.HasCloseButton = (host_node ? host_node->HasCloseButton : host_window->HasCloseButton) || (root_payload->HasCloseButton); data->FutureNode.HasCollapseButton = host_node ? true : ((host_window->Flags & ImGuiWindowFlags_NoCollapse) == 0); - data->FutureNode.Pos = host_node ? host_node->Pos : host_window->Pos; - data->FutureNode.Size = host_node ? host_node->Size : host_window->Size; + data->FutureNode.Pos = host_node ? ref_node_for_rect->Pos : host_window->Pos; + data->FutureNode.Size = host_node ? ref_node_for_rect->Size : host_window->Size; + // Figure out here we are allowed to dock const bool src_is_visibly_splitted = root_payload->DockNodeAsHost && root_payload->DockNodeAsHost->IsSplitNode() && (root_payload->DockNodeAsHost->OnlyNodeWithWindows == NULL); data->IsCenterAvailable = !is_outer_docking; if (src_is_visibly_splitted && (!host_node || !host_node->IsEmpty())) @@ -11837,6 +11847,17 @@ void ImGui::DockNodeTreeUpdateSplitter(ImGuiDockNode* node) DockNodeTreeUpdateSplitter(child_1); } +ImGuiDockNode* ImGui::DockNodeTreeFindFallbackLeafNode(ImGuiDockNode* node) +{ + if (node->IsLeafNode()) + return node; + if (ImGuiDockNode* leaf_node = DockNodeTreeFindFallbackLeafNode(node->ChildNodes[0])) + return leaf_node; + if (ImGuiDockNode* leaf_node = DockNodeTreeFindFallbackLeafNode(node->ChildNodes[1])) + return leaf_node; + return NULL; +} + ImGuiDockNode* ImGui::DockNodeTreeFindNodeByPos(ImGuiDockNode* node, ImVec2 pos) { if (!node->IsVisible) @@ -11856,6 +11877,16 @@ ImGuiDockNode* ImGui::DockNodeTreeFindNodeByPos(ImGuiDockNode* node, ImVec2 pos) return hovered_node; if (ImGuiDockNode* hovered_node = DockNodeTreeFindNodeByPos(node->ChildNodes[1], pos)) return hovered_node; + + // There is an edge case when docking into a dockspace which only has inactive nodes (because none of the windows are active) + // In this case we need to fallback into any leaf mode, possibly the central node. + if (node->IsDockSpace && node->IsRootNode()) + { + if (node->CentralNode && node->IsLeafNode()) // FIXME-20181220: We should not have to test for IsLeafNode() here but we have another bug to fix first. + return node->CentralNode; + return DockNodeTreeFindFallbackLeafNode(node); + } + return NULL; } From 8dd83c5fe8e8c16480509ebf0c58f76517e099e4 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 20 Dec 2018 22:28:31 +0100 Subject: [PATCH 382/828] Examples: SDL: SDL_GetMouseState() seems problematic, movements feels laggy in the non-viewport code path. (#1542, #2117) --- examples/imgui_impl_sdl.cpp | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/examples/imgui_impl_sdl.cpp b/examples/imgui_impl_sdl.cpp index 3e883550390b..5ac682415039 100644 --- a/examples/imgui_impl_sdl.cpp +++ b/examples/imgui_impl_sdl.cpp @@ -253,39 +253,39 @@ static void ImGui_ImplSDL2_UpdateMousePosAndButtons() io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); #endif - int mx, my; - Uint32 mouse_buttons = SDL_GetMouseState(&mx, &my); + int mouse_x, mouse_y; + Uint32 mouse_buttons = SDL_GetMouseState(&mouse_x, &mouse_y); // NB: We don't use the x/y results from SDL_GetMouseState() + SDL_GetGlobalMouseState(&mouse_x, &mouse_y); io.MouseDown[0] = g_MousePressed[0] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_LEFT)) != 0; // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame. io.MouseDown[1] = g_MousePressed[1] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_RIGHT)) != 0; io.MouseDown[2] = g_MousePressed[2] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_MIDDLE)) != 0; g_MousePressed[0] = g_MousePressed[1] = g_MousePressed[2] = false; #if SDL_HAS_CAPTURE_MOUSE && !defined(__EMSCRIPTEN__) - SDL_Window* focused_window = SDL_GetKeyboardFocus(); - if (focused_window) + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { - if (io.ConfigFlags & 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) - SDL_GetGlobalMouseState(&mx, &my); + // 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) + if (SDL_Window* focused_window = SDL_GetKeyboardFocus()) if (ImGui::FindViewportByPlatformHandle((void*)focused_window) != NULL) - io.MousePos = ImVec2((float)mx, (float)my); - } - else + io.MousePos = ImVec2((float)mouse_x, (float)mouse_y); + } + else +#endif + { + // 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) + if (SDL_GetWindowFlags(g_Window) & SDL_WINDOW_INPUT_FOCUS) { - // 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) - if (focused_window == g_Window) - io.MousePos = ImVec2((float)mx, (float)my); + int window_x, window_y; + SDL_GetWindowPosition(g_Window, &window_x, &window_y); + io.MousePos = ImVec2((float)(mouse_x - window_x), (float)(mouse_y - window_y)); } } // We already retrieve global mouse position, SDL_CaptureMouse() also let the OS know e.g. that our imgui drag outside the SDL window boundaries shouldn't trigger the OS window resize cursor // The function is only supported from SDL 2.0.4 (released Jan 2016) +#if SDL_HAS_CAPTURE_MOUSE bool any_mouse_button_down = ImGui::IsAnyMouseDown(); SDL_CaptureMouse(any_mouse_button_down ? SDL_TRUE : SDL_FALSE); -#else - if (SDL_GetWindowFlags(g_Window) & SDL_WINDOW_INPUT_FOCUS) - io.MousePos = ImVec2((float)mx, (float)my); #endif } From d9fda227637065f07f419ab5ec0669dfbd016e76 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 20 Dec 2018 22:33:51 +0100 Subject: [PATCH 383/828] Viewport: Fixed not clearing request flags in main viewport, which led some back-end (SDL) to break on resize as PlatformRequestResize would stay true forever and inhibit new sizes passed to AddUpdateViewport(). (#1542) --- imgui.cpp | 5 +++-- imgui_internal.h | 9 +++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 6eeea3259f62..a69763fa954f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3818,6 +3818,7 @@ void ImGui::EndFrame() IM_ASSERT(viewport->Window != NULL); g.PlatformIO.Viewports.push_back(viewport); } + g.Viewports[0]->ClearRequestFlags(); // Clear main viewport flags because UpdatePlatformWindows() won't do it and may not even be called // Sort the window list so that all child windows are after their parent // We cannot do that on FocusWindow() because childs may not exist yet @@ -7760,7 +7761,7 @@ void ImGui::UpdatePlatformWindows() } // Clear request flags - viewport->PlatformRequestClose = viewport->PlatformRequestMove = viewport->PlatformRequestResize = false; + viewport->ClearRequestFlags(); } // Update our implicit z-order knowledge of platform windows, which is used when the back-end cannot provide io.MouseHoveredViewport. @@ -7865,7 +7866,7 @@ void ImGui::DestroyPlatformWindow(ImGuiViewportP* viewport) viewport->PlatformHandle = NULL; viewport->RendererUserData = viewport->PlatformHandle = NULL; viewport->PlatformWindowCreated = false; - viewport->PlatformRequestClose = viewport->PlatformRequestMove = viewport->PlatformRequestResize = false; + viewport->ClearRequestFlags(); } void ImGui::DestroyPlatformWindows() diff --git a/imgui_internal.h b/imgui_internal.h index dc0e78153023..56f4f3520e36 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -672,10 +672,11 @@ struct ImGuiViewportP : public ImGuiViewport ImVec2 LastPlatformSize; ImVec2 LastRendererSize; - ImGuiViewportP() { Idx = -1; LastFrameActive = LastFrameOverlayDrawList = LastFrontMostStampCount = -1; LastNameHash = 0; Alpha = LastAlpha = 1.0f; PlatformMonitor = -1; PlatformWindowCreated = PlatformWindowMinimized = false; Window = NULL; OverlayDrawList = NULL; LastPlatformPos = LastPlatformSize = LastRendererSize = ImVec2(FLT_MAX, FLT_MAX); } - ~ImGuiViewportP() { if (OverlayDrawList) IM_DELETE(OverlayDrawList); } - ImRect GetRect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } - ImVec2 GetCenter() const{ return ImVec2(Pos.x + Size.x * 0.5f, Pos.y + Size.y * 0.5f); } + ImGuiViewportP() { Idx = -1; LastFrameActive = LastFrameOverlayDrawList = LastFrontMostStampCount = -1; LastNameHash = 0; Alpha = LastAlpha = 1.0f; PlatformMonitor = -1; PlatformWindowCreated = PlatformWindowMinimized = false; Window = NULL; OverlayDrawList = NULL; LastPlatformPos = LastPlatformSize = LastRendererSize = ImVec2(FLT_MAX, FLT_MAX); } + ~ImGuiViewportP() { if (OverlayDrawList) IM_DELETE(OverlayDrawList); } + ImRect GetRect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } + ImVec2 GetCenter() const { return ImVec2(Pos.x + Size.x * 0.5f, Pos.y + Size.y * 0.5f); } + void ClearRequestFlags() { PlatformRequestClose = PlatformRequestMove = PlatformRequestResize = false; } }; struct ImGuiNavMoveResult From 62cfdceac15b57f834d836ef944e66b70a4405e6 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 20 Dec 2018 22:40:22 +0100 Subject: [PATCH 384/828] Examples: Viewport: Moved the "make current GL context" to reduce the amount of call and hopefully be more explicit about viewport enabled vs disabled requirements. (#1542) --- examples/example_glfw_opengl2/main.cpp | 2 +- examples/example_glfw_opengl3/main.cpp | 3 +-- examples/example_sdl_opengl2/main.cpp | 3 ++- examples/example_sdl_opengl3/main.cpp | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/example_glfw_opengl2/main.cpp b/examples/example_glfw_opengl2/main.cpp index d5a5fd258b32..f87a989ac130 100644 --- a/examples/example_glfw_opengl2/main.cpp +++ b/examples/example_glfw_opengl2/main.cpp @@ -140,9 +140,9 @@ int main(int, char**) { ImGui::UpdatePlatformWindows(); ImGui::RenderPlatformWindowsDefault(); + glfwMakeContextCurrent(window); } - glfwMakeContextCurrent(window); glfwSwapBuffers(window); } diff --git a/examples/example_glfw_opengl3/main.cpp b/examples/example_glfw_opengl3/main.cpp index 93dcead54a06..ffd23cc0ecbe 100644 --- a/examples/example_glfw_opengl3/main.cpp +++ b/examples/example_glfw_opengl3/main.cpp @@ -178,7 +178,6 @@ int main(int, char**) // Rendering ImGui::Render(); int display_w, display_h; - glfwMakeContextCurrent(window); glfwGetFramebufferSize(window, &display_w, &display_h); glViewport(0, 0, display_w, display_h); glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w); @@ -190,9 +189,9 @@ int main(int, char**) { ImGui::UpdatePlatformWindows(); ImGui::RenderPlatformWindowsDefault(); + glfwMakeContextCurrent(window); } - glfwMakeContextCurrent(window); glfwSwapBuffers(window); } diff --git a/examples/example_sdl_opengl2/main.cpp b/examples/example_sdl_opengl2/main.cpp index 3366d413f796..346dee2fee4a 100644 --- a/examples/example_sdl_opengl2/main.cpp +++ b/examples/example_sdl_opengl2/main.cpp @@ -32,6 +32,7 @@ int main(int, char**) SDL_GetCurrentDisplayMode(0, ¤t); SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL2+OpenGL example", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE); SDL_GLContext gl_context = SDL_GL_CreateContext(window); + SDL_GL_MakeCurrent(window, gl_context); SDL_GL_SetSwapInterval(1); // Enable vsync // Setup Dear ImGui context @@ -142,9 +143,9 @@ int main(int, char**) { ImGui::UpdatePlatformWindows(); ImGui::RenderPlatformWindowsDefault(); + SDL_GL_MakeCurrent(window, gl_context); } - SDL_GL_MakeCurrent(window, gl_context); SDL_GL_SwapWindow(window); } diff --git a/examples/example_sdl_opengl3/main.cpp b/examples/example_sdl_opengl3/main.cpp index 93a49a10b179..1d5de155f67d 100644 --- a/examples/example_sdl_opengl3/main.cpp +++ b/examples/example_sdl_opengl3/main.cpp @@ -56,6 +56,7 @@ int main(int, char**) SDL_GetCurrentDisplayMode(0, ¤t); SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL2+OpenGL3 example", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE); SDL_GLContext gl_context = SDL_GL_CreateContext(window); + SDL_GL_MakeCurrent(window, gl_context); SDL_GL_SetSwapInterval(1); // Enable vsync // Initialize OpenGL loader @@ -177,7 +178,6 @@ int main(int, char**) // Rendering ImGui::Render(); - SDL_GL_MakeCurrent(window, gl_context); glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y); glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w); glClear(GL_COLOR_BUFFER_BIT); @@ -188,9 +188,9 @@ int main(int, char**) { ImGui::UpdatePlatformWindows(); ImGui::RenderPlatformWindowsDefault(); + SDL_GL_MakeCurrent(window, gl_context); } - SDL_GL_MakeCurrent(window, gl_context); SDL_GL_SwapWindow(window); } From afe9c5c5f749f981007b140fe612763c5eff2441 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 21 Dec 2018 16:26:17 +0100 Subject: [PATCH 385/828] Examples: SDL: Fixed compilation for SDL 2..0.3 and less (running on our test servers) and clarified a bit of the messy situation. Followup to 8dd83c5. (#1542, #2117) --- examples/imgui_impl_sdl.cpp | 73 +++++++++++++++++++++---------------- 1 file changed, 42 insertions(+), 31 deletions(-) diff --git a/examples/imgui_impl_sdl.cpp b/examples/imgui_impl_sdl.cpp index 5ac682415039..f2e28f5692c3 100644 --- a/examples/imgui_impl_sdl.cpp +++ b/examples/imgui_impl_sdl.cpp @@ -42,11 +42,10 @@ #include "imgui_impl_sdl.h" // SDL -// (the multi-viewports feature requires SDL features supported from SDL 2.0.5+) +// (the multi-viewports feature requires SDL features supported from SDL 2.0.4+. SDL 2.0.5+ is highly recommended) #include #include -#define SDL_HAS_WARP_MOUSE_GLOBAL SDL_VERSION_ATLEAST(2,0,4) -#define SDL_HAS_CAPTURE_MOUSE SDL_VERSION_ATLEAST(2,0,4) +#define SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE SDL_VERSION_ATLEAST(2,0,4) #define SDL_HAS_WINDOW_ALPHA SDL_VERSION_ATLEAST(2,0,5) #define SDL_HAS_ALWAYS_ON_TOP SDL_VERSION_ATLEAST(2,0,5) #define SDL_HAS_USABLE_DISPLAY_BOUNDS SDL_VERSION_ATLEAST(2,0,5) @@ -149,10 +148,7 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window, void* sdl_gl_context) // Setup back-end capabilities flags ImGuiIO& io = ImGui::GetIO(); io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) -#if SDL_HAS_WARP_MOUSE_GLOBAL - io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) -#endif -#if SDL_HAS_CAPTURE_MOUSE +#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional) #endif io.BackendPlatformName = "imgui_impl_sdl"; @@ -213,9 +209,9 @@ bool ImGui_ImplSDL2_InitForOpenGL(SDL_Window* window, void* sdl_gl_context) bool ImGui_ImplSDL2_InitForVulkan(SDL_Window* window) { - #if !SDL_HAS_VULKAN +#if !SDL_HAS_VULKAN IM_ASSERT(0 && "Unsupported"); - #endif +#endif return ImGui_ImplSDL2_Init(window, NULL); } @@ -235,57 +231,72 @@ void ImGui_ImplSDL2_Shutdown() memset(g_MouseCursors, 0, sizeof(g_MouseCursors)); } +// This code is incredibly messy because some of the functions we need for full viewport support are not available in SDL < 2.0.4. static void ImGui_ImplSDL2_UpdateMousePosAndButtons() { ImGuiIO& io = ImGui::GetIO(); io.MouseHoveredViewport = 0; - // Set OS mouse position if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user) - // (When multi-viewports are enabled, all imgui positions are same as OS positions.) -#if SDL_HAS_WARP_MOUSE_GLOBAL - if (!io.WantSetMousePos) - io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); - else if ((io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) == 0) - SDL_WarpMouseInWindow(g_Window, (int)io.MousePos.x, (int)io.MousePos.y); - else - SDL_WarpMouseGlobal((int)io.MousePos.x, (int)io.MousePos.y); -#else - io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); + // [1] + // Only when requested by io.WantSetMousePos: set OS mouse pos from Dear ImGui mouse pos. + // (rarely used, mostly when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user) + if (io.WantSetMousePos) + { +#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + SDL_WarpMouseGlobal((int)io.MousePos.x, (int)io.MousePos.y); + else #endif - - int mouse_x, mouse_y; - Uint32 mouse_buttons = SDL_GetMouseState(&mouse_x, &mouse_y); // NB: We don't use the x/y results from SDL_GetMouseState() - SDL_GetGlobalMouseState(&mouse_x, &mouse_y); - io.MouseDown[0] = g_MousePressed[0] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_LEFT)) != 0; // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame. + SDL_WarpMouseInWindow(g_Window, (int)io.MousePos.x, (int)io.MousePos.y); + } + else + { + io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); + } + + // [2] + // Set Dear ImGui mouse pos from OS mouse pos + get buttons. (this is the common behavior) + int mouse_x_local, mouse_y_local; + Uint32 mouse_buttons = SDL_GetMouseState(&mouse_x_local, &mouse_y_local); + io.MouseDown[0] = g_MousePressed[0] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_LEFT)) != 0; // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame. io.MouseDown[1] = g_MousePressed[1] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_RIGHT)) != 0; io.MouseDown[2] = g_MousePressed[2] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_MIDDLE)) != 0; g_MousePressed[0] = g_MousePressed[1] = g_MousePressed[2] = false; -#if SDL_HAS_CAPTURE_MOUSE && !defined(__EMSCRIPTEN__) +#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE + + // SDL 2.0.4 and later has SDL_GetGlobalMouseState() and SDL_CaptureMouse() + int mouse_x_global, mouse_y_global; + SDL_GetGlobalMouseState(&mouse_x_global, &mouse_y_global); + +#if !defined(__EMSCRIPTEN__) if (io.ConfigFlags & 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) if (SDL_Window* focused_window = SDL_GetKeyboardFocus()) if (ImGui::FindViewportByPlatformHandle((void*)focused_window) != NULL) - io.MousePos = ImVec2((float)mouse_x, (float)mouse_y); + io.MousePos = ImVec2((float)mouse_x_global, (float)mouse_y_global); } else #endif { - // 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) + // Single-viewport mode: mouse position in client window coordinatesio.MousePos is (0,0) when the mouse is on the upper-left corner of the app window) if (SDL_GetWindowFlags(g_Window) & SDL_WINDOW_INPUT_FOCUS) { int window_x, window_y; SDL_GetWindowPosition(g_Window, &window_x, &window_y); - io.MousePos = ImVec2((float)(mouse_x - window_x), (float)(mouse_y - window_y)); + io.MousePos = ImVec2((float)(mouse_x_global - window_x), (float)(mouse_y_global - window_y)); } } - // We already retrieve global mouse position, SDL_CaptureMouse() also let the OS know e.g. that our imgui drag outside the SDL window boundaries shouldn't trigger the OS window resize cursor + // SDL_CaptureMouse() will let the OS know that our drag outside the SDL window boundaries shouldn't trigger the OS window resize cursor. // The function is only supported from SDL 2.0.4 (released Jan 2016) -#if SDL_HAS_CAPTURE_MOUSE bool any_mouse_button_down = ImGui::IsAnyMouseDown(); SDL_CaptureMouse(any_mouse_button_down ? SDL_TRUE : SDL_FALSE); +#else + // SDL 2.0.3 and before: single-viewport only + if (SDL_GetWindowFlags(g_Window) & SDL_WINDOW_INPUT_FOCUS) + io.MousePos = ImVec2((float)mouse_x_local, (float)mouse_y_local); #endif } From 238321c15920ee58b2886e7917179e4e9fb51e18 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 21 Dec 2018 16:53:56 +0100 Subject: [PATCH 386/828] Fix merge in Docking branch, remove ConfigDockingWithShift flag from DX11 example + misnamed function. --- examples/example_win32_directx11/main.cpp | 1 - examples/example_win32_directx9/main.cpp | 3 --- imgui.cpp | 6 +++--- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/examples/example_win32_directx11/main.cpp b/examples/example_win32_directx11/main.cpp index da6100bd7e31..bcedfd5b3318 100644 --- a/examples/example_win32_directx11/main.cpp +++ b/examples/example_win32_directx11/main.cpp @@ -139,7 +139,6 @@ int main(int, char**) //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoMerge; io.ConfigFlags |= ImGuiConfigFlags_DpiEnableScaleFonts; // FIXME-DPI: THIS CURRENTLY DOESN'T WORK AS EXPECTED. DON'T USE IN USER APP! io.ConfigFlags |= ImGuiConfigFlags_DpiEnableScaleViewports; // FIXME-DPI - io.ConfigDockingWithShift = true; // Setup Dear ImGui style ImGui::StyleColorsDark(); diff --git a/examples/example_win32_directx9/main.cpp b/examples/example_win32_directx9/main.cpp index a6560e696ba9..565a844c5b4e 100644 --- a/examples/example_win32_directx9/main.cpp +++ b/examples/example_win32_directx9/main.cpp @@ -80,17 +80,14 @@ int main(int, char**) ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls -<<<<<<< HEAD io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoTaskBarIcons; //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoMerge; -======= // Setup Dear ImGui style ImGui::StyleColorsDark(); //ImGui::StyleColorsClassic(); ->>>>>>> viewport // Setup Platform/Renderer bindings ImGui_ImplWin32_Init(hwnd); diff --git a/imgui.cpp b/imgui.cpp index dc3a099f5bf0..4f2ff59d4686 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3734,11 +3734,11 @@ void ImGui::PopClipRect() window->ClipRect = window->DrawList->_ClipRectStack.back(); } -static ImGuiWindow* FindFromMostVisibleChildWindow(ImGuiWindow* window) +static ImGuiWindow* FindFrontMostVisibleChildWindow(ImGuiWindow* window) { for (int n = window->DC.ChildWindows.Size - 1; n >= 0; n--) if (IsWindowActiveAndVisible(window->DC.ChildWindows[n])) - return FindFromMostVisibleChildWindow(window->DC.ChildWindows[n]); + return FindFrontMostVisibleChildWindow(window->DC.ChildWindows[n]); return window; } @@ -3792,7 +3792,7 @@ void ImGui::EndFrame() { // Choose a draw list that will be front-most across all our children ImGuiWindow* window = g.NavWindowingTargetAnim; - ImDrawList* draw_list = FindFromMostVisibleChildWindow(window->RootWindow)->DrawList; + ImDrawList* draw_list = FindFrontMostVisibleChildWindow(window->RootWindow)->DrawList; draw_list->PushClipRectFullScreen(); // Docking: draw modal whitening background on other nodes of a same dock tree From a71d3c8cb367469eaaffb8855ce5979f45df4fde Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 21 Dec 2018 18:40:16 +0100 Subject: [PATCH 387/828] Viewport: Misc comments following user feedbacks.. --- examples/example_glfw_opengl2/main.cpp | 5 ++++- examples/example_glfw_opengl3/main.cpp | 5 ++++- examples/example_sdl_opengl2/main.cpp | 6 +++++- examples/example_sdl_opengl3/main.cpp | 6 +++++- imgui.cpp | 8 ++++---- imgui_internal.h | 1 + 6 files changed, 23 insertions(+), 8 deletions(-) diff --git a/examples/example_glfw_opengl2/main.cpp b/examples/example_glfw_opengl2/main.cpp index 5b0c90234ac9..a3ffcb302a3d 100644 --- a/examples/example_glfw_opengl2/main.cpp +++ b/examples/example_glfw_opengl2/main.cpp @@ -144,11 +144,14 @@ int main(int, char**) ImGui_ImplOpenGL2_RenderDrawData(ImGui::GetDrawData()); // Update and Render additional Platform Windows + // (Platform functions may change the current OpenGL context, so we save/restore it to make it easier to paste this code elsewhere. + // For this specific demo app we could also call glfwMakeContextCurrent(window) directly) if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { + GLFWwindow* backup_current_context = glfwGetCurrentContext(); ImGui::UpdatePlatformWindows(); ImGui::RenderPlatformWindowsDefault(); - glfwMakeContextCurrent(window); + glfwMakeContextCurrent(backup_current_context); } glfwSwapBuffers(window); diff --git a/examples/example_glfw_opengl3/main.cpp b/examples/example_glfw_opengl3/main.cpp index 4268a210e5ae..f5dd65f07fa9 100644 --- a/examples/example_glfw_opengl3/main.cpp +++ b/examples/example_glfw_opengl3/main.cpp @@ -189,11 +189,14 @@ int main(int, char**) ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); // Update and Render additional Platform Windows + // (Platform functions may change the current OpenGL context, so we save/restore it to make it easier to paste this code elsewhere. + // For this specific demo app we could also call glfwMakeContextCurrent(window) directly) if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { + GLFWwindow* backup_current_context = glfwGetCurrentContext(); ImGui::UpdatePlatformWindows(); ImGui::RenderPlatformWindowsDefault(); - glfwMakeContextCurrent(window); + glfwMakeContextCurrent(backup_current_context); } glfwSwapBuffers(window); diff --git a/examples/example_sdl_opengl2/main.cpp b/examples/example_sdl_opengl2/main.cpp index d27606b723aa..47a5f4d22098 100644 --- a/examples/example_sdl_opengl2/main.cpp +++ b/examples/example_sdl_opengl2/main.cpp @@ -147,11 +147,15 @@ int main(int, char**) ImGui_ImplOpenGL2_RenderDrawData(ImGui::GetDrawData()); // Update and Render additional Platform Windows + // (Platform functions may change the current OpenGL context, so we save/restore it to make it easier to paste this code elsewhere. + // For this specific demo app we could also call SDL_GL_MakeCurrent(window, gl_context) directly) if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { + SDL_Window* backup_current_window = SDL_GL_GetCurrentWindow(); + SDL_GLContext backup_current_context = SDL_GL_GetCurrentContext(); ImGui::UpdatePlatformWindows(); ImGui::RenderPlatformWindowsDefault(); - SDL_GL_MakeCurrent(window, gl_context); + SDL_GL_MakeCurrent(backup_current_window, backup_current_context); } SDL_GL_SwapWindow(window); diff --git a/examples/example_sdl_opengl3/main.cpp b/examples/example_sdl_opengl3/main.cpp index f3aae2ff984a..fc777e3046b4 100644 --- a/examples/example_sdl_opengl3/main.cpp +++ b/examples/example_sdl_opengl3/main.cpp @@ -188,11 +188,15 @@ int main(int, char**) ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); // Update and Render additional Platform Windows + // (Platform functions may change the current OpenGL context, so we save/restore it to make it easier to paste this code elsewhere. + // For this specific demo app we could also call SDL_GL_MakeCurrent(window, gl_context) directly) if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { + SDL_Window* backup_current_window = SDL_GL_GetCurrentWindow(); + SDL_GLContext backup_current_context = SDL_GL_GetCurrentContext(); ImGui::UpdatePlatformWindows(); ImGui::RenderPlatformWindowsDefault(); - SDL_GL_MakeCurrent(window, gl_context); + SDL_GL_MakeCurrent(backup_current_window, backup_current_context); } SDL_GL_SwapWindow(window); diff --git a/imgui.cpp b/imgui.cpp index a9b1fd70cb91..3b78289abfe9 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3812,10 +3812,12 @@ void ImGui::EndFrame() viewport->LastPos = viewport->Pos; if (viewport->LastFrameActive < g.FrameCount || viewport->Size.x <= 0.0f || viewport->Size.y <= 0.0f) continue; - if (viewport->Window && !IsWindowActiveAndVisible(viewport->Window)) + if (viewport->Window && !IsWindowActiveAndVisible(viewport->Window)) // Will be destroyed in UpdatePlatformWindows() continue; if (i > 0) IM_ASSERT(viewport->Window != NULL); + + // Add to user-facing list g.PlatformIO.Viewports.push_back(viewport); } g.Viewports[0]->ClearRequestFlags(); // Clear main viewport flags because UpdatePlatformWindows() won't do it and may not even be called @@ -7680,11 +7682,9 @@ void ImGui::UpdatePlatformWindows() DestroyPlatformWindow(viewport); continue; } - if (viewport->LastFrameActive < g.FrameCount) - continue; // New windows that appears directly in a new viewport won't always have a size on their first frame - if (viewport->Size.x <= 0 || viewport->Size.y <= 0) + if (viewport->LastFrameActive < g.FrameCount || viewport->Size.x <= 0 || viewport->Size.y <= 0) continue; // Update common viewport flags for owned viewports diff --git a/imgui_internal.h b/imgui_internal.h index 56f4f3520e36..151b7a4babaf 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -651,6 +651,7 @@ enum ImGuiViewportFlagsPrivate_ }; // ImGuiViewport Private/Internals fields (cardinal sin: we are using inheritance!) +// Note that every instance of ImGuiViewport is in fact a ImGuiViewportP. struct ImGuiViewportP : public ImGuiViewport { int Idx; From e194219f2e2e4b51099d45ed50017804efa9a57b Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 21 Dec 2018 17:01:08 +0100 Subject: [PATCH 388/828] Renamed ImGuiDockFamily to ImGuiWindowClass. Renamed CompatibleWithClassZero to DockingAllowUnclassed. (#2109) --- docs/CHANGELOG.txt | 2 +- docs/TODO.txt | 2 +- imgui.cpp | 46 +++++++++++++++++++++++----------------------- imgui.h | 21 ++++++++++----------- imgui_demo.cpp | 2 +- imgui_internal.h | 12 ++++++------ 6 files changed, 42 insertions(+), 43 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 8fb216692dcb..d4b0ea767e40 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -38,7 +38,7 @@ HOW TO UPDATE? Set with `io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;`. - Added DockSpace() API. - Added ImGuiDockNodeFlags flags for DockSpace(). - - Added SetNextWindowDock(), SetNextWindowDockFamily() API. + - Added SetNextWindowDock(), SetNextWindowClass() API. - Added GetWindowDockId(), IsWindowDocked() API. - Added ImGuiWindowFlags_NoDocking window flag to disable the possibility for a window to be docked. Popup, Menu and Child windows always have the ImGuiWindowFlags_NoDocking flag set. diff --git a/docs/TODO.txt b/docs/TODO.txt index 3594d8888ff7..4fd3fad49bd0 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -145,7 +145,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - dock: B- tab bar: make selected tab always shows its full title? - dock: B- tab bar: the order/focus restoring code could be part of TabBar and not DockNode? (#8) - dock: B- nav: design interactions so nav controls can dock/undock - - dock: B- dockspace: flag to lock the dock tree and/or sizes (ImGuiDockFlags_Locked?) + - dock: B- dockspace: flag to lock the dock tree and/or sizes (ImGuiDockNodeFlags_Locked?) - dock: B- reintroduce collapsing a floating dock node. also collapsing a docked dock node! - dock: B- allow dragging a non-floating dock node by clicking on the title-bar-looking section (not just the collapse/menu button) - dock: B- option to remember undocked window size? (instead of keeping their docked size) (relate to #2104) diff --git a/imgui.cpp b/imgui.cpp index 4f2ff59d4686..aaa07d05a9b7 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5084,7 +5084,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) { window->SizeContentsExplicit = ImVec2(0.0f, 0.0f); } - window->DockFamily = g.NextWindowData.DockFamily; + window->WindowClass = g.NextWindowData.WindowClass; if (g.NextWindowData.CollapsedCond) SetWindowCollapsed(window, g.NextWindowData.CollapsedVal, g.NextWindowData.CollapsedCond); if (g.NextWindowData.FocusCond) @@ -6531,10 +6531,10 @@ void ImGui::SetNextWindowDockId(ImGuiID id, ImGuiCond cond) g.NextWindowData.DockId = id; } -void ImGui::SetNextWindowDockFamily(const ImGuiDockFamily* family) +void ImGui::SetNextWindowClass(const ImGuiWindowClass* window_class) { ImGuiContext& g = *GImGui; - g.NextWindowData.DockFamily = *family; + g.NextWindowData.WindowClass = *window_class; } // In window space (not screen space!) @@ -10679,7 +10679,7 @@ struct ImGuiDockNodeUpdateScanResults ImGuiDockNode* CentralNode; ImGuiDockNode* FirstNodeWithWindows; int CountNodesWithWindows; - //ImGuiDockFamily DockFamilyForMerges; + //ImGuiWindowClass WindowClassForMerges; ImGuiDockNodeUpdateScanResults() { CentralNode = FirstNodeWithWindows = NULL; CountNodesWithWindows = 0; } }; @@ -10802,16 +10802,16 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) if (node->LastFocusedNodeID == 0 && results.FirstNodeWithWindows != NULL) node->LastFocusedNodeID = results.FirstNodeWithWindows->ID; - // Copy the dock family from of our window so it can be used for proper dock filtering. - // When node has mixed windows, prioritize the family with the most constraint (CompatibleWithNeutral = false) as the reference to copy. + // Copy the window class from of our first window so it can be used for proper dock filtering. + // When node has mixed windows, prioritize the class with the most constraint (CompatibleWithClassZero = false) as the reference to copy. // FIXME-DOCK: We don't recurse properly, this code could be reworked to work from DockNodeUpdateScanRec. if (ImGuiDockNode* first_node_with_windows = results.FirstNodeWithWindows) { - node->DockFamily = first_node_with_windows->Windows[0]->DockFamily; + node->WindowClass = first_node_with_windows->Windows[0]->WindowClass; for (int n = 1; n < first_node_with_windows->Windows.Size; n++) - if (first_node_with_windows->Windows[n]->DockFamily.CompatibleWithFamilyZero == false) + if (first_node_with_windows->Windows[n]->WindowClass.DockingAllowUnclassed == false) { - node->DockFamily = first_node_with_windows->Windows[n]->DockFamily; + node->WindowClass = first_node_with_windows->Windows[n]->WindowClass; break; } } @@ -11272,13 +11272,13 @@ static bool DockNodeIsDropAllowedOne(ImGuiWindow* payload, ImGuiWindow* host_win if ((host_window->Flags & ImGuiWindowFlags_DockNodeHost) && host_window->DockNodeAsHost->IsDockSpace && payload->BeginOrderWithinContext < host_window->BeginOrderWithinContext) return false; - ImGuiDockFamily* host_family = host_window->DockNodeAsHost ? &host_window->DockNodeAsHost->DockFamily : &host_window->DockFamily; - ImGuiDockFamily* payload_family = &payload->DockFamily; - if (host_family->FamilyId != payload_family->FamilyId) + ImGuiWindowClass* host_class = host_window->DockNodeAsHost ? &host_window->DockNodeAsHost->WindowClass : &host_window->WindowClass; + ImGuiWindowClass* payload_class = &payload->WindowClass; + if (host_class->ClassId != payload_class->ClassId) { - if (host_family->FamilyId != 0 && host_family->CompatibleWithFamilyZero && payload_family->FamilyId == 0) + if (host_class->ClassId != 0 && host_class->DockingAllowUnclassed && payload_class->ClassId == 0) return true; - if (payload_family->FamilyId != 0 && payload_family->CompatibleWithFamilyZero && host_family->FamilyId == 0) + if (payload_class->ClassId != 0 && payload_class->DockingAllowUnclassed && host_class->ClassId == 0) return true; return false; } @@ -11928,7 +11928,7 @@ void ImGui::SetWindowDock(ImGuiWindow* window, ImGuiID dock_id, ImGuiCond cond) // Create an explicit dockspace node within an existing window. Also expose dock node flags and creates a CentralNode by default. // The Central Node is always displayed even when empty and shrink/extend according to the requested size of its neighbors. -void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags dockspace_flags, const ImGuiDockFamily* dock_family) +void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags dockspace_flags, const ImGuiWindowClass* window_class) { ImGuiContext* ctx = GImGui; ImGuiContext& g = *ctx; @@ -11943,7 +11943,7 @@ void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags doc node->IsCentralNode = true; } node->Flags = dockspace_flags; - node->DockFamily = dock_family ? *dock_family : ImGuiDockFamily(); + node->WindowClass = window_class ? *window_class : ImGuiWindowClass(); // When a Dockspace transitioned form implicit to explicit this may be called a second time // It is possible that the node has already been claimed by a docked window which appeared before the DockSpace() node, so we overwrite IsDockSpace again. @@ -12006,7 +12006,7 @@ void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags doc // The limitation with this call is that your window won't have a menu bar. // Even though we could pass window flags, it would also require the user to be able to call BeginMenuBar() somehow meaning we can't Begin/End in a single function. // So if you want a menu bar you need to repeat this code manually ourselves. As with advanced other Docking API, we may change this function signature. -ImGuiID ImGui::DockSpaceOverViewport(ImGuiViewport* viewport, ImGuiDockNodeFlags dockspace_flags, const ImGuiDockFamily* dock_family) +ImGuiID ImGui::DockSpaceOverViewport(ImGuiViewport* viewport, ImGuiDockNodeFlags dockspace_flags, const ImGuiWindowClass* window_class) { if (viewport == NULL) viewport = GetMainViewport(); @@ -12031,7 +12031,7 @@ ImGuiID ImGui::DockSpaceOverViewport(ImGuiViewport* viewport, ImGuiDockNodeFlags PopStyleVar(3); ImGuiID dockspace_id = GetID("Dockspace"); - DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags, dock_family); + DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags, window_class); End(); return dockspace_id; @@ -12299,7 +12299,7 @@ void ImGui::DockBuilderCopyDockspace(ImGuiID src_dockspace_id, ImGuiID dst_docks IM_ASSERT((in_window_remap_pairs->Size % 2) == 0); // Duplicate entire dock - // FIXME: When overwriting dst_dockspace_id, windows that aren't part of our dockspace family but that are docked in a same node will be split apart, + // FIXME: When overwriting dst_dockspace_id, windows that aren't part of our dockspace window class but that are docked in a same node will be split apart, // whereas we could attempt to at least keep them together in a new, same floating node. ImVector node_remap_pairs; DockBuilderCopyNode(src_dockspace_id, dst_dockspace_id, &node_remap_pairs); @@ -13126,7 +13126,7 @@ static void SettingsHandlerWindow_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, else if (sscanf(line, "Collapsed=%d", &i) == 1) { settings->Collapsed = (i != 0); } else if (sscanf(line, "DockId=0x%X,%d", &u1, &i) == 2) { settings->DockId = u1; settings->DockOrder = (short)i; } else if (sscanf(line, "DockId=0x%X", &u1) == 1) { settings->DockId = u1; settings->DockOrder = -1; } - else if (sscanf(line, "DockFamilyId=0x%X", &u1) == 1) { settings->DockFamilyId = u1; } + else if (sscanf(line, "ClassId=0x%X", &u1) == 1) { settings->ClassId = u1; } } static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf) @@ -13153,7 +13153,7 @@ static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSetting settings->ViewportPos = window->ViewportPos; IM_ASSERT(window->DockNode == NULL || window->DockNode->ID == window->DockId); settings->DockId = window->DockId; - settings->DockFamilyId = window->DockFamily.FamilyId; + settings->ClassId = window->WindowClass.ClassId; settings->DockOrder = window->DockOrder; settings->Collapsed = window->Collapsed; } @@ -13184,8 +13184,8 @@ static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSetting buf->appendf("DockId=0x%08X\n", settings->DockId); else buf->appendf("DockId=0x%08X,%d\n", settings->DockId, settings->DockOrder); - if (settings->DockFamilyId != 0) - buf->appendf("DockFamilyId=0x%08X\n", settings->DockFamilyId); + if (settings->ClassId != 0) + buf->appendf("ClassId=0x%08X\n", settings->ClassId); } buf->appendf("\n"); } diff --git a/imgui.h b/imgui.h index 9404c9289f78..4873222a0971 100644 --- a/imgui.h +++ b/imgui.h @@ -15,7 +15,7 @@ Index of this file: // Flags & Enumerations // ImGuiStyle // ImGuiIO -// Misc data structures (ImGuiInputTextCallbackData, ImGuiSizeCallbackData, ImGuiPayload, ImGuiDockFamily) +// Misc data structures (ImGuiInputTextCallbackData, ImGuiSizeCallbackData, ImGuiPayload, ImGuiWindowClass) // Obsolete functions // Helpers (ImVector, ImGuiOnceUponAFrame, ImGuiTextFilter, ImGuiTextBuffer, ImGuiStorage, ImGuiListClipper, ImColor) // Draw List API (ImDrawCmd, ImDrawIdx, ImDrawVert, ImDrawChannel, ImDrawListFlags, ImDrawList, ImDrawData) @@ -102,7 +102,7 @@ struct ImColor; // Helper functions to create a color that c typedef void* ImTextureID; // User data to identify a texture (this is whatever to you want it to be! read the FAQ about ImTextureID in imgui.cpp) #endif struct ImGuiContext; // ImGui context (opaque) -struct ImGuiDockFamily; // Docking family for dock filtering +struct ImGuiWindowClass; // Window class/family for docking filtering or high-level identifiation of windows by user struct ImGuiIO; // Main configuration and I/O between your application and ImGui struct ImGuiInputTextCallbackData; // Shared state of InputText() when using custom ImGuiInputTextCallback (rare/advanced use) struct ImGuiListClipper; // Helper to manually clip large list of items @@ -578,10 +578,10 @@ namespace ImGui // Note: you DO NOT need to call DockSpace() to use most Docking facilities! // To dock windows: hold SHIFT anywhere while moving windows (if io.ConfigDockingWithShift == true) or drag windows from their title bar (if io.ConfigDockingWithShift = false) // Use DockSpace() to create an explicit dock node _within_ an existing window. See Docking demo for details. - IMGUI_API void DockSpace(ImGuiID id, const ImVec2& size = ImVec2(0, 0), ImGuiDockNodeFlags flags = 0, const ImGuiDockFamily* dock_family = NULL); - IMGUI_API ImGuiID DockSpaceOverViewport(ImGuiViewport* viewport = NULL, ImGuiDockNodeFlags dockspace_flags = 0, const ImGuiDockFamily* dock_family = NULL); + IMGUI_API void DockSpace(ImGuiID id, const ImVec2& size = ImVec2(0, 0), ImGuiDockNodeFlags flags = 0, const ImGuiWindowClass* window_class = NULL); + IMGUI_API ImGuiID DockSpaceOverViewport(ImGuiViewport* viewport = NULL, ImGuiDockNodeFlags dockspace_flags = 0, const ImGuiWindowClass* window_class = NULL); IMGUI_API void SetNextWindowDockId(ImGuiID dock_id, ImGuiCond cond = 0); // set next window dock id (FIXME-DOCK) - IMGUI_API void SetNextWindowDockFamily(const ImGuiDockFamily* dock_family); // set next window user type (docking filters by same user_type) + IMGUI_API void SetNextWindowClass(const ImGuiWindowClass* window_class); // set next window class / user type (docking filters by same user_type) IMGUI_API ImGuiID GetWindowDockId(); IMGUI_API bool IsWindowDocked(); // is current window docked into another window? @@ -1470,14 +1470,13 @@ struct ImGuiPayload bool IsDelivery() const { return Delivery; } }; -// [BETA] For SetNextWindowDockFamily() and DockSpace() function -struct ImGuiDockFamily +// [BETA] Rarely used, very advanced uses only. Use with SetNextWindowClass() and DockSpace() functions. +struct ImGuiWindowClass { - ImGuiID FamilyId; // 0 = unaffiliated - bool CompatibleWithFamilyZero; // true = can be docked/merged with an unaffiliated window + ImGuiID ClassId; // User data. 0 = Default class (unclassed) + bool DockingAllowUnclassed; // true = can be docked/merged with an unclassed window - ImGuiDockFamily() { FamilyId = 0; CompatibleWithFamilyZero = true; } - ImGuiDockFamily(ImGuiID family_id, bool compatible_with_family_zero = true) { FamilyId = family_id; CompatibleWithFamilyZero = compatible_with_family_zero; } + ImGuiWindowClass() { ClassId = 0; DockingAllowUnclassed = true; } }; //----------------------------------------------------------------------------- diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 5fbae2c56e9e..317e59dccc61 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1541,7 +1541,7 @@ static void ShowDemoWindowWidgets() if (test_window) { // FIXME-DOCK: This window cannot be docked within the ImGui Demo window, this will cause a feedback loop and get them stuck. - // Could we fix this through an ImGuiDockFamily feature? Or an API call to tag our parent as "don't skip items"? + // Could we fix this through an ImGuiWindowClass feature? Or an API call to tag our parent as "don't skip items"? ImGui::Begin("Title bar Hovered/Active tests", &test_window); if (ImGui::BeginPopupContextItem()) // <-- This is using IsItemHovered() { diff --git a/imgui_internal.h b/imgui_internal.h index 7578f8426306..687b53bc7510 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -566,11 +566,11 @@ struct ImGuiWindowSettings ImVec2 ViewportPos; ImGuiID ViewportId; ImGuiID DockId; // ID of last known DockNode (even if the DockNode is invisible because it has only 1 active window), or 0 if none. - ImGuiID DockFamilyId; // ID of dock family if specified + ImGuiID ClassId; // ID of window class if specified short DockOrder; // Order of the last time the window was visible within its DockNode. This is used to reorder windows that are reappearing on the same frame. Same value between windows that were active and windows that were none are possible. bool Collapsed; - ImGuiWindowSettings() { Name = NULL; ID = 0; Pos = Size = ViewportPos = ImVec2(0, 0); ViewportId = DockId = DockFamilyId = 0; DockOrder = -1; Collapsed = false; } + ImGuiWindowSettings() { Name = NULL; ID = 0; Pos = Size = ViewportPos = ImVec2(0, 0); ViewportId = DockId = ClassId = 0; DockOrder = -1; Collapsed = false; } }; struct ImGuiSettingsHandler @@ -734,7 +734,7 @@ struct ImGuiNextWindowData float BgAlphaVal; ImGuiID ViewportId; ImGuiID DockId; - ImGuiDockFamily DockFamily; + ImGuiWindowClass WindowClass; ImVec2 MenuBarOffsetMinVal; // This is not exposed publicly, so we don't clear it. ImGuiNextWindowData() @@ -754,7 +754,7 @@ struct ImGuiNextWindowData void Clear() { PosCond = SizeCond = ContentSizeCond = CollapsedCond = SizeConstraintCond = FocusCond = BgAlphaCond = ViewportCond = DockCond = 0; - DockFamily = ImGuiDockFamily(); + WindowClass = ImGuiWindowClass(); } }; @@ -781,7 +781,7 @@ struct ImGuiDockNode ImVec2 Size; // Current size ImVec2 SizeRef; // [Split node only] Last explicitly written-to size (overridden when using a splitter affecting the node), used to calculate Size. int SplitAxis; // [Split node only] Split axis (X or Y) - ImGuiDockFamily DockFamily; + ImGuiWindowClass WindowClass; ImGuiWindow* HostWindow; ImGuiWindow* VisibleWindow; @@ -1201,6 +1201,7 @@ struct IMGUI_API ImGuiWindow char* Name; ImGuiID ID; // == ImHash(Name) ImGuiWindowFlags Flags, FlagsPreviousFrame; // See enum ImGuiWindowFlags_ + ImGuiWindowClass WindowClass; // Advanced users only. Set with SetNextWindowClass() ImGuiViewportP* Viewport; // Always set in Begin(), only inactive windows may have a NULL value here ImGuiID ViewportId; // We backup the viewport id (since the viewport may disappear or never be created if the window is inactive) ImVec2 ViewportPos; // We backup the viewport position (since the viewport may disappear or never be created if the window is inactive) @@ -1247,7 +1248,6 @@ struct IMGUI_API ImGuiWindow ImGuiCond SetWindowDockAllowFlags; // store acceptable condition flags for SetNextWindowDock() use. ImVec2 SetWindowPosVal; // store window position when using a non-zero Pivot (position set needs to be processed when we know the window size) ImVec2 SetWindowPosPivot; // store window pivot for positioning. ImVec2(0,0) when positioning from top-left corner; ImVec2(0.5f,0.5f) for centering; ImVec2(1,1) for bottom right. - ImGuiDockFamily DockFamily; // set with SetNextWindowDockFamily() ImGuiWindowTempData DC; // Temporary per-window data, reset at the beginning of the frame. This used to be called ImGuiDrawContext, hence the "DC" variable name. ImVector IDStack; // ID stack. ID are hashes seeded with the value at the top of the stack From 4ea9fdbbea77408e3eda8e233ffbaca4c65e82fb Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 21 Dec 2018 17:04:39 +0100 Subject: [PATCH 389/828] Docking: Agressively assert when CentralNode is a not a leaf node in order to find our bug. --- imgui.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index aaa07d05a9b7..c880763e53d6 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10695,6 +10695,7 @@ static void DockNodeUpdateScanRec(ImGuiDockNode* node, ImGuiDockNodeUpdateScanRe if (node->IsCentralNode) { IM_ASSERT(results->CentralNode == NULL); // Should be only one + IM_ASSERT(node->IsLeafNode() && "If you get this assert: your .ini file may have been damaged by an old bug. OR please submit repro of actions leading to this"); results->CentralNode = node; } if (results->CountNodesWithWindows > 1 && results->CentralNode != NULL) @@ -11916,7 +11917,7 @@ void ImGui::SetWindowDock(ImGuiWindow* window, ImGuiID dock_id, ImGuiCond cond) dock_id = new_node->CentralNode->ID; else dock_id = new_node->LastFocusedNodeID; - } + } if (window->DockId == dock_id) return; From 5305c322422e787f4b327a45284c9d08cec3cc44 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 21 Dec 2018 19:12:24 +0100 Subject: [PATCH 390/828] Viewport: Reorder flags. Set owned viewport common decoration flags in Begin(). Moved code in UpdateViewportsEndFrame() before we introduce family/class based overrides. --- imgui.cpp | 76 +++++++++++++++++++++++++----------------------- imgui.h | 14 ++++----- imgui_internal.h | 2 +- 3 files changed, 47 insertions(+), 45 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index f9a8a65f492f..f18c61aacdef 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1010,7 +1010,8 @@ static void UpdateManualResize(ImGuiWindow* window, const ImVec2& si // Viewports const ImGuiID IMGUI_VIEWPORT_DEFAULT_ID = 0x11111111; // Using an arbitrary constant instead of e.g. ImHash("ViewportDefault", 0); so it's easier to spot in the debugger. The exact value doesn't matter. static ImGuiViewportP* AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const ImVec2& platform_pos, const ImVec2& size, ImGuiViewportFlags flags); -static void UpdateViewports(); +static void UpdateViewportsNewFrame(); +static void UpdateViewportsEndFrame(); static void UpdateSelectWindowViewport(ImGuiWindow* window); static bool UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* host_viewport); static void SetCurrentViewport(ImGuiWindow* window, ImGuiViewportP* viewport); @@ -3352,7 +3353,7 @@ void ImGui::NewFrame() g.WindowsActiveCount = 0; g.ConfigFlagsForFrame = g.IO.ConfigFlags; - UpdateViewports(); + UpdateViewportsNewFrame(); // Setup current font, and draw list shared data // FIXME-VIEWPORT: the concept of a single ClipRectFullscreen is not ideal! @@ -3846,24 +3847,8 @@ void ImGui::EndFrame() } } - // Update user-facing viewport list - g.PlatformIO.MainViewport = g.Viewports[0]; - g.PlatformIO.Viewports.resize(0); - for (int i = 0; i < g.Viewports.Size; i++) - { - ImGuiViewportP* viewport = g.Viewports[i]; - viewport->LastPos = viewport->Pos; - if (viewport->LastFrameActive < g.FrameCount || viewport->Size.x <= 0.0f || viewport->Size.y <= 0.0f) - continue; - if (viewport->Window && !IsWindowActiveAndVisible(viewport->Window)) // Will be destroyed in UpdatePlatformWindows() - continue; - if (i > 0) - IM_ASSERT(viewport->Window != NULL); - - // Add to user-facing list - g.PlatformIO.Viewports.push_back(viewport); - } - g.Viewports[0]->ClearRequestFlags(); // Clear main viewport flags because UpdatePlatformWindows() won't do it and may not even be called + // Update user-facing viewport list (g.Viewports -> g.PlatformIO.Viewports after filtering out some) + UpdateViewportsEndFrame(); // Sort the window list so that all child windows are after their parent // We cannot do that on FocusWindow() because childs may not exist yet @@ -5178,16 +5163,26 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) SetCurrentWindow(window); } - // Synchronize viewport --> window in case the platform window has been moved or resized from the OS/WM if (window->ViewportOwned) { + // Synchronize viewport -> window in case the platform window has been moved or resized from the OS/WM if (window->Viewport->PlatformRequestMove) window->Pos = window->Viewport->Pos; if (window->Viewport->PlatformRequestResize) window->Size = window->SizeFull = window->Viewport->Size; + // Update common viewport flags + ImGuiViewportFlags viewport_flags = (window->Viewport->Flags) & ~(ImGuiViewportFlags_TopMost | ImGuiViewportFlags_NoTaskBarIcon | ImGuiViewportFlags_NoDecoration); + if (flags & ImGuiWindowFlags_Tooltip) + viewport_flags |= ImGuiViewportFlags_TopMost; + if ((g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsNoTaskBarIcon) != 0 || (flags & (ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) != 0) + viewport_flags |= ImGuiViewportFlags_NoTaskBarIcon; + if ((g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsDecoration) == 0 || (flags & (ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) != 0) + viewport_flags |= ImGuiViewportFlags_NoDecoration; + // We also tell the back-end that clearing the platform window won't be necessary, as our window is filling the viewport and we have disabled BgAlpha - window->Viewport->Flags |= ImGuiViewportFlags_NoRendererClear; + viewport_flags |= ImGuiViewportFlags_NoRendererClear; + window->Viewport->Flags = viewport_flags; } // Clamp position so window stays visible within its viewport or monitor @@ -7387,8 +7382,7 @@ static ImGuiViewportP* FindViewportHoveredFromPlatformWindowStack(const ImVec2 m return best_candidate; } -// Called in NewFrame() -static void ImGui::UpdateViewports() +static void ImGui::UpdateViewportsNewFrame() { ImGuiContext& g = *GImGui; IM_ASSERT(g.PlatformIO.Viewports.Size <= g.Viewports.Size); @@ -7540,6 +7534,27 @@ static void ImGui::UpdateViewports() IM_ASSERT(g.MouseViewport != NULL); } +// Update user-facing viewport list (g.Viewports -> g.PlatformIO.Viewports after filtering out some) +static void ImGui::UpdateViewportsEndFrame() +{ + ImGuiContext& g = *GImGui; + g.PlatformIO.MainViewport = g.Viewports[0]; + g.PlatformIO.Viewports.resize(0); + for (int i = 0; i < g.Viewports.Size; i++) + { + ImGuiViewportP* viewport = g.Viewports[i]; + viewport->LastPos = viewport->Pos; + if (viewport->LastFrameActive < g.FrameCount || viewport->Size.x <= 0.0f || viewport->Size.y <= 0.0f) + continue; + if (viewport->Window && !IsWindowActiveAndVisible(viewport->Window)) + continue; + if (i > 0) + IM_ASSERT(viewport->Window != NULL); + g.PlatformIO.Viewports.push_back(viewport); + } + g.Viewports[0]->ClearRequestFlags(); // Clear main viewport flags because UpdatePlatformWindows() won't do it and may not even be called +} + // FIXME: We should ideally refactor the system to call this every frame (we currently don't) ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const ImVec2& pos, const ImVec2& size, ImGuiViewportFlags flags) { @@ -7734,19 +7749,6 @@ void ImGui::UpdatePlatformWindows() if (viewport->LastFrameActive < g.FrameCount || viewport->Size.x <= 0 || viewport->Size.y <= 0) continue; - // Update common viewport flags for owned viewports - if (ImGuiWindow* window = viewport->Window) - { - ImGuiViewportFlags flags = viewport->Flags & ~(ImGuiViewportFlags_TopMost | ImGuiViewportFlags_NoTaskBarIcon | ImGuiViewportFlags_NoDecoration); - if (window->Flags & ImGuiWindowFlags_Tooltip) - flags |= ImGuiViewportFlags_TopMost; - if ((g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsNoTaskBarIcon) != 0 || (window->Flags & (ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) != 0) - flags |= ImGuiViewportFlags_NoTaskBarIcon; - if ((g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsDecoration) == 0 || (window->Flags & (ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) != 0) - flags |= ImGuiViewportFlags_NoDecoration; - viewport->Flags = flags; - } - // Create window bool is_new_platform_window = (viewport->PlatformWindowCreated == false); if (is_new_platform_window) diff --git a/imgui.h b/imgui.h index b0ad4ed4123c..b0f1dccc79d6 100644 --- a/imgui.h +++ b/imgui.h @@ -989,8 +989,8 @@ enum ImGuiConfigFlags_ // When using viewports it is recommended that your default value for ImGuiCol_WindowBg is opaque (Alpha=1.0) so transition to a viewport won't be noticeable. ImGuiConfigFlags_ViewportsEnable = 1 << 10, // Viewport enable flags (require both ImGuiConfigFlags_PlatformHasViewports + ImGuiConfigFlags_RendererHasViewports set by the respective back-ends) ImGuiConfigFlags_ViewportsNoTaskBarIcon = 1 << 11, // Disable task bars icons for all secondary viewports (will set ImGuiViewportFlags_NoTaskBarIcon on them) - ImGuiConfigFlags_ViewportsNoMerge = 1 << 12, // All floating windows will always create their own viewport and platform window. - ImGuiConfigFlags_ViewportsDecoration = 1 << 13, // FIXME [Broken] Enable platform decoration for all secondary viewports (will not set ImGuiViewportFlags_NoDecoration on them). This currently doesn't behave well in Windows because 1) By default the new window animation get in the way of our transitions, 2) It enable a minimum window size which tends to breaks resizing. You can workaround the later by setting style.WindowMinSize to a bigger value. + ImGuiConfigFlags_ViewportsDecoration = 1 << 12, // FIXME [Broken] Enable platform decoration for all secondary viewports (will not set ImGuiViewportFlags_NoDecoration on them). This currently doesn't behave well in Windows because 1) By default the new window animation get in the way of our transitions, 2) It enable a minimum window size which tends to breaks resizing. You can workaround the later by setting style.WindowMinSize to a bigger value. + ImGuiConfigFlags_ViewportsNoMerge = 1 << 13, // All floating windows will always create their own viewport and platform window. ImGuiConfigFlags_DpiEnableScaleViewports= 1 << 14, // FIXME-DPI: Reposition and resize imgui windows when the DpiScale of a viewport changed (mostly useful for the main viewport hosting other window). Note that resizing the main window itself is up to your application. ImGuiConfigFlags_DpiEnableScaleFonts = 1 << 15, // FIXME-DPI: Request bitmap-scaled fonts to match DpiScale. This is a very low-quality workaround. The correct way to handle DPI is _currently_ to replace the atlas and/or fonts in the Platform_OnChangedViewport callback, but this is all early work in progress. @@ -2217,11 +2217,11 @@ struct ImGuiPlatformIO enum ImGuiViewportFlags_ { ImGuiViewportFlags_None = 0, - ImGuiViewportFlags_NoDecoration = 1 << 0, // Platform Window: Disable platform decorations: title bar, borders, etc. - ImGuiViewportFlags_NoFocusOnAppearing = 1 << 1, // Platform Window: Don't take focus when created. - ImGuiViewportFlags_NoInputs = 1 << 2, // Platform Window: Make mouse pass through so we can drag this window while peaking behind it. - ImGuiViewportFlags_NoTaskBarIcon = 1 << 3, // Platform Window: Disable platform task bar icon (for popups, menus, or all windows if ImGuiConfigFlags_ViewportsNoTaskBarIcons if set) - ImGuiViewportFlags_NoRendererClear = 1 << 4, // Platform Window: Renderer doesn't need to clear the framebuffer ahead. + ImGuiViewportFlags_NoDecoration = 1 << 0, // Platform Window: Disable platform decorations: title bar, borders, etc. (generally set all windows, but if ImGuiConfigFlags_ViewportsDecoration is set we only set this on popups/tooltips) + ImGuiViewportFlags_NoTaskBarIcon = 1 << 1, // Platform Window: Disable platform task bar icon (generally set on popups/tooltips, or all windows if ImGuiConfigFlags_ViewportsNoTaskBarIcon is set) + ImGuiViewportFlags_NoFocusOnAppearing = 1 << 2, // Platform Window: Don't take focus when created. + ImGuiViewportFlags_NoInputs = 1 << 3, // Platform Window: Make mouse pass through so we can drag this window while peaking behind it. + ImGuiViewportFlags_NoRendererClear = 1 << 4, // Platform Window: Renderer doesn't need to clear the framebuffer ahead (because we will fill it entirely). ImGuiViewportFlags_TopMost = 1 << 5 // Platform Window: Display on top (for tooltips only) }; diff --git a/imgui_internal.h b/imgui_internal.h index 151b7a4babaf..fee712b9ae15 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -665,7 +665,7 @@ struct ImGuiViewportP : public ImGuiViewport short PlatformMonitor; bool PlatformWindowCreated; bool PlatformWindowMinimized; - ImGuiWindow* Window; // Set when the viewport is owned by a window + ImGuiWindow* Window; // Set when the viewport is owned by a window (and ImGuiViewportFlags_CanHostOtherWindows is NOT set) ImDrawList* OverlayDrawList; // For convenience, a draw list we can render to that's always rendered last (we use it to draw software mouse cursor when io.MouseDrawCursor is set) ImDrawData DrawDataP; ImDrawDataBuilder DrawDataBuilder; From 4a6f95acc881f09de2835a93273cf725801790e1 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 2 Jan 2019 16:06:58 +0100 Subject: [PATCH 391/828] Viewport: Added Platform_UpdateWindow hook for general purpose: Rework Win32 code to reflect viewport flags changes into Win32 while the window is active. --- examples/imgui_impl_win32.cpp | 65 ++++++++++++++++++++++++++--------- imgui.cpp | 4 +++ imgui.h | 1 + 3 files changed, 53 insertions(+), 17 deletions(-) diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index ef22fa473633..fed43afdd5e5 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -431,33 +431,38 @@ struct ImGuiViewportDataWin32 ~ImGuiViewportDataWin32() { IM_ASSERT(Hwnd == NULL); } }; +static void ImGui_ImplWin32_GetWin32StyleFromViewportFlags(ImGuiViewportFlags flags, DWORD* out_style, DWORD* out_ex_style) +{ + if (flags & ImGuiViewportFlags_NoDecoration) + *out_style = WS_POPUP; + else + *out_style = WS_OVERLAPPEDWINDOW; + + if (flags & ImGuiViewportFlags_NoTaskBarIcon) + *out_ex_style = WS_EX_TOOLWINDOW; + else + *out_ex_style = WS_EX_APPWINDOW; + + if (flags & ImGuiViewportFlags_TopMost) + *out_ex_style |= WS_EX_TOPMOST; +} + static void ImGui_ImplWin32_CreateWindow(ImGuiViewport* viewport) { ImGuiViewportDataWin32* data = IM_NEW(ImGuiViewportDataWin32)(); viewport->PlatformUserData = data; - bool no_decoration = (viewport->Flags & ImGuiViewportFlags_NoDecoration) != 0; - bool no_task_bar_icon = (viewport->Flags & ImGuiViewportFlags_NoTaskBarIcon) != 0; - if (no_decoration) - { - data->DwStyle = WS_POPUP; - data->DwExStyle = no_task_bar_icon ? WS_EX_TOOLWINDOW : WS_EX_APPWINDOW; - } - else - { - data->DwStyle = WS_OVERLAPPEDWINDOW; - data->DwExStyle = no_task_bar_icon ? WS_EX_TOOLWINDOW : WS_EX_APPWINDOW; - } - if (viewport->Flags & ImGuiViewportFlags_TopMost) - data->DwExStyle |= WS_EX_TOPMOST; + // Select style and parent window + HWND parent_window = g_hWnd; + ImGui_ImplWin32_GetWin32StyleFromViewportFlags(viewport->Flags, &data->DwStyle, &data->DwExStyle); // Create window RECT rect = { (LONG)viewport->Pos.x, (LONG)viewport->Pos.y, (LONG)(viewport->Pos.x + viewport->Size.x), (LONG)(viewport->Pos.y + viewport->Size.y) }; ::AdjustWindowRectEx(&rect, data->DwStyle, FALSE, data->DwExStyle); data->Hwnd = ::CreateWindowEx( - data->DwExStyle, _T("ImGui Platform"), _T("No Title Yet"), data->DwStyle, // Style, class name, window name - rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, // Window area - g_hWnd, NULL, ::GetModuleHandle(NULL), NULL); // Parent window, Menu, Instance, Param + data->DwExStyle, _T("ImGui Platform"), _T("Untitled"), data->DwStyle, // Style, class name, window name + rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, // Window area + parent_window, NULL, ::GetModuleHandle(NULL), NULL); // Parent window, Menu, Instance, Param data->HwndOwned = true; viewport->PlatformRequestResize = false; viewport->PlatformHandle = data->Hwnd; @@ -491,6 +496,31 @@ static void ImGui_ImplWin32_ShowWindow(ImGuiViewport* viewport) ::ShowWindow(data->Hwnd, SW_SHOW); } +static void ImGui_ImplWin32_UpdateWindow(ImGuiViewport* viewport) +{ + // (Optional) Update Win32 style if it changed _after_ creation. + // Generally they won't change unless configuration flags are changed, but advanced uses (such as manually rewriting viewport flags) make this useful. + ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData; + IM_ASSERT(data->Hwnd != 0); + DWORD new_style; + DWORD new_ex_style; + ImGui_ImplWin32_GetWin32StyleFromViewportFlags(viewport->Flags, &new_style, &new_ex_style); + + // Only reapply the flags that have been changed from our point of view (as other flags are being modified by Windows) + if (data->DwStyle != new_style || data->DwExStyle != new_ex_style) + { + data->DwStyle = new_style; + data->DwExStyle = new_ex_style; + ::SetWindowLong(data->Hwnd, GWL_STYLE, data->DwStyle); + ::SetWindowLong(data->Hwnd, GWL_EXSTYLE, data->DwExStyle); + RECT rect = { (LONG)viewport->Pos.x, (LONG)viewport->Pos.y, (LONG)(viewport->Pos.x + viewport->Size.x), (LONG)(viewport->Pos.y + viewport->Size.y) }; + ::AdjustWindowRectEx(&rect, data->DwStyle, FALSE, data->DwExStyle); // Client to Screen + ::SetWindowPos(data->Hwnd, NULL, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED); + ::ShowWindow(data->Hwnd, SW_SHOWNA); // This is necessary when we alter the style + viewport->PlatformRequestMove = viewport->PlatformRequestResize = true; + } +} + static ImVec2 ImGui_ImplWin32_GetWindowPos(ImGuiViewport* viewport) { ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData; @@ -695,6 +725,7 @@ static void ImGui_ImplWin32_InitPlatformInterface() platform_io.Platform_GetWindowMinimized = ImGui_ImplWin32_GetWindowMinimized; platform_io.Platform_SetWindowTitle = ImGui_ImplWin32_SetWindowTitle; platform_io.Platform_SetWindowAlpha = ImGui_ImplWin32_SetWindowAlpha; + platform_io.Platform_UpdateWindow = ImGui_ImplWin32_UpdateWindow; platform_io.Platform_GetWindowDpiScale = ImGui_ImplWin32_GetWindowDpiScale; // FIXME-DPI platform_io.Platform_OnChangedViewport = ImGui_ImplWin32_OnChangedViewport; // FIXME-DPI #if HAS_WIN32_IME diff --git a/imgui.cpp b/imgui.cpp index f18c61aacdef..681d8210b91f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7794,6 +7794,10 @@ void ImGui::UpdatePlatformWindows() g.PlatformIO.Platform_SetWindowAlpha(viewport, viewport->Alpha); viewport->LastAlpha = viewport->Alpha; + // Optional, general purpose call to allow the back-end to perform general book-keeping even if things haven't changed. + if (g.PlatformIO.Platform_UpdateWindow) + g.PlatformIO.Platform_UpdateWindow(viewport); + if (is_new_platform_window) { // On startup ensure new platform window don't steal focus (give it a few frames, as nested contents may lead to viewport being created a few frames late) diff --git a/imgui.h b/imgui.h index b0f1dccc79d6..6d7f8dd223d1 100644 --- a/imgui.h +++ b/imgui.h @@ -2186,6 +2186,7 @@ struct ImGuiPlatformIO bool (*Platform_GetWindowMinimized)(ImGuiViewport* vp); void (*Platform_SetWindowTitle)(ImGuiViewport* vp, const char* title); void (*Platform_SetWindowAlpha)(ImGuiViewport* vp, float alpha); // (Optional) Setup window transparency + void (*Platform_UpdateWindow)(ImGuiViewport* vp); // (Optional) Called in UpdatePlatforms(). Optional hook to allow the platform back-end from doing general book-keeping every frame. void (*Platform_RenderWindow)(ImGuiViewport* vp, void* render_arg); // (Optional) Setup for render void (*Platform_SwapBuffers)(ImGuiViewport* vp, void* render_arg); // (Optional) Call Present/SwapBuffers (platform side) float (*Platform_GetWindowDpiScale)(ImGuiViewport* vp); // (Optional) [BETA] (FIXME-DPI) DPI handling: Return DPI scale for this viewport. 1.0f = 96 DPI. From cfcad42b89a2a8d8e789c10fd186b753a13bfc02 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 2 Jan 2019 18:22:31 +0100 Subject: [PATCH 392/828] Viewport: Win32: Workaround to the fact that ::WindowFromPoint() seems to return Windows using ImGuiViewportFlags_NoInputs / HTTRANSPARENT when dragging nearby the platform title bar. This is to allow using platform decoration. I don't understand this well atm. (#1542) --- examples/imgui_impl_win32.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index fed43afdd5e5..b2134b30ba0b 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -185,10 +185,11 @@ static void ImGui_ImplWin32_UpdateMousePos() // - This is _ignoring_ viewports with the ImGuiViewportFlags_NoInputs flag (pass-through windows). // - This is _regardless_ of whether another viewport is focused or being dragged from. // If ImGuiBackendFlags_HasMouseHoveredViewport is not set by the back-end, imgui will ignore this field and infer the information by relying on the - // rectangles and last focused time of every viewports it knows about. It will be unaware of other windows that may be sitting between or over your windows. + // rectangles and last focused time of every viewports it knows about. It will be unaware of foreign windows that may be sitting between or over your windows. if (HWND hovered_hwnd = ::WindowFromPoint(mouse_screen_pos)) if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle((void*)hovered_hwnd)) - io.MouseHoveredViewport = viewport->ID; + if ((viewport->Flags & ImGuiViewportFlags_NoInputs) == 0) // FIXME: We still get our NoInputs window with WM_NCHITTEST/HTTRANSPARENT code when decorated? + io.MouseHoveredViewport = viewport->ID; } void ImGui_ImplWin32_NewFrame() From 0d6e3ab2b037a7c789b01eb78e00297c04fc52ca Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 2 Jan 2019 18:56:53 +0100 Subject: [PATCH 393/828] Docking: Renamed SetNextWindowId() -> SetNextWindowID() for consistency. (function vs member are still horribly inconsistent atm) --- imgui.cpp | 4 ++-- imgui.h | 4 ++-- imgui_demo.cpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index c880763e53d6..84e372897e69 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6263,7 +6263,7 @@ bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags) } } -ImGuiID ImGui::GetWindowDockId() +ImGuiID ImGui::GetWindowDockID() { ImGuiContext& g = *GImGui; return g.CurrentWindow->DockId; @@ -6524,7 +6524,7 @@ void ImGui::SetNextWindowViewport(ImGuiID id) g.NextWindowData.ViewportId = id; } -void ImGui::SetNextWindowDockId(ImGuiID id, ImGuiCond cond) +void ImGui::SetNextWindowDockID(ImGuiID id, ImGuiCond cond) { ImGuiContext& g = *GImGui; g.NextWindowData.DockCond = cond ? cond : ImGuiCond_Always; diff --git a/imgui.h b/imgui.h index 4873222a0971..b4745319ee82 100644 --- a/imgui.h +++ b/imgui.h @@ -580,9 +580,9 @@ namespace ImGui // Use DockSpace() to create an explicit dock node _within_ an existing window. See Docking demo for details. IMGUI_API void DockSpace(ImGuiID id, const ImVec2& size = ImVec2(0, 0), ImGuiDockNodeFlags flags = 0, const ImGuiWindowClass* window_class = NULL); IMGUI_API ImGuiID DockSpaceOverViewport(ImGuiViewport* viewport = NULL, ImGuiDockNodeFlags dockspace_flags = 0, const ImGuiWindowClass* window_class = NULL); - IMGUI_API void SetNextWindowDockId(ImGuiID dock_id, ImGuiCond cond = 0); // set next window dock id (FIXME-DOCK) + IMGUI_API void SetNextWindowDockID(ImGuiID dock_id, ImGuiCond cond = 0); // set next window dock id (FIXME-DOCK) IMGUI_API void SetNextWindowClass(const ImGuiWindowClass* window_class); // set next window class / user type (docking filters by same user_type) - IMGUI_API ImGuiID GetWindowDockId(); + IMGUI_API ImGuiID GetWindowDockID(); IMGUI_API bool IsWindowDocked(); // is current window docked into another window? // Logging/Capture diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 317e59dccc61..acd557d6c278 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -4275,7 +4275,7 @@ void ShowExampleAppDocuments(bool* p_open) // FIXME-DOCK: SetNextWindowDock() //ImGuiID default_dock_id = GetDockspaceRootDocumentDockID(); //ImGuiID default_dock_id = GetDockspacePreferedDocumentDockID(); - ImGui::SetNextWindowDockId(dockspace_id, redock_all ? ImGuiCond_Always : ImGuiCond_FirstUseEver); + ImGui::SetNextWindowDockID(dockspace_id, redock_all ? ImGuiCond_Always : ImGuiCond_FirstUseEver); ImGuiWindowFlags window_flags = (doc->Dirty ? ImGuiWindowFlags_UnsavedDocument : 0); bool visible = ImGui::Begin(doc->Name, &doc->Open, window_flags); From 0cabe4dedfb5a8784ffac3e8f974a63fd0fd8dd3 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 2 Jan 2019 19:29:33 +0100 Subject: [PATCH 394/828] Viewport: Added ImGuiWindowClass / SetNextWindowClass() (concept imported from Docking ImGuiDockFamily), which currently allows to overwrite viewport flags on a per-window basis. Exposed FindViewportByID(). Win32: Support for ParentViewportId. (#1542) --- examples/imgui_impl_win32.cpp | 5 ++++- imgui.cpp | 20 ++++++++++++++++---- imgui.h | 18 +++++++++++++++++- imgui_internal.h | 4 +++- 4 files changed, 40 insertions(+), 7 deletions(-) diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index b2134b30ba0b..9d290fd05416 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -454,8 +454,11 @@ static void ImGui_ImplWin32_CreateWindow(ImGuiViewport* viewport) viewport->PlatformUserData = data; // Select style and parent window - HWND parent_window = g_hWnd; ImGui_ImplWin32_GetWin32StyleFromViewportFlags(viewport->Flags, &data->DwStyle, &data->DwExStyle); + HWND parent_window = NULL; + if (viewport->ParentViewportId != 0) + if (ImGuiViewport* parent_viewport = ImGui::FindViewportByID(viewport->ParentViewportId)) + parent_window = (HWND)parent_viewport->PlatformHandle; // Create window RECT rect = { (LONG)viewport->Pos.x, (LONG)viewport->Pos.y, (LONG)(viewport->Pos.x + viewport->Size.x), (LONG)(viewport->Pos.y + viewport->Size.y) }; diff --git a/imgui.cpp b/imgui.cpp index 681d8210b91f..0902ea3bf736 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4982,6 +4982,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) { window->SizeContentsExplicit = ImVec2(0.0f, 0.0f); } + window->WindowClass = g.NextWindowData.WindowClass; if (g.NextWindowData.CollapsedCond) SetWindowCollapsed(window, g.NextWindowData.CollapsedVal, g.NextWindowData.CollapsedCond); if (g.NextWindowData.FocusCond) @@ -5180,6 +5181,11 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if ((g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsDecoration) == 0 || (flags & (ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) != 0) viewport_flags |= ImGuiViewportFlags_NoDecoration; + // We can overwrite viewport flags using ImGuiWindowClass (advanced users) + window->Viewport->ParentViewportId = window->WindowClass.ParentViewportId ? window->WindowClass.ParentViewportId : IMGUI_VIEWPORT_DEFAULT_ID; + if (window->WindowClass.ViewportFlagsOverrideMask) + viewport_flags = (viewport_flags & ~window->WindowClass.ViewportFlagsOverrideMask) | (window->WindowClass.ViewportFlagsOverrideValue & window->WindowClass.ViewportFlagsOverrideMask); + // We also tell the back-end that clearing the platform window won't be necessary, as our window is filling the viewport and we have disabled BgAlpha viewport_flags |= ImGuiViewportFlags_NoRendererClear; window->Viewport->Flags = viewport_flags; @@ -6343,6 +6349,12 @@ void ImGui::SetNextWindowViewport(ImGuiID id) g.NextWindowData.ViewportId = id; } +void ImGui::SetNextWindowClass(const ImGuiWindowClass* window_class) +{ + ImGuiContext& g = *GImGui; + g.NextWindowData.WindowClass = *window_class; +} + // In window space (not screen space!) ImVec2 ImGui::GetContentRegionMax() { @@ -7253,7 +7265,7 @@ ImGuiViewport* ImGui::GetMainViewport() return g.Viewports[0]; } -ImGuiViewportP* ImGui::FindViewportByID(ImGuiID id) +ImGuiViewport* ImGui::FindViewportByID(ImGuiID id) { ImGuiContext& g = *GImGui; for (int n = 0; n < g.Viewports.Size; n++) @@ -7493,7 +7505,7 @@ static void ImGui::UpdateViewportsNewFrame() ImGuiViewportP* viewport_hovered = NULL; if (g.IO.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport) { - viewport_hovered = g.IO.MouseHoveredViewport ? FindViewportByID(g.IO.MouseHoveredViewport) : NULL; + viewport_hovered = g.IO.MouseHoveredViewport ? (ImGuiViewportP*)FindViewportByID(g.IO.MouseHoveredViewport) : NULL; if (viewport_hovered && (viewport_hovered->Flags & ImGuiViewportFlags_NoInputs)) { // Back-end failed at honoring its contract if it returned a viewport with the _NoInputs flag. @@ -7646,7 +7658,7 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) // Attempt to restore saved viewport id (= window that hasn't been activated yet), try to restore the viewport based on saved 'window->ViewportPos' restored from .ini file if (window->Viewport == NULL && window->ViewportId != 0) { - window->Viewport = FindViewportByID(window->ViewportId); + window->Viewport = (ImGuiViewportP*)FindViewportByID(window->ViewportId); if (window->Viewport == NULL && window->ViewportPos.x != FLT_MAX && window->ViewportPos.y != FLT_MAX) window->Viewport = AddUpdateViewport(window, window->ID, window->ViewportPos, window->Size, ImGuiViewportFlags_None); } @@ -7655,7 +7667,7 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) if (g.NextWindowData.ViewportCond) { // Code explicitly request a viewport - window->Viewport = FindViewportByID(g.NextWindowData.ViewportId); + window->Viewport = (ImGuiViewportP*)FindViewportByID(g.NextWindowData.ViewportId); window->ViewportId = g.NextWindowData.ViewportId; // Store ID even if Viewport isn't resolved yet. } else if ((flags & ImGuiWindowFlags_ChildWindow) || (flags & ImGuiWindowFlags_ChildMenu)) diff --git a/imgui.h b/imgui.h index 6d7f8dd223d1..8154df6e0f6e 100644 --- a/imgui.h +++ b/imgui.h @@ -114,6 +114,7 @@ struct ImGuiStyle; // Runtime data for styling/colors struct ImGuiTextFilter; // Helper to parse and apply text filters (e.g. "aaaaa[,bbbb][,ccccc]") struct ImGuiTextBuffer; // Helper to hold and append into a text buffer (~string builder) struct ImGuiViewport; // Viewport (generally ~1 per window to output to at the OS level. Need per-platform support to use multiple viewports) +struct ImGuiWindowClass; // Window class (rare/advanced uses: provide hints to the platform back-end via altered viewport flags and parent/child info) // Typedefs and Enums/Flags (declared as int for compatibility with old C++, to allow using as flags and to not pollute the top of this file) // Use your programming IDE "Go to definition" facility on the names of the center columns to find the actual flags/enum lists. @@ -276,6 +277,7 @@ namespace ImGui IMGUI_API void SetNextWindowFocus(); // set next window to be focused / front-most. call before Begin() IMGUI_API void SetNextWindowBgAlpha(float alpha); // set next window background color alpha. helper to easily modify ImGuiCol_WindowBg/ChildBg/PopupBg. you may also use ImGuiWindowFlags_NoBackground. IMGUI_API void SetNextWindowViewport(ImGuiID viewport_id); // set next window viewport + IMGUI_API void SetNextWindowClass(const ImGuiWindowClass* window_class); // set next window class (rare/advanced uses: provide hints to the platform back-end via altered viewport flags and parent/child info) IMGUI_API void SetWindowPos(const ImVec2& pos, ImGuiCond cond = 0); // (not recommended) set current window position - call within Begin()/End(). prefer using SetNextWindowPos(), as this may incur tearing and side-effects. IMGUI_API void SetWindowSize(const ImVec2& size, ImGuiCond cond = 0); // (not recommended) set current window size - call within Begin()/End(). set to ImVec2(0,0) to force an auto-fit. prefer using SetNextWindowSize(), as this may incur tearing and minor side-effects. IMGUI_API void SetWindowCollapsed(bool collapsed, ImGuiCond cond = 0); // (not recommended) set current window collapsed state. prefer using SetNextWindowCollapsed(). @@ -688,6 +690,7 @@ namespace ImGui IMGUI_API void UpdatePlatformWindows(); // call in main loop. will call CreateWindow/ResizeWindow/etc. platform functions for each secondary viewport, and DestroyWindow for each inactive viewport. IMGUI_API void RenderPlatformWindowsDefault(void* platform_arg = NULL, void* renderer_arg = NULL); // call in main loop. will call RenderWindow/SwapBuffers platform functions for each secondary viewport. may be reimplemented by user for custom rendering needs. IMGUI_API void DestroyPlatformWindows(); // call DestroyWindow platform functions for all viewports. call from back-end Shutdown() if you need to close platform windows before imgui shutdown. otherwise will be called by DestroyContext(). + IMGUI_API ImGuiViewport* FindViewportByID(ImGuiID id); // this is a helper for back-ends. IMGUI_API ImGuiViewport* FindViewportByPlatformHandle(void* platform_handle); // this is a helper for back-ends. the type platform_handle is decided by the back-end (e.g. HWND, MyWindow*, GLFWwindow* etc.) } // namespace ImGui @@ -1430,6 +1433,18 @@ struct ImGuiPayload bool IsDelivery() const { return Delivery; } }; +// [BETA] Rarely used / very advanced uses only. Use with SetNextWindowClass() function. +// Provide hints to the platform back-end via altered viewport flags (enable/disable OS decoration, OS task bar icons, etc.) and OS level parent/child relationships. +struct ImGuiWindowClass +{ + ImGuiID ClassId; // User data. 0 = Default class (unclassed) + ImGuiID ParentViewportId; // Hint for the platform back-end. If non-zero, the platform back-end can create a parent<>child relationship between the platform windows. Not conforming back-ends are free to e.g. parent every viewport to the main viewport or not. + ImGuiViewportFlags ViewportFlagsOverrideMask; // Viewport flags to override when a window of this class owns a viewport. This allows you to enforce OS decoration or task bar icon, override the defaults on a per-window basis. + ImGuiViewportFlags ViewportFlagsOverrideValue; // Viewport flags values to override when a window of this class owns a viewport. + + ImGuiWindowClass() { ClassId = 0; ParentViewportId = 0; ViewportFlagsOverrideMask = ViewportFlagsOverrideValue = 0x00; } +}; + //----------------------------------------------------------------------------- // Obsolete functions (Will be removed! Read 'API BREAKING CHANGES' section in imgui.cpp for details) //----------------------------------------------------------------------------- @@ -2235,6 +2250,7 @@ struct ImGuiViewport ImVec2 Size; // Size of viewport in pixel float DpiScale; // 1.0f = 96 DPI = No extra scale ImDrawData* DrawData; // The ImDrawData corresponding to this viewport. Valid after Render() and until the next call to NewFrame(). + ImGuiID ParentViewportId; // (Advanced) 0: no parent. Instruct the platform back-end to setup a parent/child relationship between platform windows. void* RendererUserData; // void* to hold custom data structure for the renderer (e.g. swap chain, frame-buffers etc.) void* PlatformUserData; // void* to hold custom data structure for the OS / platform (e.g. windowing info, render context) @@ -2243,7 +2259,7 @@ struct ImGuiViewport bool PlatformRequestMove; // Platform window requested move (e.g. window was moved by the OS / host window manager, authoritative position will be OS window position) bool PlatformRequestResize; // Platform window requested resize (e.g. window was resized by the OS / host window manager, authoritative size will be OS window size) - ImGuiViewport() { ID = 0; Flags = 0; DpiScale = 0.0f; DrawData = NULL; RendererUserData = PlatformUserData = PlatformHandle = NULL; PlatformRequestClose = PlatformRequestMove = PlatformRequestResize = false; } + ImGuiViewport() { ID = 0; Flags = 0; DpiScale = 0.0f; DrawData = NULL; ParentViewportId = 0; RendererUserData = PlatformUserData = PlatformHandle = NULL; PlatformRequestClose = PlatformRequestMove = PlatformRequestResize = false; } ~ImGuiViewport() { IM_ASSERT(PlatformUserData == NULL && RendererUserData == NULL); } }; diff --git a/imgui_internal.h b/imgui_internal.h index fee712b9ae15..16f5dae161dd 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -714,6 +714,7 @@ struct ImGuiNextWindowData void* SizeCallbackUserData; float BgAlphaVal; ImGuiID ViewportId; + ImGuiWindowClass WindowClass; ImVec2 MenuBarOffsetMinVal; // This is not exposed publicly, so we don't clear it. ImGuiNextWindowData() @@ -733,6 +734,7 @@ struct ImGuiNextWindowData void Clear() { PosCond = SizeCond = ContentSizeCond = CollapsedCond = SizeConstraintCond = FocusCond = BgAlphaCond = ViewportCond = 0; + WindowClass = ImGuiWindowClass(); } }; @@ -1121,6 +1123,7 @@ struct IMGUI_API ImGuiWindow char* Name; ImGuiID ID; // == ImHash(Name) ImGuiWindowFlags Flags, FlagsPreviousFrame; // See enum ImGuiWindowFlags_ + ImGuiWindowClass WindowClass; // Advanced users only. Set with SetNextWindowClass() ImGuiViewportP* Viewport; // Always set in Begin(), only inactive windows may have a NULL value here ImGuiID ViewportId; // We backup the viewport id (since the viewport may disappear or never be created if the window is inactive) ImVec2 ViewportPos; // We backup the viewport position (since the viewport may disappear or never be created if the window is inactive) @@ -1331,7 +1334,6 @@ namespace ImGui IMGUI_API void UpdateMouseMovingWindow(); // Viewports - IMGUI_API ImGuiViewportP* FindViewportByID(ImGuiID id); IMGUI_API void ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale); IMGUI_API void DestroyPlatformWindow(ImGuiViewportP* viewport); IMGUI_API void ShowViewportThumbnails(); From 5aebfedfadcfb071b8c7ecf164e0422d6adc949a Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 2 Jan 2019 21:41:00 +0100 Subject: [PATCH 395/828] Docking: Forward WindowClass from node to host window. --- imgui.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/imgui.cpp b/imgui.cpp index af0f2fa6396d..fc561fc7e7dc 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10952,6 +10952,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) } if (node->InitFromFirstWindowViewport && node->Windows.Size > 0) SetNextWindowViewport(node->Windows[0]->ViewportId); + SetNextWindowClass(&node->WindowClass); // Begin into the host window char window_label[20]; From c3efccaa9ca798bc545dc93b8e79972dcb382bfd Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 2 Jan 2019 23:49:31 +0100 Subject: [PATCH 396/828] Docking: Merge fix duplicate line + added assert to ease debugging. --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index e697ceab81bc..a37cc8fcb9fe 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3513,7 +3513,6 @@ void ImGui::NewFrame() // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering) UpdateMouseMovingWindowNewFrame(); - UpdateHoveredWindowAndCaptureFlags(); // Background darkening/whitening if (GetFrontMostPopupModal() != NULL || (g.NavWindowingTarget != NULL && g.NavWindowingHighlightAlpha > 0.0f)) @@ -10316,6 +10315,7 @@ void ImGui::DockContextBuildAddWindowsToNodes(ImGuiContext* ctx, ImGuiID root_id void ImGui::DockContextQueueDock(ImGuiContext* ctx, ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, float split_ratio, bool split_outer) { + IM_ASSERT(target != payload); ImGuiDockRequest req; req.Type = ImGuiDockRequestType_Dock; req.DockTargetWindow = target; From 599a52629a19ff7fdf313a81e2f80871750eec55 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 3 Jan 2019 17:43:15 +0100 Subject: [PATCH 397/828] Viewport: Added minimum viable information in the Changelog. --- docs/CHANGELOG.txt | 41 +++++++++++++++++++++++++++++++++++++++++ imgui.cpp | 6 ++---- 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index c537244600ec..8e427fb477c8 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -29,6 +29,46 @@ HOW TO UPDATE? - Please report any issue! +----------------------------------------------------------------------- + VIEWPORT BRANCH (In Progress) +----------------------------------------------------------------------- + +Breaking Changes: + +- IMPORTANT: When multi-viewports are enabled (with io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable), + all coordinates/positions will be in your natural OS coordinates space. It means that: + - Reference to hard-coded positions such as in SetNextWindowPos(ImVec2(0,0)) are _probably_ not what you want anymore. + Use GetMainViewport()->Pos to offset hard-coded positions, e.g. SetNextWindowPos(GetMainViewport()->Pos). + - Likewise io.MousePos and GetMousePos() will use OS coordinates. + If you query mouse positions to interact with non-imgui coordinates you will need to offset them. + e.g. subtract GetWindowViewport()->Pos. +- IO: Moved IME support functions from io.ImeSetInputScreenPosFn, io.ImeWindowHandle to the PlatformIO api. +- IO: Removed io.DisplayVisibleMin, io.DisplayVisibleMax settings (they were marked obsoleted, used to clip within the (0,0)..(DisplaySize) range). + + +Other changes: +(FIXME: This need a fuller explanation!) + +- Added ImGuiPlatformIO structure and GetPlatformIO(). + Similarly to ImGuiIO and GetIO(), this structure is the main point of communication for back-ends supporting multi-viewports. +- Added ImGuiPlatformMonitor to feed OS monitor information in the ImGuiPlatformIO::Monitors. +- Added GetMainViewport(). +- Added GetWindowViewport(), SetNextWindowViewport(). +- Added GetWindowDpiScale(). +- Added GetOverlayDrawList(ImGuiViewport* viewport). + The no-parameter version of GetOverlayDrawList() return the overlay for the current window's viewport. +- Added UpdatePlatformWindows(), RenderPlatformWindows(), DestroyPlatformWindows() for usage for application core. +- Added FindViewportByID(), FindViewportByPlatformHandle() for usage by back-ends. +- Added ImGuiConfigFlags_ViewportsEnable configuration flag and other viewport options. +- Added ImGuiBackendFlags_PlatformHasViewports, ImGuiBackendFlags_RendererHasViewports, ImGuiBackendFlags_HasMouseHoveredViewport backend flags. +- Added io.MainViewport, io.Viewports, io.MouseHoveredViewport (MouseHoveredViewport is optional _even_ for multi-viewport support). +- Added ImGuiViewport structure, ImGuiViewportFlags flags. +- Added ImGuiWindowClass and SetNextWindowClass() for passing viewport related hints to the OS/platform back-end. +- Examples: Renderer: OpenGL2, OpenGL3, DirectX11, DirectX12, Vulkan: Added support for multi-viewports. +- Examples: Platforms: Win32, GLFW, SDL2: Added support for multi-viewports. + Note that Linux/Mac still have inconsistent support for multi-viewports. If you want to help see https://github.com/ocornut/imgui/issues/2117. + + ----------------------------------------------------------------------- VERSION 1.67 (In Progress) ----------------------------------------------------------------------- @@ -41,6 +81,7 @@ Breaking Changes: undesirable side-effect as the window would have ID zero. In particular it is causing problems in viewport/docking branches. Other Changes: + - Added BETA api for Tab Bar/Tabs widgets: (#261, #351) - Added BeginTabBar(), EndTabBar(), BeginTabItem(), EndTabItem(), SetTabItemClosed() API. - Added ImGuiTabBarFlags flags for BeginTabBar(). diff --git a/imgui.cpp b/imgui.cpp index 310a55fdea7b..763d58d2ea26 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -365,15 +365,13 @@ CODE You can read releases logs https://github.com/ocornut/imgui/releases for more details. (Viewport Branch) - - 2018/XX/XX (1.XX) - examples: the examples imgui_impl_xxx files have been split to separate platform (Win32, Glfw, SDL2, etc.) from renderer (DX11, OpenGL, Vulkan, etc.) - when adopting new bindings follow the code in examples/ to know which functions to call. - 2018/XX/XX (1.XX) - when multi-viewports are enabled, all positions will be in your natural OS coordinates space. It means that: - reference to hard-coded positions such as in SetNextWindowPos(ImVec2(0,0)) are probably not what you want anymore. you may use GetMainViewport()->Pos to offset hard-coded positions, e.g. SetNextWindowPos(GetMainViewport()->Pos) - - likewise io.MousePos and GetMousePos() will use OS coordinates coordinates. + - likewise io.MousePos and GetMousePos() will use OS coordinates. If you query mouse positions to interact with non-imgui coordinates you will need to offset them, e.g. subtract GetWindowViewport()->Pos. - 2018/XX/XX (1.XX) - Moved IME support functions from io.ImeSetInputScreenPosFn, io.ImeWindowHandle to the PlatformIO api. - - 2018/XX/XX (1.XX) - removed io.DisplayVisibleMin, io.DisplayVisibleMax settings (it was used to clip within the DisplayMin..DisplayMax range, I don't know of anyone using it) + - 2018/XX/XX (1.XX) - removed io.DisplayVisibleMin, io.DisplayVisibleMax settings (they were used to clip within the (0,0)..DisplaySize range, I don't know of anyone using it) - 2018/12/10 (1.67) - renamed io.ConfigResizeWindowsFromEdges to io.ConfigWindowsResizeFromEdges as we are doing a large pass on configuration flags. From 05bc323be0aaf31704a7f959160f73b64abef284 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 3 Jan 2019 20:16:40 +0100 Subject: [PATCH 398/828] Viewport: Fixed minimization of main viewport leading to it being omitted from platform_io.Viewport list where the users assume it is at index 0. Fix d8ab2c1ac. It wasn't a problem when other viewports were child of the main viewport because they would all be minimized together. (#1542) --- imgui.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 763d58d2ea26..cae05b0e416f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7419,7 +7419,6 @@ static void ImGui::UpdateViewportsNewFrame() ImGuiContext& g = *GImGui; IM_ASSERT(g.PlatformIO.Viewports.Size <= g.Viewports.Size); - // Update main viewport with current platform position and size ImGuiViewportP* main_viewport = g.Viewports[0]; IM_ASSERT(main_viewport->ID == IMGUI_VIEWPORT_DEFAULT_ID); IM_ASSERT(main_viewport->Window == NULL); @@ -7461,9 +7460,6 @@ static void ImGui::UpdateViewportsNewFrame() const bool platform_funcs_available = (n == 0 || viewport->PlatformWindowCreated); if ((g.ConfigFlagsForFrame & ImGuiConfigFlags_ViewportsEnable)) { - if (g.PlatformIO.Platform_GetWindowMinimized && platform_funcs_available) - viewport->PlatformWindowMinimized = g.PlatformIO.Platform_GetWindowMinimized(viewport); - // Update Position and Size (from Platform Window to ImGui) if requested. // We do it early in the frame instead of waiting for UpdatePlatformWindows() to avoid a frame of lag when moving/resizing using OS facilities. if (!viewport->PlatformWindowMinimized && platform_funcs_available) @@ -7577,7 +7573,8 @@ static void ImGui::UpdateViewportsEndFrame() ImGuiViewportP* viewport = g.Viewports[i]; viewport->LastPos = viewport->Pos; if (viewport->LastFrameActive < g.FrameCount || viewport->Size.x <= 0.0f || viewport->Size.y <= 0.0f) - continue; + if (i > 0) // Always include main viewport in the list + continue; if (viewport->Window && !IsWindowActiveAndVisible(viewport->Window)) continue; if (i > 0) From 606175b98fd1e444e35f901473558d87dfbe7b5b Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 3 Jan 2019 20:43:28 +0100 Subject: [PATCH 399/828] Viewport: Fix for minimization of individual viewports (the current back-end forcing a parent/child relationship between secondary viewports and the main viewport have hidden this issue). Follows d8ab2c1ac. --- imgui.cpp | 20 ++++++++++++++++---- imgui_internal.h | 2 +- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index cae05b0e416f..e65a74ecc9ab 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5169,7 +5169,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip) window->Pos = FindBestWindowPosForPopup(window); - if (window->ViewportAllowPlatformMonitorExtend >= 0 && !window->ViewportOwned) + if (window->ViewportAllowPlatformMonitorExtend >= 0 && !window->ViewportOwned && !window->Viewport->PlatformWindowMinimized) if (!window->Viewport->GetRect().Contains(window->Rect())) { // Late create viewport, based on the assumption that with our calculations, the DPI will be known ahead (same as the DPI of the selection done in UpdateSelectWindowViewport) @@ -7419,13 +7419,25 @@ static void ImGui::UpdateViewportsNewFrame() ImGuiContext& g = *GImGui; IM_ASSERT(g.PlatformIO.Viewports.Size <= g.Viewports.Size); + // Update Minimized status (we need it first in order to decide if we'll apply Pos/Size of the main viewport) + for (int n = 0; n < g.Viewports.Size; n++) + { + ImGuiViewportP* viewport = g.Viewports[n]; + const bool platform_funcs_available = (n == 0 || viewport->PlatformWindowCreated); + if ((g.ConfigFlagsForFrame & ImGuiConfigFlags_ViewportsEnable)) + if (g.PlatformIO.Platform_GetWindowMinimized && platform_funcs_available) + viewport->PlatformWindowMinimized = g.PlatformIO.Platform_GetWindowMinimized(viewport); + } + + // Create/update main viewport with current platform position and size ImGuiViewportP* main_viewport = g.Viewports[0]; IM_ASSERT(main_viewport->ID == IMGUI_VIEWPORT_DEFAULT_ID); IM_ASSERT(main_viewport->Window == NULL); ImVec2 main_viewport_platform_pos = ImVec2(0.0f, 0.0f); - if ((g.ConfigFlagsForFrame & ImGuiConfigFlags_ViewportsEnable)) - main_viewport_platform_pos = g.PlatformIO.Platform_GetWindowPos(main_viewport); - AddUpdateViewport(NULL, IMGUI_VIEWPORT_DEFAULT_ID, main_viewport_platform_pos, g.IO.DisplaySize, ImGuiViewportFlags_CanHostOtherWindows); + ImVec2 main_viewport_platform_size = g.IO.DisplaySize; + if (g.ConfigFlagsForFrame & ImGuiConfigFlags_ViewportsEnable) + main_viewport_platform_pos = main_viewport->PlatformWindowMinimized ? main_viewport->Pos : g.PlatformIO.Platform_GetWindowPos(main_viewport); + AddUpdateViewport(NULL, IMGUI_VIEWPORT_DEFAULT_ID, main_viewport_platform_pos, main_viewport_platform_size, ImGuiViewportFlags_CanHostOtherWindows); g.CurrentViewport = NULL; g.MouseViewport = NULL; diff --git a/imgui_internal.h b/imgui_internal.h index 7f7af12ae9db..1df0acf870b2 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -664,7 +664,7 @@ struct ImGuiViewportP : public ImGuiViewport float LastAlpha; short PlatformMonitor; bool PlatformWindowCreated; - bool PlatformWindowMinimized; + bool PlatformWindowMinimized; // When minimized we tend to avoid using the viewport pos/size for clipping window or testing if they are contained in the viewport ImGuiWindow* Window; // Set when the viewport is owned by a window (and ImGuiViewportFlags_CanHostOtherWindows is NOT set) ImDrawList* OverlayDrawList; // For convenience, a draw list we can render to that's always rendered last (we use it to draw software mouse cursor when io.MouseDrawCursor is set) ImDrawData DrawDataP; From c8349d33052e6e65ee2b539f7ad400e4967c7fa4 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 3 Jan 2019 20:47:58 +0100 Subject: [PATCH 400/828] Viewport: Added ConfigViewportsNoParent to parent viewport default to NULL and not main viewport. Fix eg.. popups appearing erroneously focusing parent window. --- imgui.cpp | 10 ++++++++-- imgui.h | 1 + imgui_demo.cpp | 3 +++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index e65a74ecc9ab..62923d409487 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1145,6 +1145,7 @@ ImGuiIO::ImGuiIO() DisplayFramebufferScale = ImVec2(1.0f, 1.0f); // Miscellaneous configuration options + ConfigViewportsNoParent = false; #ifdef __APPLE__ ConfigMacOSXBehaviors = true; // Set Mac OS X style defaults based on __APPLE__ compile time flag #else @@ -5201,7 +5202,11 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) viewport_flags |= ImGuiViewportFlags_NoDecoration; // We can overwrite viewport flags using ImGuiWindowClass (advanced users) - window->Viewport->ParentViewportId = window->WindowClass.ParentViewportId ? window->WindowClass.ParentViewportId : IMGUI_VIEWPORT_DEFAULT_ID; + // We don't default to the main viewport because. + if (window->WindowClass.ParentViewportId) + window->Viewport->ParentViewportId = window->WindowClass.ParentViewportId; + else + window->Viewport->ParentViewportId = g.IO.ConfigViewportsNoParent ? 0 : IMGUI_VIEWPORT_DEFAULT_ID; if (window->WindowClass.ViewportFlagsOverrideMask) viewport_flags = (viewport_flags & ~window->WindowClass.ViewportFlagsOverrideMask) | (window->WindowClass.ViewportFlagsOverrideValue & window->WindowClass.ViewportFlagsOverrideMask); @@ -5476,6 +5481,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) { window->Viewport->PlatformRequestClose = false; g.NavWindowingToggleLayer = false; // Assume user mapped PlatformRequestClose on ALT-F4 so we disable ALT for menu toggle. False positive not an issue. + //IMGUI_DEBUG_LOG("Window '%s' PlatformRequestClose\n", window->Name); *p_open = false; } @@ -10307,7 +10313,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) static void NodeViewport(ImGuiViewportP* viewport) { ImGui::SetNextTreeNodeOpen(true, ImGuiCond_Once); - if (ImGui::TreeNode((void*)(intptr_t)viewport->ID, "Viewport #%d, ID: 0x%08X, Window: \"%s\"", viewport->Idx, viewport->ID, viewport->Window ? viewport->Window->Name : "N/A")) + if (ImGui::TreeNode((void*)(intptr_t)viewport->ID, "Viewport #%d, ID: 0x%08X, Parent: 0x%08X, Window: \"%s\"", viewport->Idx, viewport->ID, viewport->ParentViewportId, viewport->Window ? viewport->Window->Name : "N/A")) { ImGuiWindowFlags flags = viewport->Flags; ImGui::BulletText("Pos: (%.0f,%.0f), Size: (%.0f,%.0f), Monitor: %d, DpiScale: %.0f%%", viewport->Pos.x, viewport->Pos.y, viewport->Size.x, viewport->Size.y, viewport->PlatformMonitor, viewport->DpiScale * 100.0f); diff --git a/imgui.h b/imgui.h index 8154df6e0f6e..168cca321c19 100644 --- a/imgui.h +++ b/imgui.h @@ -1266,6 +1266,7 @@ struct ImGuiIO // Miscellaneous configuration options bool MouseDrawCursor; // = false // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). Cannot be easily renamed to 'io.ConfigXXX' because this is frequently used by back-end implementations. + bool ConfigViewportsNoParent; // = false // By default, viewports are marked with ParentViewportId = , expecting the platform back-end to setup a parent/child relationship between the OS windows (some back-end may ignore this). Set to true if you want the default to be 0, then all viewports will be top-level OS windows. bool ConfigMacOSXBehaviors; // = defined(__APPLE__) // OS X style: Text editing cursor movement using Alt instead of Ctrl, Shortcuts using Cmd/Super instead of Ctrl, Line/Text Start and End using Cmd+Arrows instead of Home/End, Double click selects by word instead of selecting whole text, Multi-selection in lists uses Cmd/Super instead of Ctrl (was called io.OptMacOSXBehaviors prior to 1.63) bool ConfigInputTextCursorBlink; // = true // Set to false to disable blinking cursor, for users who consider it distracting. (was called: io.OptCursorBlink prior to 1.63) bool ConfigWindowsResizeFromEdges; // = true // Enable resizing of windows from their edges and from the lower-left corner. This requires (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) because it needs mouse cursor feedback. (This used to be the a per-window ImGuiWindowFlags_ResizeFromAnySide flag) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 664d93f602ad..5440e40d11b7 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -327,6 +327,9 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::SameLine(); ShowHelpMarker("Toggling this at runtime is normally unsupported (most platform back-ends won't refresh the decoration right away)."); ImGui::CheckboxFlags("io.ConfigFlags: ViewportsNoMerge", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_ViewportsNoMerge); ImGui::SameLine(); ShowHelpMarker("All floating windows will always create their own viewport and platform window."); + ImGui::Checkbox("io.ConfigViewportNoParent", &io.ConfigViewportsNoParent); + ImGui::SameLine(); ShowHelpMarker("By default, viewports are marked with ParentViewportId = , expecting the platform back-end to setup a parent/child relationship between the OS windows (some back-end may ignore this). Set to true if you want the default to be 0, then all viewports will be top-level OS windows."); + ImGui::Checkbox("io.ConfigInputTextCursorBlink", &io.ConfigInputTextCursorBlink); ImGui::SameLine(); ShowHelpMarker("Set to false to disable blinking cursor, for users who consider it distracting"); ImGui::Checkbox("io.ConfigWindowsResizeFromEdges", &io.ConfigWindowsResizeFromEdges); From e1ed27aeaa059822641d9828f9ba60d5255f0491 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 3 Jan 2019 21:27:15 +0100 Subject: [PATCH 401/828] (Breaking change) Reorganized Viewports advanced flags, moved into new io.ConfigViewportsXXX flags. Pay attention that ImGuiConfigFlags_ViewportsDecoration became ConfigViewportsNoDecoeration, so the value is inverted! (#1542) --- imgui.cpp | 14 ++++++++++---- imgui.h | 16 +++++++++------- imgui_demo.cpp | 30 +++++++++++++++++++----------- 3 files changed, 38 insertions(+), 22 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 62923d409487..f938cdb30b7a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1144,8 +1144,14 @@ ImGuiIO::ImGuiIO() FontAllowUserScaling = false; DisplayFramebufferScale = ImVec2(1.0f, 1.0f); - // Miscellaneous configuration options + // Viewport options (when ImGuiConfigFlags_ViewportsEnable is set) + ConfigViewportsNoAutoMerge = false; + ConfigViewportsNoTaskBarIcon = false; + ConfigViewportsNoDecoration = true; ConfigViewportsNoParent = false; + + // Miscellaneous options + MouseDrawCursor = false; #ifdef __APPLE__ ConfigMacOSXBehaviors = true; // Set Mac OS X style defaults based on __APPLE__ compile time flag #else @@ -5196,9 +5202,9 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) ImGuiViewportFlags viewport_flags = (window->Viewport->Flags) & ~(ImGuiViewportFlags_TopMost | ImGuiViewportFlags_NoTaskBarIcon | ImGuiViewportFlags_NoDecoration); if (flags & ImGuiWindowFlags_Tooltip) viewport_flags |= ImGuiViewportFlags_TopMost; - if ((g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsNoTaskBarIcon) != 0 || (flags & (ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) != 0) + if (g.IO.ConfigViewportsNoTaskBarIcon || (flags & (ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) != 0) viewport_flags |= ImGuiViewportFlags_NoTaskBarIcon; - if ((g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsDecoration) == 0 || (flags & (ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) != 0) + if (g.IO.ConfigViewportsNoDecoration || (flags & (ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) != 0) viewport_flags |= ImGuiViewportFlags_NoDecoration; // We can overwrite viewport flags using ImGuiWindowClass (advanced users) @@ -7348,7 +7354,7 @@ static bool ImGui::GetWindowAlwaysWantOwnViewport(ImGuiWindow* window) { // Tooltips and menus are not automatically forced into their own viewport when the NoMerge flag is set, however the multiplication of viewports makes them more likely to protude and create their own. ImGuiContext& g = *GImGui; - if ((g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsNoMerge) && (g.ConfigFlagsForFrame & ImGuiConfigFlags_ViewportsEnable)) + if (g.IO.ConfigViewportsNoAutoMerge && (g.ConfigFlagsForFrame & ImGuiConfigFlags_ViewportsEnable)) //if (!window->DockIsActive) if ((window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip)) == 0) return true; diff --git a/imgui.h b/imgui.h index 168cca321c19..3d1eda8a2b1e 100644 --- a/imgui.h +++ b/imgui.h @@ -991,11 +991,8 @@ enum ImGuiConfigFlags_ // [BETA] Viewports // When using viewports it is recommended that your default value for ImGuiCol_WindowBg is opaque (Alpha=1.0) so transition to a viewport won't be noticeable. ImGuiConfigFlags_ViewportsEnable = 1 << 10, // Viewport enable flags (require both ImGuiConfigFlags_PlatformHasViewports + ImGuiConfigFlags_RendererHasViewports set by the respective back-ends) - ImGuiConfigFlags_ViewportsNoTaskBarIcon = 1 << 11, // Disable task bars icons for all secondary viewports (will set ImGuiViewportFlags_NoTaskBarIcon on them) - ImGuiConfigFlags_ViewportsDecoration = 1 << 12, // FIXME [Broken] Enable platform decoration for all secondary viewports (will not set ImGuiViewportFlags_NoDecoration on them). This currently doesn't behave well in Windows because 1) By default the new window animation get in the way of our transitions, 2) It enable a minimum window size which tends to breaks resizing. You can workaround the later by setting style.WindowMinSize to a bigger value. - ImGuiConfigFlags_ViewportsNoMerge = 1 << 13, // All floating windows will always create their own viewport and platform window. - ImGuiConfigFlags_DpiEnableScaleViewports= 1 << 14, // FIXME-DPI: Reposition and resize imgui windows when the DpiScale of a viewport changed (mostly useful for the main viewport hosting other window). Note that resizing the main window itself is up to your application. - ImGuiConfigFlags_DpiEnableScaleFonts = 1 << 15, // FIXME-DPI: Request bitmap-scaled fonts to match DpiScale. This is a very low-quality workaround. The correct way to handle DPI is _currently_ to replace the atlas and/or fonts in the Platform_OnChangedViewport callback, but this is all early work in progress. + ImGuiConfigFlags_DpiEnableScaleViewports= 1 << 14, // [BETA: Don't use] FIXME-DPI: Reposition and resize imgui windows when the DpiScale of a viewport changed (mostly useful for the main viewport hosting other window). Note that resizing the main window itself is up to your application. + ImGuiConfigFlags_DpiEnableScaleFonts = 1 << 15, // [BETA: Don't use] FIXME-DPI: Request bitmap-scaled fonts to match DpiScale. This is a very low-quality workaround. The correct way to handle DPI is _currently_ to replace the atlas and/or fonts in the Platform_OnChangedViewport callback, but this is all early work in progress. // User storage (to allow your back-end/engine to communicate to code that may be shared between multiple projects. Those flags are not used by core ImGui) ImGuiConfigFlags_IsSRGB = 1 << 20, // Application is SRGB-aware. @@ -1264,9 +1261,14 @@ struct ImGuiIO ImFont* FontDefault; // = NULL // Font to use on NewFrame(). Use NULL to uses Fonts->Fonts[0]. ImVec2 DisplayFramebufferScale; // = (1.0f,1.0f) // For hi-dpi/retina display or other situations where window coordinates are different from framebuffer coordinates. User storage only, presently not used by ImGui. - // Miscellaneous configuration options + // Viewport options (when ImGuiConfigFlags_ViewportsEnable is set) + bool ConfigViewportsNoAutoMerge; // = false; // Set to make all floating imgui windows always create their own viewport. Otherwise, they are merged into the main host viewports when overlapping it. + bool ConfigViewportsNoTaskBarIcon; // = false // Set default OS task bar icon enable flag for secondary viewports. When a viewport doesn't want a task bar icon, ImGuiViewportFlags_NoTaskBarIcon will be set on it. + bool ConfigViewportsNoDecoration; // = true // [BETA] Set default OS window decoration enable flags for secondary viewports. When a viewport doesn't want window decorations, ImGuiViewportFlags_NoDecoration will be set on it. Enabling decoration can create subsequent issues at OS levels (e.g. minimum window size). + bool ConfigViewportsNoParent; // = false // Set default OS parenting for secondary viewports. By default, viewports are marked with ParentViewportId = , expecting the platform back-end to setup a parent/child relationship between the OS windows (some back-end may ignore this). Set to true if you want the default to be 0, then all viewports will be top-level OS windows. + + // Miscellaneous options bool MouseDrawCursor; // = false // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). Cannot be easily renamed to 'io.ConfigXXX' because this is frequently used by back-end implementations. - bool ConfigViewportsNoParent; // = false // By default, viewports are marked with ParentViewportId = , expecting the platform back-end to setup a parent/child relationship between the OS windows (some back-end may ignore this). Set to true if you want the default to be 0, then all viewports will be top-level OS windows. bool ConfigMacOSXBehaviors; // = defined(__APPLE__) // OS X style: Text editing cursor movement using Alt instead of Ctrl, Shortcuts using Cmd/Super instead of Ctrl, Line/Text Start and End using Cmd+Arrows instead of Home/End, Double click selects by word instead of selecting whole text, Multi-selection in lists uses Cmd/Super instead of Ctrl (was called io.OptMacOSXBehaviors prior to 1.63) bool ConfigInputTextCursorBlink; // = true // Set to false to disable blinking cursor, for users who consider it distracting. (was called: io.OptCursorBlink prior to 1.63) bool ConfigWindowsResizeFromEdges; // = true // Enable resizing of windows from their edges and from the lower-left corner. This requires (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) because it needs mouse cursor feedback. (This used to be the a per-window ImGuiWindowFlags_ResizeFromAnySide flag) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 5440e40d11b7..753a3812b6b8 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -320,15 +320,22 @@ void ImGui::ShowDemoWindow(bool* p_open) } ImGui::CheckboxFlags("io.ConfigFlags: NoMouseCursorChange", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_NoMouseCursorChange); ImGui::SameLine(); ShowHelpMarker("Instruct back-end to not alter mouse cursor shape and visibility."); + ImGui::CheckboxFlags("io.ConfigFlags: ViewportsEnable", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_ViewportsEnable); - ImGui::CheckboxFlags("io.ConfigFlags: ViewportsNoTaskBarIcon", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_ViewportsNoTaskBarIcon); - ImGui::SameLine(); ShowHelpMarker("Toggling this at runtime is normally unsupported (most platform back-ends won't refresh the task bar icon state right away)."); - ImGui::CheckboxFlags("io.ConfigFlags: ViewportsDecoration", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_ViewportsDecoration); - ImGui::SameLine(); ShowHelpMarker("Toggling this at runtime is normally unsupported (most platform back-ends won't refresh the decoration right away)."); - ImGui::CheckboxFlags("io.ConfigFlags: ViewportsNoMerge", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_ViewportsNoMerge); - ImGui::SameLine(); ShowHelpMarker("All floating windows will always create their own viewport and platform window."); - ImGui::Checkbox("io.ConfigViewportNoParent", &io.ConfigViewportsNoParent); - ImGui::SameLine(); ShowHelpMarker("By default, viewports are marked with ParentViewportId = , expecting the platform back-end to setup a parent/child relationship between the OS windows (some back-end may ignore this). Set to true if you want the default to be 0, then all viewports will be top-level OS windows."); + ImGui::SameLine(); ShowHelpMarker("[beta] Enable beta multi-viewports support. See ImGuiPlatformIO for details."); + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + ImGui::Indent(); + ImGui::Checkbox("io.ConfigViewportsNoAutoMerge", &io.ConfigViewportsNoAutoMerge); + ImGui::SameLine(); ShowHelpMarker("Set to make all floating imgui windows always create their own viewport. Otherwise, they are merged into the main host viewports when overlapping it."); + ImGui::Checkbox("io.ConfigViewportsNoTaskBarIcon", &io.ConfigViewportsNoTaskBarIcon); + ImGui::SameLine(); ShowHelpMarker("Toggling this at runtime is normally unsupported (most platform back-ends won't refresh the task bar icon state right away)."); + ImGui::Checkbox("io.ConfigViewportsNoDecoration", &io.ConfigViewportsNoDecoration); + ImGui::SameLine(); ShowHelpMarker("Toggling this at runtime is normally unsupported (most platform back-ends won't refresh the decoration right away)."); + ImGui::Checkbox("io.ConfigViewportsNoParent", &io.ConfigViewportsNoParent); + ImGui::SameLine(); ShowHelpMarker("Toggling this at runtime is normally unsupported (most platform back-ends won't refresh the parenting right away)."); + ImGui::Unindent(); + } ImGui::Checkbox("io.ConfigInputTextCursorBlink", &io.ConfigInputTextCursorBlink); ImGui::SameLine(); ShowHelpMarker("Set to false to disable blinking cursor, for users who consider it distracting"); @@ -2669,12 +2676,13 @@ void ImGui::ShowAboutWindow(bool* p_open) if (io.ConfigFlags & ImGuiConfigFlags_NoMouse) ImGui::Text(" NoMouse"); if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) ImGui::Text(" NoMouseCursorChange"); if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) ImGui::Text(" ViewportsEnable"); - if (io.ConfigFlags & ImGuiConfigFlags_ViewportsNoTaskBarIcon) ImGui::Text(" ViewportsNoTaskBarIcon"); - if (io.ConfigFlags & ImGuiConfigFlags_ViewportsNoMerge) ImGui::Text(" ViewportsNoMerge"); - if (io.ConfigFlags & ImGuiConfigFlags_ViewportsDecoration) ImGui::Text(" ViewportsDecoration"); if (io.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleViewports) ImGui::Text(" DpiEnableScaleViewports"); if (io.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleFonts) ImGui::Text(" DpiEnableScaleFonts"); if (io.MouseDrawCursor) ImGui::Text("io.MouseDrawCursor"); + if (io.ConfigViewportsNoAutoMerge) ImGui::Text("io.ConfigViewportsNoAutoMerge"); + if (io.ConfigViewportsNoTaskBarIcon) ImGui::Text("io.ConfigViewportsNoTaskBarIcon"); + if (io.ConfigViewportsNoDecoration) ImGui::Text("io.ConfigViewportsNoDecoration"); + if (io.ConfigViewportsNoParent) ImGui::Text("io.ConfigViewportsNoParent"); if (io.ConfigMacOSXBehaviors) ImGui::Text("io.ConfigMacOSXBehaviors"); if (io.ConfigInputTextCursorBlink) ImGui::Text("io.ConfigInputTextCursorBlink"); if (io.ConfigWindowsResizeFromEdges) ImGui::Text("io.ConfigWindowsResizeFromEdges"); From 4e98d4329b1b4d41f37ea4ee0167ac03ce63a259 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 3 Jan 2019 21:59:13 +0100 Subject: [PATCH 402/828] Comments --- imgui.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/imgui.h b/imgui.h index ac276f8b76c6..d49dd1e665b0 100644 --- a/imgui.h +++ b/imgui.h @@ -1305,9 +1305,9 @@ struct ImGuiIO // Viewport options (when ImGuiConfigFlags_ViewportsEnable is set) bool ConfigViewportsNoAutoMerge; // = false; // Set to make all floating imgui windows always create their own viewport. Otherwise, they are merged into the main host viewports when overlapping it. - bool ConfigViewportsNoTaskBarIcon; // = false // Set default OS task bar icon enable flag for secondary viewports. When a viewport doesn't want a task bar icon, ImGuiViewportFlags_NoTaskBarIcon will be set on it. - bool ConfigViewportsNoDecoration; // = true // [BETA] Set default OS window decoration enable flags for secondary viewports. When a viewport doesn't want window decorations, ImGuiViewportFlags_NoDecoration will be set on it. Enabling decoration can create subsequent issues at OS levels (e.g. minimum window size). - bool ConfigViewportsNoParent; // = false // Set default OS parenting for secondary viewports. By default, viewports are marked with ParentViewportId = , expecting the platform back-end to setup a parent/child relationship between the OS windows (some back-end may ignore this). Set to true if you want the default to be 0, then all viewports will be top-level OS windows. + bool ConfigViewportsNoTaskBarIcon; // = false // Disable default OS task bar icon flag for secondary viewports. When a viewport doesn't want a task bar icon, ImGuiViewportFlags_NoTaskBarIcon will be set on it. + bool ConfigViewportsNoDecoration; // = true // [BETA] Disable default OS window decoration flag for secondary viewports. When a viewport doesn't want window decorations, ImGuiViewportFlags_NoDecoration will be set on it. Enabling decoration can create subsequent issues at OS levels (e.g. minimum window size). + bool ConfigViewportsNoParent; // = false // Disable default OS parenting to main viewport for secondary viewports. By default, viewports are marked with ParentViewportId = , expecting the platform back-end to setup a parent/child relationship between the OS windows (some back-end may ignore this). Set to true if you want the default to be 0, then all viewports will be top-level OS windows. // Miscellaneous options bool MouseDrawCursor; // = false // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). Cannot be easily renamed to 'io.ConfigXXX' because this is frequently used by back-end implementations. From 7f7e8eeecd4d4726aae20d2238a7c92bc2b4d617 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 3 Jan 2019 22:11:14 +0100 Subject: [PATCH 403/828] Docking: Fixed a bug undocking a window from its tab when it is the only docked window of a root dockspace with ConfigDockingTabBarOnSingleWindows enabled. --- imgui_widgets.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 2d1fab21f3c8..127e1df38367 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6479,8 +6479,8 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, // FIXME-DOCK: In theory we shouldn't test for the ConfigDockingNodifySingleWindows flag here. // When our single window node and OnlyNodeWithWindows are working properly we may remove this check here. ImGuiDockNode* node = docked_window ? docked_window->DockNode : NULL; - const bool single_window_node = node && node->IsRootNode() && node->Windows.Size == 1 && g.IO.ConfigDockingTabBarOnSingleWindows; - if (held && single_window_node && IsMouseDragging(0, 0.0f)) + const bool single_floating_window_node = node && node->IsRootNode() && !node->IsDockSpace && node->Windows.Size == 1 && g.IO.ConfigDockingTabBarOnSingleWindows; + if (held && single_floating_window_node && IsMouseDragging(0, 0.0f)) { // Move StartMouseMovingWindow(docked_window); From 515ecbddc270f3129642e9f1ab8d95e3778a0b95 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 3 Jan 2019 23:02:40 +0100 Subject: [PATCH 404/828] Docking: Fix for handling of orphan/inactive dock node with ConfigDockingTabBarOnSingleWindows (would crash). --- imgui.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index dc47c4595417..fc7662050b8a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5299,6 +5299,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Position child window if (flags & ImGuiWindowFlags_ChildWindow) { + IM_ASSERT(parent_window->Active); window->BeginOrderWithinParent = (short)parent_window->DC.ChildWindows.Size; parent_window->DC.ChildWindows.push_back(window); if (!(flags & ImGuiWindowFlags_Popup) && !window_pos_set_by_api && !window_is_child_tooltip) @@ -12517,7 +12518,11 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) // Create node if (dock_node == NULL) + { dock_node = DockContextAddNode(ctx, window->DockId); + if (auto_dock_node) + dock_node->LastFrameAlive = g.FrameCount; + } DockNodeAddWindow(dock_node, window, true); IM_ASSERT(dock_node == window->DockNode); @@ -12538,7 +12543,7 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) // Undock if our dockspace node disappeared // Note how we are testing for LastFrameAlive and NOT LastFrameActive. A DockSpace node can be maintained alive while being inactive with ImGuiDockNodeFlags_KeepAliveOnly. - if (dock_node->LastFrameAlive < g.FrameCount && !auto_dock_node) + if (dock_node->LastFrameAlive < g.FrameCount) { // If the window has been orphaned, transition the docknode to an implicit node processed in DockContextUpdateDocking() ImGuiDockNode* root_node = DockNodeGetRootNode(dock_node); From 7cc86d4bc9f5b6a1590201a2381e2b72a7205529 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 10 Jan 2019 18:20:52 +0100 Subject: [PATCH 405/828] Docking: Fixed docking a split node into the empty central node of a dockspace leading to the central node tag being incorrectly carried along. (#2109) --- imgui.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 01899b628039..bece4001a4cd 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10514,6 +10514,15 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) DockNodeMoveWindows(visible_node, target_node); DockSettingsMoveDockReferencesInInactiveWindow(target_node->ID, visible_node->ID); } + if (target_node->IsCentralNode) + { + // Central node property needs to be moved to a leaf node, pick the last focused one. + ImGuiDockNode* last_focused_node = DockContextFindNodeByID(ctx, payload_node->LastFocusedNodeID); + IM_ASSERT(last_focused_node != NULL && DockNodeGetRootNode(last_focused_node) == DockNodeGetRootNode(payload_node)); + last_focused_node->IsCentralNode = true; + target_node->IsCentralNode = false; + } + IM_ASSERT(target_node->Windows.Size == 0); DockNodeMoveChildNodes(target_node, payload_node); } @@ -10837,7 +10846,7 @@ static void DockNodeUpdateScanRec(ImGuiDockNode* node, ImGuiDockNodeUpdateScanRe if (node->IsCentralNode) { IM_ASSERT(results->CentralNode == NULL); // Should be only one - IM_ASSERT(node->IsLeafNode() && "If you get this assert: your .ini file may have been damaged by an old bug. OR please submit repro of actions leading to this"); + IM_ASSERT(node->IsLeafNode() && "If you get this assert: please submit .ini file + repro of actions leading to this."); results->CentralNode = node; } if (results->CountNodesWithWindows > 1 && results->CentralNode != NULL) From 79d497edae41c127b3220e64896c650cddde175b Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 15 Jan 2019 21:20:00 +0100 Subject: [PATCH 406/828] Viewport: Made platform_io.Monitors mandatory for proper multi-viewport use. --- imgui.cpp | 3 ++- imgui.h | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 79090f7b00bd..5eb583b806c3 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3427,6 +3427,7 @@ void ImGui::NewFrame() IM_ASSERT(g.PlatformIO.Platform_SetWindowPos != NULL && "Platform init didn't install handlers?"); IM_ASSERT(g.PlatformIO.Platform_GetWindowSize != NULL && "Platform init didn't install handlers?"); IM_ASSERT(g.PlatformIO.Platform_SetWindowSize != NULL && "Platform init didn't install handlers?"); + IM_ASSERT(g.PlatformIO.Monitors.Size > 0 && "Platform init didn't setup Monitors list?"); IM_ASSERT((g.Viewports[0]->PlatformUserData != NULL || g.Viewports[0]->PlatformHandle != NULL) && "Platform init didn't setup main viewport."); #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS IM_ASSERT(g.IO.RenderDrawListsFn == NULL); // Call ImGui::Render() then pass ImGui::GetDrawData() yourself to your render function! @@ -10445,7 +10446,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::Unindent(ImGui::GetTreeNodeToLabelSpacing()); if (g.PlatformIO.Monitors.Size > 0 && ImGui::TreeNode("Monitors", "Monitors (%d)", g.PlatformIO.Monitors.Size)) { - ImGui::TextWrapped("(When viewports are enabled, imgui optionally uses monitor data to position popup/tooltips so they don't straddle monitors.)"); + ImGui::TextWrapped("(When viewports are enabled, imgui needs uses monitor data to position popup/tooltips so they don't straddle monitors.)"); for (int i = 0; i < g.PlatformIO.Monitors.Size; i++) { const ImGuiPlatformMonitor& mon = g.PlatformIO.Monitors[i]; diff --git a/imgui.h b/imgui.h index f22fd40d1077..03f5bbc5a6f7 100644 --- a/imgui.h +++ b/imgui.h @@ -2174,8 +2174,8 @@ struct ImFont // - if you are new to dear imgui and trying to integrate it into your engine, you should probably ignore this for now. //----------------------------------------------------------------------------- -// (Optional) Represent the bounds of each connected monitor/display and their DPI -// This is used for: multiple DPI support + clamping the position of popups and tooltips so they don't straddle multiple monitors. +// (Optional) This is required when enabling multi-viewport. Represent the bounds of each connected monitor/display and their DPI. +// We use this information for multiple DPI support + clamping the position of popups and tooltips so they don't straddle multiple monitors. struct ImGuiPlatformMonitor { ImVec2 MainPos, MainSize; // Coordinates of the area displayed on this monitor (Min = upper left, Max = bottom right) From 2f9bae140b8b770804053668d1a0814ce5c4fb11 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 16 Jan 2019 14:43:27 +0100 Subject: [PATCH 407/828] Docking: Demo: Fixed docking document window into parent window. (#2286) --- imgui_demo.cpp | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 5f7313614500..66528bc63e41 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -4205,24 +4205,32 @@ void ShowExampleAppDocuments(bool* p_open) { static ExampleAppDocuments app; - if (!ImGui::Begin("Example: Documents", p_open, ImGuiWindowFlags_MenuBar)) - { - ImGui::End(); - return; - } - // Options enum Target { Target_None, - Target_Tab, // Create document as a local tab into a local tab bar - Target_Window // Create document as a regular window + Target_Tab, // Create documents as local tab into a local tab bar + Target_DockspaceAndWindow // Create documents as regular windows, and create an embedded dockspace }; static Target opt_target = Target_Tab; static bool opt_reorderable = true; static ImGuiTabBarFlags opt_fitting_flags = ImGuiTabBarFlags_FittingPolicyDefault_; - // Menu + // When (opt_target == Target_DockspaceAndWindow) there is the possibily that one of our child Document window (e.g. "Eggplant") + // that we emit gets docked into the same spot as the parent window ("Example: Documents"). + // This would create a problematic feedback loop because selecting the "Eggplant" tab would make the "Example: Documents" tab + // not visible, which in turn would stop submitting the "Eggplant" window. + // We avoid this problem by submitting our documents window even if our parent window is not currently visible. + // Another solution may be to make the "Example: Documents" window use the ImGuiWindowFlags_NoDocking. + + bool window_contents_visible = ImGui::Begin("Example: Documents", p_open, ImGuiWindowFlags_MenuBar); + if (!window_contents_visible && opt_target != Target_DockspaceAndWindow) + { + ImGui::End(); + return; + } + + // Menu Bar if (ImGui::BeginMenuBar()) { if (ImGui::BeginMenu("File")) @@ -4267,8 +4275,8 @@ void ShowExampleAppDocuments(bool* p_open) ImGui::Combo("Output", (int*)&opt_target, "None\0TabBar+Tabs\0DockSpace+Window\0"); ImGui::PopItemWidth(); bool redock_all = false; - if (opt_target == Target_Tab) { ImGui::SameLine(); ImGui::Checkbox("Reorderable Tabs", &opt_reorderable); } - if (opt_target == Target_Window) { ImGui::SameLine(); redock_all = ImGui::Button("Redock all"); } + if (opt_target == Target_Tab) { ImGui::SameLine(); ImGui::Checkbox("Reorderable Tabs", &opt_reorderable); } + if (opt_target == Target_DockspaceAndWindow) { ImGui::SameLine(); redock_all = ImGui::Button("Redock all"); } ImGui::Separator(); @@ -4313,7 +4321,7 @@ void ShowExampleAppDocuments(bool* p_open) ImGui::EndTabBar(); } } - else if (opt_target == Target_Window) + else if (opt_target == Target_DockspaceAndWindow) { if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_DockingEnable) { @@ -4357,6 +4365,13 @@ void ShowExampleAppDocuments(bool* p_open) } } + // Early out other contents + if (!window_contents_visible) + { + ImGui::End(); + return; + } + // Update closing queue static ImVector close_queue; if (close_queue.empty()) From 32c4e01267efca5bc97566a6df1ef5bb83cc29f7 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 16 Jan 2019 16:10:51 +0100 Subject: [PATCH 408/828] Various tweaks and fixes as suggested by PVS Studio (thanks PVS Studio!) --- imgui.cpp | 7 ++++--- imgui_demo.cpp | 34 ++++++++++++++++++---------------- imgui_draw.cpp | 7 ++++--- imgui_widgets.cpp | 20 +++++++++++++------- 4 files changed, 39 insertions(+), 29 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 999a45db3708..d49d6584672f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5595,7 +5595,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // in order for their pos/size to be matching their undocking state.) if (!(flags & ImGuiWindowFlags_NoTitleBar) && !window->DockIsActive) { - ImU32 title_bar_col = GetColorU32(window->Collapsed ? ImGuiCol_TitleBgCollapsed : title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg); + ImU32 title_bar_col = GetColorU32(title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg); window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawCornerFlags_Top); } @@ -8840,6 +8840,7 @@ static void ImGui::NavUpdate() NavUpdateWindowing(); // Set output flags for user application + // FIXME: g.NavInitRequest is always false at this point, investigate the intent of operation done here. g.IO.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs); g.IO.NavVisible = (g.IO.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL) || g.NavInitRequest; @@ -9075,7 +9076,7 @@ static float ImGui::NavUpdatePageUpPageDown(int allowed_dir_flags) ImGuiWindow* window = g.NavWindow; bool page_up_held = IsKeyDown(g.IO.KeyMap[ImGuiKey_PageUp]) && (allowed_dir_flags & (1 << ImGuiDir_Up)); bool page_down_held = IsKeyDown(g.IO.KeyMap[ImGuiKey_PageDown]) && (allowed_dir_flags & (1 << ImGuiDir_Down)); - if ((page_up_held && !page_down_held) || (page_down_held && !page_up_held)) + if (page_up_held != page_down_held) // If either (not both) are pressed { if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll) { @@ -9761,7 +9762,7 @@ bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags) // Target can request the Source to not display its tooltip (we use a dedicated flag to make this request explicit) // We unfortunately can't just modify the source flags and skip the call to BeginTooltip, as caller may be emitting contents. BeginTooltip(); - if (g.DragDropActive && g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip)) + if (g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip)) { ImGuiWindow* tooltip_window = g.CurrentWindow; tooltip_window->SkipItems = true; diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 66528bc63e41..fb3f22cba1ae 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -924,17 +924,18 @@ static void ShowDemoWindowWidgets() } if (ImGui::TreeNode("Grid")) { - static bool selected[16] = { true, false, false, false, false, true, false, false, false, false, true, false, false, false, false, true }; - for (int i = 0; i < 16; i++) + static bool selected[4*4] = { true, false, false, false, false, true, false, false, false, false, true, false, false, false, false, true }; + for (int i = 0; i < 4*4; i++) { ImGui::PushID(i); if (ImGui::Selectable("Sailor", &selected[i], 0, ImVec2(50,50))) { - int x = i % 4, y = i / 4; - if (x > 0) selected[i - 1] ^= 1; - if (x < 3) selected[i + 1] ^= 1; - if (y > 0) selected[i - 4] ^= 1; - if (y < 3) selected[i + 4] ^= 1; + int x = i % 4; + int y = i / 4; + if (x > 0) { selected[i - 1] ^= 1; } + if (x < 3) { selected[i + 1] ^= 1; } + if (y > 0) { selected[i - 4] ^= 1; } + if (y < 3) { selected[i + 4] ^= 1; } } if ((i % 4) < 3) ImGui::SameLine(); ImGui::PopID(); @@ -1000,7 +1001,7 @@ static void ShowDemoWindowWidgets() static float values[90] = { 0 }; static int values_offset = 0; static double refresh_time = 0.0; - if (!animate || refresh_time == 0.0f) + if (!animate || refresh_time == 0.0) refresh_time = ImGui::GetTime(); while (refresh_time < ImGui::GetTime()) // Create dummy data at fixed 60 hz rate for the demo { @@ -2032,9 +2033,9 @@ static void ShowDemoWindowLayout() ImGui::EndChild(); ImGui::PopStyleVar(2); float scroll_x_delta = 0.0f; - ImGui::SmallButton("<<"); if (ImGui::IsItemActive()) scroll_x_delta = -ImGui::GetIO().DeltaTime * 1000.0f; ImGui::SameLine(); + ImGui::SmallButton("<<"); if (ImGui::IsItemActive()) { scroll_x_delta = -ImGui::GetIO().DeltaTime * 1000.0f; } ImGui::SameLine(); ImGui::Text("Scroll from code"); ImGui::SameLine(); - ImGui::SmallButton(">>"); if (ImGui::IsItemActive()) scroll_x_delta = +ImGui::GetIO().DeltaTime * 1000.0f; ImGui::SameLine(); + ImGui::SmallButton(">>"); if (ImGui::IsItemActive()) { scroll_x_delta = +ImGui::GetIO().DeltaTime * 1000.0f; } ImGui::SameLine(); ImGui::Text("%.0f/%.0f", scroll_x, scroll_max_x); if (scroll_x_delta != 0.0f) { @@ -2562,9 +2563,9 @@ static void ShowDemoWindowMisc() // Use >= 0 parameter to SetKeyboardFocusHere() to focus an upcoming item static float f3[3] = { 0.0f, 0.0f, 0.0f }; int focus_ahead = -1; - if (ImGui::Button("Focus on X")) focus_ahead = 0; ImGui::SameLine(); - if (ImGui::Button("Focus on Y")) focus_ahead = 1; ImGui::SameLine(); - if (ImGui::Button("Focus on Z")) focus_ahead = 2; + if (ImGui::Button("Focus on X")) { focus_ahead = 0; } ImGui::SameLine(); + if (ImGui::Button("Focus on Y")) { focus_ahead = 1; } ImGui::SameLine(); + if (ImGui::Button("Focus on Z")) { focus_ahead = 2; } if (focus_ahead != -1) ImGui::SetKeyboardFocusHere(focus_ahead); ImGui::SliderFloat3("Float3", &f3[0], 0.0f, 1.0f); @@ -3811,8 +3812,8 @@ static void ShowExampleAppConstrainedResize(bool* p_open) if (type == 2) ImGui::SetNextWindowSizeConstraints(ImVec2(100, 100), ImVec2(FLT_MAX, FLT_MAX)); // Width > 100, Height > 100 if (type == 3) ImGui::SetNextWindowSizeConstraints(ImVec2(400, -1), ImVec2(500, -1)); // Width 400-500 if (type == 4) ImGui::SetNextWindowSizeConstraints(ImVec2(-1, 400), ImVec2(-1, 500)); // Height 400-500 - if (type == 5) ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::Square); // Always Square - if (type == 6) ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::Step, (void*)100);// Fixed Step + if (type == 5) ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::Square); // Always Square + if (type == 6) ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::Step, (void*)(intptr_t)100); // Fixed Step ImGuiWindowFlags flags = auto_resize ? ImGuiWindowFlags_AlwaysAutoResize : 0; if (ImGui::Begin("Example: Constrained Resize", p_open, flags)) @@ -3853,6 +3854,7 @@ static void ShowExampleAppSimpleOverlay(bool* p_open) // FIXME-VIEWPORT-ABS: Select a default viewport const float DISTANCE = 10.0f; static int corner = 0; + ImGuiIO& io = ImGui::GetIO(); if (corner != -1) { ImGuiViewport* viewport = ImGui::GetMainViewport(); @@ -3867,7 +3869,7 @@ static void ShowExampleAppSimpleOverlay(bool* p_open) ImGui::Text("Simple overlay\n" "in the corner of the screen.\n" "(right-click to change position)"); ImGui::Separator(); if (ImGui::IsMousePosValid()) - ImGui::Text("Mouse Position: (%.1f,%.1f)", ImGui::GetIO().MousePos.x, ImGui::GetIO().MousePos.y); + ImGui::Text("Mouse Position: (%.1f,%.1f)", io.MousePos.x, io.MousePos.y); else ImGui::Text("Mouse Position: "); if (ImGui::BeginPopupContextWindow()) diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 5d7582397b73..4c30ccb5f4f8 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1524,7 +1524,7 @@ void ImFontAtlas::GetTexDataAsRGBA32(unsigned char** out_pixels, int* out_wid GetTexDataAsAlpha8(&pixels, NULL, NULL); if (pixels) { - TexPixelsRGBA32 = (unsigned int*)ImGui::MemAlloc((size_t)(TexWidth * TexHeight * 4)); + TexPixelsRGBA32 = (unsigned int*)ImGui::MemAlloc((size_t)TexWidth * (size_t)TexHeight * 4); const unsigned char* src = pixels; unsigned int* dst = TexPixelsRGBA32; for (int n = TexWidth * TexHeight; n > 0; n--) @@ -3026,13 +3026,14 @@ void ImGui::RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, Im const float inv_rounding = 1.0f / rounding; const float arc0_b = ImAcos01(1.0f - (p0.x - rect.Min.x) * inv_rounding); const float arc0_e = ImAcos01(1.0f - (p1.x - rect.Min.x) * inv_rounding); + const float half_pi = IM_PI * 0.5f; // We will == compare to this because we know this is the exact value ImAcos01 can return. const float x0 = ImMax(p0.x, rect.Min.x + rounding); if (arc0_b == arc0_e) { draw_list->PathLineTo(ImVec2(x0, p1.y)); draw_list->PathLineTo(ImVec2(x0, p0.y)); } - else if (arc0_b == 0.0f && arc0_e == IM_PI*0.5f) + else if (arc0_b == 0.0f && arc0_e == half_pi) { draw_list->PathArcToFast(ImVec2(x0, p1.y - rounding), rounding, 3, 6); // BL draw_list->PathArcToFast(ImVec2(x0, p0.y + rounding), rounding, 6, 9); // TR @@ -3052,7 +3053,7 @@ void ImGui::RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, Im draw_list->PathLineTo(ImVec2(x1, p0.y)); draw_list->PathLineTo(ImVec2(x1, p1.y)); } - else if (arc1_b == 0.0f && arc1_e == IM_PI*0.5f) + else if (arc1_b == 0.0f && arc1_e == half_pi) { draw_list->PathArcToFast(ImVec2(x1, p0.y + rounding), rounding, 9, 12); // TR draw_list->PathArcToFast(ImVec2(x1, p1.y - rounding), rounding, 0, 3); // BR diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 60004526888d..22278109d6c9 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -673,7 +673,7 @@ bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos, float radius) // Render ImVec2 center = bb.GetCenter(); if (hovered) - window->DrawList->AddCircleFilled(center, ImMax(2.0f, radius), GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : ImGuiCol_ButtonHovered), 9); + window->DrawList->AddCircleFilled(center, ImMax(2.0f, radius), GetColorU32(held ? ImGuiCol_ButtonActive : ImGuiCol_ButtonHovered), 9); float cross_extent = (radius * 0.7071f) - 1.0f; ImU32 cross_col = GetColorU32(ImGuiCol_Text); @@ -1697,7 +1697,7 @@ static float GetMinimumStepAtDecimalPrecision(int decimal_precision) static const float min_steps[10] = { 1.0f, 0.1f, 0.01f, 0.001f, 0.0001f, 0.00001f, 0.000001f, 0.0000001f, 0.00000001f, 0.000000001f }; if (decimal_precision < 0) return FLT_MIN; - return (decimal_precision >= 0 && decimal_precision < 10) ? min_steps[decimal_precision] : ImPow(10.0f, (float)-decimal_precision); + return (decimal_precision < IM_ARRAYSIZE(min_steps)) ? min_steps[decimal_precision] : ImPow(10.0f, (float)-decimal_precision); } template @@ -3929,9 +3929,14 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag if (n + 1 == components) PushItemWidth(w_item_last); if (flags & ImGuiColorEditFlags_Float) - value_changed = value_changed_as_float = value_changed | DragFloat(ids[n], &f[n], 1.0f/255.0f, 0.0f, hdr ? 0.0f : 1.0f, fmt_table_float[fmt_idx][n]); + { + value_changed |= DragFloat(ids[n], &f[n], 1.0f/255.0f, 0.0f, hdr ? 0.0f : 1.0f, fmt_table_float[fmt_idx][n]); + value_changed_as_float |= value_changed; + } else + { value_changed |= DragInt(ids[n], &i[n], 1.0f, 0, hdr ? 0 : 255, fmt_table_int[fmt_idx][n]); + } if (!(flags & ImGuiColorEditFlags_NoOptions)) OpenPopupOnItemClick("context"); } @@ -4034,7 +4039,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag { if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F)) { - memcpy((float*)col, payload->Data, sizeof(float) * 3); + memcpy((float*)col, payload->Data, sizeof(float) * 3); // Preserve alpha if any value_changed = true; } if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F)) @@ -4616,7 +4621,7 @@ void ImGui::ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags fl g.ColorEditOptions = (g.ColorEditOptions & ~ImGuiColorEditFlags__PickerMask) | (picker_flags & ImGuiColorEditFlags__PickerMask); SetCursorScreenPos(backup_pos); ImVec4 dummy_ref_col; - memcpy(&dummy_ref_col.x, ref_col, sizeof(float) * (picker_flags & ImGuiColorEditFlags_NoAlpha ? 3 : 4)); + memcpy(&dummy_ref_col, ref_col, sizeof(float) * ((picker_flags & ImGuiColorEditFlags_NoAlpha) ? 3 : 4)); ColorPicker4("##dummypicker", &dummy_ref_col.x, picker_flags); PopID(); } @@ -5019,7 +5024,7 @@ bool ImGui::CollapsingHeader(const char* label, bool* p_open, ImGuiTreeNodeFlags ImGuiItemHoveredDataBackup last_item_backup; float button_radius = g.FontSize * 0.5f; ImVec2 button_center = ImVec2(ImMin(window->DC.LastItemRect.Max.x, window->ClipRect.Max.x) - g.Style.FramePadding.x - button_radius, window->DC.LastItemRect.GetCenter().y); - if (CloseButton(window->GetID((void*)(intptr_t)(id+1)), button_center, button_radius)) + if (CloseButton(window->GetID((void*)((intptr_t)id+1)), button_center, button_radius)) *p_open = false; last_item_backup.Restore(); } @@ -5842,6 +5847,7 @@ ImGuiTabBar::ImGuiTabBar() ID = 0; SelectedTabId = NextSelectedTabId = VisibleTabId = 0; CurrFrameVisible = PrevFrameVisible = -1; + ContentsHeight = 0.0f; OffsetMax = OffsetNextTab = 0.0f; ScrollingAnim = ScrollingTarget = 0.0f; Flags = ImGuiTabBarFlags_None; @@ -6576,7 +6582,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, flags |= ImGuiTabItemFlags_NoCloseWithMiddleMouseButton; // Render tab label, process close button - const ImGuiID close_button_id = p_open ? window->GetID((void*)(intptr_t)(id + 1)) : 0; + const ImGuiID close_button_id = p_open ? window->GetID((void*)((intptr_t)id + 1)) : 0; bool just_closed = TabItemLabelAndCloseButton(display_draw_list, bb, flags, label, id, close_button_id); if (just_closed) { From d1851ed6b7f7a350f50a275bbbfd2b61d5553111 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 16 Jan 2019 16:19:38 +0100 Subject: [PATCH 409/828] Various tweaks and fixes as suggested by PVS Studio (thanks PVS Studio!) [docking branch] --- imgui.cpp | 18 ++++++++---------- imgui_internal.h | 1 + imgui_widgets.cpp | 2 +- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index d49d6584672f..438f89faf4c1 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5064,10 +5064,11 @@ void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags window->ParentWindow = parent_window; window->RootWindow = window->RootWindowDockStop = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window; if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip)) + { window->RootWindow = parent_window->RootWindow; - if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip)) if (!window->DockIsActive && !(parent_window->Flags & ImGuiWindowFlags_DockNodeHost)) window->RootWindowDockStop = parent_window->RootWindowDockStop; + } if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup))) window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight; while (window->RootWindowForNav->Flags & ImGuiWindowFlags_NavFlattened) @@ -10507,12 +10508,9 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) ImGuiDockNode* inheritor_node = target_node->ChildNodes[split_inheritor_child_idx]; ImGuiDockNode* new_node = target_node->ChildNodes[split_inheritor_child_idx ^ 1]; new_node->HostWindow = target_node->HostWindow; - if (target_node) - { - inheritor_node->IsCentralNode = target_node->IsCentralNode; - inheritor_node->IsHiddenTabBar = target_node->IsHiddenTabBar; - target_node->IsCentralNode = false; - } + inheritor_node->IsCentralNode = target_node->IsCentralNode; + inheritor_node->IsHiddenTabBar = target_node->IsHiddenTabBar; + target_node->IsCentralNode = false; target_node = new_node; } target_node->IsHiddenTabBar = false; @@ -11453,7 +11451,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w static bool DockNodeIsDropAllowedOne(ImGuiWindow* payload, ImGuiWindow* host_window) { - if ((host_window->Flags & ImGuiWindowFlags_DockNodeHost) && host_window->DockNodeAsHost->IsDockSpace && payload->BeginOrderWithinContext < host_window->BeginOrderWithinContext) + if (host_window->DockNodeAsHost && host_window->DockNodeAsHost->IsDockSpace && payload->BeginOrderWithinContext < host_window->BeginOrderWithinContext) return false; ImGuiWindowClass* host_class = host_window->DockNodeAsHost ? &host_window->DockNodeAsHost->WindowClass : &host_window->WindowClass; @@ -12776,7 +12774,7 @@ void ImGui::BeginAsDockableDragDropTarget(ImGuiWindow* window) DockNodePreviewDockRender(window, target_node, payload_window, &split_outer); // Queue docking request - if (split_data && split_data->IsDropAllowed && payload->IsDelivery()) + if (split_data->IsDropAllowed && payload->IsDelivery()) DockContextQueueDock(ctx, window, split_data->SplitNode, payload_window, split_data->SplitDir, split_data->SplitRatio, split_data == &split_outer); } } @@ -13057,7 +13055,7 @@ void ImGui::LogToFile(int max_depth, const char* filename) IM_ASSERT(g.LogFile == NULL); g.LogFile = ImFileOpen(filename, "ab"); - if (!g.LogFile) + if (g.LogFile == NULL) { IM_ASSERT(g.LogFile != NULL); // Consider this an error return; diff --git a/imgui_internal.h b/imgui_internal.h index d8655b1f958c..d62088ce5074 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1057,6 +1057,7 @@ struct ImGuiContext CurrentWindow = NULL; HoveredWindow = NULL; HoveredRootWindow = NULL; + HoveredWindowUnderMovingWindow = NULL; HoveredId = 0; HoveredIdAllowOverlap = false; HoveredIdPreviousFrame = 0; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 22278109d6c9..3832ae137b18 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6527,7 +6527,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, { // We use a variable threshold to distinguish dragging tabs within a tab bar and extracting them out of the tab bar bool undocking_tab = (g.DragDropActive && g.DragDropPayload.SourceId == id); - if (!undocking_tab && held) + if (!undocking_tab) { //if (!g.IO.ConfigDockingWithShift || g.IO.KeyShift) { From acdb4823dd9a4c71ef621935c0310180ac29531a Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 17 Jan 2019 14:35:26 +0100 Subject: [PATCH 410/828] Examples: Win32: Fix for older Windows SDK. --- examples/imgui_impl_win32.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index 230fc22b8fb9..882ef805bde1 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -411,6 +411,8 @@ typedef enum { MDT_EFFECTIVE_DPI = 0, MDT_ANGULAR_DPI = 1, MDT_RAW_DPI = 2, MDT_ #ifndef _DPI_AWARENESS_CONTEXTS_ DECLARE_HANDLE(DPI_AWARENESS_CONTEXT); #define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE (DPI_AWARENESS_CONTEXT)-3 +#endif +#ifndef DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 #define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 (DPI_AWARENESS_CONTEXT)-4 #endif typedef HRESULT(WINAPI * PFN_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS); // Shcore.lib+dll, Windows 8.1 From 2d21a64fed99f8262f4cb6bb76e7bfad19d2b545 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 21 Jan 2019 14:25:13 +0100 Subject: [PATCH 411/828] Comments --- imgui.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 998b2d15b9fb..de9456e85294 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5263,10 +5263,11 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip) window->Pos = FindBestWindowPosForPopup(window); + // Late create viewport if we don't fit within our current host viewport. if (window->ViewportAllowPlatformMonitorExtend >= 0 && !window->ViewportOwned && !window->Viewport->PlatformWindowMinimized) if (!window->Viewport->GetRect().Contains(window->Rect())) { - // Late create viewport, based on the assumption that with our calculations, the DPI will be known ahead (same as the DPI of the selection done in UpdateSelectWindowViewport) + // This is based on the assumption that the DPI will be known ahead (same as the DPI of the selection done in UpdateSelectWindowViewport) //ImGuiViewport* old_viewport = window->Viewport; window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoFocusOnAppearing); @@ -7413,7 +7414,7 @@ void ImGui::SetCurrentViewport(ImGuiWindow* current_window, ImGuiViewportP* view /* if (viewport->LastFrameActive < g.FrameCount && viewport->Window != current_window && viewport->Window != NULL && current_window != NULL) { - //IMGUI_DEBUG_LOG("[%05d] Window '%s' steal Viewport %08X from Window '%s'\n", g.FrameCount, current_window->Name, viewport->ID, viewport->Window->Name); + //IMGUI_DEBUG_LOG("Window '%s' steal Viewport %08X from Window '%s'\n", g.FrameCount, current_window->Name, viewport->ID, viewport->Window->Name); viewport->Window = current_window; viewport->ID = current_window->ID; } @@ -7423,6 +7424,7 @@ void ImGui::SetCurrentViewport(ImGuiWindow* current_window, ImGuiViewportP* view if (g.CurrentViewport == viewport) return; g.CurrentViewport = viewport; + //IMGUI_DEBUG_LOG("SetCurrentViewport changed '%s' 0x%08X\n", current_window ? current_window->Name : NULL, viewport ? viewport->ID : 0); // Notify platform layer of viewport changes // FIXME-DPI: This is only currently used for experimenting with handling of multiple DPI From c81a5a6070ee3807375de8a61210ccd35b1985cc Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 22 Jan 2019 12:38:10 +0100 Subject: [PATCH 412/828] Docking: Comments and renaming locals to facilitate debugging. --- imgui.cpp | 71 +++++++++++++++++++++++++----------------------- imgui_internal.h | 4 +-- 2 files changed, 39 insertions(+), 36 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index baa2982b9bad..f943b7d0c2e8 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10462,10 +10462,10 @@ void ImGui::DockContextBuildAddWindowsToNodes(ImGuiContext* ctx, ImGuiID root_id if (window->DockNode != NULL) continue; - ImGuiDockNode* dock_node = DockContextFindNodeByID(ctx, window->DockId); - IM_ASSERT(dock_node != NULL); // This should have been called after DockContextBuildNodesFromSettings() - if (root_id == 0 || DockNodeGetRootNode(dock_node)->ID == root_id) - DockNodeAddWindow(dock_node, window, true); + ImGuiDockNode* node = DockContextFindNodeByID(ctx, window->DockId); + IM_ASSERT(node != NULL); // This should have been called after DockContextBuildNodesFromSettings() + if (root_id == 0 || DockNodeGetRootNode(node)->ID == root_id) + DockNodeAddWindow(node, window, true); } } @@ -12589,10 +12589,10 @@ void ImGui::DockBuilderCopyDockspace(ImGuiID src_dockspace_id, ImGuiID dst_docks if (ImGuiID src_dock_id = node_remap_pairs[dock_remap_n]) { ImGuiID dst_dock_id = node_remap_pairs[dock_remap_n + 1]; - ImGuiDockNode* dock_node = DockBuilderGetNode(src_dock_id); - for (int window_n = 0; window_n < dock_node->Windows.Size; window_n++) + ImGuiDockNode* node = DockBuilderGetNode(src_dock_id); + for (int window_n = 0; window_n < node->Windows.Size; window_n++) { - ImGuiWindow* window = dock_node->Windows[window_n]; + ImGuiWindow* window = node->Windows[window_n]; if (src_windows.contains(window->ID)) continue; @@ -12644,30 +12644,30 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) } // Bind to our dock node - ImGuiDockNode* dock_node = window->DockNode; - if (dock_node != NULL) - IM_ASSERT(window->DockId == dock_node->ID); - if (window->DockId != 0 && dock_node == NULL) + ImGuiDockNode* node = window->DockNode; + if (node != NULL) + IM_ASSERT(window->DockId == node->ID); + if (window->DockId != 0 && node == NULL) { - dock_node = DockContextFindNodeByID(ctx, window->DockId); + node = DockContextFindNodeByID(ctx, window->DockId); // We should not be docking into a split node (SetWindowDock should avoid this) - if (dock_node && dock_node->IsSplitNode()) + if (node && node->IsSplitNode()) { DockContextProcessUndockWindow(ctx, window); return; } // Create node - if (dock_node == NULL) + if (node == NULL) { - dock_node = DockContextAddNode(ctx, window->DockId); + node = DockContextAddNode(ctx, window->DockId); if (auto_dock_node) - dock_node->LastFrameAlive = g.FrameCount; + node->LastFrameAlive = g.FrameCount; } - DockNodeAddWindow(dock_node, window, true); - IM_ASSERT(dock_node == window->DockNode); + DockNodeAddWindow(node, window, true); + IM_ASSERT(node == window->DockNode); // Fix an edge case with auto-resizing windows: if they are created on the same frame they are creating their dock node, // we don't want their initial zero-size to spread to the DockNode. We preserve their size. @@ -12677,7 +12677,7 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) } // Undock if the ImGuiDockNodeFlags_NoDockingInCentralNode got set - if (dock_node->IsCentralNode && (dock_node->Flags & ImGuiDockNodeFlags_NoDockingInCentralNode)) + if (node->IsCentralNode && (node->Flags & ImGuiDockNodeFlags_NoDockingInCentralNode)) { DockContextProcessUndockWindow(ctx, window); return; @@ -12685,10 +12685,10 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) // Undock if our dockspace node disappeared // Note how we are testing for LastFrameAlive and NOT LastFrameActive. A DockSpace node can be maintained alive while being inactive with ImGuiDockNodeFlags_KeepAliveOnly. - if (dock_node->LastFrameAlive < g.FrameCount) + if (node->LastFrameAlive < g.FrameCount) { // If the window has been orphaned, transition the docknode to an implicit node processed in DockContextUpdateDocking() - ImGuiDockNode* root_node = DockNodeGetRootNode(dock_node); + ImGuiDockNode* root_node = DockNodeGetRootNode(node); if (root_node->LastFrameAlive < g.FrameCount) { DockContextProcessUndockWindow(ctx, window); @@ -12702,34 +12702,34 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) } // Undock if we are submitted earlier than the host window - if (dock_node->HostWindow && window->BeginOrderWithinContext < dock_node->HostWindow->BeginOrderWithinContext) + if (node->HostWindow && window->BeginOrderWithinContext < node->HostWindow->BeginOrderWithinContext) { DockContextProcessUndockWindow(ctx, window); return; } // FIXME-DOCK: replace ->HostWindow NULL compare with something more explicit (~was initially intended as a first frame test) - if (dock_node->HostWindow == NULL) + if (node->HostWindow == NULL) { window->DockTabIsVisible = false; return; } - IM_ASSERT(dock_node->HostWindow); - IM_ASSERT(dock_node->IsLeafNode()); + IM_ASSERT(node->HostWindow); + IM_ASSERT(node->IsLeafNode()); // Position window - SetNextWindowPos(dock_node->Pos); - SetNextWindowSize(dock_node->Size); + SetNextWindowPos(node->Pos); + SetNextWindowSize(node->Size); g.NextWindowData.PosUndock = false; // Cancel implicit undocking of SetNextWindowPos() window->DockIsActive = true; window->DockTabIsVisible = false; - if (dock_node->Flags & ImGuiDockNodeFlags_KeepAliveOnly) + if (node->Flags & ImGuiDockNodeFlags_KeepAliveOnly) return; // When the window is selected we mark it as visible. - if (dock_node->TabBar && dock_node->TabBar->VisibleTabId == window->ID) + if (node->TabBar && node->TabBar->VisibleTabId == window->ID) window->DockTabIsVisible = true; // When we are about to select this tab (which will only be visible on the _next frame_), flag it with a non-zero HiddenFramesForResize. @@ -12737,22 +12737,22 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) // This is analogous to regular windows being hidden from one frame. It is especially important as nested TabBars would otherwise generate flicker in the form // of one empty frame. // Note that we set HiddenFramesForResize=2 because BeginDocked() is called just before Begin() has a chance to decrement the value. Effectively it'll be a 1 frame thing. - if (!window->DockTabIsVisible && dock_node->TabBar && dock_node->TabBar->NextSelectedTabId == window->ID) + if (!window->DockTabIsVisible && node->TabBar && node->TabBar->NextSelectedTabId == window->ID) window->HiddenFramesForResize = 2; // Update window flag IM_ASSERT((window->Flags & ImGuiWindowFlags_ChildWindow) == 0); window->Flags |= ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_NoResize; - if (dock_node->IsHiddenTabBar) + if (node->IsHiddenTabBar) window->Flags |= ImGuiWindowFlags_NoTitleBar; else window->Flags &= ~ImGuiWindowFlags_NoTitleBar; // Clear the NoTitleBar flag in case the user set it: confusingly enough we need a title bar height so we are correctly offset, but it won't be displayed! // Save new dock order only if the tab bar is active - if (dock_node->TabBar) + if (node->TabBar) window->DockOrder = (short)DockNodeGetTabOrder(window); - if ((dock_node->WantCloseAll || dock_node->WantCloseTabID == window->ID) && p_open != NULL) + if ((node->WantCloseAll || node->WantCloseTabID == window->ID) && p_open != NULL) *p_open = false; // Update ChildId to allow returning from Child to Parent with Escape @@ -13847,13 +13847,16 @@ void ImGui::ShowDockingDebug() if (node->Windows.Size > 0) open = ImGui::TreeNode((void*)(intptr_t)node->ID, "%s 0x%04X%s: %d windows (vis: '%s')", label, node->ID, node->IsVisible ? "" : " (hidden)", node->Windows.Size, node->VisibleWindow ? node->VisibleWindow->Name : "NULL"); else - open = ImGui::TreeNode((void*)(intptr_t)node->ID, "%s 0x%04X%s: split %s (act: '%s')", label, node->ID, node->IsVisible ? "" : " (hidden)", (node->SplitAxis == ImGuiAxis_X) ? "horizontal" : (node->SplitAxis == ImGuiAxis_Y) ? "vertical" : "n/a", node->VisibleWindow ? node->VisibleWindow->Name : "NULL"); + open = ImGui::TreeNode((void*)(intptr_t)node->ID, "%s 0x%04X%s: %s split (vis: '%s')", label, node->ID, node->IsVisible ? "" : " (hidden)", (node->SplitAxis == ImGuiAxis_X) ? "horizontal" : (node->SplitAxis == ImGuiAxis_Y) ? "vertical" : "n/a", node->VisibleWindow ? node->VisibleWindow->Name : "NULL"); if (open) { IM_ASSERT(node->ChildNodes[0] == NULL || node->ChildNodes[0]->ParentNode == node); IM_ASSERT(node->ChildNodes[1] == NULL || node->ChildNodes[1]->ParentNode == node); ImGui::BulletText("Pos (%.0f,%.0f), Size (%.0f, %.0f) Ref (%.0f, %.0f)", node->Pos.x, node->Pos.y, node->Size.x, node->Size.y, node->SizeRef.x, node->SizeRef.y); + ImGui::BulletText("VisibleWindow: 0x%08X %s", node->VisibleWindow ? node->VisibleWindow->ID : 0, node->VisibleWindow ? node->VisibleWindow->Name : "NULL"); + ImGui::BulletText("SelectedTabID: 0x%08X", node->SelectedTabID); + ImGui::BulletText("LastFocusedNodeID: 0x%08X", node->LastFocusedNodeID); ImGui::BulletText("Flags 0x%02X%s%s%s%s", node->Flags, node->IsDockSpace ? ", IsDockSpace" : "", node->IsCentralNode ? ", IsCentralNode" : "", (GImGui->FrameCount - node->LastFrameAlive < 2) ? ", IsAlive" : "", (GImGui->FrameCount - node->LastFrameActive < 2) ? ", IsActive" : ""); diff --git a/imgui_internal.h b/imgui_internal.h index ace7e26d57b5..e609ed06167c 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -829,13 +829,13 @@ struct ImGuiDockNode ImGuiWindowClass WindowClass; ImGuiWindow* HostWindow; - ImGuiWindow* VisibleWindow; + ImGuiWindow* VisibleWindow; // Generally point to window which is ID is == SelectedTabID, but when CTRL+Tabbing this can be a different window. ImGuiDockNode* CentralNode; // [Root node only] Pointer to central node. ImGuiDockNode* OnlyNodeWithWindows; // [Root node only] Set when there is a single visible node within the hierarchy. int LastFrameAlive; // Last frame number the node was updated or kept alive explicitly with DockSpace() + ImGuiDockNodeFlags_KeepAliveOnly int LastFrameActive; // Last frame number the node was updated. int LastFrameFocused; // Last frame number the node was focused. - ImGuiID LastFocusedNodeID; // [Root node only] Which of our child node (any ancestor in the hierarchy) was last focused. + ImGuiID LastFocusedNodeID; // [Root node only] Which of our child docking node (any ancestor in the hierarchy) was last focused. ImGuiID SelectedTabID; // [Tab node only] Which of our tab is selected. ImGuiID WantCloseTabID; // [Tab node only] Set when closing a specific tab. bool InitFromFirstWindowPosSize :1; From 86fce79a6c23fe6cf0caaee1aa1e3ba47ace0d05 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 22 Jan 2019 17:09:14 +0100 Subject: [PATCH 413/828] Comments + clear out VisibleWinodw field (should have no effect) --- imgui.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index f943b7d0c2e8..0b7dd3f5df3a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3164,7 +3164,7 @@ void ImGui::StartMouseMovingWindow(ImGuiWindow* window) { // Set ActiveId even if the _NoMove flag is set. Without it, dragging away from a window with _NoMove would activate hover on other windows. // We _also_ call this when clicking in a window empty space when io.ConfigWindowsMoveFromTitleBarOnly is set, but clear g.MovingWindow afterward. - // This is because we want ActiveId to be set even when the window is stuck from moving. + // This is because we want ActiveId to be set even when the window is not permitted to move. ImGuiContext& g = *GImGui; FocusWindow(window); SetActiveID(window->MoveId, window); @@ -3252,7 +3252,8 @@ void ImGui::UpdateMouseMovingWindowEndFrame() } else if (g.NavWindow != NULL && GetFrontMostPopupModal() == NULL) { - FocusWindow(NULL); // Clicking on void disable focus + // Clicking on void disable focus + FocusWindow(NULL); } } @@ -5598,6 +5599,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) } // Draw window + handle manual resize + // As we highlight the title bar when want_focus is set, multiple reappearing windows will have have their title bar highlighted on their reappearing frame. const float window_rounding = window->WindowRounding; const float window_border_size = window->WindowBorderSize; const ImGuiWindow* window_to_highlight = g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow; @@ -10796,6 +10798,8 @@ static void ImGui::DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window break; } IM_ASSERT(erased); + if (node->VisibleWindow == window) + node->VisibleWindow = NULL; // Remove tab and possibly tab bar if (node->TabBar) From bfacbac7c4bdf01e1d5661271e2566010f7945b5 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 23 Jan 2019 11:05:00 +0100 Subject: [PATCH 414/828] Docking: Fix a focusing issue where dock node wouldn't be moved to the front as expected. --- imgui.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/imgui.cpp b/imgui.cpp index 0b7dd3f5df3a..5519019a7688 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11156,6 +11156,15 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) host_window->DC.CursorPos = host_window->Pos; node->Pos = host_window->Pos; node->Size = host_window->Size; + + // We set ImGuiWindowFlags_NoFocusOnAppearing because we don't want the host window to take full focus (e.g. steal NavWindow) + // But we still it bring it to the front of display. There's no way to choose this precise behavior via window flags. + // One simple case to ponder if: window A has a toggle to create windows B/C/D. Dock B/C/D together, clear the toggle and enable it again. + // When reappearing B/C/D will request focus and be moved to the top of the display pile, but they are not linked to the dock host window + // during the frame they appear. The dock host window would keep its old display order, and the sorting in EndFrame would move B/C/D back + // after the dock host window, losing their top-most status. + if (node->HostWindow->Appearing) + BringWindowToDisplayFront(node->HostWindow); } else if (node->ParentNode) { From 9f96fcff3c21c5a31bd01eb5f590465da5979f5b Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 23 Jan 2019 11:29:44 +0100 Subject: [PATCH 415/828] Docking: Added ImGuiDockNodeFlags_Dockspace instead of node internal IsDockspace toward allowing the DockBuilder API to create non-dockspace nodes. --- imgui.cpp | 44 ++++++++++++++++++++++---------------------- imgui_internal.h | 12 ++++++++---- imgui_widgets.cpp | 6 +++--- 3 files changed, 33 insertions(+), 29 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 5519019a7688..54dbccb04650 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10292,7 +10292,7 @@ void ImGui::DockContextNewFrameUpdateDocking(ImGuiContext* ctx) // We can have NULL pointers when we delete nodes, but because ID are recycled this should amortize nicely (and our node count will never be very high) for (int n = 0; n < dc->Nodes.Data.Size; n++) if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p) - if (!node->IsDockSpace && node->IsRootNode()) + if (node->IsRootNode() && !node->IsDockSpace()) DockNodeUpdate(node); } @@ -10440,7 +10440,8 @@ static void ImGui::DockContextBuildNodesFromSettings(ImGuiContext* ctx, ImGuiDoc node->ParentNode->ChildNodes[1] = node; node->SelectedTabID = node_settings->SelectedTabID; node->SplitAxis = node_settings->SplitAxis; - node->IsDockSpace = node_settings->IsDockSpace != 0; + if (node_settings->IsDockSpace) + node->Flags |= ImGuiDockNodeFlags_Dockspace; node->IsCentralNode = node_settings->IsCentralNode != 0; node->IsHiddenTabBar = node_settings->IsHiddenTabBar != 0; @@ -10703,7 +10704,7 @@ ImGuiDockNode::ImGuiDockNode(ImGuiID id) WantCloseTabID = 0; InitFromFirstWindowPosSize = InitFromFirstWindowViewport = false; IsVisible = true; - IsFocused = IsDockSpace = IsCentralNode = IsHiddenTabBar = HasCloseButton = HasCollapseButton = false; + IsFocused = IsCentralNode = IsHiddenTabBar = HasCloseButton = HasCollapseButton = false; WantCloseAll = WantLockSizeOnce = WantMouseMove = WantHiddenTabBarToggle = false; } @@ -10750,7 +10751,7 @@ static void ImGui::DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window, b // When reactivating a node with one or two loose window, the window pos/size/viewport are authoritative over the node storage. // In particular it is important we init the viewport from the first window so we don't create two viewports and drop one. - if (node->HostWindow == NULL && !node->IsDockSpace && node->IsRootNode()) + if (node->HostWindow == NULL && !node->IsDockSpace() && node->IsRootNode()) node->InitFromFirstWindowPosSize = node->InitFromFirstWindowViewport = true; // Add to tab bar if requested @@ -10956,9 +10957,10 @@ static void ImGui::DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* nod IM_ASSERT(node->ParentNode == NULL || node->ParentNode->ChildNodes[0] == node || node->ParentNode->ChildNodes[1] == node); - // Inherit flags + // Inherit most flags + ImGuiDockNodeFlags flags_to_inherit = ~0 & ~ImGuiDockNodeFlags_Dockspace; if (node->ParentNode) - node->Flags = node->ParentNode->Flags; + node->Flags = node->ParentNode->Flags & flags_to_inherit; // Recurse into children // There is the possibility that one of our child becoming empty will delete itself and moving its sibling contents into 'node'. @@ -11006,7 +11008,7 @@ static void ImGui::DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* nod static void ImGui::DockNodeUpdateVisibleFlag(ImGuiDockNode* node) { // Update visibility flag - bool is_visible = (node->ParentNode == 0) ? node->IsDockSpace : node->IsCentralNode; + bool is_visible = (node->ParentNode == 0) ? node->IsDockSpace() : node->IsCentralNode; is_visible |= (node->Windows.Size > 0); is_visible |= (node->ChildNodes[0] && node->ChildNodes[0]->IsVisible); is_visible |= (node->ChildNodes[1] && node->ChildNodes[1]->IsVisible); @@ -11061,7 +11063,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) } // Early out for hidden root dock nodes (when all DockId references are in inactive windows, or there is only 1 floating window holding on the DockId) - if (node->Windows.Size <= 1 && node->IsRootNode() && node->IsLeafNode() && !node->IsDockSpace && !g.IO.ConfigDockingTabBarOnSingleWindows) + if (node->Windows.Size <= 1 && node->IsRootNode() && node->IsLeafNode() && !node->IsDockSpace() && !g.IO.ConfigDockingTabBarOnSingleWindows) { if (node->Windows.Size == 1) { @@ -11099,7 +11101,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) ImGuiWindow* host_window = NULL; bool beginned_into_host_window = false; - if (node->IsDockSpace) + if (node->IsDockSpace()) { // [Explicit root dockspace node] IM_ASSERT(node->HostWindow); @@ -11202,7 +11204,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) if (central_node_hole_register_hit_test_hole) { // Add a little padding to match the "resize from edges" behavior and allow grabbing the splitter easily. - IM_ASSERT(node->IsDockSpace); // We cannot pass this flag without the DockSpace() api. Testing this because we also setup the hole in host_window->ParentNode + IM_ASSERT(node->IsDockSpace()); // We cannot pass this flag without the DockSpace() api. Testing this because we also setup the hole in host_window->ParentNode ImRect central_hole(central_node->Pos, central_node->Pos + central_node->Size); central_hole.Expand(ImVec2(-WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS, -WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS)); if (central_node_hole && !central_hole.IsInverted()) @@ -11321,7 +11323,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w // Move ourselves to the Menu layer (so we can be accessed by tapping Alt) + undo SkipItems flag in order to draw over the title bar even if the window is collapsed bool backup_skip_item = host_window->SkipItems; - if (!node->IsDockSpace) + if (!node->IsDockSpace()) { host_window->SkipItems = false; host_window->DC.NavLayerCurrent = ImGuiNavLayer_Menu; @@ -11411,8 +11413,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w // Begin tab bar const ImRect tab_bar_rect = DockNodeCalcTabBarRect(node); ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_Reorderable | ImGuiTabBarFlags_AutoSelectNewTabs | ImGuiTabBarFlags_NoTabListPopupButton;// | ImGuiTabBarFlags_NoTabListScrollingButtons); - tab_bar_flags |= ImGuiTabBarFlags_SaveSettings; - tab_bar_flags |= ImGuiTabBarFlags_DockNode | (node->IsDockSpace ? ImGuiTabBarFlags_DockNodeIsDockSpace : 0); + tab_bar_flags |= ImGuiTabBarFlags_SaveSettings | ImGuiTabBarFlags_DockNode; if (!host_window->Collapsed && is_focused) tab_bar_flags |= ImGuiTabBarFlags_IsFocused; BeginTabBarEx(tab_bar, tab_bar_rect, tab_bar_flags, node); @@ -11512,7 +11513,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w PopID(); // Restore SkipItems flag - if (!node->IsDockSpace) + if (!node->IsDockSpace()) { host_window->DC.NavLayerCurrent = ImGuiNavLayer_Main; host_window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Main); @@ -11522,7 +11523,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w static bool DockNodeIsDropAllowedOne(ImGuiWindow* payload, ImGuiWindow* host_window) { - if (host_window->DockNodeAsHost && host_window->DockNodeAsHost->IsDockSpace && payload->BeginOrderWithinContext < host_window->BeginOrderWithinContext) + if (host_window->DockNodeAsHost && host_window->DockNodeAsHost->IsDockSpace() && payload->BeginOrderWithinContext < host_window->BeginOrderWithinContext) return false; ImGuiWindowClass* host_class = host_window->DockNodeAsHost ? &host_window->DockNodeAsHost->WindowClass : &host_window->WindowClass; @@ -12134,7 +12135,7 @@ ImGuiDockNode* ImGui::DockNodeTreeFindNodeByPos(ImGuiDockNode* node, ImVec2 pos) // There is an edge case when docking into a dockspace which only has inactive nodes (because none of the windows are active) // In this case we need to fallback into any leaf mode, possibly the central node. - if (node->IsDockSpace && node->IsRootNode()) + if (node->IsDockSpace() && node->IsRootNode()) { if (node->CentralNode && node->IsLeafNode()) // FIXME-20181220: We should not have to test for IsLeafNode() here but we have another bug to fix first. return node->CentralNode; @@ -12202,11 +12203,11 @@ void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags doc // It is possible that the node has already been claimed by a docked window which appeared before the DockSpace() node, so we overwrite IsDockSpace again. if (node->LastFrameActive == g.FrameCount && !(dockspace_flags & ImGuiDockNodeFlags_KeepAliveOnly)) { - IM_ASSERT(node->IsDockSpace == false && "Cannot call DockSpace() twice a frame with the same ID"); - node->IsDockSpace = true; + IM_ASSERT(node->IsDockSpace() == false && "Cannot call DockSpace() twice a frame with the same ID"); + node->Flags |= ImGuiDockNodeFlags_Dockspace; return; } - node->IsDockSpace = true; + node->Flags |= ImGuiDockNodeFlags_Dockspace; // Keep alive mode, this is allow windows docked into this node so stay docked even if they are not visible if (dockspace_flags & ImGuiDockNodeFlags_KeepAliveOnly) @@ -12480,7 +12481,6 @@ static ImGuiDockNode* DockBuilderCopyNodeRec(ImGuiDockNode* src_node, ImGuiID ds dst_node->Size = src_node->Size; dst_node->SizeRef = src_node->SizeRef; dst_node->SplitAxis = src_node->SplitAxis; - dst_node->IsDockSpace = src_node->IsDockSpace; dst_node->IsCentralNode = src_node->IsCentralNode; out_node_remap_pairs->push_back(src_node->ID); @@ -12955,7 +12955,7 @@ static void DockSettingsHandler_DockNodeToSettings(ImGuiDockContext* dc, ImGuiDo node_settings.SelectedTabID = node->SelectedTabID; node_settings.SplitAxis = node->IsSplitNode() ? (char)node->SplitAxis : ImGuiAxis_None; node_settings.Depth = (char)depth; - node_settings.IsDockSpace = (char)node->IsDockSpace; + node_settings.IsDockSpace = (char)node->IsDockSpace(); node_settings.IsCentralNode = (char)node->IsCentralNode; node_settings.IsHiddenTabBar = (char)node->IsHiddenTabBar; node_settings.Pos = ImVec2ih((short)node->Pos.x, (short)node->Pos.y); @@ -13871,7 +13871,7 @@ void ImGui::ShowDockingDebug() ImGui::BulletText("SelectedTabID: 0x%08X", node->SelectedTabID); ImGui::BulletText("LastFocusedNodeID: 0x%08X", node->LastFocusedNodeID); ImGui::BulletText("Flags 0x%02X%s%s%s%s", - node->Flags, node->IsDockSpace ? ", IsDockSpace" : "", node->IsCentralNode ? ", IsCentralNode" : "", + node->Flags, node->IsDockSpace() ? ", IsDockSpace" : "", node->IsCentralNode ? ", IsCentralNode" : "", (GImGui->FrameCount - node->LastFrameAlive < 2) ? ", IsAlive" : "", (GImGui->FrameCount - node->LastFrameActive < 2) ? ", IsActive" : ""); if (node->ChildNodes[0]) NodeDockNode(node->ChildNodes[0], "Child[0]"); diff --git a/imgui_internal.h b/imgui_internal.h index e609ed06167c..de762b5206ba 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -813,6 +813,11 @@ struct ImGuiTabBarSortItem float Width; }; +enum ImGuiDockNodeFlagsPrivate_ +{ + ImGuiDockNodeFlags_Dockspace = 1 << 10 +}; + // sizeof() 116~160 struct ImGuiDockNode { @@ -842,7 +847,6 @@ struct ImGuiDockNode bool InitFromFirstWindowViewport :1; bool IsVisible :1; // Set to false when the node is hidden (usually disabled as it has no active window) bool IsFocused :1; - bool IsDockSpace :1; // Root node was created by a DockSpace() call. bool IsCentralNode :1; bool IsHiddenTabBar :1; bool HasCloseButton :1; @@ -855,6 +859,7 @@ struct ImGuiDockNode ImGuiDockNode(ImGuiID id); ~ImGuiDockNode(); bool IsRootNode() const { return ParentNode == NULL; } + bool IsDockSpace() const { return (Flags & ImGuiDockNodeFlags_Dockspace) != 0; } bool IsSplitNode() const { return ChildNodes[0] != NULL; } bool IsLeafNode() const { return ChildNodes[0] == NULL; } bool IsEmpty() const { return ChildNodes[0] == NULL && Windows.Size == 0; } @@ -1384,9 +1389,8 @@ struct ImGuiItemHoveredDataBackup enum ImGuiTabBarFlagsPrivate_ { ImGuiTabBarFlags_DockNode = 1 << 20, // Part of a dock node - ImGuiTabBarFlags_DockNodeIsDockSpace = 1 << 21, // Part of an explicit dockspace node node - ImGuiTabBarFlags_IsFocused = 1 << 22, - ImGuiTabBarFlags_SaveSettings = 1 << 23 // FIXME: Settings are handled by the docking system, this only request the tab bar to mark settings dirty when reordering tabs + ImGuiTabBarFlags_IsFocused = 1 << 21, + ImGuiTabBarFlags_SaveSettings = 1 << 22 // FIXME: Settings are handled by the docking system, this only request the tab bar to mark settings dirty when reordering tabs }; enum ImGuiTabItemFlagsPrivate_ diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index fa545290b2d5..17d87f48f17e 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -5937,8 +5937,8 @@ bool ImGui::BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& tab_bar_bb, ImG } else { - const float separator_min_x = tab_bar->BarRect.Min.x - ((flags & ImGuiTabBarFlags_DockNodeIsDockSpace) ? 0.0f : window->WindowPadding.x); - const float separator_max_x = tab_bar->BarRect.Max.x + ((flags & ImGuiTabBarFlags_DockNodeIsDockSpace) ? 0.0f : window->WindowPadding.x); + const float separator_min_x = tab_bar->BarRect.Min.x - window->WindowPadding.x; + const float separator_max_x = tab_bar->BarRect.Max.x + window->WindowPadding.x; window->DrawList->AddLine(ImVec2(separator_min_x, y), ImVec2(separator_max_x, y), col, 1.0f); } return true; @@ -6497,7 +6497,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, // FIXME-DOCK: In theory we shouldn't test for the ConfigDockingNodifySingleWindows flag here. // When our single window node and OnlyNodeWithWindows are working properly we may remove this check here. ImGuiDockNode* node = docked_window ? docked_window->DockNode : NULL; - const bool single_floating_window_node = node && node->IsRootNode() && !node->IsDockSpace && node->Windows.Size == 1 && g.IO.ConfigDockingTabBarOnSingleWindows; + const bool single_floating_window_node = node && node->IsRootNode() && !node->IsDockSpace() && node->Windows.Size == 1 && g.IO.ConfigDockingTabBarOnSingleWindows; if (held && single_floating_window_node && IsMouseDragging(0, 0.0f)) { // Move From 0bda7f196d4050ec6d24d7a484bf05fc173d457f Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 23 Jan 2019 13:03:59 +0100 Subject: [PATCH 416/828] Docking: Fixed overlapping issue with greyed out close button. --- imgui.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 54dbccb04650..742298213531 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11561,8 +11561,8 @@ static ImRect ImGui::DockNodeCalcTabBarRect(const ImGuiDockNode* node) ImRect r = ImRect(node->Pos.x, node->Pos.y, node->Pos.x + node->Size.x, node->Pos.y + (g.FontSize + g.Style.FramePadding.y * 2.0f)); if (node->HasCollapseButton) r.Min.x += g.Style.FramePadding.x + g.FontSize; // + g.Style.ItemInnerSpacing.x; // <-- Adding ItemInnerSpacing makes the title text moves slightly when in a tab bar. Instead we adjusted RenderArrowDockMenu() - if (node->HasCloseButton) - r.Max.x -= g.Style.FramePadding.x + g.FontSize + 1.0f; + // In DockNodeUpdateTabBar() we currently display a disabled close button even if there is none. + r.Max.x -= g.Style.FramePadding.x + g.FontSize + 1.0f; return r; } From 0737433c71a5e360be49271afc6dfa9d0cce680e Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 23 Jan 2019 17:31:32 +0100 Subject: [PATCH 417/828] When resizing from an edge, the border is more visible and better follow the rounded corners. Border rendering moved to RenderOuterBorders so it can be called in a different order for docking. (#1495, #822) --- docs/CHANGELOG.txt | 1 + imgui.cpp | 77 +++++++++++++++++++++++++++++++--------------- 2 files changed, 54 insertions(+), 24 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index ffc6d3f722a1..1fd983b9138e 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -93,6 +93,7 @@ Other changes: Other Changes: - Added .editorconfig file for text editors to standardize using spaces. (#2038) [@kudaba] - Fixed range-version of PushID() and GetID() not honoring the ### operator to restart from the seed value. +- Window: When resizing from an edge, the border is more visible and better follow the rounded corners. - ImDrawList: Fixed AddCircle(), AddCircleFilled() angle step being off, which was visible when drawing a "circle" with a small number of segments (e.g. an hexagon). (#2287) [@baktery] - ImGuiTextBuffer: Added append() function (unformatted). diff --git a/imgui.cpp b/imgui.cpp index 742298213531..fd670da69e86 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1046,6 +1046,7 @@ static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window); static void UpdateMouseInputs(); static void UpdateMouseWheel(); static void UpdateManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4]); +static void RenderOuterBorders(ImGuiWindow* window, int border_held); static void EndFrameDrawDimmedBackgrounds(); // Viewports @@ -4971,12 +4972,12 @@ static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& co struct ImGuiResizeGripDef { - ImVec2 CornerPos; + ImVec2 CornerPosN; ImVec2 InnerDir; int AngleMin12, AngleMax12; }; -const ImGuiResizeGripDef resize_grip_def[4] = +static const ImGuiResizeGripDef resize_grip_def[4] = { { ImVec2(1,1), ImVec2(-1,-1), 0, 3 }, // Lower right { ImVec2(0,1), ImVec2(+1,-1), 3, 6 }, // Lower left @@ -4988,10 +4989,10 @@ static ImRect GetResizeBorderRect(ImGuiWindow* window, int border_n, float perp_ { ImRect rect = window->Rect(); if (thickness == 0.0f) rect.Max -= ImVec2(1,1); - if (border_n == 0) return ImRect(rect.Min.x + perp_padding, rect.Min.y - thickness, rect.Max.x - perp_padding, rect.Min.y + thickness); - if (border_n == 1) return ImRect(rect.Max.x - thickness, rect.Min.y + perp_padding, rect.Max.x + thickness, rect.Max.y - perp_padding); - if (border_n == 2) return ImRect(rect.Min.x + perp_padding, rect.Max.y - thickness, rect.Max.x - perp_padding, rect.Max.y + thickness); - if (border_n == 3) return ImRect(rect.Min.x - thickness, rect.Min.y + perp_padding, rect.Min.x + thickness, rect.Max.y - perp_padding); + if (border_n == 0) return ImRect(rect.Min.x + perp_padding, rect.Min.y - thickness, rect.Max.x - perp_padding, rect.Min.y + thickness); // Top + if (border_n == 1) return ImRect(rect.Max.x - thickness, rect.Min.y + perp_padding, rect.Max.x + thickness, rect.Max.y - perp_padding); // Right + if (border_n == 2) return ImRect(rect.Min.x + perp_padding, rect.Max.y - thickness, rect.Max.x - perp_padding, rect.Max.y + thickness); // Bottom + if (border_n == 3) return ImRect(rect.Min.x - thickness, rect.Min.y + perp_padding, rect.Min.x + thickness, rect.Max.y - perp_padding); // Left IM_ASSERT(0); return ImRect(); } @@ -5019,7 +5020,7 @@ static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++) { const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n]; - const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPos); + const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN); // Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window ImRect resize_rect(corner - grip.InnerDir * grip_hover_outer_size, corner + grip.InnerDir * grip_hover_inner_size); @@ -5041,8 +5042,8 @@ static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au { // Resize from any of the four corners // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position - ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + ImLerp(grip.InnerDir * grip_hover_outer_size, grip.InnerDir * -grip_hover_inner_size, grip.CornerPos); // Corner of the window corresponding to our corner grip - CalcResizePosSizeFromAnyCorner(window, corner_target, grip.CornerPos, &pos_target, &size_target); + ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + ImLerp(grip.InnerDir * grip_hover_outer_size, grip.InnerDir * -grip_hover_inner_size, grip.CornerPosN); // Corner of the window corresponding to our corner grip + CalcResizePosSizeFromAnyCorner(window, corner_target, grip.CornerPosN, &pos_target, &size_target); } if (resize_grip_n == 0 || held || hovered) resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip); @@ -5056,16 +5057,17 @@ static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au if ((hovered && g.HoveredIdTimer > WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER) || held) { g.MouseCursor = (border_n & 1) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS; - if (held) *border_held = border_n; + if (held) + *border_held = border_n; } if (held) { ImVec2 border_target = window->Pos; ImVec2 border_posn; - if (border_n == 0) { border_posn = ImVec2(0, 0); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } - if (border_n == 1) { border_posn = ImVec2(1, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } - if (border_n == 2) { border_posn = ImVec2(0, 1); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } - if (border_n == 3) { border_posn = ImVec2(0, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } + if (border_n == 0) { border_posn = ImVec2(0, 0); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Top + if (border_n == 1) { border_posn = ImVec2(1, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Right + if (border_n == 2) { border_posn = ImVec2(0, 1); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Bottom + if (border_n == 3) { border_posn = ImVec2(0, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Left CalcResizePosSizeFromAnyCorner(window, border_target, border_posn, &pos_target, &size_target); } } @@ -5113,6 +5115,41 @@ static inline void ClampWindowRect(ImGuiWindow* window, const ImRect& rect, cons window->Pos = ImMin(rect.Max - padding, ImMax(window->Pos + size_for_clamping, rect.Min + padding) - size_for_clamping); } +static void ImGui::RenderOuterBorders(ImGuiWindow* window, int border_held) +{ + ImGuiContext& g = *GImGui; + float rounding = window->WindowRounding; + float border_size = window->WindowBorderSize; + if (border_size > 0.0f && !(window->Flags & ImGuiWindowFlags_NoBackground)) + window->DrawList->AddRect(window->Pos, window->Pos + window->Size, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size); + if (border_held != -1) + { + struct ImGuiResizeBorderDef + { + ImVec2 InnerDir; + ImVec2 CornerPosN1, CornerPosN2; + float OuterAngle; + }; + static const ImGuiResizeBorderDef resize_border_def[4] = + { + { ImVec2(0,+1), ImVec2(0,0), ImVec2(1,0), IM_PI*1.50f }, // Top + { ImVec2(-1,0), ImVec2(1,0), ImVec2(1,1), IM_PI*0.00f }, // Right + { ImVec2(0,-1), ImVec2(1,1), ImVec2(0,1), IM_PI*0.50f }, // Bottom + { ImVec2(+1,0), ImVec2(0,1), ImVec2(0,0), IM_PI*1.00f } // Left + }; + const ImGuiResizeBorderDef& def = resize_border_def[border_held]; + ImRect border_r = GetResizeBorderRect(window, border_held, rounding, 0.0f); + window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.CornerPosN1) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle - IM_PI*0.25f, def.OuterAngle); + window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.CornerPosN2) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle, def.OuterAngle + IM_PI*0.25f); + window->DrawList->PathStroke(GetColorU32(ImGuiCol_SeparatorActive), false, ImMax(2.0f, border_size)); // Thicker than usual + } + if (g.Style.FrameBorderSize > 0 && !(window->Flags & ImGuiWindowFlags_NoTitleBar) && !window->DockIsActive) + { + float y = window->Pos.y + window->TitleBarHeight() - 1; + window->DrawList->AddLine(ImVec2(window->Pos.x + border_size, y), ImVec2(window->Pos.x + window->Size.x - border_size, y), GetColorU32(ImGuiCol_Border), g.Style.FrameBorderSize); + } +} + void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window) { window->ParentWindow = parent_window; @@ -5693,7 +5730,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++) { const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n]; - const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPos); + const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN); window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(window_border_size, grip_draw_size) : ImVec2(grip_draw_size, window_border_size))); window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(grip_draw_size, window_border_size) : ImVec2(window_border_size, grip_draw_size))); window->DrawList->PathArcToFast(ImVec2(corner.x + grip.InnerDir.x * (window_rounding + window_border_size), corner.y + grip.InnerDir.y * (window_rounding + window_border_size)), window_rounding, grip.AngleMin12, grip.AngleMax12); @@ -5702,15 +5739,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) } // Borders - if (window_border_size > 0.0f && !(flags & ImGuiWindowFlags_NoBackground)) - window->DrawList->AddRect(window->Pos, window->Pos + window->Size, GetColorU32(ImGuiCol_Border), window_rounding, ImDrawCornerFlags_All, window_border_size); - if (border_held != -1) - { - ImRect border = GetResizeBorderRect(window, border_held, grip_draw_size, 0.0f); - window->DrawList->AddLine(border.Min, border.Max, GetColorU32(ImGuiCol_SeparatorActive), ImMax(1.0f, window_border_size)); - } - if (style.FrameBorderSize > 0 && !(flags & ImGuiWindowFlags_NoTitleBar) && !window->DockIsActive) - window->DrawList->AddLine(title_bar_rect.GetBL() + ImVec2(style.WindowBorderSize, -1), title_bar_rect.GetBR() + ImVec2(-style.WindowBorderSize, -1), GetColorU32(ImGuiCol_Border), style.FrameBorderSize); + RenderOuterBorders(window, border_held); } // Store a backup of SizeFull which we will use next frame to decide if we need scrollbars. From 07ff47bf1b7fb3dced2dc91ca39efedaff34e337 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 23 Jan 2019 19:54:36 +0100 Subject: [PATCH 418/828] Docking: Fixed various border / padding related inconsistency with dock node vs floating windows. (#2109) --- docs/TODO.txt | 5 ++--- imgui.cpp | 31 ++++++++++++++++++++----------- imgui_internal.h | 1 + imgui_widgets.cpp | 6 +++--- 4 files changed, 26 insertions(+), 17 deletions(-) diff --git a/docs/TODO.txt b/docs/TODO.txt index acc334a95471..b32a5a0d0fcb 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -135,8 +135,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - dock: B~ SetNextWindowDock() calls (with conditional) -> defer everything to DockContextUpdate (repro: Documents->[X]Windows->Dock 1 elsewhere->Click Redock All - dock: B~ tidy up tab list popup buttons features (available with manual tab-bar, see ImGuiTabBarFlags_NoTabListPopupButton code, not used by docking nodes) - dock: B- SetNextWindowDockId(0) with a second Begin() in the frame will asserts - - dock: B- inconsistent clipping/border 1-pixel issue (#2) - - dock: B- fix/disable auto-resize grip on split host nodes (~#2) + - dock: B: resize grip drawn in host window typically appears under scrollbar. - dock: B- SetNextWindowFocus() doesn't seem to apply if the window is hidden this frame, need repro (#4) - dock: B- resizing a dock tree small currently has glitches (overlapping collapse and close button, etc.) - dock: B- dpi: look at interaction with the hi-dpi and multi-dpi stuff. @@ -323,7 +322,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - viewport: store per-viewport/monitor DPI in .ini file so an application reload or main window changing DPI on reload can be properly patched for. - viewport: implicit/fallback Debug window can hog a zombie viewport (harmless, noisy?) > could at least clear out the reference on a per session basis? - viewport: need to clarify how to use GetMousePos() from a user point of view. - - platform: glfw: no support for ImGuiBackendFlags_HasMouseHoveredViewport. + - platform: glfw: no support for ImGuiBackendFlags_HasMouseHoveredViewport. - platform: sdl: no support for ImGuiBackendFlags_HasMouseHoveredViewport. maybe we could use SDL_GetMouseFocus() / SDL_WINDOW_MOUSE_FOCUS if imgui could fallback on its heuristic when NoInputs is set - platform: sdl: no refresh of monitor/display (SDL doesn't seem to have an event for it). - platform: sdl: multi-viewport + minimized window seems to break mouse wheel events (at least under Win32). diff --git a/imgui.cpp b/imgui.cpp index fd670da69e86..7d93d53da577 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1046,7 +1046,7 @@ static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window); static void UpdateMouseInputs(); static void UpdateMouseWheel(); static void UpdateManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4]); -static void RenderOuterBorders(ImGuiWindow* window, int border_held); +static void RenderOuterBorders(ImGuiWindow* window); static void EndFrameDrawDimmedBackgrounds(); // Viewports @@ -2567,6 +2567,7 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) Appearing = false; Hidden = false; HasCloseButton = false; + ResizeBorderHeld = -1; BeginCount = 0; BeginOrderWithinParent = -1; BeginOrderWithinContext = -1; @@ -5115,13 +5116,15 @@ static inline void ClampWindowRect(ImGuiWindow* window, const ImRect& rect, cons window->Pos = ImMin(rect.Max - padding, ImMax(window->Pos + size_for_clamping, rect.Min + padding) - size_for_clamping); } -static void ImGui::RenderOuterBorders(ImGuiWindow* window, int border_held) +static void ImGui::RenderOuterBorders(ImGuiWindow* window) { ImGuiContext& g = *GImGui; float rounding = window->WindowRounding; float border_size = window->WindowBorderSize; if (border_size > 0.0f && !(window->Flags & ImGuiWindowFlags_NoBackground)) window->DrawList->AddRect(window->Pos, window->Pos + window->Size, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size); + + int border_held = window->ResizeBorderHeld; if (border_held != -1) { struct ImGuiResizeBorderDef @@ -5373,9 +5376,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) flags = window->Flags; // Lock border size and padding for the frame (so that altering them doesn't cause inconsistencies) - if (window->DockIsActive) - window->WindowBorderSize = 0.0f; - else if (flags & ImGuiWindowFlags_ChildWindow) + if (flags & ImGuiWindowFlags_ChildWindow) window->WindowBorderSize = style.ChildBorderSize; else window->WindowBorderSize = ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize; @@ -5579,13 +5580,17 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) want_focus = true; } + // Decide if we are going to handle borders and resize grips + const bool handle_borders_and_resize_grips = (window->DockNodeAsHost || !window->DockIsActive); + // Handle manual resize: Resize Grips, Borders, Gamepad int border_held = -1; ImU32 resize_grip_col[4] = { 0 }; const int resize_grip_count = g.IO.ConfigWindowsResizeFromEdges ? 2 : 1; // 4 const float grip_draw_size = (float)(int)ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f); - if (!window->Collapsed) + if (handle_borders_and_resize_grips && !window->Collapsed) UpdateManualResize(window, size_auto_fit, &border_held, resize_grip_count, &resize_grip_col[0]); + window->ResizeBorderHeld = (signed char)border_held; // Synchronize window --> viewport if (window->ViewportOwned) @@ -5698,7 +5703,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) { ImRect menu_bar_rect = window->MenuBarRect(); menu_bar_rect.ClipWith(window->Rect()); // Soft clipping, in particular child window don't have minimum size covering the menu bar so this is useful for them. - window->DrawList->AddRectFilled(menu_bar_rect.Min, menu_bar_rect.Max, GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, ImDrawCornerFlags_Top); + window->DrawList->AddRectFilled(menu_bar_rect.Min+ImVec2(window_border_size,0), menu_bar_rect.Max-ImVec2(window_border_size,0), GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, ImDrawCornerFlags_Top); if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y) window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border), style.FrameBorderSize); } @@ -5725,7 +5730,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) Scrollbar(ImGuiLayoutType_Vertical); // Render resize grips (after their input handling so we don't have a frame of latency) - if (!(flags & ImGuiWindowFlags_NoResize)) + if (handle_borders_and_resize_grips && !(flags & ImGuiWindowFlags_NoResize)) { for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++) { @@ -5738,8 +5743,9 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) } } - // Borders - RenderOuterBorders(window, border_held); + // Borders (for dock node host they will be rendered over after the tab bar) + if (handle_borders_and_resize_grips && !window->DockNodeAsHost) + RenderOuterBorders(window); } // Store a backup of SizeFull which we will use next frame to decide if we need scrollbars. @@ -11300,6 +11306,10 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) if (beginned_into_host_window) End(); } + + // Render outer borders last (after the tab bar) + if (node->IsRootNode() && host_window) + RenderOuterBorders(host_window); } // Compare TabItem nodes given the last known DockOrder (will persist in .ini file as hint), used to sort tabs when multiple tabs are added on the same frame. @@ -11403,7 +11413,6 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w ImRect title_bar_rect = ImRect(node->Pos, node->Pos + ImVec2(node->Size.x, g.FontSize + style.FramePadding.y * 2.0f)); ImU32 title_bar_col = GetColorU32(host_window->Collapsed ? ImGuiCol_TitleBgCollapsed : is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg); host_window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, host_window->WindowRounding, ImDrawCornerFlags_Top); - host_window->DrawList->AddLine(title_bar_rect.GetBL(), title_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border), style.WindowBorderSize); // Collapse button if (CollapseButton(host_window->GetID("#COLLAPSE"), title_bar_rect.Min, node)) diff --git a/imgui_internal.h b/imgui_internal.h index de762b5206ba..a90ff53fcb5b 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1285,6 +1285,7 @@ struct IMGUI_API ImGuiWindow bool Appearing; // Set during the frame where the window is appearing (or re-appearing) bool Hidden; // Do not display (== (HiddenFramesForResize > 0) || bool HasCloseButton; // Set when the window has a close button (p_open != NULL) + signed char ResizeBorderHeld; // Current border being held for resize (-1: none, otherwise 0-3) short BeginCount; // Number of Begin() during the current frame (generally 0 or 1, 1+ if appending via multiple Begin/End pairs) short BeginOrderWithinParent; // Order within immediate parent window, if we are a child window. Otherwise 0. short BeginOrderWithinContext; // Order within entire imgui context. This is mostly used for debugging submission order related issues. diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 17d87f48f17e..b547f82f4aed 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -5551,7 +5551,7 @@ bool ImGui::BeginMenuBar() // We don't clip with current window clipping rectangle as it is already set to the area below. However we clip with window full rect. // We remove 1 worth of rounding to Max.x to that text in long menus and small windows don't tend to display over the lower-right rounded area, which looks particularly glitchy. ImRect bar_rect = window->MenuBarRect(); - ImRect clip_rect(ImFloor(bar_rect.Min.x + 0.5f), ImFloor(bar_rect.Min.y + window->WindowBorderSize + 0.5f), ImFloor(ImMax(bar_rect.Min.x, bar_rect.Max.x - window->WindowRounding) + 0.5f), ImFloor(bar_rect.Max.y + 0.5f)); + ImRect clip_rect(ImFloor(bar_rect.Min.x + window->WindowBorderSize + 0.5f), ImFloor(bar_rect.Min.y + window->WindowBorderSize + 0.5f), ImFloor(ImMax(bar_rect.Min.x, bar_rect.Max.x - ImMax(window->WindowRounding, window->WindowBorderSize)) + 0.5f), ImFloor(bar_rect.Max.y + 0.5f)); clip_rect.ClipWith(window->OuterRectClipped); PushClipRect(clip_rect.Min, clip_rect.Max, false); @@ -5931,8 +5931,8 @@ bool ImGui::BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& tab_bar_bb, ImG const float y = tab_bar->BarRect.Max.y - 1.0f; if (dock_node != NULL) { - const float separator_min_x = dock_node->Pos.x; - const float separator_max_x = dock_node->Pos.x + dock_node->Size.x; + const float separator_min_x = dock_node->Pos.x + window->WindowBorderSize; + const float separator_max_x = dock_node->Pos.x + dock_node->Size.x - window->WindowBorderSize; window->DrawList->AddLine(ImVec2(separator_min_x, y), ImVec2(separator_max_x, y), col, 1.0f); } else From 737a3644fc6249d7bf53d3e74120dde1ac226e41 Mon Sep 17 00:00:00 2001 From: omar Date: Sun, 27 Jan 2019 14:57:07 +0100 Subject: [PATCH 419/828] Removed trailing spaces (docking branch) --- docs/CHANGELOG.txt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 1fd983b9138e..e146fb25c708 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -53,11 +53,11 @@ HOW TO UPDATE? Breaking Changes: -- IMPORTANT: When multi-viewports are enabled (with io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable), +- IMPORTANT: When multi-viewports are enabled (with io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable), all coordinates/positions will be in your natural OS coordinates space. It means that: - - Reference to hard-coded positions such as in SetNextWindowPos(ImVec2(0,0)) are _probably_ not what you want anymore. + - Reference to hard-coded positions such as in SetNextWindowPos(ImVec2(0,0)) are _probably_ not what you want anymore. Use GetMainViewport()->Pos to offset hard-coded positions, e.g. SetNextWindowPos(GetMainViewport()->Pos). - - Likewise io.MousePos and GetMousePos() will use OS coordinates. + - Likewise io.MousePos and GetMousePos() will use OS coordinates. If you query mouse positions to interact with non-imgui coordinates you will need to offset them. e.g. subtract GetWindowViewport()->Pos. - IO: Moved IME support functions from io.ImeSetInputScreenPosFn, io.ImeWindowHandle to the PlatformIO api. @@ -72,7 +72,7 @@ Other changes: - Added GetMainViewport(). - Added GetWindowViewport(), SetNextWindowViewport(). - Added GetWindowDpiScale(). -- Added GetOverlayDrawList(ImGuiViewport* viewport). +- Added GetOverlayDrawList(ImGuiViewport* viewport). The no-parameter version of GetOverlayDrawList() return the overlay for the current window's viewport. - Added UpdatePlatformWindows(), RenderPlatformWindows(), DestroyPlatformWindows() for usage for application core. - Added FindViewportByID(), FindViewportByPlatformHandle() for usage by back-ends. @@ -82,7 +82,7 @@ Other changes: - Added ImGuiViewport structure, ImGuiViewportFlags flags. - Added ImGuiWindowClass and SetNextWindowClass() for passing viewport related hints to the OS/platform back-end. - Examples: Renderer: OpenGL2, OpenGL3, DirectX11, DirectX12, Vulkan: Added support for multi-viewports. -- Examples: Platforms: Win32, GLFW, SDL2: Added support for multi-viewports. +- Examples: Platforms: Win32, GLFW, SDL2: Added support for multi-viewports. Note that Linux/Mac still have inconsistent support for multi-viewports. If you want to help see https://github.com/ocornut/imgui/issues/2117. @@ -518,7 +518,7 @@ VIEWPORT BRANCH - Viewport: Added support for multi-viewport [...] blah blah - Viewport: Rendering: the ImDrawData structure now contains 'DisplayPos' and 'DisplaySize' fields. To support multi-viewport, you need to use those values when creating your orthographic projection matrix. Use 'draw_data->DisplaySize' instead of 'io.DisplaySize', and 'draw_data->DisplayPos' instead of (0,0) as the upper-left point. - You also need to subtract 'draw_data->DisplayPos' from your scissor rectangles, as scissor rectangles are specified in the space of your target viewport. + You also need to subtract 'draw_data->DisplayPos' from your scissor rectangles, as scissor rectangles are specified in the space of your target viewport. - Examples: Back-ends have been refactored to separate the platform code (e.g. Win32, Glfw, SDL2) from the renderer code (e.g. DirectX11, OpenGL3, Vulkan). before: imgui_impl_dx11.cpp --> after: imgui_impl_win32.cpp + imgui_impl_dx11.cpp before: imgui_impl_dx12.cpp --> after: imgui_impl_win32.cpp + imgui_impl_dx12.cpp @@ -533,7 +533,7 @@ VIEWPORT BRANCH amount of redundancy accross files was becoming too difficult to maintain. - Some frameworks (such as the Allegro, Marmalade) handle both the "platform" and "rendering" part, and your custom engine may as well. - Each example still has its own main.cpp which you may refer you to understand how to initialize and glue everything together. - - Examples: Win32: Added DPI-related helpers to access DPI features _without_ requiring the latest Windows SDK at compile time, and _without_ requiring Windows 10 at runtime. + - Examples: Win32: Added DPI-related helpers to access DPI features _without_ requiring the latest Windows SDK at compile time, and _without_ requiring Windows 10 at runtime. - Examples: Platforms currently supporting multi-viewport: Win32, Glfw, SDL2. - Examples: Renderers currently supporting multi-viewport: DirectX10, DirectX11, OpenGL2, OpenGL3, Vulkan (WIP). - Examples: All imgui_impl_xxx files now have an individual Changelog at the top of the file, making it easier to follow how back-ends are evolving. From 2ccc6d2ed1dc1e54b4713e5ca6b9af082790f912 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 29 Jan 2019 15:40:18 +0100 Subject: [PATCH 420/828] Docking: Exposing extra flag in Configuration panel. Moved some forgotten Changelog entries at the right place. --- docs/CHANGELOG.txt | 46 +++++++++++----------------------------------- imgui.cpp | 4 ++-- imgui_demo.cpp | 3 +++ 3 files changed, 16 insertions(+), 37 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index e146fb25c708..4237bf917b30 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -33,6 +33,8 @@ HOW TO UPDATE? DOCKING BRANCH (In Progress) ----------------------------------------------------------------------- +DOCKING FEATURES + - Added Docking system: [BETA] (#2109, #351) - Added ImGuiConfigFlags_DockingEnable flag to enable Docking. Set with `io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;`. @@ -46,10 +48,7 @@ HOW TO UPDATE? - Style: Added ImGuiCol_DockingPreview, ImGuiCol_DockingEmptyBg colors. - Demo: Added "DockSpace" example app showcasing use of explicit dockspace nodes. - ------------------------------------------------------------------------ - VIEWPORT BRANCH (In Progress) ------------------------------------------------------------------------ +MULTI-VIEWPORT FEATURES (was previously 'viewport' branch, merged into 'docking') Breaking Changes: @@ -60,6 +59,10 @@ Breaking Changes: - Likewise io.MousePos and GetMousePos() will use OS coordinates. If you query mouse positions to interact with non-imgui coordinates you will need to offset them. e.g. subtract GetWindowViewport()->Pos. +- Render function: the ImDrawData structure now contains 'DisplayPos' and 'DisplaySize' fields. + To support multi-viewport, you need to use those values when creating your orthographic projection matrix. + Use 'draw_data->DisplaySize' instead of 'io.DisplaySize', and 'draw_data->DisplayPos' instead of (0,0) as the upper-left point. + You need to subtract 'draw_data->DisplayPos' from your scissor rectangles to convert them from global coordinates to frame-buffer coordinates. - IO: Moved IME support functions from io.ImeSetInputScreenPosFn, io.ImeWindowHandle to the PlatformIO api. - IO: Removed io.DisplayVisibleMin, io.DisplayVisibleMax settings (they were marked obsoleted, used to clip within the (0,0)..(DisplaySize) range). @@ -84,6 +87,10 @@ Other changes: - Examples: Renderer: OpenGL2, OpenGL3, DirectX11, DirectX12, Vulkan: Added support for multi-viewports. - Examples: Platforms: Win32, GLFW, SDL2: Added support for multi-viewports. Note that Linux/Mac still have inconsistent support for multi-viewports. If you want to help see https://github.com/ocornut/imgui/issues/2117. +- Examples: Win32: Added DPI-related helpers to access DPI features without requiring the latest Windows SDK at compile time, + and without requiring Windows 10 at runtime. +- Examples: Vulkan: Added various optional helpers in imgui_impl_vulkan.h (they are used for multi-viewport support) + to make the examples main.cpp easier to read. ----------------------------------------------------------------------- @@ -512,37 +519,6 @@ The gamepad/keyboard navigation branch (which has been in the work since July 20 Gamepad/keyboard navigation is still marked as Beta and has to be enabled explicitly. Various internal refactoring have also been done, as part of the navigation work and as part of the upcoming viewport/docking work. -VIEWPORT BRANCH -(IN PROGRESS, WILL MERGE INTO THE MAIN LISTS WHEN WE MERGE THE BRANCH) - - - Viewport: Added support for multi-viewport [...] blah blah - - Viewport: Rendering: the ImDrawData structure now contains 'DisplayPos' and 'DisplaySize' fields. To support multi-viewport, you need to use those values when - creating your orthographic projection matrix. Use 'draw_data->DisplaySize' instead of 'io.DisplaySize', and 'draw_data->DisplayPos' instead of (0,0) as the upper-left point. - You also need to subtract 'draw_data->DisplayPos' from your scissor rectangles, as scissor rectangles are specified in the space of your target viewport. - - Examples: Back-ends have been refactored to separate the platform code (e.g. Win32, Glfw, SDL2) from the renderer code (e.g. DirectX11, OpenGL3, Vulkan). - before: imgui_impl_dx11.cpp --> after: imgui_impl_win32.cpp + imgui_impl_dx11.cpp - before: imgui_impl_dx12.cpp --> after: imgui_impl_win32.cpp + imgui_impl_dx12.cpp - before: imgui_impl_glfw_gl3.cpp --> after: imgui_impl_glfw.cpp + imgui_impl_opengl2.cpp - before: imgui_impl_glfw_vulkan.cpp --> after: imgui_impl_glfw.cpp + imgui_impl_vulkan.cpp - before: imgui_impl_sdl_gl3.cpp --> after: imgui_impl_sdl2.cpp + imgui_impl_opengl2.cpp - before: imgui_impl_sdl_gl3.cpp --> after: imgui_impl_sdl2.cpp + imgui_impl_opengl3.cpp - etc. - - The idea is what we can now easily combine and maintain back-ends and reduce code redundancy. Integration of imgui into a new/custom engine may also - be easier as there is less overlap between "windowing / inputs" and "rendering" code, so you may study or grab one half of the code and not the other. - - This change was motivated by the fact that adding support for multi-viewport requires more work from the platform and renderer back-ends, and the - amount of redundancy accross files was becoming too difficult to maintain. - - Some frameworks (such as the Allegro, Marmalade) handle both the "platform" and "rendering" part, and your custom engine may as well. - - Each example still has its own main.cpp which you may refer you to understand how to initialize and glue everything together. - - Examples: Win32: Added DPI-related helpers to access DPI features _without_ requiring the latest Windows SDK at compile time, and _without_ requiring Windows 10 at runtime. - - Examples: Platforms currently supporting multi-viewport: Win32, Glfw, SDL2. - - Examples: Renderers currently supporting multi-viewport: DirectX10, DirectX11, OpenGL2, OpenGL3, Vulkan (WIP). - - Examples: All imgui_impl_xxx files now have an individual Changelog at the top of the file, making it easier to follow how back-ends are evolving. - - Examples: Vulkan: Added various optional helpers in imgui_impl_vulkan.h (they are used for multi-viewport support) to make the examples main.cpp easier to read. - - Examples: Allegro: Renamed imgui_impl_a5.xxx files to imgui_impl_allegro5.xxx, ImGui_ImplA5_** symbols to ImGui_ImplAllegro5_xxx. - - Examples: Vulkan+SDL: Added a Vulkan+SDL example. (#1367) [@gmueckl] - - Metrics: Added a "Show window begin order" checkbox to visualize the order windows are submitted. - - Internal: Settings: Added ReadCloseFn handler to be able to patch/alter a loaded object after all the fields are known. - Breaking Changes: - Obsoleted the io.RenderDrawListsFn callback, you can call your graphics engine render function after ImGui::Render(). diff --git a/imgui.cpp b/imgui.cpp index 10038d5afd50..d4b76a134f45 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10697,10 +10697,9 @@ void ImGui::DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node) IM_ASSERT(node->IsLeafNode()); IM_ASSERT(node->Windows.Size >= 1); - // In the case of a root node or central node, the node will have to stay in place. Create a new node to receive the payload. - // Otherwise delete the previous node by merging the other sibling back into the parent node. if (node->IsRootNode() || node->IsCentralNode) { + // In the case of a root node or central node, the node will have to stay in place. Create a new node to receive the payload. ImGuiDockNode* new_node = DockContextAddNode(ctx, 0); DockNodeMoveWindows(new_node, node); DockSettingsMoveDockReferencesInInactiveWindow(node->ID, new_node->ID); @@ -10710,6 +10709,7 @@ void ImGui::DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node) } else { + // Otherwise delete the previous node by merging the other sibling back into the parent node. IM_ASSERT(node->ParentNode->ChildNodes[0] == node || node->ParentNode->ChildNodes[1] == node); int index_in_parent = (node->ParentNode->ChildNodes[0] == node) ? 0 : 1; node->ParentNode->ChildNodes[index_in_parent] = NULL; diff --git a/imgui_demo.cpp b/imgui_demo.cpp index a78075fbc918..c807f5d23905 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -355,6 +355,8 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::SameLine(); ShowHelpMarker("Enable docking when holding Shift only (allows to drop in wider space, reduce visual noise)"); ImGui::Checkbox("io.ConfigDockingTabBarOnSingleWindows", &io.ConfigDockingTabBarOnSingleWindows); ImGui::SameLine(); ShowHelpMarker("Create a docking node and tab-bar on single floating windows."); + ImGui::Checkbox("io.ConfigDockingTransparentPayload", &io.ConfigDockingTransparentPayload); + ImGui::SameLine(); ShowHelpMarker("Make window or viewport transparent when docking and only display docking boxes on the target viewport. Useful if rendering of multiple viewport cannot be synced. Best used with ConfigViewportsNoAutoMerge."); ImGui::Unindent(); } @@ -387,6 +389,7 @@ void ImGui::ShowDemoWindow(bool* p_open) if (ImGui::TreeNode("Backend Flags")) { + ShowHelpMarker("Those flags are set by the back-ends (imgui_impl_xxx files) to specify their capabilities."); ImGuiBackendFlags backend_flags = io.BackendFlags; // Make a local copy to avoid modifying the back-end flags. ImGui::CheckboxFlags("io.BackendFlags: HasGamepad", (unsigned int *)&backend_flags, ImGuiBackendFlags_HasGamepad); ImGui::CheckboxFlags("io.BackendFlags: HasMouseCursors", (unsigned int *)&backend_flags, ImGuiBackendFlags_HasMouseCursors); From 86d3bba157a59605c40fe221179666a3eea1b28d Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 29 Jan 2019 15:40:38 +0100 Subject: [PATCH 421/828] Added ImGuiDockNodeFlags_AutoHideTabBar. (#2109) --- imgui.cpp | 11 +++++++++-- imgui.h | 5 +++-- imgui_demo.cpp | 1 + imgui_internal.h | 1 + 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index d4b76a134f45..2ec30c32942c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10743,7 +10743,7 @@ ImGuiDockNode::ImGuiDockNode(ImGuiID id) InitFromFirstWindowPosSize = InitFromFirstWindowViewport = false; IsVisible = true; IsFocused = IsCentralNode = IsHiddenTabBar = HasCloseButton = HasCollapseButton = false; - WantCloseAll = WantLockSizeOnce = WantMouseMove = WantHiddenTabBarToggle = false; + WantCloseAll = WantLockSizeOnce = WantMouseMove = WantHiddenTabBarUpdate = WantHiddenTabBarToggle = false; } ImGuiDockNode::~ImGuiDockNode() @@ -10774,6 +10774,7 @@ static void ImGui::DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window, b IM_ASSERT(window->DockNode == NULL || window->DockNodeAsHost == NULL); node->Windows.push_back(window); + node->WantHiddenTabBarUpdate = true; window->DockNode = node; window->DockId = node->ID; window->DockIsActive = (node->Windows.Size > 1); @@ -10841,6 +10842,7 @@ static void ImGui::DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window node->VisibleWindow = NULL; // Remove tab and possibly tab bar + node->WantHiddenTabBarUpdate = true; if (node->TabBar) { TabBarRemoveTab(node->TabBar, window->ID); @@ -11033,6 +11035,11 @@ static void ImGui::DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* nod window_n--; } + // Auto-hide tab bar option + if (node->WantHiddenTabBarUpdate && node->Windows.Size == 1 && (node->Flags & ImGuiDockNodeFlags_AutoHideTabBar) && !node->IsHiddenTabBar) + node->WantHiddenTabBarToggle = true; + node->WantHiddenTabBarUpdate = false; + // Apply toggles at a single point of the frame (here!) if (node->Windows.Size > 1) node->IsHiddenTabBar = false; @@ -12863,7 +12870,7 @@ void ImGui::BeginAsDockableDragDropTarget(ImGuiWindow* window) else allow_null_target_node = true; // Dock into a regular window - const ImRect explicit_target_rect = (target_node && target_node->TabBar) ? target_node->TabBar->BarRect : ImRect(window->Pos, window->Pos + ImVec2(window->Size.x, GetFrameHeight())); + const ImRect explicit_target_rect = (target_node && target_node->TabBar && !target_node->IsHiddenTabBar) ? target_node->TabBar->BarRect : ImRect(window->Pos, window->Pos + ImVec2(window->Size.x, GetFrameHeight())); const bool is_explicit_target = g.IO.ConfigDockingWithShift || IsMouseHoveringRect(explicit_target_rect.Min, explicit_target_rect.Max); // Preview docking request and find out split direction/ratio diff --git a/imgui.h b/imgui.h index 7ec77c495288..00f8cd5b9544 100644 --- a/imgui.h +++ b/imgui.h @@ -879,7 +879,8 @@ enum ImGuiDockNodeFlags_ ImGuiDockNodeFlags_NoDockingInCentralNode = 1 << 3, // Disable docking inside the Central Node, which will be always kept empty. //ImGuiDockNodeFlags_NoLayoutChanges = 1 << 4, // Disable adding/removing nodes interactively. Useful with programatically setup dockspaces. ImGuiDockNodeFlags_NoResize = 1 << 5, // Disable resizing child nodes using the splitter/separators. Useful with programatically setup dockspaces. - ImGuiDockNodeFlags_PassthruDockspace = 1 << 6 // 1) DockSpace() will render a ImGuiCol_WindowBg background covering everything excepted the Central Node when empty. Meaning the host window should probably use SetNextWindowBgAlpha(0.0f) prior to Begin() when using this. 2) When Central Node is empty: let inputs pass-through + won't display a DockingEmptyBg background. + ImGuiDockNodeFlags_PassthruDockspace = 1 << 6, // Enable passthru dockspace: 1) DockSpace() will render a ImGuiCol_WindowBg background covering everything excepted the Central Node when empty. Meaning the host window should probably use SetNextWindowBgAlpha(0.0f) prior to Begin() when using this. 2) When Central Node is empty: let inputs pass-through + won't display a DockingEmptyBg background. + ImGuiDockNodeFlags_AutoHideTabBar = 1 << 7 // Tab bar will automatically hide when there is a single window in the dock node. }; // Flags for ImGui::IsWindowFocused() @@ -1373,7 +1374,7 @@ struct ImGuiIO bool ConfigDockingNoSplit; // = false // Simplified docking mode: disable window splitting, so docking is limited to merging multiple windows together into tab-bars. bool ConfigDockingWithShift; // = false // Enable docking with holding Shift key (reduce visual noise, allows dropping in wider space) bool ConfigDockingTabBarOnSingleWindows; // = false // [BETA] Make every single floating window display within a docking node. - bool ConfigDockingTransparentPayload;// = false // [BETA] Make window or viewport transparent when docking and only display docking boxes on the target viewport. Useful if rendering of multiple viewport can be synced. Best used with ImGuiConfigFlags_ViewportsNoMerge. + bool ConfigDockingTransparentPayload;// = false // [BETA] Make window or viewport transparent when docking and only display docking boxes on the target viewport. Useful if rendering of multiple viewport cannot be synced. Best used with ConfigViewportsNoAutoMerge. // Viewport options (when ImGuiConfigFlags_ViewportsEnable is set) bool ConfigViewportsNoAutoMerge; // = false; // Set to make all floating imgui windows always create their own viewport. Otherwise, they are merged into the main host viewports when overlapping it. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index c807f5d23905..9c082636bb9a 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -4100,6 +4100,7 @@ void ShowExampleAppDockSpace(bool* p_open) if (ImGui::MenuItem("Flag: NoDockingInCentralNode", "", (opt_flags & ImGuiDockNodeFlags_NoDockingInCentralNode) != 0)) opt_flags ^= ImGuiDockNodeFlags_NoDockingInCentralNode; if (ImGui::MenuItem("Flag: NoResize", "", (opt_flags & ImGuiDockNodeFlags_NoResize) != 0)) opt_flags ^= ImGuiDockNodeFlags_NoResize; if (ImGui::MenuItem("Flag: PassthruDockspace", "", (opt_flags & ImGuiDockNodeFlags_PassthruDockspace) != 0)) opt_flags ^= ImGuiDockNodeFlags_PassthruDockspace; + if (ImGui::MenuItem("Flag: AutoHideTabBar", "", (opt_flags & ImGuiDockNodeFlags_AutoHideTabBar) != 0)) opt_flags ^= ImGuiDockNodeFlags_AutoHideTabBar; ImGui::Separator(); if (ImGui::MenuItem("Close DockSpace", NULL, false, p_open != NULL)) *p_open = false; diff --git a/imgui_internal.h b/imgui_internal.h index 7fb41c28c491..6ee34b71bad1 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -854,6 +854,7 @@ struct ImGuiDockNode bool WantCloseAll :1; // Set when closing all tabs at once. bool WantLockSizeOnce :1; bool WantMouseMove :1; // After a node extraction we need to transition toward moving the newly created host window + bool WantHiddenTabBarUpdate :1; bool WantHiddenTabBarToggle :1; ImGuiDockNode(ImGuiID id); From 37fb531d1c8d4a1f4fa34e1e8ba4d8c9ddfda1f2 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 29 Jan 2019 18:54:56 +0100 Subject: [PATCH 422/828] Docking: Comments and tidying up (should be no-op) --- imgui.cpp | 90 +++++++++++++++++++++++++++---------------------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 2ec30c32942c..cbd9a3a18aa2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10062,7 +10062,7 @@ void ImGui::EndDragDropTarget() // Docking: ImGuiDockNode // Docking: ImGuiDockNode Tree manipulation functions // Docking: Public Functions (SetWindowDock, DockSpace) -// Docking: Public Builder Functions +// Docking: Builder Functions // Docking: Begin/End Functions (called from Begin/End) // Docking: Settings //----------------------------------------------------------------------------- @@ -10162,9 +10162,9 @@ namespace ImGui static void DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node); static void DockContextPruneUnusedSettingsNodes(ImGuiContext* ctx); static ImGuiDockNode* DockContextFindNodeByID(ImGuiContext* ctx, ImGuiID id); - static void DockContextClearNodes(ImGuiContext* ctx, ImGuiID root_id, bool clear_persistent_docking_refs); // Set root_id==0 to clear all + static void DockContextClearNodes(ImGuiContext* ctx, ImGuiID root_id, bool clear_persistent_docking_refs); // Use root_id==0 to clear all static void DockContextBuildNodesFromSettings(ImGuiContext* ctx, ImGuiDockNodeSettings* node_settings_array, int node_settings_count); - static void DockContextBuildAddWindowsToNodes(ImGuiContext* ctx, ImGuiID root_id); // Use root_id==0 to add all + static void DockContextBuildAddWindowsToNodes(ImGuiContext* ctx, ImGuiID root_id); // Use root_id==0 to add all // ImGuiDockNode static void DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window, bool add_to_tab_bar); @@ -10197,8 +10197,8 @@ namespace ImGui static ImGuiDockNode* DockNodeTreeFindFallbackLeafNode(ImGuiDockNode* node); // Settings - static void DockSettingsMoveDockReferencesInInactiveWindow(ImGuiID old_dock_id, ImGuiID new_dock_id); - static void DockSettingsRemoveReferencesToNodes(ImGuiID* node_ids, int node_ids_count); + static void DockSettingsRenameNodeReferences(ImGuiID old_node_id, ImGuiID new_node_id); + static void DockSettingsRemoveNodeReferences(ImGuiID* node_ids, int node_ids_count); static ImGuiDockNodeSettings* DockSettingsFindNodeSettings(ImGuiContext* ctx, ImGuiID node_id); static void* DockSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name); static void DockSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line); @@ -10212,8 +10212,8 @@ namespace ImGui // or we always hold the entire docking node tree. Nodes are frequently hidden, e.g. if the window(s) or child nodes they host are not active. // At boot time only, we run a simple GC to remove nodes that have no references. // Because dock node settings (which are small, contiguous structures) are always mirrored by their corresponding dock nodes (more complete structures), -// we can also very easily recreate the nodes from scratch given the settings data (this is what DockContextRebuild() does). -// This is convenient as docking reconfiguration can be implemented by mostly poking at the simpler setttings data. +// we can also very easily recreate the nodes from scratch given the settings data (this is what DockContextRebuild() does). +// This is convenient as docking reconfiguration can be implemented by mostly poking at the simpler settings data. //----------------------------------------------------------------------------- void ImGui::DockContextInitialize(ImGuiContext* ctx) @@ -10280,8 +10280,9 @@ void ImGui::DockContextNewFrameUpdateUndocking(ImGuiContext* ctx) DockContextClearNodes(ctx, 0, true); return; } + + // Setting NoSplit at runtime merges all nodes if (g.IO.ConfigDockingNoSplit) - { for (int n = 0; n < dc->Nodes.Data.Size; n++) if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p) if (node->IsRootNode() && node->IsSplitNode()) @@ -10289,8 +10290,8 @@ void ImGui::DockContextNewFrameUpdateUndocking(ImGuiContext* ctx) DockBuilderRemoveNodeChildNodes(node->ID); //dc->WantFullRebuild = true; } - } - + + // Process full rebuild #if 0 if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_C))) dc->WantFullRebuild = true; @@ -10342,7 +10343,7 @@ static ImGuiDockNode* ImGui::DockContextFindNodeByID(ImGuiContext* ctx, ImGuiID static ImGuiID ImGui::DockContextGenNodeID(ImGuiContext* ctx) { // Generate an ID for new node (the exact ID value doesn't matter as long as it is not already used) - // FIXME-OPT: This is suboptimal, even if the node count is small enough not to be a worry. We should poke in ctx->Nodes to find a suitable ID faster. + // FIXME-OPT FIXME-DOCKING: This is suboptimal, even if the node count is small enough not to be a worry. We should poke in ctx->Nodes to find a suitable ID faster. ImGuiID id = 0x0001; while (DockContextFindNodeByID(ctx, id) != NULL) id++; @@ -10400,11 +10401,11 @@ static int IMGUI_CDECL DockNodeComparerDepthMostFirst(const void* lhs, const voi return ImGui::DockNodeGetDepth(b) - ImGui::DockNodeGetDepth(a); } -// Pre C++0x doesn't allow us to use a local type (without linkage) as template parameter, so we moved this here. +// Pre C++0x doesn't allow us to use a function-local type (without linkage) as template parameter, so we moved this here. struct ImGuiDockContextPruneNodeData { - int CountWindows, CountChildWindows, CountChildNodes; - ImGuiID RootID; + int CountWindows, CountChildWindows, CountChildNodes; + ImGuiID RootID; ImGuiDockContextPruneNodeData() { CountWindows = CountChildWindows = CountChildNodes = 0; RootID = 0; } }; @@ -10453,7 +10454,7 @@ static void ImGui::DockContextPruneUnusedSettingsNodes(ImGuiContext* ctx) remove |= (data_root->CountChildWindows == 0); if (remove) { - DockSettingsRemoveReferencesToNodes(&settings->ID, 1); + DockSettingsRemoveNodeReferences(&settings->ID, 1); settings->ID = 0; } } @@ -10464,24 +10465,24 @@ static void ImGui::DockContextBuildNodesFromSettings(ImGuiContext* ctx, ImGuiDoc // Build nodes for (int node_n = 0; node_n < node_settings_count; node_n++) { - ImGuiDockNodeSettings* node_settings = &node_settings_array[node_n]; - if (node_settings->ID == 0) + ImGuiDockNodeSettings* settings = &node_settings_array[node_n]; + if (settings->ID == 0) continue; - ImGuiDockNode* node = DockContextAddNode(ctx, node_settings->ID); - node->ParentNode = node_settings->ParentID ? DockContextFindNodeByID(ctx, node_settings->ParentID) : NULL; - node->Pos = ImVec2(node_settings->Pos.x, node_settings->Pos.y); - node->Size = ImVec2(node_settings->Size.x, node_settings->Size.y); - node->SizeRef = ImVec2(node_settings->SizeRef.x, node_settings->SizeRef.y); + ImGuiDockNode* node = DockContextAddNode(ctx, settings->ID); + node->ParentNode = settings->ParentID ? DockContextFindNodeByID(ctx, settings->ParentID) : NULL; + node->Pos = ImVec2(settings->Pos.x, settings->Pos.y); + node->Size = ImVec2(settings->Size.x, settings->Size.y); + node->SizeRef = ImVec2(settings->SizeRef.x, settings->SizeRef.y); if (node->ParentNode && node->ParentNode->ChildNodes[0] == NULL) node->ParentNode->ChildNodes[0] = node; else if (node->ParentNode && node->ParentNode->ChildNodes[1] == NULL) node->ParentNode->ChildNodes[1] = node; - node->SelectedTabID = node_settings->SelectedTabID; - node->SplitAxis = node_settings->SplitAxis; - if (node_settings->IsDockSpace) + node->SelectedTabID = settings->SelectedTabID; + node->SplitAxis = settings->SplitAxis; + if (settings->IsDockSpace) node->Flags |= ImGuiDockNodeFlags_Dockspace; - node->IsCentralNode = node_settings->IsCentralNode != 0; - node->IsHiddenTabBar = node_settings->IsHiddenTabBar != 0; + node->IsCentralNode = settings->IsCentralNode != 0; + node->IsHiddenTabBar = settings->IsHiddenTabBar != 0; // Bind host window immediately if it already exist (in case of a rebuild) // This is useful as the RootWindowForTitleBarHighlight links necessary to highlight the currently focused node requires node->HostWindow to be set. @@ -10576,7 +10577,6 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) } // FIXME-DOCK: When we are trying to dock an existing single-window node into a loose window, transfer Node ID as well - if (target_node) IM_ASSERT(target_node->LastFrameAlive < g.FrameCount); if (target_node && target_window && target_node == target_window->DockNodeAsHost) @@ -10601,15 +10601,11 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) { // Split into one, one side will be our payload node unless we are dropping a loose window const ImGuiAxis split_axis = (split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y; - const int split_inheritor_child_idx = (split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Up) ? 1 : 0; + const int split_inheritor_child_idx = (split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Up) ? 1 : 0; // Current contents will be moved to the opposite side const float split_ratio = req->DockSplitRatio; DockNodeTreeSplit(ctx, target_node, split_axis, split_inheritor_child_idx, split_ratio, payload_node); // payload_node may be NULL here! - ImGuiDockNode* inheritor_node = target_node->ChildNodes[split_inheritor_child_idx]; ImGuiDockNode* new_node = target_node->ChildNodes[split_inheritor_child_idx ^ 1]; new_node->HostWindow = target_node->HostWindow; - inheritor_node->IsCentralNode = target_node->IsCentralNode; - inheritor_node->IsHiddenTabBar = target_node->IsHiddenTabBar; - target_node->IsCentralNode = false; target_node = new_node; } target_node->IsHiddenTabBar = false; @@ -10640,7 +10636,7 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) IM_ASSERT(visible_node->TabBar->Tabs.Size > 0); DockNodeMoveWindows(target_node, visible_node); DockNodeMoveWindows(visible_node, target_node); - DockSettingsMoveDockReferencesInInactiveWindow(target_node->ID, visible_node->ID); + DockSettingsRenameNodeReferences(target_node->ID, visible_node->ID); } if (target_node->IsCentralNode) { @@ -10658,7 +10654,7 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) { const ImGuiID payload_dock_id = payload_node->ID; DockNodeMoveWindows(target_node, payload_node); - DockSettingsMoveDockReferencesInInactiveWindow(payload_dock_id, target_node->ID); + DockSettingsRenameNodeReferences(payload_dock_id, target_node->ID); } DockContextRemoveNode(ctx, payload_node, true); } @@ -10669,7 +10665,7 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) target_node->VisibleWindow = payload_window; DockNodeAddWindow(target_node, payload_window, true); if (payload_dock_id != 0) - DockSettingsMoveDockReferencesInInactiveWindow(payload_dock_id, target_node->ID); + DockSettingsRenameNodeReferences(payload_dock_id, target_node->ID); } } @@ -10702,7 +10698,7 @@ void ImGui::DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node) // In the case of a root node or central node, the node will have to stay in place. Create a new node to receive the payload. ImGuiDockNode* new_node = DockContextAddNode(ctx, 0); DockNodeMoveWindows(new_node, node); - DockSettingsMoveDockReferencesInInactiveWindow(node->ID, new_node->ID); + DockSettingsRenameNodeReferences(node->ID, new_node->ID); for (int n = 0; n < new_node->Windows.Size; n++) UpdateWindowParentAndRootLinks(new_node->Windows[n], new_node->Windows[n]->Flags, NULL); new_node->WantMouseMove = true; @@ -11911,6 +11907,10 @@ void ImGui::DockNodeTreeSplit(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImG DockNodeMoveWindows(parent_node->ChildNodes[split_inheritor_child_idx], parent_node); DockNodeTreeUpdatePosSize(parent_node, parent_node->Pos, parent_node->Size); + + child_inheritor->IsCentralNode = parent_node->IsCentralNode; + child_inheritor->IsHiddenTabBar = parent_node->IsHiddenTabBar; + parent_node->IsCentralNode = false; } void ImGui::DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiDockNode* merge_lead_child) @@ -11931,12 +11931,12 @@ void ImGui::DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImG if (child_0) { DockNodeMoveWindows(parent_node, child_0); // Generally only 1 of the 2 child node will have windows - DockSettingsMoveDockReferencesInInactiveWindow(child_0->ID, parent_node->ID); + DockSettingsRenameNodeReferences(child_0->ID, parent_node->ID); } if (child_1) { DockNodeMoveWindows(parent_node, child_1); - DockSettingsMoveDockReferencesInInactiveWindow(child_1->ID, parent_node->ID); + DockSettingsRenameNodeReferences(child_1->ID, parent_node->ID); } DockNodeApplyPosSizeToWindows(parent_node); parent_node->InitFromFirstWindowPosSize = parent_node->InitFromFirstWindowViewport = false; @@ -12904,25 +12904,25 @@ void ImGui::BeginAsDockableDragDropTarget(ImGuiWindow* window) // Docking: Settings //----------------------------------------------------------------------------- -static void ImGui::DockSettingsMoveDockReferencesInInactiveWindow(ImGuiID old_dock_id, ImGuiID new_dock_id) +static void ImGui::DockSettingsRenameNodeReferences(ImGuiID old_node_id, ImGuiID new_node_id) { ImGuiContext& g = *GImGui; for (int window_n = 0; window_n < g.Windows.Size; window_n++) { ImGuiWindow* window = g.Windows[window_n]; - if (window->DockId == old_dock_id && window->DockNode == NULL) - window->DockId = new_dock_id; + if (window->DockId == old_node_id && window->DockNode == NULL) + window->DockId = new_node_id; } for (int settings_n = 0; settings_n < g.SettingsWindows.Size; settings_n++) // FIXME-OPT: We could remove this loop by storing the index in the map { ImGuiWindowSettings* window_settings = &g.SettingsWindows[settings_n]; - if (window_settings->DockId == old_dock_id) - window_settings->DockId = new_dock_id; + if (window_settings->DockId == old_node_id) + window_settings->DockId = new_node_id; } } // Remove references stored in ImGuiWindowSettings to the given ImGuiDockNodeSettings -static void ImGui::DockSettingsRemoveReferencesToNodes(ImGuiID* node_ids, int node_ids_count) +static void ImGui::DockSettingsRemoveNodeReferences(ImGuiID* node_ids, int node_ids_count) { ImGuiContext& g = *GImGui; int found = 0; From 8563ef3ce4de14745d75c7c2a9443e77f23f69c3 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 30 Jan 2019 21:13:07 +0100 Subject: [PATCH 423/828] Viewport: Popups by default merge into parent/host viewport as they have no decoration (same as menu/child). (#1542) --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index cbd9a3a18aa2..83470b3ee11c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7754,7 +7754,7 @@ static bool ImGui::GetWindowAlwaysWantOwnViewport(ImGuiWindow* window) ImGuiContext& g = *GImGui; if (g.IO.ConfigViewportsNoAutoMerge && (g.ConfigFlagsForFrame & ImGuiConfigFlags_ViewportsEnable)) if (!window->DockIsActive) - if ((window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip)) == 0) + if ((window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) == 0) return true; return false; } From e1143377c2621184551d1dcac136e7e4a76e409b Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 30 Jan 2019 21:21:59 +0100 Subject: [PATCH 424/828] Viewport: Added ImGuiViewportFlags_NoFocusOnClick + support in imgui_impl_win32. Made windows with no decoration always set the _NoFocus flags. (#1542, #2117) Fix e.g. clicking on protruding combo box stealing highlight from parent window with decoration. --- examples/example_win32_directx11/main.cpp | 4 ++++ examples/imgui_impl_win32.cpp | 4 ++++ imgui.cpp | 2 ++ imgui.h | 7 ++++--- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/examples/example_win32_directx11/main.cpp b/examples/example_win32_directx11/main.cpp index d0a8ae101f42..a8156cac862e 100644 --- a/examples/example_win32_directx11/main.cpp +++ b/examples/example_win32_directx11/main.cpp @@ -139,6 +139,10 @@ int main(int, char**) //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoMerge; io.ConfigFlags |= ImGuiConfigFlags_DpiEnableScaleFonts; // FIXME-DPI: THIS CURRENTLY DOESN'T WORK AS EXPECTED. DON'T USE IN USER APP! io.ConfigFlags |= ImGuiConfigFlags_DpiEnableScaleViewports; // FIXME-DPI + //io.ConfigViewportsNoAutoMerge = true; + //io.ConfigViewportsNoTaskBarIcon = false; + //io.ConfigDockingTabBarOnSingleWindows = true; + //io.ConfigDockingTransparentPayload = true; // Setup Dear ImGui style ImGui::StyleColorsDark(); diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index dc55c8e29329..7b60dd1b5a0e 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -737,6 +737,10 @@ static LRESULT CALLBACK ImGui_ImplWin32_WndProcHandler_PlatformWindow(HWND hWnd, case WM_SIZE: viewport->PlatformRequestResize = true; break; + case WM_MOUSEACTIVATE: + if (viewport->Flags & ImGuiViewportFlags_NoFocusOnClick) + return MA_NOACTIVATE; + break; case WM_NCHITTEST: // Let mouse pass-through the window. This will allow the back-end to set io.MouseHoveredViewport properly (which is OPTIONAL). // The ImGuiViewportFlags_NoInputs flag is set while dragging a viewport, as want to detect the window behind the one we are dragging. diff --git a/imgui.cpp b/imgui.cpp index 83470b3ee11c..9d84c7eef63f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5520,6 +5520,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) viewport_flags |= ImGuiViewportFlags_NoTaskBarIcon; if (g.IO.ConfigViewportsNoDecoration || (flags & (ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) != 0) viewport_flags |= ImGuiViewportFlags_NoDecoration; + if ((viewport_flags & ImGuiViewportFlags_NoDecoration) && (viewport_flags & ImGuiViewportFlags_NoTaskBarIcon)) + viewport_flags |= ImGuiViewportFlags_NoFocusOnAppearing | ImGuiViewportFlags_NoFocusOnClick; // We can overwrite viewport flags using ImGuiWindowClass (advanced users) // We don't default to the main viewport because. diff --git a/imgui.h b/imgui.h index 00f8cd5b9544..212c16b34399 100644 --- a/imgui.h +++ b/imgui.h @@ -2298,9 +2298,10 @@ enum ImGuiViewportFlags_ ImGuiViewportFlags_NoDecoration = 1 << 0, // Platform Window: Disable platform decorations: title bar, borders, etc. (generally set all windows, but if ImGuiConfigFlags_ViewportsDecoration is set we only set this on popups/tooltips) ImGuiViewportFlags_NoTaskBarIcon = 1 << 1, // Platform Window: Disable platform task bar icon (generally set on popups/tooltips, or all windows if ImGuiConfigFlags_ViewportsNoTaskBarIcon is set) ImGuiViewportFlags_NoFocusOnAppearing = 1 << 2, // Platform Window: Don't take focus when created. - ImGuiViewportFlags_NoInputs = 1 << 3, // Platform Window: Make mouse pass through so we can drag this window while peaking behind it. - ImGuiViewportFlags_NoRendererClear = 1 << 4, // Platform Window: Renderer doesn't need to clear the framebuffer ahead (because we will fill it entirely). - ImGuiViewportFlags_TopMost = 1 << 5 // Platform Window: Display on top (for tooltips only) + ImGuiViewportFlags_NoFocusOnClick = 1 << 3, // Platform Window: Don't take focus when clicked on. + ImGuiViewportFlags_NoInputs = 1 << 4, // Platform Window: Make mouse pass through so we can drag this window while peaking behind it. + ImGuiViewportFlags_NoRendererClear = 1 << 5, // Platform Window: Renderer doesn't need to clear the framebuffer ahead (because we will fill it entirely). + ImGuiViewportFlags_TopMost = 1 << 6 // Platform Window: Display on top (for tooltips only) }; // The viewports created and managed by imgui. The role of the platform back-end is to create the platform/OS windows corresponding to each viewport. From 578e15f006ef87ce6e5c28173146bb12f993cd42 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 31 Jan 2019 14:55:00 +0100 Subject: [PATCH 425/828] Docking: Removed unnecessary ImGuiTabItemFlags_DockedWindow internal flag. --- imgui.cpp | 2 +- imgui_internal.h | 3 +-- imgui_widgets.cpp | 4 ++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 9d84c7eef63f..ebef15a1afb5 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11474,7 +11474,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w continue; if (window->LastFrameActive + 1 >= g.FrameCount || !node_was_active) { - ImGuiTabItemFlags tab_item_flags = ImGuiTabItemFlags_DockedWindow; + ImGuiTabItemFlags tab_item_flags = 0; if (window->Flags & ImGuiWindowFlags_UnsavedDocument) tab_item_flags |= ImGuiTabItemFlags_UnsavedDocument; if (tab_bar->Flags & ImGuiTabBarFlags_NoCloseWithMiddleMouseButton) diff --git a/imgui_internal.h b/imgui_internal.h index 6ee34b71bad1..872d20ead98e 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1397,8 +1397,7 @@ enum ImGuiTabBarFlagsPrivate_ enum ImGuiTabItemFlagsPrivate_ { - ImGuiTabItemFlags_DockedWindow = 1 << 20, // [Docking] - ImGuiTabItemFlags_Unsorted = 1 << 22, // [Docking] Trailing tabs with the _Unsorted flag will be sorted based on the DockOrder of their Window. + ImGuiTabItemFlags_Unsorted = 1 << 20, // [Docking] Trailing tabs with the _Unsorted flag will be sorted based on the DockOrder of their Window. ImGuiTabItemFlags_Preview = 1 << 21 // [Docking] Display tab shape for docking preview (height is adjusted slightly to compensate for the yet missing tab bar) }; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 2b45e8dca39d..7590875a6ff2 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6507,7 +6507,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, { // Drag and drop: re-order tabs float drag_distance_from_edge_x = 0.0f; - if (!g.DragDropActive && ((tab_bar->Flags & ImGuiTabBarFlags_Reorderable) || (flags & ImGuiTabItemFlags_DockedWindow))) + if (!g.DragDropActive && ((tab_bar->Flags & ImGuiTabBarFlags_Reorderable) || (docked_window != NULL))) { // While moving a tab it will jump on the other side of the mouse, so we also test for MouseDelta.x if (g.IO.MouseDelta.x < 0.0f && g.IO.MousePos.x < bb.Min.x) @@ -6525,7 +6525,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, } // Extract a Dockable window out of it's tab bar - if (flags & ImGuiTabItemFlags_DockedWindow) + if (docked_window != NULL) { // We use a variable threshold to distinguish dragging tabs within a tab bar and extracting them out of the tab bar bool undocking_tab = (g.DragDropActive && g.DragDropPayload.SourceId == id); From 5536edede93dae8855e595f118d801a92680d9e2 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 31 Jan 2019 14:59:45 +0100 Subject: [PATCH 426/828] Docking: Fixed faulty undocking of windows with the _NoMove flag. (#2325, #2109) Whereas BeginAsDockableDragDropTarget could be reworked to filter, we simply set g.HoveredWindowUnderMovingWindow to be NULL when MovingWindow is not set, which was the initial intent. Also fixed some comments and removed unused braces in TabItemEx(). --- imgui.cpp | 10 +++++----- imgui_internal.h | 2 +- imgui_widgets.cpp | 30 ++++++++++++++---------------- 3 files changed, 20 insertions(+), 22 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index ebef15a1afb5..e82eefc9afb0 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3638,11 +3638,11 @@ void ImGui::NewFrame() g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame))) : FLT_MAX; // Undocking - // (needs to be before UpdateMouseMovingWindow so the window is already offset and following the mouse on the detaching frame) + // (needs to be before UpdateMouseMovingWindowNewFrame so the window is already offset and following the mouse on the detaching frame) DockContextNewFrameUpdateUndocking(&g); // Find hovered window - // (needs to be before UpdateMouseMovingWindow() so we fill g.HoveredWindowUnderMovingWindow on the mouse release frame) + // (needs to be before UpdateMouseMovingWindowNewFrame so we fill g.HoveredWindowUnderMovingWindow on the mouse release frame) UpdateHoveredWindowAndCaptureFlags(); // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering) @@ -4284,7 +4284,7 @@ static void FindHoveredWindow() g.HoveredWindow = hovered_window; g.HoveredRootWindow = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL; - g.HoveredWindowUnderMovingWindow = hovered_window_ignoring_moving_window; + g.HoveredWindowUnderMovingWindow = g.MovingWindow ? hovered_window_ignoring_moving_window : NULL; if (g.MovingWindow) g.MovingWindow->Viewport = moving_window_viewport; @@ -7434,7 +7434,7 @@ void ImGui::ClosePopupToLevel(int remaining, bool apply_focus_to_window_under) // FIXME: This code is faulty and we may want to eventually to replace or remove the 'apply_focus_to_window_under=true' path completely. // Instead of using g.OpenPopupStack[remaining-1].Window etc. we should find the highest root window that is behind the popups we are closing. // The current code will set focus to the parent of the popup window which is incorrect. - // It rarely manifested until now because UpdateMouseMovingWindow() would call FocusWindow() again on the clicked window, + // It rarely manifested until now because UpdateMouseMovingWindowNewFrame() would call FocusWindow() again on the clicked window, // leading to a chain of focusing A (clicked window) then B (parent window of the popup) then A again. // However if the clicked window has the _NoMove flag set we would be left with B focused. // For now, we have disabled this path when called from ClosePopupsOverWindow() because the users of ClosePopupsOverWindow() don't need to alter focus anyway, @@ -10304,7 +10304,7 @@ void ImGui::DockContextNewFrameUpdateUndocking(ImGuiContext* ctx) dc->WantFullRebuild = false; } - // Process Undocking requests (we need to process them _before_ the UpdateMouseMovingWindow call in NewFrame) + // Process Undocking requests (we need to process them _before_ the UpdateMouseMovingWindowNewFrame call in NewFrame) for (int n = 0; n < dc->Requests.Size; n++) { ImGuiDockRequest* req = &dc->Requests[n]; diff --git a/imgui_internal.h b/imgui_internal.h index 872d20ead98e..8f91bfc74df5 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -901,7 +901,7 @@ struct ImGuiContext ImGuiWindow* CurrentWindow; // Being drawn into ImGuiWindow* HoveredWindow; // Will catch mouse inputs ImGuiWindow* HoveredRootWindow; // Will catch mouse inputs (for focus/move only) - ImGuiWindow* HoveredWindowUnderMovingWindow; + ImGuiWindow* HoveredWindowUnderMovingWindow; // Hovered window ignoring MovingWindow. Only set if MovingWindow is set. ImGuiID HoveredId; // Hovered widget bool HoveredIdAllowOverlap; ImGuiID HoveredIdPreviousFrame; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 7590875a6ff2..53bbbd0b0393 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6525,27 +6525,25 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, } // Extract a Dockable window out of it's tab bar - if (docked_window != NULL) + if (docked_window != NULL && !(docked_window->Flags & ImGuiWindowFlags_NoMove)) { // We use a variable threshold to distinguish dragging tabs within a tab bar and extracting them out of the tab bar bool undocking_tab = (g.DragDropActive && g.DragDropPayload.SourceId == id); - if (!undocking_tab) + + if (!undocking_tab) //&& (!g.IO.ConfigDockingWithShift || g.IO.KeyShift) { - //if (!g.IO.ConfigDockingWithShift || g.IO.KeyShift) - { - float threshold_base = g.FontSize; - //float threshold_base = g.IO.ConfigDockingWithShift ? g.FontSize * 0.5f : g.FontSize; - float threshold_x = (threshold_base * 2.2f); - float threshold_y = (threshold_base * 1.5f) + ImClamp((ImFabs(g.IO.MouseDragMaxDistanceAbs[0].x) - threshold_base * 2.0f) * 0.20f, 0.0f, threshold_base * 4.0f); - //GetOverlayDrawList(window)->AddRect(ImVec2(bb.Min.x - threshold_x, bb.Min.y - threshold_y), ImVec2(bb.Max.x + threshold_x, bb.Max.y + threshold_y), IM_COL32_WHITE); // [DEBUG] - - float distance_from_edge_y = ImMax(bb.Min.y - g.IO.MousePos.y, g.IO.MousePos.y - bb.Max.y); - if (distance_from_edge_y >= threshold_y) + float threshold_base = g.FontSize; + //float threshold_base = g.IO.ConfigDockingWithShift ? g.FontSize * 0.5f : g.FontSize; + float threshold_x = (threshold_base * 2.2f); + float threshold_y = (threshold_base * 1.5f) + ImClamp((ImFabs(g.IO.MouseDragMaxDistanceAbs[0].x) - threshold_base * 2.0f) * 0.20f, 0.0f, threshold_base * 4.0f); + //GetOverlayDrawList(window)->AddRect(ImVec2(bb.Min.x - threshold_x, bb.Min.y - threshold_y), ImVec2(bb.Max.x + threshold_x, bb.Max.y + threshold_y), IM_COL32_WHITE); // [DEBUG] + + float distance_from_edge_y = ImMax(bb.Min.y - g.IO.MousePos.y, g.IO.MousePos.y - bb.Max.y); + if (distance_from_edge_y >= threshold_y) + undocking_tab = true; + else if (drag_distance_from_edge_x > threshold_x) + if ((tab_bar->ReorderRequestDir < 0 && tab_bar->GetTabOrder(tab) == 0) || (tab_bar->ReorderRequestDir > 0 && tab_bar->GetTabOrder(tab) == tab_bar->Tabs.Size - 1)) undocking_tab = true; - else if (drag_distance_from_edge_x > threshold_x) - if ((tab_bar->ReorderRequestDir < 0 && tab_bar->GetTabOrder(tab) == 0) || (tab_bar->ReorderRequestDir > 0 && tab_bar->GetTabOrder(tab) == tab_bar->Tabs.Size - 1)) - undocking_tab = true; - } } if (undocking_tab) From dc8ff68871ce0da856a533c9dd06826530df821f Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 31 Jan 2019 15:22:40 +0100 Subject: [PATCH 427/828] Docking: VisibleWindow of a node spread its _NoMove attribute to the node (fixed dragging or undocking of dock node host from collapse button). (#2325, #2109) --- imgui.cpp | 13 +++++++++++-- imgui_widgets.cpp | 2 +- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index e82eefc9afb0..b4689e465643 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3172,7 +3172,14 @@ void ImGui::StartMouseMovingWindow(ImGuiWindow* window) SetActiveID(window->MoveId, window); g.NavDisableHighlight = true; g.ActiveIdClickOffset = g.IO.MousePos - window->RootWindow->Pos; - if (!(window->Flags & ImGuiWindowFlags_NoMove) && !(window->RootWindow->Flags & ImGuiWindowFlags_NoMove)) + + bool can_move_window = true; + if ((window->Flags & ImGuiWindowFlags_NoMove) || (window->RootWindow->Flags & ImGuiWindowFlags_NoMove)) + can_move_window = false; + if (ImGuiDockNode* node = window->DockNodeAsHost) + if (node->VisibleWindow && (node->VisibleWindow->Flags & ImGuiWindowFlags_NoMove)) + can_move_window = false; + if (can_move_window) g.MovingWindow = window; } @@ -11064,7 +11071,7 @@ static void ImGui::DockNodeStartMouseMovingWindow(ImGuiDockNode* node, ImGuiWind IM_ASSERT(node->WantMouseMove == true); ImVec2 backup_active_click_offset = g.ActiveIdClickOffset; StartMouseMovingWindow(window); - g.MovingWindow = window; // If we are docked into a non moveable root widnow, StartMouseMovingWindow() won't set g.MovingWindow. OVerride that decision. + g.MovingWindow = window; // If we are docked into a non moveable root window, StartMouseMovingWindow() won't set g.MovingWindow. Override that decision. node->WantMouseMove = false; g.ActiveIdClickOffset = backup_active_click_offset; } @@ -11533,6 +11540,8 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w if (IsMouseClicked(0)) { focus_tab_id = tab_bar->SelectedTabId; + + // Forward moving request to selected window if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, focus_tab_id)) StartMouseMovingWindow(tab->Window); } diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 53bbbd0b0393..395d4dadefae 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -712,7 +712,7 @@ bool ImGui::CollapseButton(ImGuiID id, const ImVec2& pos, ImGuiDockNode* dock_no if (IsItemActive() && IsMouseDragging(0)) { bool can_extract_dock_node = false; - if (dock_node != NULL) + if (dock_node != NULL && dock_node->VisibleWindow && !(dock_node->VisibleWindow->Flags & ImGuiWindowFlags_NoMove)) { ImGuiDockNode* root_node = DockNodeGetRootNode(dock_node); if (root_node->OnlyNodeWithWindows != dock_node || (root_node->CentralNode != NULL)) From 1f2bdd37b3d78046d875f74349f203170c5b34a1 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 31 Jan 2019 17:01:07 +0100 Subject: [PATCH 428/828] Docking: Builder: Added DockBuilderSetNodePos, DockBuilderSetNodeSize, allow DockBuilderAddNode creating floating node (dockspace requires ImGuiDockNodeFlags_Dockspace) (#2109) --- imgui.cpp | 109 ++++++++++++++++++++++++++++++++++++----------- imgui_internal.h | 16 +++++-- 2 files changed, 96 insertions(+), 29 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index b4689e465643..aa4d64854447 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10367,7 +10367,6 @@ static ImGuiDockNode* ImGui::DockContextAddNode(ImGuiContext* ctx, ImGuiID id) else IM_ASSERT(DockContextFindNodeByID(ctx, id) == NULL); ImGuiDockNode* node = IM_NEW(ImGuiDockNode)(id); - node->InitFromFirstWindowPosSize = node->InitFromFirstWindowViewport = true; ctx->DockContext->Nodes.SetVoidPtr(node->ID, node); return node; } @@ -10719,9 +10718,9 @@ void ImGui::DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node) int index_in_parent = (node->ParentNode->ChildNodes[0] == node) ? 0 : 1; node->ParentNode->ChildNodes[index_in_parent] = NULL; DockNodeTreeMerge(ctx, node->ParentNode, node->ParentNode->ChildNodes[index_in_parent ^ 1]); - node->ParentNode->InitFromFirstWindowViewport = true; // The node that stays in place keeps the viewport, so our newly dragged out node will create a new viewport + node->ParentNode->AutorityForViewport = ImGuiDataAutority_Window; // The node that stays in place keeps the viewport, so our newly dragged out node will create a new viewport node->ParentNode = NULL; - node->InitFromFirstWindowPosSize = true; + node->AutorityForPos = node->AutorityForSize = ImGuiDataAutority_Window; node->WantMouseMove = true; } MarkIniSettingsDirty(); @@ -10745,7 +10744,7 @@ ImGuiDockNode::ImGuiDockNode(ImGuiID id) LastFocusedNodeID = 0; SelectedTabID = 0; WantCloseTabID = 0; - InitFromFirstWindowPosSize = InitFromFirstWindowViewport = false; + AutorityForPos = AutorityForSize = AutorityForViewport = ImGuiDataAutority_Auto; IsVisible = true; IsFocused = IsCentralNode = IsHiddenTabBar = HasCloseButton = HasCollapseButton = false; WantCloseAll = WantLockSizeOnce = WantMouseMove = WantHiddenTabBarUpdate = WantHiddenTabBarToggle = false; @@ -10796,7 +10795,14 @@ static void ImGui::DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window, b // When reactivating a node with one or two loose window, the window pos/size/viewport are authoritative over the node storage. // In particular it is important we init the viewport from the first window so we don't create two viewports and drop one. if (node->HostWindow == NULL && !node->IsDockSpace() && node->IsRootNode()) - node->InitFromFirstWindowPosSize = node->InitFromFirstWindowViewport = true; + { + if (node->AutorityForPos == ImGuiDataAutority_Auto) + node->AutorityForPos = ImGuiDataAutority_Window; + if (node->AutorityForSize == ImGuiDataAutority_Auto) + node->AutorityForSize = ImGuiDataAutority_Window; + if (node->AutorityForViewport == ImGuiDataAutority_Auto) + node->AutorityForViewport = ImGuiDataAutority_Window; + } // Add to tab bar if requested if (add_to_tab_bar) @@ -11138,7 +11144,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) } DockNodeHideHostWindow(node); - node->InitFromFirstWindowPosSize = node->InitFromFirstWindowViewport = false; + node->AutorityForPos = node->AutorityForSize = node->AutorityForViewport = ImGuiDataAutority_Auto; node->WantCloseAll = false; node->WantCloseTabID = 0; node->HasCloseButton = node->HasCollapseButton = false; @@ -11174,20 +11180,28 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) if (node->IsRootNode() && node->IsVisible) { - if (node->InitFromFirstWindowPosSize && node->Windows.Size > 0) - { - ImGuiWindow* init_window = node->Windows[0]; - SetNextWindowPos(init_window->Pos); - SetNextWindowSize(init_window->SizeFull); - SetNextWindowCollapsed(init_window->Collapsed); - } - else if (node->HostWindow == NULL) - { + ImGuiWindow* ref_window = (node->Windows.Size > 0) ? node->Windows[0] : NULL; + + // Sync Pos + if (node->AutorityForPos == ImGuiDataAutority_Window && ref_window) + SetNextWindowPos(ref_window->Pos); + else if (node->AutorityForPos == ImGuiDataAutority_DockNode) SetNextWindowPos(node->Pos); + + // Sync Size + if (node->AutorityForSize == ImGuiDataAutority_Window && ref_window) + SetNextWindowSize(ref_window->SizeFull); + else if (node->AutorityForSize == ImGuiDataAutority_DockNode) SetNextWindowSize(node->Size); - } - if (node->InitFromFirstWindowViewport && node->Windows.Size > 0) - SetNextWindowViewport(node->Windows[0]->ViewportId); + + // Sync Collapsed + if (node->AutorityForSize == ImGuiDataAutority_Window && ref_window) + SetNextWindowCollapsed(ref_window->Collapsed); + + // Sync Viewport + if (node->AutorityForViewport == ImGuiDataAutority_Window && ref_window) + SetNextWindowViewport(ref_window->ViewportId); + SetNextWindowClass(&node->WindowClass); // Begin into the host window @@ -11222,7 +11236,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) { node->HostWindow = host_window = node->ParentNode->HostWindow; } - node->InitFromFirstWindowPosSize = node->InitFromFirstWindowViewport = false; + node->AutorityForPos = node->AutorityForSize = node->AutorityForViewport = ImGuiDataAutority_Auto; if (node->WantMouseMove && node->HostWindow) DockNodeStartMouseMovingWindow(node, node->HostWindow); } @@ -11950,7 +11964,7 @@ void ImGui::DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImG DockSettingsRenameNodeReferences(child_1->ID, parent_node->ID); } DockNodeApplyPosSizeToWindows(parent_node); - parent_node->InitFromFirstWindowPosSize = parent_node->InitFromFirstWindowViewport = false; + parent_node->AutorityForPos = parent_node->AutorityForSize = parent_node->AutorityForViewport = ImGuiDataAutority_Auto; parent_node->VisibleWindow = merge_lead_child->VisibleWindow; parent_node->IsCentralNode = (child_0 && child_0->IsCentralNode) || (child_1 && child_1->IsCentralNode); parent_node->IsHiddenTabBar = merge_lead_child->IsHiddenTabBar; @@ -12359,11 +12373,13 @@ ImGuiID ImGui::DockSpaceOverViewport(ImGuiViewport* viewport, ImGuiDockNodeFlags void ImGui::DockBuilderDockWindow(const char* window_name, ImGuiID node_id) { + // We don't preserve relative order of multiple docked windows (by clearing DockOrder back to -1) ImGuiID window_id = ImHashStr(window_name, 0); if (ImGuiWindow* window = FindWindowByID(window_id)) { // Apply to created window SetWindowDock(window, node_id, ImGuiCond_Always); + window->DockOrder = -1; } else { @@ -12372,6 +12388,7 @@ void ImGui::DockBuilderDockWindow(const char* window_name, ImGuiID node_id) if (settings == NULL) settings = CreateNewWindowSettings(window_name); settings->DockId = node_id; + settings->DockOrder = -1; } } @@ -12381,13 +12398,47 @@ ImGuiDockNode* ImGui::DockBuilderGetNode(ImGuiID node_id) return DockContextFindNodeByID(ctx, node_id); } -void ImGui::DockBuilderAddNode(ImGuiID id, ImVec2 ref_size, ImGuiDockNodeFlags flags) +void ImGui::DockBuilderSetNodePos(ImGuiID node_id, ImVec2 pos) { ImGuiContext* ctx = GImGui; - DockSpace(id, ImVec2(0,0), flags | ImGuiDockNodeFlags_KeepAliveOnly); - ImGuiDockNode* node = DockContextFindNodeByID(ctx, id); - node->SizeRef = node->Size = ref_size; - node->LastFrameAlive = -1; + ImGuiDockNode* node = DockContextFindNodeByID(ctx, node_id); + if (node == NULL) + return; + node->Pos = pos; + node->AutorityForPos = ImGuiDataAutority_DockNode; +} + +void ImGui::DockBuilderSetNodeSize(ImGuiID node_id, ImVec2 size) +{ + ImGuiContext* ctx = GImGui; + ImGuiDockNode* node = DockContextFindNodeByID(ctx, node_id); + if (node == NULL) + return; + node->Size = node->SizeRef = size; + node->AutorityForSize = ImGuiDataAutority_DockNode; +} + +// If you create a regular node, both ref_pos/ref_size will position the window. +// If you create a dockspace node: ref_pos won't be used, ref_size is useful on the first frame to... +ImGuiID ImGui::DockBuilderAddNode(ImGuiID id, ImGuiDockNodeFlags flags) +{ + ImGuiContext* ctx = GImGui; + ImGuiDockNode* node = NULL; + if (flags & ImGuiDockNodeFlags_Dockspace) + { + DockSpace(id, ImVec2(0, 0), flags | ImGuiDockNodeFlags_KeepAliveOnly); + node = DockContextFindNodeByID(ctx, id); + node->LastFrameAlive = -1; + } + else + { + if (id != 0) + node = DockContextFindNodeByID(ctx, id); + if (!node) + node = DockContextAddNode(ctx, id); + node->LastFrameAlive = ctx->FrameCount; + } + return node->ID; } void ImGui::DockBuilderRemoveNode(ImGuiID node_id) @@ -12413,6 +12464,9 @@ void ImGui::DockBuilderRemoveNodeChildNodes(ImGuiID root_id) return; bool has_document_root = false; + ImGuiDataAutority backup_root_node_autority_for_pos = root_node ? root_node->AutorityForPos : ImGuiDataAutority_Auto; + ImGuiDataAutority backup_root_node_autority_for_size = root_node ? root_node->AutorityForSize : ImGuiDataAutority_Auto; + // Process active windows ImVector nodes_to_remove; for (int n = 0; n < dc->Nodes.Data.Size; n++) @@ -12434,7 +12488,10 @@ void ImGui::DockBuilderRemoveNodeChildNodes(ImGuiID root_id) // DockNodeMoveWindows->DockNodeAddWindow will normally set those when reaching two windows (which is only adequate during interactive merge) // Make sure we don't lose our current pos/size. (FIXME-DOCK: Consider tidying up that code in DockNodeAddWindow instead) if (root_node) - root_node->InitFromFirstWindowPosSize = false; + { + root_node->AutorityForPos = backup_root_node_autority_for_pos; + root_node->AutorityForSize = backup_root_node_autority_for_size; + } // Apply to settings for (int settings_n = 0; settings_n < ctx->SettingsWindows.Size; settings_n++) diff --git a/imgui_internal.h b/imgui_internal.h index 8f91bfc74df5..60e732fd0064 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -818,6 +818,13 @@ enum ImGuiDockNodeFlagsPrivate_ ImGuiDockNodeFlags_Dockspace = 1 << 10 }; +enum ImGuiDataAutority +{ + ImGuiDataAutority_Auto, + ImGuiDataAutority_DockNode, + ImGuiDataAutority_Window +}; + // sizeof() 116~160 struct ImGuiDockNode { @@ -843,8 +850,9 @@ struct ImGuiDockNode ImGuiID LastFocusedNodeID; // [Root node only] Which of our child docking node (any ancestor in the hierarchy) was last focused. ImGuiID SelectedTabID; // [Tab node only] Which of our tab is selected. ImGuiID WantCloseTabID; // [Tab node only] Set when closing a specific tab. - bool InitFromFirstWindowPosSize :1; - bool InitFromFirstWindowViewport :1; + ImGuiDataAutority AutorityForPos :3; + ImGuiDataAutority AutorityForSize :3; + ImGuiDataAutority AutorityForViewport :3; bool IsVisible :1; // Set to false when the node is hidden (usually disabled as it has no active window) bool IsFocused :1; bool IsCentralNode :1; @@ -1577,10 +1585,12 @@ namespace ImGui IMGUI_API void DockBuilderDockWindow(const char* window_name, ImGuiID node_id); IMGUI_API ImGuiDockNode*DockBuilderGetNode(ImGuiID node_id); // Warning: DO NOT HOLD ON ImGuiDockNode* pointer, will be invalided by any split/merge/remove operation. inline ImGuiDockNode* DockBuilderGetCentralNode(ImGuiID node_id) { ImGuiDockNode* node = DockBuilderGetNode(node_id); if (!node) return NULL; return DockNodeGetRootNode(node)->CentralNode; } - IMGUI_API void DockBuilderAddNode(ImGuiID node_id, ImVec2 ref_size, ImGuiDockNodeFlags flags = 0); + IMGUI_API ImGuiID DockBuilderAddNode(ImGuiID node_id, ImGuiDockNodeFlags flags = 0); IMGUI_API void DockBuilderRemoveNode(ImGuiID node_id); // Remove node and all its child, undock all windows IMGUI_API void DockBuilderRemoveNodeDockedWindows(ImGuiID node_id, bool clear_persistent_docking_references = true); IMGUI_API void DockBuilderRemoveNodeChildNodes(ImGuiID node_id); // Remove all split/hierarchy. All remaining docked windows will be re-docked to the root. + IMGUI_API void DockBuilderSetNodePos(ImGuiID node_id, ImVec2 pos); + IMGUI_API void DockBuilderSetNodeSize(ImGuiID node_id, ImVec2 size); IMGUI_API ImGuiID DockBuilderSplitNode(ImGuiID node_id, ImGuiDir split_dir, float size_ratio_for_node_at_dir, ImGuiID* out_id_dir, ImGuiID* out_id_other); IMGUI_API void DockBuilderCopyDockspace(ImGuiID src_dockspace_id, ImGuiID dst_dockspace_id, ImVector* in_window_remap_pairs); IMGUI_API void DockBuilderCopyNode(ImGuiID src_node_id, ImGuiID dst_node_id, ImVector* out_node_remap_pairs); From cbf24a91518b1005b571b2d362d258e87e3af6db Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 1 Feb 2019 11:04:04 +0100 Subject: [PATCH 429/828] Comments. Fix duplicate entries in About box. Synchronize a few small changes from Master branch. --- imgui.cpp | 22 +++++++++++----------- imgui_demo.cpp | 9 +-------- imgui_internal.h | 2 +- 3 files changed, 13 insertions(+), 20 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 598a41f5ed45..ceda20de095e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -884,8 +884,8 @@ CODE (The ImGuiWindowFlags_NoDecoration flag itself is a shortcut for NoTitleBar | NoResize | NoScrollbar | NoCollapse) Then you can retrieve the ImDrawList* via GetWindowDrawList() and draw to it in any way you like. - You can call ImGui::GetOverlayDrawList() and use this draw list to display contents over every other imgui windows (1 overlay per viewport). - - You can create your own ImDrawList instance. You'll need to initialize them ImGui::GetDrawListSharedData(), or create your own ImDrawListSharedData, - and then call your rendered code with your own ImDrawList or ImDrawData data. + - You can create your own ImDrawList instance. You'll need to initialize them ImGui::GetDrawListSharedData(), or create + your own ImDrawListSharedData, and then call your rendered code with your own ImDrawList or ImDrawData data. Q: How can I use this without a mouse, without a keyboard or without a screen? (gamepad, input share, remote display) A: - You can control Dear ImGui with a gamepad. Read about navigation in "Using gamepad/keyboard navigation controls". @@ -3576,7 +3576,7 @@ void ImGui::NewFrame() UpdateViewportsNewFrame(); - // Setup current font, and draw list shared data + // Setup current font and draw list shared data // FIXME-VIEWPORT: the concept of a single ClipRectFullscreen is not ideal! g.IO.Fonts->Locked = true; SetCurrentFont(GetDefaultFont()); @@ -3587,7 +3587,8 @@ void ImGui::NewFrame() g.DrawListSharedData.ClipRectFullscreen = ImVec4(0.0f, 0.0f, virtual_space_max.x, virtual_space_max.y); g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol; - // Mark rendering data as invalid to prevent user who may have a handle on it to use it. Setup Overlay draw list for the viewport. + // Setup Overlay draw list for the viewport. + // Mark rendering data as invalid to prevent user who may have a handle on it to use it. for (int n = 0; n < g.Viewports.Size; n++) { ImGuiViewportP* viewport = g.Viewports[n]; @@ -5110,7 +5111,7 @@ static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au } // Apply back modified position/size to window - if (size_target.x != FLT_MAX && (size_target.x != window->SizeFull.x || size_target.y != window->SizeFull.y)) + if (size_target.x != FLT_MAX) { window->SizeFull = size_target; MarkIniSettingsDirty(window); @@ -5451,9 +5452,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) MarkIniSettingsDirty(window); } - //if (window->DockNode && window->DockIsActive) - // size_full_modified = window->SizeFull; - // Apply minimum/maximum window size constraints and final size window->SizeFull = CalcSizeAfterConstraint(window, window->SizeFull); window->Size = window->Collapsed && !(flags & ImGuiWindowFlags_ChildWindow) ? window->TitleBarRect().GetSize() : window->SizeFull; @@ -5596,7 +5594,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) { if (flags & ImGuiWindowFlags_Popup) want_focus = true; - else if ((window->DockIsActive || !(flags & ImGuiWindowFlags_ChildWindow)) && !(flags & ImGuiWindowFlags_Tooltip)) + else if ((window->DockIsActive || (flags & ImGuiWindowFlags_ChildWindow) == 0) && !(flags & ImGuiWindowFlags_Tooltip)) want_focus = true; } @@ -11398,10 +11396,9 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w node->IsFocused = is_focused; if (is_focused) node->LastFrameFocused = g.FrameCount; - - // Notify root of visible window (used to display title in OS task bar) if (node->VisibleWindow) { + // Notify root of visible window (used to display title in OS task bar) if (is_focused || root_node->VisibleWindow == NULL) root_node->VisibleWindow = node->VisibleWindow; if (node->TabBar) @@ -13173,6 +13170,9 @@ static void ImGui::DockSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettings //----------------------------------------------------------------------------- // [SECTION] LOGGING/CAPTURING //----------------------------------------------------------------------------- +// All text output from the interface can be captured into tty/file/clipboard. +// By default, tree nodes are automatically opened during logging. +//----------------------------------------------------------------------------- // Pass text data straight to log (without being displayed) void ImGui::LogText(const char* fmt, ...) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index f001fff2ff52..652628053e5e 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2729,9 +2729,6 @@ void ImGui::ShowAboutWindow(bool* p_open) #endif #ifdef IMGUI_HAS_DOCK ImGui::Text("define: IMGUI_HAS_DOCK"); -#endif -#ifdef IMGUI_HAS_TABS - ImGui::Text("define: IMGUI_HAS_TABS"); #endif ImGui::Separator(); ImGui::Text("io.BackendPlatformName: %s", io.BackendPlatformName ? io.BackendPlatformName : "NULL"); @@ -2756,10 +2753,6 @@ void ImGui::ShowAboutWindow(bool* p_open) if (io.ConfigDockingWithShift) ImGui::Text("io.ConfigDockingWithShift"); if (io.ConfigDockingTabBarOnSingleWindows) ImGui::Text("io.ConfigDockingTabBarOnSingleWindows"); if (io.ConfigDockingTransparentPayload) ImGui::Text("io.ConfigDockingTransparentPayload"); - if (io.ConfigViewportsNoAutoMerge) ImGui::Text("io.ConfigViewportsNoAutoMerge"); - if (io.ConfigViewportsNoTaskBarIcon) ImGui::Text("io.ConfigViewportsNoTaskBarIcon"); - if (io.ConfigViewportsNoDecoration) ImGui::Text("io.ConfigViewportsNoDecoration"); - if (io.ConfigViewportsNoParent) ImGui::Text("io.ConfigViewportsNoParent"); if (io.ConfigMacOSXBehaviors) ImGui::Text("io.ConfigMacOSXBehaviors"); if (io.ConfigInputTextCursorBlink) ImGui::Text("io.ConfigInputTextCursorBlink"); if (io.ConfigWindowsResizeFromEdges) ImGui::Text("io.ConfigWindowsResizeFromEdges"); @@ -4263,7 +4256,7 @@ void ShowExampleAppDocuments(bool* p_open) return; } - // Menu Bar + // Menu if (ImGui::BeginMenuBar()) { if (ImGui::BeginMenu("File")) diff --git a/imgui_internal.h b/imgui_internal.h index 2f6600af12b3..ea79bc7fe852 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -352,7 +352,7 @@ enum ImGuiSeparatorFlags_ ImGuiSeparatorFlags_Vertical = 1 << 1 }; -// Transient per-window ItemFlags, reset at the beginning of the frame. For child windows: inherited from parent on first Begin(). +// Transient per-window flags, reset at the beginning of the frame. For child window, inherited from parent on first Begin(). // This is going to be exposed in imgui.h when stabilized enough. enum ImGuiItemFlags_ { From 65a2350a5f41447ad630d16de1ec6f0e365d0f1c Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 1 Feb 2019 11:12:37 +0100 Subject: [PATCH 430/828] Docking: Extracted code into a DocknodeUpdateTabListMenu() functions + minor other changes. --- imgui.cpp | 80 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 41 insertions(+), 39 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index ceda20de095e..b67fa92d8789 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10204,6 +10204,7 @@ namespace ImGui static void DockNodeUpdate(ImGuiDockNode* node); static void DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* node); static void DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_window); + static ImGuiID DockNodeUpdateTabListMenu(ImGuiDockNode* node, ImGuiTabBar* tab_bar); static void DockNodeUpdateVisibleFlag(ImGuiDockNode* node); static void DockNodeStartMouseMovingWindow(ImGuiDockNode* node, ImGuiWindow* window); static bool DockNodeIsDropAllowed(ImGuiWindow* host_window, ImGuiWindow* payload_window); @@ -11370,6 +11371,36 @@ static int IMGUI_CDECL TabItemComparerByDockOrder(const void* lhs, const void* r return (a->BeginOrderWithinContext - b->BeginOrderWithinContext); } +static ImGuiID ImGui::DockNodeUpdateTabListMenu(ImGuiDockNode* node, ImGuiTabBar* tab_bar) +{ + // Try to position the menu so it is more likely to stays within the same viewport + ImGuiID ret_tab_id = 0; + SetNextWindowPos(ImVec2(node->Pos.x, node->Pos.y + GetFrameHeight())); + if (BeginPopup("#TabListMenu")) + { + node->IsFocused = true; + if (tab_bar->Tabs.Size == 1) + { + if (MenuItem("Hide tab bar", NULL, node->IsHiddenTabBar)) + node->WantHiddenTabBarToggle = true; + } + else + { + for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++) + { + ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; + IM_ASSERT(tab->Window != NULL); + if (Selectable(tab->Window->Name, tab->ID == tab_bar->SelectedTabId)) + ret_tab_id = tab->ID; + SameLine(); + Text(" "); + } + } + EndPopup(); + } + return ret_tab_id; +} + static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_window) { ImGuiContext& g = *GImGui; @@ -11423,38 +11454,17 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w tab_bar = node->TabBar = IM_NEW(ImGuiTabBar)(); ImGuiID focus_tab_id = 0; + node->IsFocused = is_focused; // Collapse button changes shape and display a list if (IsPopupOpen("#TabListMenu")) { - // Try to position the menu so it is more likely to stays within the same viewport - SetNextWindowPos(ImVec2(node->Pos.x, node->Pos.y + GetFrameHeight())); - if (BeginPopup("#TabListMenu")) - { - is_focused = true; - if (tab_bar->Tabs.Size == 1) - { - if (MenuItem("Hide tab bar", NULL, node->IsHiddenTabBar)) - node->WantHiddenTabBarToggle = true; - } - else - { - for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++) - { - ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; - IM_ASSERT(tab->Window != NULL); - if (Selectable(tab->Window->Name, tab->ID == tab_bar->SelectedTabId)) - focus_tab_id = tab_bar->NextSelectedTabId = tab->ID; - SameLine(); - Text(" "); - } - } - EndPopup(); - } + if (ImGuiID tab_id = DockNodeUpdateTabListMenu(node, tab_bar)) + focus_tab_id = tab_bar->NextSelectedTabId = tab_id; + is_focused |= node->IsFocused; } // Title bar - node->IsFocused = is_focused; if (is_focused) node->LastFrameFocused = g.FrameCount; ImRect title_bar_rect = ImRect(node->Pos, node->Pos + ImVec2(node->Size.x, g.FontSize + style.FramePadding.y * 2.0f)); @@ -12134,10 +12144,10 @@ void ImGui::DockNodeTreeUpdateSplitter(ImGuiDockNode* node) // [DEBUG] Render limits ImDrawList* draw_list = node->HostWindow ? GetOverlayDrawList(node->HostWindow) : GetOverlayDrawList((ImGuiViewportP*)GetMainViewport()); for (int n = 0; n < 2; n++) - if (axis == ImGuiAxis_X) - draw_list->AddLine(ImVec2(resize_limits[n], node->ChildNodes[n]->Pos.y), ImVec2(resize_limits[n], node->ChildNodes[n]->Pos.y + node->ChildNodes[n]->Size.y), IM_COL32(255, 0, 255, 255), 3.0f); - else - draw_list->AddLine(ImVec2(node->ChildNodes[n]->Pos.x, resize_limits[n]), ImVec2(node->ChildNodes[n]->Pos.x + node->ChildNodes[n]->Size.x, resize_limits[n]), IM_COL32(255, 0, 255, 255), 3.0f); + if (axis == ImGuiAxis_X) + draw_list->AddLine(ImVec2(resize_limits[n], node->ChildNodes[n]->Pos.y), ImVec2(resize_limits[n], node->ChildNodes[n]->Pos.y + node->ChildNodes[n]->Size.y), IM_COL32(255, 0, 255, 255), 3.0f); + else + draw_list->AddLine(ImVec2(node->ChildNodes[n]->Pos.x, resize_limits[n]), ImVec2(node->ChildNodes[n]->Pos.x + node->ChildNodes[n]->Size.x, resize_limits[n]), IM_COL32(255, 0, 255, 255), 3.0f); */ } @@ -12156,7 +12166,6 @@ void ImGui::DockNodeTreeUpdateSplitter(ImGuiDockNode* node) // Lock the size of every node that is a sibling of the node we are touching // This might be less desirable if we can merge sibling of a same axis into the same parental level. -#if 1 for (int side_n = 0; side_n < 2; side_n++) for (int touching_node_n = 0; touching_node_n < touching_nodes[side_n].Size; touching_node_n++) { @@ -12176,7 +12185,6 @@ void ImGui::DockNodeTreeUpdateSplitter(ImGuiDockNode* node) touching_node = touching_node->ParentNode; } } -#endif DockNodeTreeUpdatePosSize(child_0, child_0->Pos, child_0->Size); DockNodeTreeUpdatePosSize(child_1, child_1->Pos, child_1->Size); @@ -12767,7 +12775,6 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) ImGuiContext& g = *ctx; const bool auto_dock_node = (g.IO.ConfigDockingTabBarOnSingleWindows) && !(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoDocking)); - if (auto_dock_node) { if (window->DockId == 0) @@ -12861,7 +12868,6 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) window->DockTabIsVisible = false; return; } - IM_ASSERT(node->HostWindow); IM_ASSERT(node->IsLeafNode()); @@ -12869,7 +12875,6 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) SetNextWindowPos(node->Pos); SetNextWindowSize(node->Size); g.NextWindowData.PosUndock = false; // Cancel implicit undocking of SetNextWindowPos() - window->DockIsActive = true; window->DockTabIsVisible = false; if (node->Flags & ImGuiDockNodeFlags_KeepAliveOnly) @@ -12931,7 +12936,6 @@ void ImGui::BeginAsDockableDragDropTarget(ImGuiWindow* window) IM_ASSERT((window->Flags & ImGuiWindowFlags_NoDocking) == 0); if (!g.DragDropActive) return; - if (!BeginDragDropTargetCustom(window->Rect(), window->ID)) return; @@ -13027,7 +13031,7 @@ static void ImGui::DockSettingsRemoveNodeReferences(ImGuiID* node_ids, int node_ } } -static ImGuiDockNodeSettings* ImGui::DockSettingsFindNodeSettings(ImGuiContext* ctx, ImGuiID id) +static ImGuiDockNodeSettings* ImGui::DockSettingsFindNodeSettings(ImGuiContext* ctx, ImGuiID id) { // FIXME-OPT ImGuiDockContext* dc = ctx->DockContext; @@ -13126,9 +13130,7 @@ static void ImGui::DockSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettings buf->appendf("[%s][Data]\n", handler->TypeName); for (int node_n = 0; node_n < dc->SettingsNodes.Size; node_n++) { -#if IMGUI_DEBUG_DOCKING_INI - const int line_start_pos = buf->size(); -#endif + const int line_start_pos = buf->size(); (void)line_start_pos; const ImGuiDockNodeSettings* node_settings = &dc->SettingsNodes[node_n]; buf->appendf("%*s%s%*s", node_settings->Depth * 2, "", node_settings->IsDockSpace ? "DockSpace" : "DockNode ", (max_depth - node_settings->Depth) * 2, ""); // Text align nodes to facilitate looking at .ini file buf->appendf(" ID=0x%08X", node_settings->ID); From e30babef093319d2a5e5511e12b01d528dc86046 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 1 Feb 2019 11:22:53 +0100 Subject: [PATCH 431/828] Fixed Clang/Win32 warning. --- imgui_internal.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/imgui_internal.h b/imgui_internal.h index ea79bc7fe852..936e44a6cdd9 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -85,6 +85,7 @@ struct ImGuiWindowSettings; // Storage for window settings stored in .in // Use your programming IDE "Go to definition" facility on the names of the center columns to find the actual flags/enum lists. typedef int ImGuiLayoutType; // -> enum ImGuiLayoutType_ // Enum: Horizontal or vertical +typedef int ImGuiDataAutority; // -> enum ImGuiDataAutority_ // Enum: for storing the source autority (dock node vs window) of a field typedef int ImGuiButtonFlags; // -> enum ImGuiButtonFlags_ // Flags: for ButtonEx(), ButtonBehavior() typedef int ImGuiItemFlags; // -> enum ImGuiItemFlags_ // Flags: for PushItemFlag() typedef int ImGuiItemStatusFlags; // -> enum ImGuiItemStatusFlags_ // Flags: for DC.LastItemStatusFlags @@ -818,7 +819,7 @@ enum ImGuiDockNodeFlagsPrivate_ ImGuiDockNodeFlags_Dockspace = 1 << 10 }; -enum ImGuiDataAutority +enum ImGuiDataAutority_ { ImGuiDataAutority_Auto, ImGuiDataAutority_DockNode, From 03b0266b5921a5191ca33961ceb17d5b74b698f1 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 1 Feb 2019 15:23:25 +0100 Subject: [PATCH 432/828] Examples: Made imgui_impl_win32 drag gdi32.lib for GetDeviceCaps(). (#2327) --- examples/imgui_impl_win32.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index 7b60dd1b5a0e..f34d840533ed 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -447,6 +447,10 @@ void ImGui_ImplWin32_EnableDpiAwareness() } } +#ifdef _MSC_VER +#pragma comment(lib, "gdi32") // GetDeviceCaps() +#endif + float ImGui_ImplWin32_GetDpiScaleForMonitor(void* monitor) { UINT xdpi = 96, ydpi = 96; From e215809c4d06397bbe1da71d51087967b8ded68f Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 1 Feb 2019 16:37:07 +0100 Subject: [PATCH 433/828] Removed io.DisplayVisibleMin/DisplayVisibleMax (which were marked obsolete and removed from viewport/docking branch already). --- docs/CHANGELOG.txt | 4 ++++ imgui.cpp | 4 ++-- imgui_demo.cpp | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index b4669fb1deec..f937c4cd9fa9 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -97,6 +97,10 @@ Other changes: VERSION 1.68 (In progress) ----------------------------------------------------------------------- +Breaking Changes: + +- Removed io.DisplayVisibleMin/DisplayVisibleMax (which were marked obsolete and removed from viewport/docking branch already). + Other Changes: - Added .editorconfig file for text editors to standardize using spaces. (#2038) [@kudaba] - InputText: Fixed a bug where ESCAPE would not restore the initial value in all situations. (#2321) [@relick] diff --git a/imgui.cpp b/imgui.cpp index 61ee26c546ac..92c2694bf6c3 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -373,9 +373,9 @@ CODE - likewise io.MousePos and GetMousePos() will use OS coordinates. If you query mouse positions to interact with non-imgui coordinates you will need to offset them, e.g. subtract GetWindowViewport()->Pos. - 2018/XX/XX (1.XX) - Moved IME support functions from io.ImeSetInputScreenPosFn, io.ImeWindowHandle to the PlatformIO api. - - 2018/XX/XX (1.XX) - removed io.DisplayVisibleMin, io.DisplayVisibleMax settings (they were used to clip within the (0,0)..DisplaySize range, I don't know of anyone using it) - + + - 2019/02/01 (1.68) - removed io.DisplayVisibleMin/DisplayVisibleMax (which were marked obsolete and removed from viewport/docking branch already). - 2019/01/06 (1.67) - renamed io.InputCharacters[], marked internal as was always intended. Please don't access directly, and use AddInputCharacter() instead! - 2019/01/06 (1.67) - renamed ImFontAtlas::GlyphRangesBuilder to ImFontGlyphRangesBuilder. Keep redirection typedef (will obsolete). - 2018/12/20 (1.67) - made it illegal to call Begin("") with an empty string. This somehow half-worked before but had various undesirable side-effects. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 652628053e5e..c6df1628ab8b 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2767,6 +2767,7 @@ void ImGui::ShowAboutWindow(bool* p_open) ImGui::Separator(); ImGui::Text("io.Fonts: %d fonts, Flags: 0x%08X, TexSize: %d,%d", io.Fonts->Fonts.Size, io.Fonts->Flags, io.Fonts->TexWidth, io.Fonts->TexHeight); ImGui::Text("io.DisplaySize: %.2f,%.2f", io.DisplaySize.x, io.DisplaySize.y); + ImGui::Text("io.DisplayFramebufferScale: %.2f,%.2f", io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y); ImGui::Separator(); ImGui::Text("style.WindowPadding: %.2f,%.2f", style.WindowPadding.x, style.WindowPadding.y); ImGui::Text("style.WindowBorderSize: %.2f", style.WindowBorderSize); From f087359621d27b808e363670d52cd7ad14be5675 Mon Sep 17 00:00:00 2001 From: omar Date: Sun, 3 Feb 2019 13:54:04 +0100 Subject: [PATCH 434/828] Revert part of change from 5536eded. Fixed drag and drop in docking branch. (#2331, reopening #2325) --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 92c2694bf6c3..f47be8d6bf8d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4297,7 +4297,7 @@ static void FindHoveredWindow() g.HoveredWindow = hovered_window; g.HoveredRootWindow = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL; - g.HoveredWindowUnderMovingWindow = g.MovingWindow ? hovered_window_ignoring_moving_window : NULL; + g.HoveredWindowUnderMovingWindow = hovered_window_ignoring_moving_window; if (g.MovingWindow) g.MovingWindow->Viewport = moving_window_viewport; From 80d51c692a6661f06ad193d5df05fe32cede4b65 Mon Sep 17 00:00:00 2001 From: omar Date: Sun, 3 Feb 2019 18:44:30 +0100 Subject: [PATCH 435/828] Docking: Fixed dragging docked window with _NoMove flag (#2325) --- imgui.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index b124b7f784cd..593db341ee1c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5925,8 +5925,9 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Docking: Dragging a dockable window (or any of its child) turns it into a drag and drop source. // We need to do this _before_ we overwrite window->DC.LastItemId below because BeginAsDockableDragDropSource() also overwrites it. if ((g.ActiveId == window->MoveId) && ((g.IO.ConfigDockingWithShift && g.IO.KeyShift) || (!g.IO.ConfigDockingWithShift))) - if ((window->RootWindow->Flags & (ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDocking)) == 0) - BeginAsDockableDragDropSource(window); + if ((window->Flags & ImGuiWindowFlags_NoMove) == 0) + if ((window->RootWindow->Flags & (ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDocking)) == 0) + BeginAsDockableDragDropSource(window); // Docking: Any dockable window can act as a target. For dock node hosts we call BeginAsDockableDragDropTarget() in DockNodeUpdate() instead. if (g.DragDropActive && !(flags & ImGuiWindowFlags_NoDocking)) From f902435a538cf336ade6b9d4ddafb0b467df0443 Mon Sep 17 00:00:00 2001 From: omar Date: Sun, 3 Feb 2019 18:58:07 +0100 Subject: [PATCH 436/828] Docking: Fixed less of node size/pos caused by 1f2bdd37 (#2109) --- imgui.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 593db341ee1c..975f8dd7c9fc 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10770,7 +10770,8 @@ ImGuiDockNode::ImGuiDockNode(ImGuiID id) LastFocusedNodeID = 0; SelectedTabID = 0; WantCloseTabID = 0; - AutorityForPos = AutorityForSize = AutorityForViewport = ImGuiDataAutority_Auto; + AutorityForPos = AutorityForSize = ImGuiDataAutority_DockNode; + AutorityForViewport = ImGuiDataAutority_Auto; IsVisible = true; IsFocused = IsCentralNode = IsHiddenTabBar = HasCloseButton = HasCollapseButton = false; WantCloseAll = WantLockSizeOnce = WantMouseMove = WantHiddenTabBarUpdate = WantHiddenTabBarToggle = false; @@ -11257,12 +11258,14 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) // after the dock host window, losing their top-most status. if (node->HostWindow->Appearing) BringWindowToDisplayFront(node->HostWindow); + + node->AutorityForPos = node->AutorityForSize = node->AutorityForViewport = ImGuiDataAutority_Auto; } else if (node->ParentNode) { node->HostWindow = host_window = node->ParentNode->HostWindow; + node->AutorityForPos = node->AutorityForSize = node->AutorityForViewport = ImGuiDataAutority_Auto; } - node->AutorityForPos = node->AutorityForSize = node->AutorityForViewport = ImGuiDataAutority_Auto; if (node->WantMouseMove && node->HostWindow) DockNodeStartMouseMovingWindow(node, node->HostWindow); } From f6fbb99a9c628187be24f5b57e5c2dd4f5b046cf Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 5 Feb 2019 15:45:26 +0100 Subject: [PATCH 437/828] Examples: SDL: Fix for Emscripten/Android/iOS on Docking branch. --- examples/imgui_impl_sdl.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/examples/imgui_impl_sdl.cpp b/examples/imgui_impl_sdl.cpp index e1c505f58d27..a1ab790d8bef 100644 --- a/examples/imgui_impl_sdl.cpp +++ b/examples/imgui_impl_sdl.cpp @@ -269,13 +269,12 @@ static void ImGui_ImplSDL2_UpdateMousePosAndButtons() io.MouseDown[2] = g_MousePressed[2] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_MIDDLE)) != 0; g_MousePressed[0] = g_MousePressed[1] = g_MousePressed[2] = false; -#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE +#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE && !defined(__EMSCRIPTEN__) && !defined(__ANDROID__) && !(defined(__APPLE__) && TARGET_OS_IOS) // SDL 2.0.4 and later has SDL_GetGlobalMouseState() and SDL_CaptureMouse() int mouse_x_global, mouse_y_global; SDL_GetGlobalMouseState(&mouse_x_global, &mouse_y_global); -#if !defined(__EMSCRIPTEN__) && !defined(__ANDROID__) && !(defined(__APPLE__) && TARGET_OS_IOS) if (io.ConfigFlags & 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) @@ -284,7 +283,6 @@ static void ImGui_ImplSDL2_UpdateMousePosAndButtons() io.MousePos = ImVec2((float)mouse_x_global, (float)mouse_y_global); } else -#endif { // Single-viewport mode: mouse position in client window coordinatesio.MousePos is (0,0) when the mouse is on the upper-left corner of the app window) if (SDL_GetWindowFlags(g_Window) & SDL_WINDOW_INPUT_FOCUS) From 7f6a025c93063a969d53091d269a846cbfd15db1 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 11 Feb 2019 19:00:33 +0100 Subject: [PATCH 438/828] Viewport: SDL: Inherit SDL_WINDOW_ALLOW_HIGHDPI flag from main viewport. (#2306) --- examples/imgui_impl_sdl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/imgui_impl_sdl.cpp b/examples/imgui_impl_sdl.cpp index a1ab790d8bef..902afad15904 100644 --- a/examples/imgui_impl_sdl.cpp +++ b/examples/imgui_impl_sdl.cpp @@ -382,9 +382,9 @@ static void ImGui_ImplSDL2_CreateWindow(ImGuiViewport* viewport) SDL_GL_MakeCurrent(main_viewport_data->Window, main_viewport_data->GLContext); } - // We don't enable SDL_WINDOW_RESIZABLE because it enforce windows decorations Uint32 sdl_flags = 0; sdl_flags |= use_opengl ? SDL_WINDOW_OPENGL : SDL_WINDOW_VULKAN; + sdl_flags |= SDL_GetWindowFlags(g_Window) & SDL_WINDOW_ALLOW_HIGHDPI; sdl_flags |= SDL_WINDOW_HIDDEN; sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? SDL_WINDOW_BORDERLESS : 0; sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? 0 : SDL_WINDOW_RESIZABLE; From b46076458c66ebd3e0de6f7ec9bd27b4db33586e Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 13 Feb 2019 18:29:49 +0100 Subject: [PATCH 439/828] Examples: Win32: Removed unused code left-over from merge e9c625a1dc91818385aea650716200e6119fa5f6 --- examples/imgui_impl_win32.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index f34d840533ed..d9c43676a28f 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -197,14 +197,6 @@ static void ImGui_ImplWin32_UpdateMousePos() if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle((void*)hovered_hwnd)) if ((viewport->Flags & ImGuiViewportFlags_NoInputs) == 0) // FIXME: We still get our NoInputs window with WM_NCHITTEST/HTTRANSPARENT code when decorated? io.MouseHoveredViewport = viewport->ID; - -#if 0 - POINT pos; - if (HWND active_window = ::GetForegroundWindow()) - if (active_window == g_hWnd || ::IsChild(active_window, g_hWnd)) - if (::GetCursorPos(&pos) && ::ScreenToClient(g_hWnd, &pos)) - io.MousePos = ImVec2((float)pos.x, (float)pos.y); -#endif } #ifdef _MSC_VER From 3de440fda225160f6c1bf2435d245a8947a18bdb Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 18 Feb 2019 16:13:17 +0100 Subject: [PATCH 440/828] Docking: Fixed assert in DockContextProcessDock() preventing some uses of DockNodeBuilder api. (#2357, #2109) --- imgui.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 08223e3e376d..8f09219bfd59 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10624,8 +10624,9 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) } // FIXME-DOCK: When we are trying to dock an existing single-window node into a loose window, transfer Node ID as well + // When processing an interactive split, usually LastFrameAlive will be < g.FrameCount. But DockBuilder operations can make it ==. if (target_node) - IM_ASSERT(target_node->LastFrameAlive < g.FrameCount); + IM_ASSERT(target_node->LastFrameAlive <= g.FrameCount); if (target_node && target_window && target_node == target_window->DockNodeAsHost) IM_ASSERT(target_node->Windows.Size > 0 || target_node->IsSplitNode() || target_node->IsCentralNode); @@ -11975,7 +11976,7 @@ void ImGui::DockNodeTreeSplit(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImG parent_node->VisibleWindow = NULL; float size_avail = (parent_node->Size[split_axis] - IMGUI_DOCK_SPLITTER_SIZE); - IM_ASSERT(size_avail > 0.0f); + IM_ASSERT(size_avail > 0.0f); // If you created a node manually with DockBuilderAddNode(), you need to also call DockBuilderSetNodeSize() before splitting. child_0->SizeRef = child_1->SizeRef = parent_node->Size; child_0->SizeRef[split_axis] = ImFloor(size_avail * split_ratio); child_1->SizeRef[split_axis] = ImFloor(size_avail - child_0->SizeRef[split_axis]); @@ -12476,7 +12477,6 @@ ImGuiID ImGui::DockBuilderAddNode(ImGuiID id, ImGuiDockNodeFlags flags) { DockSpace(id, ImVec2(0, 0), flags | ImGuiDockNodeFlags_KeepAliveOnly); node = DockContextFindNodeByID(ctx, id); - node->LastFrameAlive = -1; } else { @@ -12484,8 +12484,8 @@ ImGuiID ImGui::DockBuilderAddNode(ImGuiID id, ImGuiDockNodeFlags flags) node = DockContextFindNodeByID(ctx, id); if (!node) node = DockContextAddNode(ctx, id); - node->LastFrameAlive = ctx->FrameCount; } + node->LastFrameAlive = ctx->FrameCount; // Set this otherwise BeginDocked will undock during the same frame. return node->ID; } From 5412cdf2c865c08a6713ed1fbd860e21de411996 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 18 Feb 2019 16:23:54 +0100 Subject: [PATCH 441/828] Docking: Made DockBuilderSplitNode/DockNodeTreeSplit work even if the node doesn't have a size yet. (#2357, #2109) Followup to fa0ce4b7d, at that time I came to the conclusion that programmatic split couldn't work without knowing the size ahead of it. I forgot the reason for that. May bite us back! --- imgui.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/imgui.cpp b/imgui.cpp index 8f09219bfd59..b03f5bfbaeca 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11959,6 +11959,7 @@ static void ImGui::DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDock void ImGui::DockNodeTreeSplit(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiAxis split_axis, int split_inheritor_child_idx, float split_ratio, ImGuiDockNode* new_node) { + ImGuiContext& g = *GImGui; IM_ASSERT(split_axis != ImGuiAxis_None); ImGuiDockNode* child_0 = (new_node && split_inheritor_child_idx != 0) ? new_node : DockContextAddNode(ctx, 0); @@ -11976,6 +11977,7 @@ void ImGui::DockNodeTreeSplit(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImG parent_node->VisibleWindow = NULL; float size_avail = (parent_node->Size[split_axis] - IMGUI_DOCK_SPLITTER_SIZE); + size_avail = ImMax(size_avail, g.Style.WindowMinSize[split_axis] * 2.0f); IM_ASSERT(size_avail > 0.0f); // If you created a node manually with DockBuilderAddNode(), you need to also call DockBuilderSetNodeSize() before splitting. child_0->SizeRef = child_1->SizeRef = parent_node->Size; child_0->SizeRef[split_axis] = ImFloor(size_avail * split_ratio); From 7573d10a4ad3a09dfba9f32d2df0bb1d49f5f2a2 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 18 Feb 2019 16:50:39 +0100 Subject: [PATCH 442/828] Docking: Fixed bad ever-growing/ leak (accumulating text into TabsNames forever, fix d38f4dc14 from February 5th, affected docking branch only). (#2109) --- imgui_widgets.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 8f4b7391c093..6d2250c89369 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6452,8 +6452,17 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, tab->Window = docked_window; // Append name with zero-terminator - tab->NameOffset = tab_bar->TabsNames.size(); - tab_bar->TabsNames.append(label, label + strlen(label) + 1); + if (tab_bar->Flags & ImGuiTabBarFlags_DockNode) + { + IM_ASSERT(tab->Window != NULL); + tab->NameOffset = -1; + } + else + { + IM_ASSERT(tab->Window == NULL); + tab->NameOffset = tab_bar->TabsNames.size(); + tab_bar->TabsNames.append(label, label + strlen(label) + 1); // Append name _with_ the zero-terminator. + } // If we are not reorderable, always reset offset based on submission order. // (We already handled layout and sizing using the previous known order, but sizing is not affected by order!) From ff0f9aa8565e2b5092cbe05e44798e9e04dd445e Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 19 Feb 2019 16:36:06 +0100 Subject: [PATCH 443/828] Comments for Linux/Mac (#2117) --- examples/imgui_impl_glfw.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index e67af8221272..45b2d0e786fb 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -471,6 +471,7 @@ static void ImGui_ImplGlfw_DestroyWindow(ImGuiViewport* viewport) viewport->PlatformUserData = viewport->PlatformHandle = NULL; } +// FIXME-VIEWPORT: Implement same work-around for Linux/OSX in the meanwhile. #if defined(_WIN32) && GLFW_HAS_GLFW_HOVERED static WNDPROC g_GlfwWndProc = NULL; static LRESULT CALLBACK WndProcNoInputs(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) @@ -513,7 +514,9 @@ static void ImGui_ImplGlfw_ShowWindow(ImGuiViewport* viewport) #endif // GLFW hack: GLFW 3.2 has a bug where glfwShowWindow() also activates/focus the window. - // The fix was pushed to GLFW repository on 2018/01/09 and should be included in GLFW 3.3. See https://github.com/glfw/glfw/issues/1179 + // The fix was pushed to GLFW repository on 2018/01/09 and should be included in GLFW 3.3 via a GLFW_FOCUS_ON_SHOW window attribute. + // See https://github.com/glfw/glfw/issues/1189 + // FIXME-VIEWPORT: Implement same work-around for Linux/OSX in the meanwhile. if (viewport->Flags & ImGuiViewportFlags_NoFocusOnAppearing) { ::ShowWindow(hwnd, SW_SHOWNA); From 104294c7e44ecad5093cd03fa56c5a7d1076834a Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 26 Feb 2019 15:33:50 +0100 Subject: [PATCH 444/828] Moved Logging/Capturing section above Docking to facilitate master<>docking merges. --- imgui.cpp | 402 +++++++++++++++++++++++++++--------------------------- 1 file changed, 202 insertions(+), 200 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 2b1d4a989437..12aba98180d9 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -73,8 +73,8 @@ CODE // [SECTION] KEYBOARD/GAMEPAD NAVIGATION // [SECTION] COLUMNS // [SECTION] DRAG AND DROP -// [SECTION] DOCKING // [SECTION] LOGGING/CAPTURING +// [SECTION] DOCKING // [SECTION] SETTINGS // [SECTION] PLATFORM DEPENDENT HELPERS // [SECTION] METRICS/DEBUG WINDOW @@ -10100,6 +10100,207 @@ void ImGui::EndDragDropTarget() g.DragDropWithinSourceOrTarget = false; } +//----------------------------------------------------------------------------- +// [SECTION] LOGGING/CAPTURING +//----------------------------------------------------------------------------- +// All text output from the interface can be captured into tty/file/clipboard. +// By default, tree nodes are automatically opened during logging. +//----------------------------------------------------------------------------- + +// Pass text data straight to log (without being displayed) +void ImGui::LogText(const char* fmt, ...) +{ + ImGuiContext& g = *GImGui; + if (!g.LogEnabled) + return; + + va_list args; + va_start(args, fmt); + if (g.LogFile) + vfprintf(g.LogFile, fmt, args); + else + g.LogBuffer.appendfv(fmt, args); + va_end(args); +} + +// Internal version that takes a position to decide on newline placement and pad items according to their depth. +// We split text into individual lines to add current tree level padding +void ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + + if (!text_end) + text_end = FindRenderedTextEnd(text, text_end); + + const bool log_new_line = ref_pos && (ref_pos->y > g.LogLinePosY + 1); + if (ref_pos) + g.LogLinePosY = ref_pos->y; + if (log_new_line) + g.LogLineFirstItem = true; + + const char* text_remaining = text; + if (g.LogDepthRef > window->DC.TreeDepth) // Re-adjust padding if we have popped out of our starting depth + g.LogDepthRef = window->DC.TreeDepth; + const int tree_depth = (window->DC.TreeDepth - g.LogDepthRef); + for (;;) + { + // Split the string. Each new line (after a '\n') is followed by spacing corresponding to the current depth of our log entry. + // We don't add a trailing \n to allow a subsequent item on the same line to be captured. + const char* line_start = text_remaining; + const char* line_end = ImStreolRange(line_start, text_end); + const bool is_first_line = (line_start == text); + const bool is_last_line = (line_end == text_end); + if (!is_last_line || (line_start != line_end)) + { + const int char_count = (int)(line_end - line_start); + if (log_new_line || !is_first_line) + LogText(IM_NEWLINE "%*s%.*s", tree_depth * 4, "", char_count, line_start); + else if (g.LogLineFirstItem) + LogText("%*s%.*s", tree_depth * 4, "", char_count, line_start); + else + LogText(" %.*s", char_count, line_start); + g.LogLineFirstItem = false; + } + else if (log_new_line) + { + // An empty "" string at a different Y position should output a carriage return. + LogText(IM_NEWLINE); + break; + } + + if (is_last_line) + break; + text_remaining = line_end + 1; + } +} + +// Start logging/capturing text output +void ImGui::LogBegin(ImGuiLogType type, int auto_open_depth) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + IM_ASSERT(g.LogEnabled == false); + IM_ASSERT(g.LogFile == NULL); + IM_ASSERT(g.LogBuffer.empty()); + g.LogEnabled = true; + g.LogType = type; + g.LogDepthRef = window->DC.TreeDepth; + g.LogDepthToExpand = ((auto_open_depth >= 0) ? auto_open_depth : g.LogDepthToExpandDefault); + g.LogLinePosY = FLT_MAX; + g.LogLineFirstItem = true; +} + +void ImGui::LogToTTY(int auto_open_depth) +{ + ImGuiContext& g = *GImGui; + if (g.LogEnabled) + return; + LogBegin(ImGuiLogType_TTY, auto_open_depth); + g.LogFile = stdout; +} + +// Start logging/capturing text output to given file +void ImGui::LogToFile(int auto_open_depth, const char* filename) +{ + ImGuiContext& g = *GImGui; + if (g.LogEnabled) + return; + + // FIXME: We could probably open the file in text mode "at", however note that clipboard/buffer logging will still + // be subject to outputting OS-incompatible carriage return if within strings the user doesn't use IM_NEWLINE. + // By opening the file in binary mode "ab" we have consistent output everywhere. + if (!filename) + filename = g.IO.LogFilename; + if (!filename || !filename[0]) + return; + FILE* f = ImFileOpen(filename, "ab"); + if (f == NULL) + { + IM_ASSERT(0); + return; + } + + LogBegin(ImGuiLogType_File, auto_open_depth); + g.LogFile = f; +} + +// Start logging/capturing text output to clipboard +void ImGui::LogToClipboard(int auto_open_depth) +{ + ImGuiContext& g = *GImGui; + if (g.LogEnabled) + return; + LogBegin(ImGuiLogType_Clipboard, auto_open_depth); +} + +void ImGui::LogToBuffer(int auto_open_depth) +{ + ImGuiContext& g = *GImGui; + if (g.LogEnabled) + return; + LogBegin(ImGuiLogType_Buffer, auto_open_depth); +} + +void ImGui::LogFinish() +{ + ImGuiContext& g = *GImGui; + if (!g.LogEnabled) + return; + + LogText(IM_NEWLINE); + switch (g.LogType) + { + case ImGuiLogType_TTY: + fflush(g.LogFile); + break; + case ImGuiLogType_File: + fclose(g.LogFile); + break; + case ImGuiLogType_Buffer: + break; + case ImGuiLogType_Clipboard: + if (!g.LogBuffer.empty()) + SetClipboardText(g.LogBuffer.begin()); + break; + case ImGuiLogType_None: + IM_ASSERT(0); + break; + } + + g.LogEnabled = false; + g.LogType = ImGuiLogType_None; + g.LogFile = NULL; + g.LogBuffer.clear(); +} + +// Helper to display logging buttons +// FIXME-OBSOLETE: We should probably obsolete this and let the user have their own helper (this is one of the oldest function alive!) +void ImGui::LogButtons() +{ + ImGuiContext& g = *GImGui; + + PushID("LogButtons"); + const bool log_to_tty = Button("Log To TTY"); SameLine(); + const bool log_to_file = Button("Log To File"); SameLine(); + const bool log_to_clipboard = Button("Log To Clipboard"); SameLine(); + PushItemWidth(80.0f); + PushAllowKeyboardFocus(false); + SliderInt("Default Depth", &g.LogDepthToExpandDefault, 0, 9, NULL); + PopAllowKeyboardFocus(); + PopItemWidth(); + PopID(); + + // Start logging at the end of the function so that the buttons don't appear in the log + if (log_to_tty) + LogToTTY(); + if (log_to_file) + LogToFile(); + if (log_to_clipboard) + LogToClipboard(); +} + + //----------------------------------------------------------------------------- // [SECTION] DOCKING //----------------------------------------------------------------------------- @@ -13194,205 +13395,6 @@ static void ImGui::DockSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettings buf->appendf("\n"); } -//----------------------------------------------------------------------------- -// [SECTION] LOGGING/CAPTURING -//----------------------------------------------------------------------------- -// All text output from the interface can be captured into tty/file/clipboard. -// By default, tree nodes are automatically opened during logging. -//----------------------------------------------------------------------------- - -// Pass text data straight to log (without being displayed) -void ImGui::LogText(const char* fmt, ...) -{ - ImGuiContext& g = *GImGui; - if (!g.LogEnabled) - return; - - va_list args; - va_start(args, fmt); - if (g.LogFile) - vfprintf(g.LogFile, fmt, args); - else - g.LogBuffer.appendfv(fmt, args); - va_end(args); -} - -// Internal version that takes a position to decide on newline placement and pad items according to their depth. -// We split text into individual lines to add current tree level padding -void ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - if (!text_end) - text_end = FindRenderedTextEnd(text, text_end); - - const bool log_new_line = ref_pos && (ref_pos->y > g.LogLinePosY + 1); - if (ref_pos) - g.LogLinePosY = ref_pos->y; - if (log_new_line) - g.LogLineFirstItem = true; - - const char* text_remaining = text; - if (g.LogDepthRef > window->DC.TreeDepth) // Re-adjust padding if we have popped out of our starting depth - g.LogDepthRef = window->DC.TreeDepth; - const int tree_depth = (window->DC.TreeDepth - g.LogDepthRef); - for (;;) - { - // Split the string. Each new line (after a '\n') is followed by spacing corresponding to the current depth of our log entry. - // We don't add a trailing \n to allow a subsequent item on the same line to be captured. - const char* line_start = text_remaining; - const char* line_end = ImStreolRange(line_start, text_end); - const bool is_first_line = (line_start == text); - const bool is_last_line = (line_end == text_end); - if (!is_last_line || (line_start != line_end)) - { - const int char_count = (int)(line_end - line_start); - if (log_new_line || !is_first_line) - LogText(IM_NEWLINE "%*s%.*s", tree_depth * 4, "", char_count, line_start); - else if (g.LogLineFirstItem) - LogText("%*s%.*s", tree_depth * 4, "", char_count, line_start); - else - LogText(" %.*s", char_count, line_start); - g.LogLineFirstItem = false; - } - else if (log_new_line) - { - // An empty "" string at a different Y position should output a carriage return. - LogText(IM_NEWLINE); - break; - } - - if (is_last_line) - break; - text_remaining = line_end + 1; - } -} - -// Start logging/capturing text output -void ImGui::LogBegin(ImGuiLogType type, int auto_open_depth) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - IM_ASSERT(g.LogEnabled == false); - IM_ASSERT(g.LogFile == NULL); - IM_ASSERT(g.LogBuffer.empty()); - g.LogEnabled = true; - g.LogType = type; - g.LogDepthRef = window->DC.TreeDepth; - g.LogDepthToExpand = ((auto_open_depth >= 0) ? auto_open_depth : g.LogDepthToExpandDefault); - g.LogLinePosY = FLT_MAX; - g.LogLineFirstItem = true; -} - -void ImGui::LogToTTY(int auto_open_depth) -{ - ImGuiContext& g = *GImGui; - if (g.LogEnabled) - return; - LogBegin(ImGuiLogType_TTY, auto_open_depth); - g.LogFile = stdout; -} - -// Start logging/capturing text output to given file -void ImGui::LogToFile(int auto_open_depth, const char* filename) -{ - ImGuiContext& g = *GImGui; - if (g.LogEnabled) - return; - - // FIXME: We could probably open the file in text mode "at", however note that clipboard/buffer logging will still - // be subject to outputting OS-incompatible carriage return if within strings the user doesn't use IM_NEWLINE. - // By opening the file in binary mode "ab" we have consistent output everywhere. - if (!filename) - filename = g.IO.LogFilename; - if (!filename || !filename[0]) - return; - FILE* f = ImFileOpen(filename, "ab"); - if (f == NULL) - { - IM_ASSERT(0); - return; - } - - LogBegin(ImGuiLogType_File, auto_open_depth); - g.LogFile = f; -} - -// Start logging/capturing text output to clipboard -void ImGui::LogToClipboard(int auto_open_depth) -{ - ImGuiContext& g = *GImGui; - if (g.LogEnabled) - return; - LogBegin(ImGuiLogType_Clipboard, auto_open_depth); -} - -void ImGui::LogToBuffer(int auto_open_depth) -{ - ImGuiContext& g = *GImGui; - if (g.LogEnabled) - return; - LogBegin(ImGuiLogType_Buffer, auto_open_depth); -} - -void ImGui::LogFinish() -{ - ImGuiContext& g = *GImGui; - if (!g.LogEnabled) - return; - - LogText(IM_NEWLINE); - switch (g.LogType) - { - case ImGuiLogType_TTY: - fflush(g.LogFile); - break; - case ImGuiLogType_File: - fclose(g.LogFile); - break; - case ImGuiLogType_Buffer: - break; - case ImGuiLogType_Clipboard: - if (!g.LogBuffer.empty()) - SetClipboardText(g.LogBuffer.begin()); - break; - case ImGuiLogType_None: - IM_ASSERT(0); - break; - } - - g.LogEnabled = false; - g.LogType = ImGuiLogType_None; - g.LogFile = NULL; - g.LogBuffer.clear(); -} - -// Helper to display logging buttons -// FIXME-OBSOLETE: We should probably obsolete this and let the user have their own helper (this is one of the oldest function alive!) -void ImGui::LogButtons() -{ - ImGuiContext& g = *GImGui; - - PushID("LogButtons"); - const bool log_to_tty = Button("Log To TTY"); SameLine(); - const bool log_to_file = Button("Log To File"); SameLine(); - const bool log_to_clipboard = Button("Log To Clipboard"); SameLine(); - PushItemWidth(80.0f); - PushAllowKeyboardFocus(false); - SliderInt("Default Depth", &g.LogDepthToExpandDefault, 0, 9, NULL); - PopAllowKeyboardFocus(); - PopItemWidth(); - PopID(); - - // Start logging at the end of the function so that the buttons don't appear in the log - if (log_to_tty) - LogToTTY(); - if (log_to_file) - LogToFile(); - if (log_to_clipboard) - LogToClipboard(); -} //----------------------------------------------------------------------------- // [SECTION] SETTINGS From 4eecf80a4b253f359b9879e6538e41b4edeae1de Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 26 Feb 2019 15:34:47 +0100 Subject: [PATCH 445/828] Moved Settings section above Docking to facilitate master<>docking merges. --- imgui.cpp | 497 +++++++++++++++++++++++++++--------------------------- 1 file changed, 249 insertions(+), 248 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 12aba98180d9..0558043f39e0 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -74,8 +74,8 @@ CODE // [SECTION] COLUMNS // [SECTION] DRAG AND DROP // [SECTION] LOGGING/CAPTURING -// [SECTION] DOCKING // [SECTION] SETTINGS +// [SECTION] DOCKING // [SECTION] PLATFORM DEPENDENT HELPERS // [SECTION] METRICS/DEBUG WINDOW @@ -10301,6 +10301,254 @@ void ImGui::LogButtons() } +//----------------------------------------------------------------------------- +// [SECTION] SETTINGS +//----------------------------------------------------------------------------- + +void ImGui::MarkIniSettingsDirty() +{ + ImGuiContext& g = *GImGui; + if (g.SettingsDirtyTimer <= 0.0f) + g.SettingsDirtyTimer = g.IO.IniSavingRate; +} + +void ImGui::MarkIniSettingsDirty(ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; + if (!(window->Flags & ImGuiWindowFlags_NoSavedSettings)) + if (g.SettingsDirtyTimer <= 0.0f) + g.SettingsDirtyTimer = g.IO.IniSavingRate; +} + +ImGuiWindowSettings* ImGui::CreateNewWindowSettings(const char* name) +{ + ImGuiContext& g = *GImGui; + g.SettingsWindows.push_back(ImGuiWindowSettings()); + ImGuiWindowSettings* settings = &g.SettingsWindows.back(); + settings->Name = ImStrdup(name); + settings->ID = ImHashStr(name, 0); + return settings; +} + +ImGuiWindowSettings* ImGui::FindWindowSettings(ImGuiID id) +{ + ImGuiContext& g = *GImGui; + for (int i = 0; i != g.SettingsWindows.Size; i++) + if (g.SettingsWindows[i].ID == id) + return &g.SettingsWindows[i]; + return NULL; +} + +ImGuiWindowSettings* ImGui::FindOrCreateWindowSettings(const char* name) +{ + if (ImGuiWindowSettings* settings = FindWindowSettings(ImHashStr(name, 0))) + return settings; + return CreateNewWindowSettings(name); +} + +void ImGui::LoadIniSettingsFromDisk(const char* ini_filename) +{ + size_t file_data_size = 0; + char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_data_size); + if (!file_data) + return; + LoadIniSettingsFromMemory(file_data, (size_t)file_data_size); + ImGui::MemFree(file_data); +} + +ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name) +{ + ImGuiContext& g = *GImGui; + const ImGuiID type_hash = ImHashStr(type_name, 0); + for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++) + if (g.SettingsHandlers[handler_n].TypeHash == type_hash) + return &g.SettingsHandlers[handler_n]; + return NULL; +} + +// Zero-tolerance, no error reporting, cheap .ini parsing +void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(g.Initialized); + IM_ASSERT(g.SettingsLoaded == false && g.FrameCount == 0); + + // For user convenience, we allow passing a non zero-terminated string (hence the ini_size parameter). + // For our convenience and to make the code simpler, we'll also write zero-terminators within the buffer. So let's create a writable copy.. + if (ini_size == 0) + ini_size = strlen(ini_data); + char* buf = (char*)ImGui::MemAlloc(ini_size + 1); + char* buf_end = buf + ini_size; + memcpy(buf, ini_data, ini_size); + buf[ini_size] = 0; + + void* entry_data = NULL; + ImGuiSettingsHandler* entry_handler = NULL; + + char* line_end = NULL; + for (char* line = buf; line < buf_end; line = line_end + 1) + { + // Skip new lines markers, then find end of the line + while (*line == '\n' || *line == '\r') + line++; + line_end = line; + while (line_end < buf_end && *line_end != '\n' && *line_end != '\r') + line_end++; + line_end[0] = 0; + if (line[0] == ';') + continue; + if (line[0] == '[' && line_end > line && line_end[-1] == ']') + { + // Parse "[Type][Name]". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code. + line_end[-1] = 0; + const char* name_end = line_end - 1; + const char* type_start = line + 1; + char* type_end = (char*)(intptr_t)ImStrchrRange(type_start, name_end, ']'); + const char* name_start = type_end ? ImStrchrRange(type_end + 1, name_end, '[') : NULL; + if (!type_end || !name_start) + { + name_start = type_start; // Import legacy entries that have no type + type_start = "Window"; + } + else + { + *type_end = 0; // Overwrite first ']' + name_start++; // Skip second '[' + } + entry_handler = FindSettingsHandler(type_start); + entry_data = entry_handler ? entry_handler->ReadOpenFn(&g, entry_handler, name_start) : NULL; + } + else if (entry_handler != NULL && entry_data != NULL) + { + // Let type handler parse the line + entry_handler->ReadLineFn(&g, entry_handler, entry_data, line); + } + } + ImGui::MemFree(buf); + g.SettingsLoaded = true; + DockContextOnLoadSettings(&g); +} + +void ImGui::SaveIniSettingsToDisk(const char* ini_filename) +{ + ImGuiContext& g = *GImGui; + g.SettingsDirtyTimer = 0.0f; + if (!ini_filename) + return; + + size_t ini_data_size = 0; + const char* ini_data = SaveIniSettingsToMemory(&ini_data_size); + FILE* f = ImFileOpen(ini_filename, "wt"); + if (!f) + return; + fwrite(ini_data, sizeof(char), ini_data_size, f); + fclose(f); +} + +// Call registered handlers (e.g. SettingsHandlerWindow_WriteAll() + custom handlers) to write their stuff into a text buffer +const char* ImGui::SaveIniSettingsToMemory(size_t* out_size) +{ + ImGuiContext& g = *GImGui; + g.SettingsDirtyTimer = 0.0f; + g.SettingsIniData.Buf.resize(0); + g.SettingsIniData.Buf.push_back(0); + for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++) + { + ImGuiSettingsHandler* handler = &g.SettingsHandlers[handler_n]; + handler->WriteAllFn(&g, handler, &g.SettingsIniData); + } + if (out_size) + *out_size = (size_t)g.SettingsIniData.size(); + return g.SettingsIniData.c_str(); +} + +static void* SettingsHandlerWindow_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name) +{ + ImGuiWindowSettings* settings = ImGui::FindWindowSettings(ImHashStr(name, 0)); + if (!settings) + settings = ImGui::CreateNewWindowSettings(name); + return (void*)settings; +} + +static void SettingsHandlerWindow_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line) +{ + ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry; + float x, y; + int i; + ImU32 u1; + if (sscanf(line, "Pos=%f,%f", &x, &y) == 2) { settings->Pos = ImVec2(x, y); } + else if (sscanf(line, "Size=%f,%f", &x, &y) == 2) { settings->Size = ImMax(ImVec2(x, y), GImGui->Style.WindowMinSize); } + else if (sscanf(line, "ViewportId=0x%08X", &u1) == 1) { settings->ViewportId = u1; } + else if (sscanf(line, "ViewportPos=%f,%f", &x, &y) == 2) { settings->ViewportPos = ImVec2(x, y); } + else if (sscanf(line, "Collapsed=%d", &i) == 1) { settings->Collapsed = (i != 0); } + else if (sscanf(line, "DockId=0x%X,%d", &u1, &i) == 2) { settings->DockId = u1; settings->DockOrder = (short)i; } + else if (sscanf(line, "DockId=0x%X", &u1) == 1) { settings->DockId = u1; settings->DockOrder = -1; } + else if (sscanf(line, "ClassId=0x%X", &u1) == 1) { settings->ClassId = u1; } +} + +static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf) +{ + // Gather data from windows that were active during this session + // (if a window wasn't opened in this session we preserve its settings) + ImGuiContext& g = *imgui_ctx; + for (int i = 0; i != g.Windows.Size; i++) + { + ImGuiWindow* window = g.Windows[i]; + if (window->Flags & ImGuiWindowFlags_NoSavedSettings) + continue; + + ImGuiWindowSettings* settings = (window->SettingsIdx != -1) ? &g.SettingsWindows[window->SettingsIdx] : ImGui::FindWindowSettings(window->ID); + if (!settings) + { + settings = ImGui::CreateNewWindowSettings(window->Name); + window->SettingsIdx = g.SettingsWindows.index_from_ptr(settings); + } + IM_ASSERT(settings->ID == window->ID); + settings->Pos = window->Pos - window->ViewportPos; + settings->Size = window->SizeFull; + settings->ViewportId = window->ViewportId; + settings->ViewportPos = window->ViewportPos; + IM_ASSERT(window->DockNode == NULL || window->DockNode->ID == window->DockId); + settings->DockId = window->DockId; + settings->ClassId = window->WindowClass.ClassId; + settings->DockOrder = window->DockOrder; + settings->Collapsed = window->Collapsed; + } + + // Write to text buffer + buf->reserve(buf->size() + g.SettingsWindows.Size * 96); // ballpark reserve + for (int i = 0; i != g.SettingsWindows.Size; i++) + { + const ImGuiWindowSettings* settings = &g.SettingsWindows[i]; + const char* name = settings->Name; + if (const char* p = strstr(name, "###")) // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID() + name = p; + buf->appendf("[%s][%s]\n", handler->TypeName, name); + if (settings->ViewportId != 0 && settings->ViewportId != ImGui::IMGUI_VIEWPORT_DEFAULT_ID) + { + buf->appendf("ViewportPos=%d,%d\n", (int)settings->ViewportPos.x, (int)settings->ViewportPos.y); + buf->appendf("ViewportId=0x%08X\n", settings->ViewportId); + } + if (settings->Pos.x != 0.0f || settings->Pos.y != 0.0f || settings->ViewportId == ImGui::IMGUI_VIEWPORT_DEFAULT_ID) + buf->appendf("Pos=%d,%d\n", (int)settings->Pos.x, (int)settings->Pos.y); + if (settings->Size.x != 0.0f || settings->Size.y != 0.0f) + buf->appendf("Size=%d,%d\n", (int)settings->Size.x, (int)settings->Size.y); + buf->appendf("Collapsed=%d\n", settings->Collapsed); + if (settings->DockId != 0) + { + // Write DockId as 4 digits if possible. Automatic DockId are small numbers, but full explicit DockSpace() are full ImGuiID range. + if (settings->DockOrder == -1) + buf->appendf("DockId=0x%08X\n", settings->DockId); + else + buf->appendf("DockId=0x%08X,%d\n", settings->DockId, settings->DockOrder); + if (settings->ClassId != 0) + buf->appendf("ClassId=0x%08X\n", settings->ClassId); + } + buf->appendf("\n"); + } +} + + //----------------------------------------------------------------------------- // [SECTION] DOCKING //----------------------------------------------------------------------------- @@ -13396,253 +13644,6 @@ static void ImGui::DockSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettings } -//----------------------------------------------------------------------------- -// [SECTION] SETTINGS -//----------------------------------------------------------------------------- - -void ImGui::MarkIniSettingsDirty() -{ - ImGuiContext& g = *GImGui; - if (g.SettingsDirtyTimer <= 0.0f) - g.SettingsDirtyTimer = g.IO.IniSavingRate; -} - -void ImGui::MarkIniSettingsDirty(ImGuiWindow* window) -{ - ImGuiContext& g = *GImGui; - if (!(window->Flags & ImGuiWindowFlags_NoSavedSettings)) - if (g.SettingsDirtyTimer <= 0.0f) - g.SettingsDirtyTimer = g.IO.IniSavingRate; -} - -ImGuiWindowSettings* ImGui::CreateNewWindowSettings(const char* name) -{ - ImGuiContext& g = *GImGui; - g.SettingsWindows.push_back(ImGuiWindowSettings()); - ImGuiWindowSettings* settings = &g.SettingsWindows.back(); - settings->Name = ImStrdup(name); - settings->ID = ImHashStr(name, 0); - return settings; -} - -ImGuiWindowSettings* ImGui::FindWindowSettings(ImGuiID id) -{ - ImGuiContext& g = *GImGui; - for (int i = 0; i != g.SettingsWindows.Size; i++) - if (g.SettingsWindows[i].ID == id) - return &g.SettingsWindows[i]; - return NULL; -} - -ImGuiWindowSettings* ImGui::FindOrCreateWindowSettings(const char* name) -{ - if (ImGuiWindowSettings* settings = FindWindowSettings(ImHashStr(name, 0))) - return settings; - return CreateNewWindowSettings(name); -} - -void ImGui::LoadIniSettingsFromDisk(const char* ini_filename) -{ - size_t file_data_size = 0; - char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_data_size); - if (!file_data) - return; - LoadIniSettingsFromMemory(file_data, (size_t)file_data_size); - ImGui::MemFree(file_data); -} - -ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name) -{ - ImGuiContext& g = *GImGui; - const ImGuiID type_hash = ImHashStr(type_name, 0); - for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++) - if (g.SettingsHandlers[handler_n].TypeHash == type_hash) - return &g.SettingsHandlers[handler_n]; - return NULL; -} - -// Zero-tolerance, no error reporting, cheap .ini parsing -void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(g.Initialized); - IM_ASSERT(g.SettingsLoaded == false && g.FrameCount == 0); - - // For user convenience, we allow passing a non zero-terminated string (hence the ini_size parameter). - // For our convenience and to make the code simpler, we'll also write zero-terminators within the buffer. So let's create a writable copy.. - if (ini_size == 0) - ini_size = strlen(ini_data); - char* buf = (char*)ImGui::MemAlloc(ini_size + 1); - char* buf_end = buf + ini_size; - memcpy(buf, ini_data, ini_size); - buf[ini_size] = 0; - - void* entry_data = NULL; - ImGuiSettingsHandler* entry_handler = NULL; - - char* line_end = NULL; - for (char* line = buf; line < buf_end; line = line_end + 1) - { - // Skip new lines markers, then find end of the line - while (*line == '\n' || *line == '\r') - line++; - line_end = line; - while (line_end < buf_end && *line_end != '\n' && *line_end != '\r') - line_end++; - line_end[0] = 0; - if (line[0] == ';') - continue; - if (line[0] == '[' && line_end > line && line_end[-1] == ']') - { - // Parse "[Type][Name]". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code. - line_end[-1] = 0; - const char* name_end = line_end - 1; - const char* type_start = line + 1; - char* type_end = (char*)(intptr_t)ImStrchrRange(type_start, name_end, ']'); - const char* name_start = type_end ? ImStrchrRange(type_end + 1, name_end, '[') : NULL; - if (!type_end || !name_start) - { - name_start = type_start; // Import legacy entries that have no type - type_start = "Window"; - } - else - { - *type_end = 0; // Overwrite first ']' - name_start++; // Skip second '[' - } - entry_handler = FindSettingsHandler(type_start); - entry_data = entry_handler ? entry_handler->ReadOpenFn(&g, entry_handler, name_start) : NULL; - } - else if (entry_handler != NULL && entry_data != NULL) - { - // Let type handler parse the line - entry_handler->ReadLineFn(&g, entry_handler, entry_data, line); - } - } - ImGui::MemFree(buf); - g.SettingsLoaded = true; - DockContextOnLoadSettings(&g); -} - -void ImGui::SaveIniSettingsToDisk(const char* ini_filename) -{ - ImGuiContext& g = *GImGui; - g.SettingsDirtyTimer = 0.0f; - if (!ini_filename) - return; - - size_t ini_data_size = 0; - const char* ini_data = SaveIniSettingsToMemory(&ini_data_size); - FILE* f = ImFileOpen(ini_filename, "wt"); - if (!f) - return; - fwrite(ini_data, sizeof(char), ini_data_size, f); - fclose(f); -} - -// Call registered handlers (e.g. SettingsHandlerWindow_WriteAll() + custom handlers) to write their stuff into a text buffer -const char* ImGui::SaveIniSettingsToMemory(size_t* out_size) -{ - ImGuiContext& g = *GImGui; - g.SettingsDirtyTimer = 0.0f; - g.SettingsIniData.Buf.resize(0); - g.SettingsIniData.Buf.push_back(0); - for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++) - { - ImGuiSettingsHandler* handler = &g.SettingsHandlers[handler_n]; - handler->WriteAllFn(&g, handler, &g.SettingsIniData); - } - if (out_size) - *out_size = (size_t)g.SettingsIniData.size(); - return g.SettingsIniData.c_str(); -} - -static void* SettingsHandlerWindow_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name) -{ - ImGuiWindowSettings* settings = ImGui::FindWindowSettings(ImHashStr(name, 0)); - if (!settings) - settings = ImGui::CreateNewWindowSettings(name); - return (void*)settings; -} - -static void SettingsHandlerWindow_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line) -{ - ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry; - float x, y; - int i; - ImU32 u1; - if (sscanf(line, "Pos=%f,%f", &x, &y) == 2) { settings->Pos = ImVec2(x, y); } - else if (sscanf(line, "Size=%f,%f", &x, &y) == 2) { settings->Size = ImMax(ImVec2(x, y), GImGui->Style.WindowMinSize); } - else if (sscanf(line, "ViewportId=0x%08X", &u1) == 1) { settings->ViewportId = u1; } - else if (sscanf(line, "ViewportPos=%f,%f", &x, &y) == 2) { settings->ViewportPos = ImVec2(x, y); } - else if (sscanf(line, "Collapsed=%d", &i) == 1) { settings->Collapsed = (i != 0); } - else if (sscanf(line, "DockId=0x%X,%d", &u1, &i) == 2) { settings->DockId = u1; settings->DockOrder = (short)i; } - else if (sscanf(line, "DockId=0x%X", &u1) == 1) { settings->DockId = u1; settings->DockOrder = -1; } - else if (sscanf(line, "ClassId=0x%X", &u1) == 1) { settings->ClassId = u1; } -} - -static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf) -{ - // Gather data from windows that were active during this session - // (if a window wasn't opened in this session we preserve its settings) - ImGuiContext& g = *imgui_ctx; - for (int i = 0; i != g.Windows.Size; i++) - { - ImGuiWindow* window = g.Windows[i]; - if (window->Flags & ImGuiWindowFlags_NoSavedSettings) - continue; - - ImGuiWindowSettings* settings = (window->SettingsIdx != -1) ? &g.SettingsWindows[window->SettingsIdx] : ImGui::FindWindowSettings(window->ID); - if (!settings) - { - settings = ImGui::CreateNewWindowSettings(window->Name); - window->SettingsIdx = g.SettingsWindows.index_from_ptr(settings); - } - IM_ASSERT(settings->ID == window->ID); - settings->Pos = window->Pos - window->ViewportPos; - settings->Size = window->SizeFull; - settings->ViewportId = window->ViewportId; - settings->ViewportPos = window->ViewportPos; - IM_ASSERT(window->DockNode == NULL || window->DockNode->ID == window->DockId); - settings->DockId = window->DockId; - settings->ClassId = window->WindowClass.ClassId; - settings->DockOrder = window->DockOrder; - settings->Collapsed = window->Collapsed; - } - - // Write to text buffer - buf->reserve(buf->size() + g.SettingsWindows.Size * 96); // ballpark reserve - for (int i = 0; i != g.SettingsWindows.Size; i++) - { - const ImGuiWindowSettings* settings = &g.SettingsWindows[i]; - const char* name = settings->Name; - if (const char* p = strstr(name, "###")) // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID() - name = p; - buf->appendf("[%s][%s]\n", handler->TypeName, name); - if (settings->ViewportId != 0 && settings->ViewportId != ImGui::IMGUI_VIEWPORT_DEFAULT_ID) - { - buf->appendf("ViewportPos=%d,%d\n", (int)settings->ViewportPos.x, (int)settings->ViewportPos.y); - buf->appendf("ViewportId=0x%08X\n", settings->ViewportId); - } - if (settings->Pos.x != 0.0f || settings->Pos.y != 0.0f || settings->ViewportId == ImGui::IMGUI_VIEWPORT_DEFAULT_ID) - buf->appendf("Pos=%d,%d\n", (int)settings->Pos.x, (int)settings->Pos.y); - if (settings->Size.x != 0.0f || settings->Size.y != 0.0f) - buf->appendf("Size=%d,%d\n", (int)settings->Size.x, (int)settings->Size.y); - buf->appendf("Collapsed=%d\n", settings->Collapsed); - if (settings->DockId != 0) - { - // Write DockId as 4 digits if possible. Automatic DockId are small numbers, but full explicit DockSpace() are full ImGuiID range. - if (settings->DockOrder == -1) - buf->appendf("DockId=0x%08X\n", settings->DockId); - else - buf->appendf("DockId=0x%08X,%d\n", settings->DockId, settings->DockOrder); - if (settings->ClassId != 0) - buf->appendf("ClassId=0x%08X\n", settings->ClassId); - } - buf->appendf("\n"); - } -} - //----------------------------------------------------------------------------- // [SECTION] PLATFORM DEPENDENT HELPERS //----------------------------------------------------------------------------- From 7a536f1bd2138c47933f8c26f49ebf9e45139bdc Mon Sep 17 00:00:00 2001 From: Richard Mitton Date: Fri, 1 Mar 2019 18:55:55 -0800 Subject: [PATCH 446/828] Examples + Viewport: GLFW: context wasn't set when using multiple windows. (#2392) --- examples/imgui_impl_glfw.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index 45b2d0e786fb..8fae67ccb661 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -602,8 +602,11 @@ static void ImGui_ImplGlfw_RenderWindow(ImGuiViewport* viewport, void*) static void ImGui_ImplGlfw_SwapBuffers(ImGuiViewport* viewport, void*) { ImGuiViewportDataGlfw* data = (ImGuiViewportDataGlfw*)viewport->PlatformUserData; - if (g_ClientApi == GlfwClientApi_OpenGL) + if (g_ClientApi == GlfwClientApi_OpenGL) + { + glfwMakeContextCurrent(data->Window); glfwSwapBuffers(data->Window); + } } //-------------------------------------------------------------------------------------------------------- From 8b8ab1db5b8047f95761d8ab74bd951246f497be Mon Sep 17 00:00:00 2001 From: David Maas Date: Wed, 6 Mar 2019 00:39:16 -0800 Subject: [PATCH 447/828] Removed redundant declaration of SetNextWindowClass. (#2402) --- imgui.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/imgui.h b/imgui.h index d5988bde27a2..c00e0b42a92d 100644 --- a/imgui.h +++ b/imgui.h @@ -290,7 +290,6 @@ namespace ImGui IMGUI_API void SetNextWindowFocus(); // set next window to be focused / front-most. call before Begin() IMGUI_API void SetNextWindowBgAlpha(float alpha); // set next window background color alpha. helper to easily modify ImGuiCol_WindowBg/ChildBg/PopupBg. you may also use ImGuiWindowFlags_NoBackground. IMGUI_API void SetNextWindowViewport(ImGuiID viewport_id); // set next window viewport - IMGUI_API void SetNextWindowClass(const ImGuiWindowClass* window_class); // set next window class (rare/advanced uses: provide hints to the platform back-end via altered viewport flags and parent/child info) IMGUI_API void SetWindowPos(const ImVec2& pos, ImGuiCond cond = 0); // (not recommended) set current window position - call within Begin()/End(). prefer using SetNextWindowPos(), as this may incur tearing and side-effects. IMGUI_API void SetWindowSize(const ImVec2& size, ImGuiCond cond = 0); // (not recommended) set current window size - call within Begin()/End(). set to ImVec2(0,0) to force an auto-fit. prefer using SetNextWindowSize(), as this may incur tearing and minor side-effects. IMGUI_API void SetWindowCollapsed(bool collapsed, ImGuiCond cond = 0); // (not recommended) set current window collapsed state. prefer using SetNextWindowCollapsed(). @@ -595,7 +594,7 @@ namespace ImGui IMGUI_API void DockSpace(ImGuiID id, const ImVec2& size = ImVec2(0, 0), ImGuiDockNodeFlags flags = 0, const ImGuiWindowClass* window_class = NULL); IMGUI_API ImGuiID DockSpaceOverViewport(ImGuiViewport* viewport = NULL, ImGuiDockNodeFlags dockspace_flags = 0, const ImGuiWindowClass* window_class = NULL); IMGUI_API void SetNextWindowDockID(ImGuiID dock_id, ImGuiCond cond = 0); // set next window dock id (FIXME-DOCK) - IMGUI_API void SetNextWindowClass(const ImGuiWindowClass* window_class); // set next window class / user type (docking filters by same user_type) + IMGUI_API void SetNextWindowClass(const ImGuiWindowClass* window_class); // set next window class (rare/advanced uses: provide hints to the platform back-end via altered viewport flags and parent/child info) IMGUI_API ImGuiID GetWindowDockID(); IMGUI_API bool IsWindowDocked(); // is current window docked into another window? From 0a6c5bc2347a27e4922281353ee6cb56cd66fe28 Mon Sep 17 00:00:00 2001 From: Gilad Reich Date: Wed, 6 Mar 2019 21:34:37 +0100 Subject: [PATCH 448/828] Examples: DirectX9: Added support for multi-viewport (#2394) --- examples/example_win32_directx9/main.cpp | 31 ++++++ examples/imgui_impl_dx9.cpp | 123 ++++++++++++++++++++++- examples/imgui_impl_dx9.h | 1 + 3 files changed, 154 insertions(+), 1 deletion(-) diff --git a/examples/example_win32_directx9/main.cpp b/examples/example_win32_directx9/main.cpp index 6b5e731eef3e..9833ca9354fe 100644 --- a/examples/example_win32_directx9/main.cpp +++ b/examples/example_win32_directx9/main.cpp @@ -49,6 +49,10 @@ void ResetDevice() ImGui_ImplDX9_CreateDeviceObjects(); } +#ifndef WM_DPICHANGED +#define WM_DPICHANGED 0x02E0 // From Windows SDK 8.1+ headers +#endif + extern LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { @@ -72,12 +76,23 @@ LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) case WM_DESTROY: PostQuitMessage(0); return 0; + case WM_DPICHANGED: + if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_DpiEnableScaleViewports) + { + //const int dpi = HIWORD(wParam); + //printf("WM_DPICHANGED to %d (%.0f%%)\n", dpi, (float)dpi / 96.0f * 100.0f); + const RECT* suggested_rect = (RECT*)lParam; + ::SetWindowPos(hWnd, NULL, suggested_rect->left, suggested_rect->top, suggested_rect->right - suggested_rect->left, suggested_rect->bottom - suggested_rect->top, SWP_NOZORDER | SWP_NOACTIVATE); + } + break; } return DefWindowProc(hWnd, msg, wParam, lParam); } int main(int, char**) { + ImGui_ImplWin32_EnableDpiAwareness(); + // Create application window WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, WndProc, 0L, 0L, GetModuleHandle(NULL), NULL, NULL, NULL, NULL, _T("ImGui Example"), NULL }; RegisterClassEx(&wc); @@ -109,6 +124,14 @@ int main(int, char**) ImGui::StyleColorsDark(); //ImGui::StyleColorsClassic(); + // When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones. + ImGuiStyle& style = ImGui::GetStyle(); + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + style.WindowRounding = 0.0f; + style.Colors[ImGuiCol_WindowBg].w = 1.0f; + } + // Setup Platform/Renderer bindings ImGui_ImplWin32_Init(hwnd); ImGui_ImplDX9_Init(g_pd3dDevice); @@ -205,6 +228,14 @@ int main(int, char**) ImGui_ImplDX9_RenderDrawData(ImGui::GetDrawData()); g_pd3dDevice->EndScene(); } + + // Update and Render additional Platform Windows + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); + } + HRESULT result = g_pd3dDevice->Present(NULL, NULL, NULL, NULL); // Handle loss of D3D9 device diff --git a/examples/imgui_impl_dx9.cpp b/examples/imgui_impl_dx9.cpp index 378524e09d7a..e4f46850c953 100644 --- a/examples/imgui_impl_dx9.cpp +++ b/examples/imgui_impl_dx9.cpp @@ -3,6 +3,7 @@ // Implemented features: // [X] Renderer: User texture binding. Use 'LPDIRECT3DTEXTURE9' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. +// [X] Renderer: Multi-viewport support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp. @@ -10,6 +11,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2018-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. // 2019-01-16: Misc: Disabled fog before drawing UI's. Fixes issue #2288. // 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window. // 2018-06-08: Misc: Extracted imgui_impl_dx9.cpp/.h away from the old combined DX9+Win32 example. @@ -41,6 +43,10 @@ struct CUSTOMVERTEX }; #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_TEX1) +// Forward Declarations +static void ImGui_ImplDX9_InitPlatformInterface(); +static void ImGui_ImplDX9_ShutdownPlatformInterface(); + // Render function. // (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop) void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data) @@ -203,16 +209,22 @@ void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data) bool ImGui_ImplDX9_Init(IDirect3DDevice9* device) { ImGuiIO& io = ImGui::GetIO(); + io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional) io.BackendRendererName = "imgui_impl_dx9"; g_pd3dDevice = device; + + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + ImGui_ImplDX9_InitPlatformInterface(); + return true; } void ImGui_ImplDX9_Shutdown() { + ImGui_ImplDX9_ShutdownPlatformInterface(); ImGui_ImplDX9_InvalidateDeviceObjects(); - g_pd3dDevice = NULL; + if (g_pd3dDevice) { g_pd3dDevice->Release(); g_pd3dDevice = NULL; } } static bool ImGui_ImplDX9_CreateFontsTexture() @@ -278,3 +290,112 @@ void ImGui_ImplDX9_NewFrame() if (!g_FontTexture) ImGui_ImplDX9_CreateDeviceObjects(); } + +//-------------------------------------------------------------------------------------------------------- +// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT +// This is an _advanced_ and _optional_ feature, allowing the back-end to create and handle multiple viewports simultaneously. +// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first.. +//-------------------------------------------------------------------------------------------------------- + +struct ImGuiViewportDataDx9 +{ + IDirect3DSwapChain9* SwapChain; + D3DPRESENT_PARAMETERS d3dpp; + + ImGuiViewportDataDx9() { SwapChain = NULL; ZeroMemory(&d3dpp, sizeof(D3DPRESENT_PARAMETERS)); } + ~ImGuiViewportDataDx9() { IM_ASSERT(SwapChain == NULL); } +}; + +static void ImGui_ImplDX9_CreateWindow(ImGuiViewport* viewport) +{ + ImGuiViewportDataDx9* data = IM_NEW(ImGuiViewportDataDx9)(); + viewport->RendererUserData = data; + + HWND hWnd = (HWND)viewport->PlatformHandle; + IM_ASSERT(hWnd != 0); + + ZeroMemory(&data->d3dpp, sizeof(D3DPRESENT_PARAMETERS)); + data->d3dpp.Windowed = TRUE; + data->d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; + data->d3dpp.BackBufferFormat = D3DFMT_UNKNOWN; + data->d3dpp.hDeviceWindow = hWnd; + data->d3dpp.EnableAutoDepthStencil = TRUE; + data->d3dpp.AutoDepthStencilFormat = D3DFMT_D16; + data->d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; // Present without vsync + + g_pd3dDevice->CreateAdditionalSwapChain(&data->d3dpp, &data->SwapChain); + IM_ASSERT(data->SwapChain != NULL); +} + +static void ImGui_ImplDX9_DestroyWindow(ImGuiViewport* viewport) +{ + // The main viewport (owned by the application) will always have RendererUserData == NULL since we didn't create the data for it. + if (ImGuiViewportDataDx9* data = (ImGuiViewportDataDx9*)viewport->RendererUserData) + { + if (data->SwapChain) + data->SwapChain->Release(); + data->SwapChain = NULL; + ZeroMemory(&data->d3dpp, sizeof(D3DPRESENT_PARAMETERS)); + IM_DELETE(data); + } + viewport->RendererUserData = NULL; +} + +static void ImGui_ImplDX9_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) +{ + ImGuiViewportDataDx9* data = (ImGuiViewportDataDx9*)viewport->RendererUserData; + if (data->SwapChain) + { + data->SwapChain->Release(); + data->SwapChain = NULL; + data->d3dpp.BackBufferWidth = (UINT)size.x; + data->d3dpp.BackBufferHeight = (UINT)size.y; + g_pd3dDevice->CreateAdditionalSwapChain(&data->d3dpp, &data->SwapChain); + } +} + +static void ImGui_ImplDX9_RenderWindow(ImGuiViewport* viewport, void*) +{ + ImGuiViewportDataDx9* data = (ImGuiViewportDataDx9*)viewport->RendererUserData; + ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); + + LPDIRECT3DSURFACE9 render_target = NULL; + LPDIRECT3DSURFACE9 last_render_target = NULL; + data->SwapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &render_target); + g_pd3dDevice->GetRenderTarget(0, &last_render_target); + g_pd3dDevice->SetRenderTarget(0, render_target); + + if (!(viewport->Flags & ImGuiViewportFlags_NoRendererClear)) + { + D3DCOLOR clear_col_dx = D3DCOLOR_RGBA((int)(clear_color.x*255.0f), (int)(clear_color.y*255.0f), (int)(clear_color.z*255.0f), (int)(clear_color.w*255.0f)); + g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, clear_col_dx, 1.0f, 0); + } + + ImGui_ImplDX9_RenderDrawData(viewport->DrawData); + + // Restore render target + g_pd3dDevice->SetRenderTarget(0, last_render_target); + render_target->Release(); + last_render_target->Release(); +} + +static void ImGui_ImplDX9_SwapBuffers(ImGuiViewport* viewport, void*) +{ + ImGuiViewportDataDx9* data = (ImGuiViewportDataDx9*)viewport->RendererUserData; + data->SwapChain->Present(NULL, NULL, data->d3dpp.hDeviceWindow, NULL, NULL); +} + +static void ImGui_ImplDX9_InitPlatformInterface() +{ + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.Renderer_CreateWindow = ImGui_ImplDX9_CreateWindow; + platform_io.Renderer_DestroyWindow = ImGui_ImplDX9_DestroyWindow; + platform_io.Renderer_SetWindowSize = ImGui_ImplDX9_SetWindowSize; + platform_io.Renderer_RenderWindow = ImGui_ImplDX9_RenderWindow; + platform_io.Renderer_SwapBuffers = ImGui_ImplDX9_SwapBuffers; +} + +static void ImGui_ImplDX9_ShutdownPlatformInterface() +{ + ImGui::DestroyPlatformWindows(); +} diff --git a/examples/imgui_impl_dx9.h b/examples/imgui_impl_dx9.h index 95902f74c8d9..8695368418ba 100644 --- a/examples/imgui_impl_dx9.h +++ b/examples/imgui_impl_dx9.h @@ -3,6 +3,7 @@ // Implemented features: // [X] Renderer: User texture binding. Use 'LPDIRECT3DTEXTURE9' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. +// [X] Renderer: Multi-viewport support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp. From 28d8eb220b7307df4a5c9b90b8cdf6f9cc44f2c8 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 7 Mar 2019 16:07:16 +0100 Subject: [PATCH 449/828] Fix for Android char being unsigned by default (#2408) --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index deedfc151b65..214cfc058292 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10684,7 +10684,7 @@ struct ImGuiDockNodeSettings ImGuiID ID; ImGuiID ParentID; ImGuiID SelectedTabID; - char SplitAxis; + signed char SplitAxis; char Depth; char IsDockSpace; char IsCentralNode; From bbb543fc16d38952ff6b72231889e69abbd456dd Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 7 Mar 2019 18:33:45 +0100 Subject: [PATCH 450/828] Refactor: Move viewport code under other subsystem to simplify merging (1) (moving in multiple commits to make diff/patch behave nicely) --- imgui.cpp | 258 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 132 insertions(+), 126 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 214cfc058292..21d4b2a0b8e7 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -69,12 +69,12 @@ CODE // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!) // [SECTION] TOOLTIPS // [SECTION] POPUPS -// [SECTION] VIEWPORTS, PLATFORM WINDOWS // [SECTION] KEYBOARD/GAMEPAD NAVIGATION // [SECTION] COLUMNS // [SECTION] DRAG AND DROP // [SECTION] LOGGING/CAPTURING // [SECTION] SETTINGS +// [SECTION] VIEWPORTS, PLATFORM WINDOWS // [SECTION] DOCKING // [SECTION] PLATFORM DEPENDENT HELPERS // [SECTION] METRICS/DEBUG WINDOW @@ -7786,135 +7786,11 @@ ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window) return window->Pos; } + //----------------------------------------------------------------------------- // [SECTION] VIEWPORTS, PLATFORM WINDOWS //----------------------------------------------------------------------------- -ImGuiViewport* ImGui::GetMainViewport() -{ - ImGuiContext& g = *GImGui; - return g.Viewports[0]; -} - -ImGuiViewport* ImGui::FindViewportByID(ImGuiID id) -{ - ImGuiContext& g = *GImGui; - for (int n = 0; n < g.Viewports.Size; n++) - if (g.Viewports[n]->ID == id) - return g.Viewports[n]; - return NULL; -} - -ImGuiViewport* ImGui::FindViewportByPlatformHandle(void* platform_handle) -{ - ImGuiContext& g = *GImGui; - for (int i = 0; i != g.Viewports.Size; i++) - if (g.Viewports[i]->PlatformHandle == platform_handle) - return g.Viewports[i]; - return NULL; -} - -void ImGui::SetCurrentViewport(ImGuiWindow* current_window, ImGuiViewportP* viewport) -{ - ImGuiContext& g = *GImGui; - (void)current_window; - - if (viewport) - viewport->LastFrameActive = g.FrameCount; - if (g.CurrentViewport == viewport) - return; - g.CurrentViewport = viewport; - //IMGUI_DEBUG_LOG("SetCurrentViewport changed '%s' 0x%08X\n", current_window ? current_window->Name : NULL, viewport ? viewport->ID : 0); - - // Notify platform layer of viewport changes - // FIXME-DPI: This is only currently used for experimenting with handling of multiple DPI - if (g.CurrentViewport && g.PlatformIO.Platform_OnChangedViewport) - g.PlatformIO.Platform_OnChangedViewport(g.CurrentViewport); -} - -static void SetWindowViewport(ImGuiWindow* window, ImGuiViewportP* viewport) -{ - window->Viewport = viewport; - window->ViewportId = viewport->ID; - window->ViewportOwned = (viewport->Window == window); -} - -static bool ImGui::GetWindowAlwaysWantOwnViewport(ImGuiWindow* window) -{ - // Tooltips and menus are not automatically forced into their own viewport when the NoMerge flag is set, however the multiplication of viewports makes them more likely to protude and create their own. - ImGuiContext& g = *GImGui; - if (g.IO.ConfigViewportsNoAutoMerge && (g.ConfigFlagsForFrame & ImGuiConfigFlags_ViewportsEnable)) - if (!window->DockIsActive) - if ((window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) == 0) - return true; - return false; -} - -static bool ImGui::UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* viewport) -{ - ImGuiContext& g = *GImGui; - if (!(viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) || window->Viewport == viewport || viewport->PlatformWindowMinimized) - return false; - if (!viewport->GetRect().Contains(window->Rect())) - return false; - if (GetWindowAlwaysWantOwnViewport(window)) - return false; - - for (int n = 0; n < g.Windows.Size; n++) - { - ImGuiWindow* window_behind = g.Windows[n]; - if (window_behind == window) - break; - if (window_behind->WasActive && window_behind->ViewportOwned && !(window_behind->Flags & ImGuiWindowFlags_ChildWindow)) - if (window_behind->Viewport->GetRect().Overlaps(window->Rect())) - return false; - } - - // Move to the existing viewport, Move child/hosted windows as well (FIXME-OPT: iterate child) - ImGuiViewportP* old_viewport = window->Viewport; - if (window->ViewportOwned) - for (int n = 0; n < g.Windows.Size; n++) - if (g.Windows[n]->Viewport == old_viewport) - SetWindowViewport(g.Windows[n], viewport); - SetWindowViewport(window, viewport); - BringWindowToDisplayFront(window); - - return true; -} - -// Scale all windows (position, size). Use when e.g. changing DPI. (This is a lossy operation!) -void ImGui::ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale) -{ - ImGuiContext& g = *GImGui; - if (viewport->Window) - { - ScaleWindow(viewport->Window, scale); - } - else - { - for (int i = 0; i != g.Windows.Size; i++) - if (g.Windows[i]->Viewport == viewport) - ScaleWindow(g.Windows[i], scale); - } -} - -// If the back-end doesn't set MouseLastHoveredViewport or doesn't honor ImGuiViewportFlags_NoInputs, we do a search ourselves. -// A) It won't take account of the possibility that non-imgui windows may be in-between our dragged window and our target window. -// B) It requires Platform_GetWindowFocus to be implemented by back-end. -static ImGuiViewportP* FindViewportHoveredFromPlatformWindowStack(const ImVec2 mouse_platform_pos) -{ - ImGuiContext& g = *GImGui; - ImGuiViewportP* best_candidate = NULL; - for (int n = 0; n < g.Viewports.Size; n++) - { - ImGuiViewportP* viewport = g.Viewports[n]; - if (!(viewport->Flags & ImGuiViewportFlags_NoInputs) && !viewport->PlatformWindowMinimized && viewport->GetRect().Contains(mouse_platform_pos)) - if (best_candidate == NULL || best_candidate->LastFrontMostStampCount < viewport->LastFrontMostStampCount) - best_candidate = viewport; - } - return best_candidate; -} - static void ImGui::UpdateViewportsNewFrame() { ImGuiContext& g = *GImGui; @@ -10611,6 +10487,136 @@ static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSetting } +//----------------------------------------------------------------------------- +// [SECTION] VIEWPORTS, PLATFORM WINDOWS +//----------------------------------------------------------------------------- + +ImGuiViewport* ImGui::GetMainViewport() +{ + ImGuiContext& g = *GImGui; + return g.Viewports[0]; +} + +ImGuiViewport* ImGui::FindViewportByID(ImGuiID id) +{ + ImGuiContext& g = *GImGui; + for (int n = 0; n < g.Viewports.Size; n++) + if (g.Viewports[n]->ID == id) + return g.Viewports[n]; + return NULL; +} + +ImGuiViewport* ImGui::FindViewportByPlatformHandle(void* platform_handle) +{ + ImGuiContext& g = *GImGui; + for (int i = 0; i != g.Viewports.Size; i++) + if (g.Viewports[i]->PlatformHandle == platform_handle) + return g.Viewports[i]; + return NULL; +} + +void ImGui::SetCurrentViewport(ImGuiWindow* current_window, ImGuiViewportP* viewport) +{ + ImGuiContext& g = *GImGui; + (void)current_window; + + if (viewport) + viewport->LastFrameActive = g.FrameCount; + if (g.CurrentViewport == viewport) + return; + g.CurrentViewport = viewport; + //IMGUI_DEBUG_LOG("SetCurrentViewport changed '%s' 0x%08X\n", current_window ? current_window->Name : NULL, viewport ? viewport->ID : 0); + + // Notify platform layer of viewport changes + // FIXME-DPI: This is only currently used for experimenting with handling of multiple DPI + if (g.CurrentViewport && g.PlatformIO.Platform_OnChangedViewport) + g.PlatformIO.Platform_OnChangedViewport(g.CurrentViewport); +} + +static void SetWindowViewport(ImGuiWindow* window, ImGuiViewportP* viewport) +{ + window->Viewport = viewport; + window->ViewportId = viewport->ID; + window->ViewportOwned = (viewport->Window == window); +} + +static bool ImGui::GetWindowAlwaysWantOwnViewport(ImGuiWindow* window) +{ + // Tooltips and menus are not automatically forced into their own viewport when the NoMerge flag is set, however the multiplication of viewports makes them more likely to protude and create their own. + ImGuiContext& g = *GImGui; + if (g.IO.ConfigViewportsNoAutoMerge && (g.ConfigFlagsForFrame & ImGuiConfigFlags_ViewportsEnable)) + if (!window->DockIsActive) + if ((window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) == 0) + return true; + return false; +} + +static bool ImGui::UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* viewport) +{ + ImGuiContext& g = *GImGui; + if (!(viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) || window->Viewport == viewport || viewport->PlatformWindowMinimized) + return false; + if (!viewport->GetRect().Contains(window->Rect())) + return false; + if (GetWindowAlwaysWantOwnViewport(window)) + return false; + + for (int n = 0; n < g.Windows.Size; n++) + { + ImGuiWindow* window_behind = g.Windows[n]; + if (window_behind == window) + break; + if (window_behind->WasActive && window_behind->ViewportOwned && !(window_behind->Flags & ImGuiWindowFlags_ChildWindow)) + if (window_behind->Viewport->GetRect().Overlaps(window->Rect())) + return false; + } + + // Move to the existing viewport, Move child/hosted windows as well (FIXME-OPT: iterate child) + ImGuiViewportP* old_viewport = window->Viewport; + if (window->ViewportOwned) + for (int n = 0; n < g.Windows.Size; n++) + if (g.Windows[n]->Viewport == old_viewport) + SetWindowViewport(g.Windows[n], viewport); + SetWindowViewport(window, viewport); + BringWindowToDisplayFront(window); + + return true; +} + +// Scale all windows (position, size). Use when e.g. changing DPI. (This is a lossy operation!) +void ImGui::ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale) +{ + ImGuiContext& g = *GImGui; + if (viewport->Window) + { + ScaleWindow(viewport->Window, scale); + } + else + { + for (int i = 0; i != g.Windows.Size; i++) + if (g.Windows[i]->Viewport == viewport) + ScaleWindow(g.Windows[i], scale); + } +} + +// If the back-end doesn't set MouseLastHoveredViewport or doesn't honor ImGuiViewportFlags_NoInputs, we do a search ourselves. +// A) It won't take account of the possibility that non-imgui windows may be in-between our dragged window and our target window. +// B) It requires Platform_GetWindowFocus to be implemented by back-end. +static ImGuiViewportP* FindViewportHoveredFromPlatformWindowStack(const ImVec2 mouse_platform_pos) +{ + ImGuiContext& g = *GImGui; + ImGuiViewportP* best_candidate = NULL; + for (int n = 0; n < g.Viewports.Size; n++) + { + ImGuiViewportP* viewport = g.Viewports[n]; + if (!(viewport->Flags & ImGuiViewportFlags_NoInputs) && !viewport->PlatformWindowMinimized && viewport->GetRect().Contains(mouse_platform_pos)) + if (best_candidate == NULL || best_candidate->LastFrontMostStampCount < viewport->LastFrontMostStampCount) + best_candidate = viewport; + } + return best_candidate; +} + + //----------------------------------------------------------------------------- // [SECTION] DOCKING //----------------------------------------------------------------------------- From bdf60dac6a723d2bb937929a7174145143929aee Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 7 Mar 2019 18:35:14 +0100 Subject: [PATCH 451/828] Refactor: Move viewport code under other subsystem to simplify merging (2) (moving in multiple commits to make diff/patch behave nicely) --- imgui.cpp | 485 +++++++++++++++++++++++++++--------------------------- 1 file changed, 241 insertions(+), 244 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 21d4b2a0b8e7..86c93e8f2039 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7791,246 +7791,6 @@ ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window) // [SECTION] VIEWPORTS, PLATFORM WINDOWS //----------------------------------------------------------------------------- -static void ImGui::UpdateViewportsNewFrame() -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(g.PlatformIO.Viewports.Size <= g.Viewports.Size); - - // Update Minimized status (we need it first in order to decide if we'll apply Pos/Size of the main viewport) - for (int n = 0; n < g.Viewports.Size; n++) - { - ImGuiViewportP* viewport = g.Viewports[n]; - const bool platform_funcs_available = (n == 0 || viewport->PlatformWindowCreated); - if ((g.ConfigFlagsForFrame & ImGuiConfigFlags_ViewportsEnable)) - if (g.PlatformIO.Platform_GetWindowMinimized && platform_funcs_available) - viewport->PlatformWindowMinimized = g.PlatformIO.Platform_GetWindowMinimized(viewport); - } - - // Create/update main viewport with current platform position and size - ImGuiViewportP* main_viewport = g.Viewports[0]; - IM_ASSERT(main_viewport->ID == IMGUI_VIEWPORT_DEFAULT_ID); - IM_ASSERT(main_viewport->Window == NULL); - ImVec2 main_viewport_platform_pos = ImVec2(0.0f, 0.0f); - ImVec2 main_viewport_platform_size = g.IO.DisplaySize; - if (g.ConfigFlagsForFrame & ImGuiConfigFlags_ViewportsEnable) - main_viewport_platform_pos = main_viewport->PlatformWindowMinimized ? main_viewport->Pos : g.PlatformIO.Platform_GetWindowPos(main_viewport); - AddUpdateViewport(NULL, IMGUI_VIEWPORT_DEFAULT_ID, main_viewport_platform_pos, main_viewport_platform_size, ImGuiViewportFlags_CanHostOtherWindows); - - g.CurrentViewport = NULL; - g.MouseViewport = NULL; - for (int n = 0; n < g.Viewports.Size; n++) - { - // Erase unused viewports - ImGuiViewportP* viewport = g.Viewports[n]; - viewport->Idx = n; - - if (n > 0 && viewport->LastFrameActive < g.FrameCount - 2) - { - // Clear references to this viewport in windows (window->ViewportId becomes the master data) - for (int window_n = 0; window_n < g.Windows.Size; window_n++) - if (g.Windows[window_n]->Viewport == viewport) - { - g.Windows[window_n]->Viewport = NULL; - g.Windows[window_n]->ViewportOwned = false; - } - if (viewport == g.MouseLastHoveredViewport) - g.MouseLastHoveredViewport = NULL; - g.Viewports.erase(g.Viewports.Data + n); - - // Destroy - //IMGUI_DEBUG_LOG("Delete Viewport %08X (%s)\n", viewport->ID, viewport->Window ? viewport->Window->Name : "n/a"); - DestroyPlatformWindow(viewport); // In most circumstances the platform window will already be destroyed here. - IM_ASSERT(g.PlatformIO.Viewports.contains(viewport) == false); - IM_DELETE(viewport); - n--; - continue; - } - - const bool platform_funcs_available = (n == 0 || viewport->PlatformWindowCreated); - if ((g.ConfigFlagsForFrame & ImGuiConfigFlags_ViewportsEnable)) - { - // Update Position and Size (from Platform Window to ImGui) if requested. - // We do it early in the frame instead of waiting for UpdatePlatformWindows() to avoid a frame of lag when moving/resizing using OS facilities. - if (!viewport->PlatformWindowMinimized && platform_funcs_available) - { - if (viewport->PlatformRequestMove) - viewport->Pos = viewport->LastPlatformPos = g.PlatformIO.Platform_GetWindowPos(viewport); - if (viewport->PlatformRequestResize) - viewport->Size = viewport->LastPlatformSize = g.PlatformIO.Platform_GetWindowSize(viewport); - } - - // Update monitor (we'll use this info to clamp windows and save windows lost in a removed monitor) - viewport->PlatformMonitor = (short)FindPlatformMonitorForRect(viewport->GetRect()); - } - - // Reset alpha every frame. Users of transparency (docking) needs to request a lower alpha back. - viewport->Alpha = 1.0f; - - // Translate imgui windows when a Host Viewport has been moved - // (This additionally keeps windows at the same place when ImGuiConfigFlags_ViewportsEnable is toggled!) - ImVec2 viewport_delta = viewport->Pos - viewport->LastPos; - if ((viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) && (viewport_delta.x != 0.0f || viewport_delta.y != 0.0f)) - for (int window_n = 0; window_n < g.Windows.Size; window_n++) - if (g.Windows[window_n]->Viewport == viewport || (g.ConfigFlagsForFrame & ImGuiConfigFlags_ViewportsEnable) == 0) - TranslateWindow(g.Windows[window_n], viewport_delta); - - // Update DPI scale - float new_dpi_scale; - if (g.PlatformIO.Platform_GetWindowDpiScale && platform_funcs_available) - new_dpi_scale = g.PlatformIO.Platform_GetWindowDpiScale(viewport); - else if (viewport->PlatformMonitor != -1) - new_dpi_scale = g.PlatformIO.Monitors[viewport->PlatformMonitor].DpiScale; - else - new_dpi_scale = (viewport->DpiScale != 0.0f) ? viewport->DpiScale : 1.0f; - if (viewport->DpiScale != 0.0f && new_dpi_scale != viewport->DpiScale) - { - float scale_factor = new_dpi_scale / viewport->DpiScale; - if (g.IO.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleViewports) - ScaleWindowsInViewport(viewport, scale_factor); - //if (viewport == GetMainViewport()) - // g.PlatformInterface.SetWindowSize(viewport, viewport->Size * scale_factor); - - // Scale our window moving pivot so that the window will rescale roughly around the mouse position. - // FIXME-VIEWPORT: This currently creates a resizing feedback loop when a window is straddling a DPI transition border. - // (Minor: since our sizes do not perfectly linearly scale, deferring the click offset scale until we know the actual window scale ratio may get us slightly more precise mouse positioning.) - //if (g.MovingWindow != NULL && g.MovingWindow->Viewport == viewport) - // g.ActiveIdClickOffset = ImFloor(g.ActiveIdClickOffset * scale_factor); - } - viewport->DpiScale = new_dpi_scale; - } - - if (!(g.ConfigFlagsForFrame & ImGuiConfigFlags_ViewportsEnable)) - { - g.MouseViewport = main_viewport; - return; - } - - // Mouse handling: decide on the actual mouse viewport for this frame between the active/focused viewport and the hovered viewport. - // Note that 'viewport_hovered' should skip over any viewport that has the ImGuiViewportFlags_NoInputs flags set. - ImGuiViewportP* viewport_hovered = NULL; - if (g.IO.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport) - { - viewport_hovered = g.IO.MouseHoveredViewport ? (ImGuiViewportP*)FindViewportByID(g.IO.MouseHoveredViewport) : NULL; - if (viewport_hovered && (viewport_hovered->Flags & ImGuiViewportFlags_NoInputs)) - { - // Back-end failed at honoring its contract if it returned a viewport with the _NoInputs flag. - IM_ASSERT(0); - viewport_hovered = FindViewportHoveredFromPlatformWindowStack(g.IO.MousePos); - } - } - else - { - // If the back-end doesn't know how to honor ImGuiViewportFlags_NoInputs, we do a search ourselves. Note that this search: - // A) won't take account of the possibility that non-imgui windows may be in-between our dragged window and our target window. - // B) uses LastFrameAsRefViewport as a flawed replacement for the last time a window was focused (we could/should fix that by introducing Focus functions in PlatformIO) - viewport_hovered = FindViewportHoveredFromPlatformWindowStack(g.IO.MousePos); - } - if (viewport_hovered != NULL) - g.MouseLastHoveredViewport = viewport_hovered; - else if (g.MouseLastHoveredViewport == NULL) - g.MouseLastHoveredViewport = g.Viewports[0]; - - // Update mouse reference viewport - // (when moving a window we aim at its viewport, but this will be overwritten below if we go in drag and drop mode) - if (g.MovingWindow) - g.MouseViewport = g.MovingWindow->Viewport; - else - g.MouseViewport = g.MouseLastHoveredViewport; - - // When dragging something, always refer to the last hovered viewport. - // - when releasing a moving window we will revert to aiming behind (at viewport_hovered) - // - when we are between viewports, our dragged preview will tend to show in the last viewport _even_ if we don't have tooltips in their viewports (when lacking monitor info) - // - consider the case of holding on a menu item to browse child menus: even thou a mouse button is held, there's no active id because menu items only react on mouse release. - const bool is_mouse_dragging_with_an_expected_destination = g.DragDropActive; - if (is_mouse_dragging_with_an_expected_destination && viewport_hovered == NULL) - viewport_hovered = g.MouseLastHoveredViewport; - if (is_mouse_dragging_with_an_expected_destination || g.ActiveId == 0 || !IsAnyMouseDown()) - if (viewport_hovered != NULL && viewport_hovered != g.MouseViewport && !(viewport_hovered->Flags & ImGuiViewportFlags_NoInputs)) - g.MouseViewport = viewport_hovered; - - IM_ASSERT(g.MouseViewport != NULL); -} - -// Update user-facing viewport list (g.Viewports -> g.PlatformIO.Viewports after filtering out some) -static void ImGui::UpdateViewportsEndFrame() -{ - ImGuiContext& g = *GImGui; - g.PlatformIO.MainViewport = g.Viewports[0]; - g.PlatformIO.Viewports.resize(0); - for (int i = 0; i < g.Viewports.Size; i++) - { - ImGuiViewportP* viewport = g.Viewports[i]; - viewport->LastPos = viewport->Pos; - if (viewport->LastFrameActive < g.FrameCount || viewport->Size.x <= 0.0f || viewport->Size.y <= 0.0f) - if (i > 0) // Always include main viewport in the list - continue; - if (viewport->Window && !IsWindowActiveAndVisible(viewport->Window)) - continue; - if (i > 0) - IM_ASSERT(viewport->Window != NULL); - g.PlatformIO.Viewports.push_back(viewport); - } - g.Viewports[0]->ClearRequestFlags(); // Clear main viewport flags because UpdatePlatformWindows() won't do it and may not even be called -} - -// FIXME: We should ideally refactor the system to call this every frame (we currently don't) -ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const ImVec2& pos, const ImVec2& size, ImGuiViewportFlags flags) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(id != 0); - - if (window != NULL) - { - if (g.MovingWindow && g.MovingWindow->RootWindow == window) - flags |= ImGuiViewportFlags_NoInputs | ImGuiViewportFlags_NoFocusOnAppearing; - if ((window->Flags & ImGuiWindowFlags_NoMouseInputs) && (window->Flags & ImGuiWindowFlags_NoNavInputs)) - flags |= ImGuiViewportFlags_NoInputs; - if (window->Flags & ImGuiWindowFlags_NoFocusOnAppearing) - flags |= ImGuiViewportFlags_NoFocusOnAppearing; - } - - ImGuiViewportP* viewport = (ImGuiViewportP*)FindViewportByID(id); - if (viewport) - { - if (!viewport->PlatformRequestMove) - viewport->Pos = pos; - if (!viewport->PlatformRequestResize) - viewport->Size = size; - } - else - { - // New viewport - viewport = IM_NEW(ImGuiViewportP)(); - viewport->ID = id; - viewport->Idx = g.Viewports.Size; - viewport->Pos = viewport->LastPos = pos; - viewport->Size = size; - viewport->PlatformMonitor = (short)FindPlatformMonitorForRect(viewport->GetRect()); - g.Viewports.push_back(viewport); - //IMGUI_DEBUG_LOG("Add Viewport %08X (%s)\n", id, window->Name); - - // We normally setup for all viewports in NewFrame() but here need to handle the mid-frame creation of a new viewport. - // We need to extend the fullscreen clip rect so the OverlayDrawList clip is correct for that the first frame - g.DrawListSharedData.ClipRectFullscreen.z = ImMax(g.DrawListSharedData.ClipRectFullscreen.z, viewport->Pos.x + viewport->Size.x); - g.DrawListSharedData.ClipRectFullscreen.w = ImMax(g.DrawListSharedData.ClipRectFullscreen.w, viewport->Pos.y + viewport->Size.y); - - // Store initial DpiScale before the OS platform window creation, based on expected monitor data. - // This is so we can select an appropriate font size on the first frame of our window lifetime - if (viewport->PlatformMonitor != -1) - viewport->DpiScale = g.PlatformIO.Monitors[viewport->PlatformMonitor].DpiScale; - } - - viewport->Window = window; - viewport->Flags = flags; - viewport->LastFrameActive = g.FrameCount; - IM_ASSERT(window == NULL || viewport->ID == window->ID); - - if (window != NULL) - window->ViewportOwned = true; - - return viewport; -} - // FIXME-VIEWPORT: This is all super messy and ought to be clarified or rewritten. static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) { @@ -8045,7 +7805,6 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) SetWindowViewport(window, main_viewport); return; } - window->ViewportOwned = false; // Appearing popups reset their viewport so they can inherit again @@ -8138,7 +7897,6 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoFocusOnAppearing); } } - // Regular (non-child, non-popup) windows by default are also allowed to protrude // Child windows are kept contained within their parent. else if (window->ViewportAllowPlatformMonitorExtend < 0 && (flags & ImGuiWindowFlags_ChildWindow) == 0) @@ -8146,12 +7904,11 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) // Update flags window->ViewportOwned = (window == window->Viewport->Window); + window->ViewportId = window->Viewport->ID; // If the OS window has a title bar, hide our imgui title bar //if (window->ViewportOwned && !(window->Viewport->Flags & ImGuiViewportFlags_NoDecoration)) // window->Flags |= ImGuiWindowFlags_NoTitleBar; - - window->ViewportId = window->Viewport->ID; } // Called by user at the end of the main loop, after EndFrame() @@ -10616,6 +10373,246 @@ static ImGuiViewportP* FindViewportHoveredFromPlatformWindowStack(const ImVec2 m return best_candidate; } +static void ImGui::UpdateViewportsNewFrame() +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(g.PlatformIO.Viewports.Size <= g.Viewports.Size); + + // Update Minimized status (we need it first in order to decide if we'll apply Pos/Size of the main viewport) + for (int n = 0; n < g.Viewports.Size; n++) + { + ImGuiViewportP* viewport = g.Viewports[n]; + const bool platform_funcs_available = (n == 0 || viewport->PlatformWindowCreated); + if ((g.ConfigFlagsForFrame & ImGuiConfigFlags_ViewportsEnable)) + if (g.PlatformIO.Platform_GetWindowMinimized && platform_funcs_available) + viewport->PlatformWindowMinimized = g.PlatformIO.Platform_GetWindowMinimized(viewport); + } + + // Create/update main viewport with current platform position and size + ImGuiViewportP* main_viewport = g.Viewports[0]; + IM_ASSERT(main_viewport->ID == IMGUI_VIEWPORT_DEFAULT_ID); + IM_ASSERT(main_viewport->Window == NULL); + ImVec2 main_viewport_platform_pos = ImVec2(0.0f, 0.0f); + ImVec2 main_viewport_platform_size = g.IO.DisplaySize; + if (g.ConfigFlagsForFrame & ImGuiConfigFlags_ViewportsEnable) + main_viewport_platform_pos = main_viewport->PlatformWindowMinimized ? main_viewport->Pos : g.PlatformIO.Platform_GetWindowPos(main_viewport); + AddUpdateViewport(NULL, IMGUI_VIEWPORT_DEFAULT_ID, main_viewport_platform_pos, main_viewport_platform_size, ImGuiViewportFlags_CanHostOtherWindows); + + g.CurrentViewport = NULL; + g.MouseViewport = NULL; + for (int n = 0; n < g.Viewports.Size; n++) + { + // Erase unused viewports + ImGuiViewportP* viewport = g.Viewports[n]; + viewport->Idx = n; + + if (n > 0 && viewport->LastFrameActive < g.FrameCount - 2) + { + // Clear references to this viewport in windows (window->ViewportId becomes the master data) + for (int window_n = 0; window_n < g.Windows.Size; window_n++) + if (g.Windows[window_n]->Viewport == viewport) + { + g.Windows[window_n]->Viewport = NULL; + g.Windows[window_n]->ViewportOwned = false; + } + if (viewport == g.MouseLastHoveredViewport) + g.MouseLastHoveredViewport = NULL; + g.Viewports.erase(g.Viewports.Data + n); + + // Destroy + //IMGUI_DEBUG_LOG("Delete Viewport %08X (%s)\n", viewport->ID, viewport->Window ? viewport->Window->Name : "n/a"); + DestroyPlatformWindow(viewport); // In most circumstances the platform window will already be destroyed here. + IM_ASSERT(g.PlatformIO.Viewports.contains(viewport) == false); + IM_DELETE(viewport); + n--; + continue; + } + + const bool platform_funcs_available = (n == 0 || viewport->PlatformWindowCreated); + if ((g.ConfigFlagsForFrame & ImGuiConfigFlags_ViewportsEnable)) + { + // Update Position and Size (from Platform Window to ImGui) if requested. + // We do it early in the frame instead of waiting for UpdatePlatformWindows() to avoid a frame of lag when moving/resizing using OS facilities. + if (!viewport->PlatformWindowMinimized && platform_funcs_available) + { + if (viewport->PlatformRequestMove) + viewport->Pos = viewport->LastPlatformPos = g.PlatformIO.Platform_GetWindowPos(viewport); + if (viewport->PlatformRequestResize) + viewport->Size = viewport->LastPlatformSize = g.PlatformIO.Platform_GetWindowSize(viewport); + } + + // Update monitor (we'll use this info to clamp windows and save windows lost in a removed monitor) + viewport->PlatformMonitor = (short)FindPlatformMonitorForRect(viewport->GetRect()); + } + + // Reset alpha every frame. Users of transparency (docking) needs to request a lower alpha back. + viewport->Alpha = 1.0f; + + // Translate imgui windows when a Host Viewport has been moved + // (This additionally keeps windows at the same place when ImGuiConfigFlags_ViewportsEnable is toggled!) + ImVec2 viewport_delta = viewport->Pos - viewport->LastPos; + if ((viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) && (viewport_delta.x != 0.0f || viewport_delta.y != 0.0f)) + for (int window_n = 0; window_n < g.Windows.Size; window_n++) + if (g.Windows[window_n]->Viewport == viewport || (g.ConfigFlagsForFrame & ImGuiConfigFlags_ViewportsEnable) == 0) + TranslateWindow(g.Windows[window_n], viewport_delta); + + // Update DPI scale + float new_dpi_scale; + if (g.PlatformIO.Platform_GetWindowDpiScale && platform_funcs_available) + new_dpi_scale = g.PlatformIO.Platform_GetWindowDpiScale(viewport); + else if (viewport->PlatformMonitor != -1) + new_dpi_scale = g.PlatformIO.Monitors[viewport->PlatformMonitor].DpiScale; + else + new_dpi_scale = (viewport->DpiScale != 0.0f) ? viewport->DpiScale : 1.0f; + if (viewport->DpiScale != 0.0f && new_dpi_scale != viewport->DpiScale) + { + float scale_factor = new_dpi_scale / viewport->DpiScale; + if (g.IO.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleViewports) + ScaleWindowsInViewport(viewport, scale_factor); + //if (viewport == GetMainViewport()) + // g.PlatformInterface.SetWindowSize(viewport, viewport->Size * scale_factor); + + // Scale our window moving pivot so that the window will rescale roughly around the mouse position. + // FIXME-VIEWPORT: This currently creates a resizing feedback loop when a window is straddling a DPI transition border. + // (Minor: since our sizes do not perfectly linearly scale, deferring the click offset scale until we know the actual window scale ratio may get us slightly more precise mouse positioning.) + //if (g.MovingWindow != NULL && g.MovingWindow->Viewport == viewport) + // g.ActiveIdClickOffset = ImFloor(g.ActiveIdClickOffset * scale_factor); + } + viewport->DpiScale = new_dpi_scale; + } + + if (!(g.ConfigFlagsForFrame & ImGuiConfigFlags_ViewportsEnable)) + { + g.MouseViewport = main_viewport; + return; + } + + // Mouse handling: decide on the actual mouse viewport for this frame between the active/focused viewport and the hovered viewport. + // Note that 'viewport_hovered' should skip over any viewport that has the ImGuiViewportFlags_NoInputs flags set. + ImGuiViewportP* viewport_hovered = NULL; + if (g.IO.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport) + { + viewport_hovered = g.IO.MouseHoveredViewport ? (ImGuiViewportP*)FindViewportByID(g.IO.MouseHoveredViewport) : NULL; + if (viewport_hovered && (viewport_hovered->Flags & ImGuiViewportFlags_NoInputs)) + { + // Back-end failed at honoring its contract if it returned a viewport with the _NoInputs flag. + IM_ASSERT(0); + viewport_hovered = FindViewportHoveredFromPlatformWindowStack(g.IO.MousePos); + } + } + else + { + // If the back-end doesn't know how to honor ImGuiViewportFlags_NoInputs, we do a search ourselves. Note that this search: + // A) won't take account of the possibility that non-imgui windows may be in-between our dragged window and our target window. + // B) uses LastFrameAsRefViewport as a flawed replacement for the last time a window was focused (we could/should fix that by introducing Focus functions in PlatformIO) + viewport_hovered = FindViewportHoveredFromPlatformWindowStack(g.IO.MousePos); + } + if (viewport_hovered != NULL) + g.MouseLastHoveredViewport = viewport_hovered; + else if (g.MouseLastHoveredViewport == NULL) + g.MouseLastHoveredViewport = g.Viewports[0]; + + // Update mouse reference viewport + // (when moving a window we aim at its viewport, but this will be overwritten below if we go in drag and drop mode) + if (g.MovingWindow) + g.MouseViewport = g.MovingWindow->Viewport; + else + g.MouseViewport = g.MouseLastHoveredViewport; + + // When dragging something, always refer to the last hovered viewport. + // - when releasing a moving window we will revert to aiming behind (at viewport_hovered) + // - when we are between viewports, our dragged preview will tend to show in the last viewport _even_ if we don't have tooltips in their viewports (when lacking monitor info) + // - consider the case of holding on a menu item to browse child menus: even thou a mouse button is held, there's no active id because menu items only react on mouse release. + const bool is_mouse_dragging_with_an_expected_destination = g.DragDropActive; + if (is_mouse_dragging_with_an_expected_destination && viewport_hovered == NULL) + viewport_hovered = g.MouseLastHoveredViewport; + if (is_mouse_dragging_with_an_expected_destination || g.ActiveId == 0 || !IsAnyMouseDown()) + if (viewport_hovered != NULL && viewport_hovered != g.MouseViewport && !(viewport_hovered->Flags & ImGuiViewportFlags_NoInputs)) + g.MouseViewport = viewport_hovered; + + IM_ASSERT(g.MouseViewport != NULL); +} + +// Update user-facing viewport list (g.Viewports -> g.PlatformIO.Viewports after filtering out some) +static void ImGui::UpdateViewportsEndFrame() +{ + ImGuiContext& g = *GImGui; + g.PlatformIO.MainViewport = g.Viewports[0]; + g.PlatformIO.Viewports.resize(0); + for (int i = 0; i < g.Viewports.Size; i++) + { + ImGuiViewportP* viewport = g.Viewports[i]; + viewport->LastPos = viewport->Pos; + if (viewport->LastFrameActive < g.FrameCount || viewport->Size.x <= 0.0f || viewport->Size.y <= 0.0f) + if (i > 0) // Always include main viewport in the list + continue; + if (viewport->Window && !IsWindowActiveAndVisible(viewport->Window)) + continue; + if (i > 0) + IM_ASSERT(viewport->Window != NULL); + g.PlatformIO.Viewports.push_back(viewport); + } + g.Viewports[0]->ClearRequestFlags(); // Clear main viewport flags because UpdatePlatformWindows() won't do it and may not even be called +} + +// FIXME: We should ideally refactor the system to call this every frame (we currently don't) +ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const ImVec2& pos, const ImVec2& size, ImGuiViewportFlags flags) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(id != 0); + + if (window != NULL) + { + if (g.MovingWindow && g.MovingWindow->RootWindow == window) + flags |= ImGuiViewportFlags_NoInputs | ImGuiViewportFlags_NoFocusOnAppearing; + if ((window->Flags & ImGuiWindowFlags_NoMouseInputs) && (window->Flags & ImGuiWindowFlags_NoNavInputs)) + flags |= ImGuiViewportFlags_NoInputs; + if (window->Flags & ImGuiWindowFlags_NoFocusOnAppearing) + flags |= ImGuiViewportFlags_NoFocusOnAppearing; + } + + ImGuiViewportP* viewport = (ImGuiViewportP*)FindViewportByID(id); + if (viewport) + { + if (!viewport->PlatformRequestMove) + viewport->Pos = pos; + if (!viewport->PlatformRequestResize) + viewport->Size = size; + } + else + { + // New viewport + viewport = IM_NEW(ImGuiViewportP)(); + viewport->ID = id; + viewport->Idx = g.Viewports.Size; + viewport->Pos = viewport->LastPos = pos; + viewport->Size = size; + viewport->PlatformMonitor = (short)FindPlatformMonitorForRect(viewport->GetRect()); + g.Viewports.push_back(viewport); + //IMGUI_DEBUG_LOG("Add Viewport %08X (%s)\n", id, window->Name); + + // We normally setup for all viewports in NewFrame() but here need to handle the mid-frame creation of a new viewport. + // We need to extend the fullscreen clip rect so the OverlayDrawList clip is correct for that the first frame + g.DrawListSharedData.ClipRectFullscreen.z = ImMax(g.DrawListSharedData.ClipRectFullscreen.z, viewport->Pos.x + viewport->Size.x); + g.DrawListSharedData.ClipRectFullscreen.w = ImMax(g.DrawListSharedData.ClipRectFullscreen.w, viewport->Pos.y + viewport->Size.y); + + // Store initial DpiScale before the OS platform window creation, based on expected monitor data. + // This is so we can select an appropriate font size on the first frame of our window lifetime + if (viewport->PlatformMonitor != -1) + viewport->DpiScale = g.PlatformIO.Monitors[viewport->PlatformMonitor].DpiScale; + } + + viewport->Window = window; + viewport->Flags = flags; + viewport->LastFrameActive = g.FrameCount; + IM_ASSERT(window == NULL || viewport->ID == window->ID); + + if (window != NULL) + window->ViewportOwned = true; + + return viewport; +} + //----------------------------------------------------------------------------- // [SECTION] DOCKING From 54a129a2e2f688e80455c46d8b8c6530b0ef4d28 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 7 Mar 2019 18:38:40 +0100 Subject: [PATCH 452/828] Refactor: Move viewport code under other subsystem to simplify merging (3) (moving in multiple commits to make diff/patch behave nicely) --- imgui.cpp | 480 +++++++++++++++++++++++++++--------------------------- 1 file changed, 240 insertions(+), 240 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 86c93e8f2039..ca41697186c7 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7791,246 +7791,6 @@ ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window) // [SECTION] VIEWPORTS, PLATFORM WINDOWS //----------------------------------------------------------------------------- -// FIXME-VIEWPORT: This is all super messy and ought to be clarified or rewritten. -static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) -{ - ImGuiContext& g = *GImGui; - ImGuiWindowFlags flags = window->Flags; - window->ViewportAllowPlatformMonitorExtend = -1; - - // Restore main viewport if multi-viewport is not supported by the back-end - ImGuiViewportP* main_viewport = g.Viewports[0]; - if (!(g.ConfigFlagsForFrame & ImGuiConfigFlags_ViewportsEnable)) - { - SetWindowViewport(window, main_viewport); - return; - } - window->ViewportOwned = false; - - // Appearing popups reset their viewport so they can inherit again - if ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && window->Appearing) - { - window->Viewport = NULL; - window->ViewportId = 0; - } - - if (!g.NextWindowData.ViewportCond) - { - // By default inherit from parent window - if (window->Viewport == NULL && window->ParentWindow) - window->Viewport = window->ParentWindow->Viewport; - - // Attempt to restore saved viewport id (= window that hasn't been activated yet), try to restore the viewport based on saved 'window->ViewportPos' restored from .ini file - if (window->Viewport == NULL && window->ViewportId != 0) - { - window->Viewport = (ImGuiViewportP*)FindViewportByID(window->ViewportId); - if (window->Viewport == NULL && window->ViewportPos.x != FLT_MAX && window->ViewportPos.y != FLT_MAX) - window->Viewport = AddUpdateViewport(window, window->ID, window->ViewportPos, window->Size, ImGuiViewportFlags_None); - } - } - - if (g.NextWindowData.ViewportCond) - { - // Code explicitly request a viewport - window->Viewport = (ImGuiViewportP*)FindViewportByID(g.NextWindowData.ViewportId); - window->ViewportId = g.NextWindowData.ViewportId; // Store ID even if Viewport isn't resolved yet. - } - else if ((flags & ImGuiWindowFlags_ChildWindow) || (flags & ImGuiWindowFlags_ChildMenu)) - { - // Always inherit viewport from parent window - window->Viewport = window->ParentWindow->Viewport; - } - else if (flags & ImGuiWindowFlags_Tooltip) - { - window->Viewport = g.MouseViewport; - } - else if (GetWindowAlwaysWantOwnViewport(window)) - { - window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_None); - } - else if (g.MovingWindow && g.MovingWindow->RootWindow == window && IsMousePosValid()) - { - if (window->Viewport != NULL && window->Viewport->Window == window) - window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_None); - } - else - { - // Merge into host viewport? - // We cannot test window->ViewportOwned as it set lower in the function. - bool try_to_merge_into_host_viewport = (window->Viewport && window == window->Viewport->Window && g.ActiveId == 0); - if (try_to_merge_into_host_viewport) - UpdateTryMergeWindowIntoHostViewport(window, g.Viewports[0]); - } - - // Fallback to default viewport - if (window->Viewport == NULL) - window->Viewport = main_viewport; - - // Mark window as allowed to protrude outside of its viewport and into the current monitor - if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) - { - // We need to take account of the possibility that mouse may become invalid. - // Popups/Tooltip always set ViewportAllowPlatformMonitorExtend so GetWindowAllowedExtentRect() will return full monitor bounds. - ImVec2 mouse_ref = (flags & ImGuiWindowFlags_Tooltip) ? g.IO.MousePos : g.BeginPopupStack.back().OpenMousePos; - bool use_mouse_ref = (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow); - bool mouse_valid = IsMousePosValid(&mouse_ref); - if ((window->Appearing || (flags & ImGuiWindowFlags_Tooltip)) && (!use_mouse_ref || mouse_valid)) - window->ViewportAllowPlatformMonitorExtend = FindPlatformMonitorForPos((use_mouse_ref && mouse_valid) ? mouse_ref : NavCalcPreferredRefPos()); - else - window->ViewportAllowPlatformMonitorExtend = window->Viewport->PlatformMonitor; - } - else if (window->Viewport && window != window->Viewport->Window && window->Viewport->Window && !(flags & ImGuiWindowFlags_ChildWindow)) - { - // When called from Begin() we don't have access to a proper version of the Hidden flag yet, so we replicate this code. - const bool will_be_visible = (window->DockIsActive && !window->DockTabIsVisible) ? false : true; - if ((window->Flags & ImGuiWindowFlags_DockNodeHost) && window->Viewport->LastFrameActive < g.FrameCount && will_be_visible) - { - // Steal/transfer ownership - //IMGUI_DEBUG_LOG("[%05d] Window '%s' steal Viewport %08X from Window '%s'\n", g.FrameCount, window->Name, window->Viewport->ID, window->Viewport->Window->Name); - window->Viewport->Window = window; - window->Viewport->ID = window->ID; - window->Viewport->LastNameHash = 0; - } - else if (!UpdateTryMergeWindowIntoHostViewport(window, g.Viewports[0])) // Merge? - { - // New viewport - window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoFocusOnAppearing); - } - } - // Regular (non-child, non-popup) windows by default are also allowed to protrude - // Child windows are kept contained within their parent. - else if (window->ViewportAllowPlatformMonitorExtend < 0 && (flags & ImGuiWindowFlags_ChildWindow) == 0) - window->ViewportAllowPlatformMonitorExtend = window->Viewport->PlatformMonitor; - - // Update flags - window->ViewportOwned = (window == window->Viewport->Window); - window->ViewportId = window->Viewport->ID; - - // If the OS window has a title bar, hide our imgui title bar - //if (window->ViewportOwned && !(window->Viewport->Flags & ImGuiViewportFlags_NoDecoration)) - // window->Flags |= ImGuiWindowFlags_NoTitleBar; -} - -// Called by user at the end of the main loop, after EndFrame() -// This will handle the creation/update of all OS windows via function defined in the ImGuiPlatformIO api. -void ImGui::UpdatePlatformWindows() -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(g.FrameCountEnded == g.FrameCount && "Forgot to call Render() or EndFrame() before UpdatePlatformWindows()?"); - IM_ASSERT(g.FrameCountPlatformEnded < g.FrameCount); - g.FrameCountPlatformEnded = g.FrameCount; - if (!(g.ConfigFlagsForFrame & ImGuiConfigFlags_ViewportsEnable)) - return; - - // Create/resize/destroy platform windows to match each active viewport. - // Skip the main viewport (index 0), which is always fully handled by the application! - for (int i = 1; i < g.Viewports.Size; i++) - { - ImGuiViewportP* viewport = g.Viewports[i]; - - // Destroy platform window if the viewport hasn't been submitted or if it is hosting a hidden window (the implicit/fallback Debug window will be registered its viewport then be disabled) - bool destroy_platform_window = false; - destroy_platform_window |= (viewport->LastFrameActive < g.FrameCount - 1); - destroy_platform_window |= (viewport->Window && !IsWindowActiveAndVisible(viewport->Window)); - if (destroy_platform_window) - { - DestroyPlatformWindow(viewport); - continue; - } - - // New windows that appears directly in a new viewport won't always have a size on their first frame - if (viewport->LastFrameActive < g.FrameCount || viewport->Size.x <= 0 || viewport->Size.y <= 0) - continue; - - // Create window - bool is_new_platform_window = (viewport->PlatformWindowCreated == false); - if (is_new_platform_window) - { - //IMGUI_DEBUG_LOG("Create Platform Window %08X (%s)\n", viewport->ID, viewport->Window ? viewport->Window->Name : "n/a"); - g.PlatformIO.Platform_CreateWindow(viewport); - if (g.PlatformIO.Renderer_CreateWindow != NULL) - g.PlatformIO.Renderer_CreateWindow(viewport); - viewport->LastNameHash = 0; - viewport->LastPlatformPos = viewport->LastPlatformSize = ImVec2(FLT_MAX, FLT_MAX); // By clearing those we'll enforce a call to Platform_SetWindowPos/Size below, before Platform_ShowWindow (FIXME: Is that necessary?) - viewport->LastRendererSize = viewport->Size; // We don't need to call Renderer_SetWindowSize() as it is expected Renderer_CreateWindow() already did it. - viewport->PlatformWindowCreated = true; - } - - // Apply Position and Size (from ImGui to Platform/Renderer back-ends) - if ((viewport->LastPlatformPos.x != viewport->Pos.x || viewport->LastPlatformPos.y != viewport->Pos.y) && !viewport->PlatformRequestMove) - g.PlatformIO.Platform_SetWindowPos(viewport, viewport->Pos); - if ((viewport->LastPlatformSize.x != viewport->Size.x || viewport->LastPlatformSize.y != viewport->Size.y) && !viewport->PlatformRequestResize) - g.PlatformIO.Platform_SetWindowSize(viewport, viewport->Size); - if ((viewport->LastRendererSize.x != viewport->Size.x || viewport->LastRendererSize.y != viewport->Size.y) && g.PlatformIO.Renderer_SetWindowSize) - g.PlatformIO.Renderer_SetWindowSize(viewport, viewport->Size); - viewport->LastPlatformPos = viewport->Pos; - viewport->LastPlatformSize = viewport->LastRendererSize = viewport->Size; - - // Update title bar (if it changed) - if (ImGuiWindow* window_for_title = GetWindowForTitleDisplay(viewport->Window)) - { - const char* title_begin = window_for_title->Name; - char* title_end = (char*)(intptr_t)FindRenderedTextEnd(title_begin); - const ImGuiID title_hash = ImHashStr(title_begin, title_end - title_begin); - if (viewport->LastNameHash != title_hash) - { - char title_end_backup_c = *title_end; - *title_end = 0; // Cut existing buffer short instead of doing an alloc/free, no small gain. - g.PlatformIO.Platform_SetWindowTitle(viewport, title_begin); - *title_end = title_end_backup_c; - viewport->LastNameHash = title_hash; - } - } - - // Update alpha (if it changed) - if (viewport->LastAlpha != viewport->Alpha && g.PlatformIO.Platform_SetWindowAlpha) - g.PlatformIO.Platform_SetWindowAlpha(viewport, viewport->Alpha); - viewport->LastAlpha = viewport->Alpha; - - // Optional, general purpose call to allow the back-end to perform general book-keeping even if things haven't changed. - if (g.PlatformIO.Platform_UpdateWindow) - g.PlatformIO.Platform_UpdateWindow(viewport); - - if (is_new_platform_window) - { - // On startup ensure new platform window don't steal focus (give it a few frames, as nested contents may lead to viewport being created a few frames late) - if (g.FrameCount < 3) - viewport->Flags |= ImGuiViewportFlags_NoFocusOnAppearing; - - // Show window - g.PlatformIO.Platform_ShowWindow(viewport); - - // Even without focus, we assume the window becomes front-most. - // This is useful for our platform z-order heuristic when io.MouseHoveredViewport is not available. - if (viewport->LastFrontMostStampCount != g.WindowsFrontMostStampCount) - viewport->LastFrontMostStampCount = ++g.WindowsFrontMostStampCount; - } - - // Clear request flags - viewport->ClearRequestFlags(); - } - - // Update our implicit z-order knowledge of platform windows, which is used when the back-end cannot provide io.MouseHoveredViewport. - // When setting Platform_GetWindowFocus, it is expected that the platform back-end can handle calls without crashing if it doesn't have data stored. - if (g.PlatformIO.Platform_GetWindowFocus != NULL) - { - ImGuiViewportP* focused_viewport = NULL; - for (int n = 0; n < g.Viewports.Size && focused_viewport == NULL; n++) - { - ImGuiViewportP* viewport = g.Viewports[n]; - if (n == 0 || viewport->PlatformWindowCreated) - if (g.PlatformIO.Platform_GetWindowFocus(viewport)) - focused_viewport = viewport; - } - if (focused_viewport && g.PlatformLastFocusedViewport != focused_viewport->ID) - { - if (focused_viewport->LastFrontMostStampCount != g.WindowsFrontMostStampCount) - focused_viewport->LastFrontMostStampCount = ++g.WindowsFrontMostStampCount; - g.PlatformLastFocusedViewport = focused_viewport->ID; - } - } -} - // This is a default/basic function for performing the rendering/swap of multiple Platform Windows. // Custom renderers may prefer to not call this function at all, and instead iterate the publicly exposed platform data and handle rendering/sync themselves. // The Render/Swap functions stored in ImGuiPlatformIO are merely here to allow for this helper to exist, but you can do it yourself: @@ -10613,6 +10373,246 @@ ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const return viewport; } +// FIXME-VIEWPORT: This is all super messy and ought to be clarified or rewritten. +static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; + ImGuiWindowFlags flags = window->Flags; + window->ViewportAllowPlatformMonitorExtend = -1; + + // Restore main viewport if multi-viewport is not supported by the back-end + ImGuiViewportP* main_viewport = g.Viewports[0]; + if (!(g.ConfigFlagsForFrame & ImGuiConfigFlags_ViewportsEnable)) + { + SetWindowViewport(window, main_viewport); + return; + } + window->ViewportOwned = false; + + // Appearing popups reset their viewport so they can inherit again + if ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && window->Appearing) + { + window->Viewport = NULL; + window->ViewportId = 0; + } + + if (!g.NextWindowData.ViewportCond) + { + // By default inherit from parent window + if (window->Viewport == NULL && window->ParentWindow) + window->Viewport = window->ParentWindow->Viewport; + + // Attempt to restore saved viewport id (= window that hasn't been activated yet), try to restore the viewport based on saved 'window->ViewportPos' restored from .ini file + if (window->Viewport == NULL && window->ViewportId != 0) + { + window->Viewport = (ImGuiViewportP*)FindViewportByID(window->ViewportId); + if (window->Viewport == NULL && window->ViewportPos.x != FLT_MAX && window->ViewportPos.y != FLT_MAX) + window->Viewport = AddUpdateViewport(window, window->ID, window->ViewportPos, window->Size, ImGuiViewportFlags_None); + } + } + + if (g.NextWindowData.ViewportCond) + { + // Code explicitly request a viewport + window->Viewport = (ImGuiViewportP*)FindViewportByID(g.NextWindowData.ViewportId); + window->ViewportId = g.NextWindowData.ViewportId; // Store ID even if Viewport isn't resolved yet. + } + else if ((flags & ImGuiWindowFlags_ChildWindow) || (flags & ImGuiWindowFlags_ChildMenu)) + { + // Always inherit viewport from parent window + window->Viewport = window->ParentWindow->Viewport; + } + else if (flags & ImGuiWindowFlags_Tooltip) + { + window->Viewport = g.MouseViewport; + } + else if (GetWindowAlwaysWantOwnViewport(window)) + { + window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_None); + } + else if (g.MovingWindow && g.MovingWindow->RootWindow == window && IsMousePosValid()) + { + if (window->Viewport != NULL && window->Viewport->Window == window) + window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_None); + } + else + { + // Merge into host viewport? + // We cannot test window->ViewportOwned as it set lower in the function. + bool try_to_merge_into_host_viewport = (window->Viewport && window == window->Viewport->Window && g.ActiveId == 0); + if (try_to_merge_into_host_viewport) + UpdateTryMergeWindowIntoHostViewport(window, g.Viewports[0]); + } + + // Fallback to default viewport + if (window->Viewport == NULL) + window->Viewport = main_viewport; + + // Mark window as allowed to protrude outside of its viewport and into the current monitor + if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) + { + // We need to take account of the possibility that mouse may become invalid. + // Popups/Tooltip always set ViewportAllowPlatformMonitorExtend so GetWindowAllowedExtentRect() will return full monitor bounds. + ImVec2 mouse_ref = (flags & ImGuiWindowFlags_Tooltip) ? g.IO.MousePos : g.BeginPopupStack.back().OpenMousePos; + bool use_mouse_ref = (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow); + bool mouse_valid = IsMousePosValid(&mouse_ref); + if ((window->Appearing || (flags & ImGuiWindowFlags_Tooltip)) && (!use_mouse_ref || mouse_valid)) + window->ViewportAllowPlatformMonitorExtend = FindPlatformMonitorForPos((use_mouse_ref && mouse_valid) ? mouse_ref : NavCalcPreferredRefPos()); + else + window->ViewportAllowPlatformMonitorExtend = window->Viewport->PlatformMonitor; + } + else if (window->Viewport && window != window->Viewport->Window && window->Viewport->Window && !(flags & ImGuiWindowFlags_ChildWindow)) + { + // When called from Begin() we don't have access to a proper version of the Hidden flag yet, so we replicate this code. + const bool will_be_visible = (window->DockIsActive && !window->DockTabIsVisible) ? false : true; + if ((window->Flags & ImGuiWindowFlags_DockNodeHost) && window->Viewport->LastFrameActive < g.FrameCount && will_be_visible) + { + // Steal/transfer ownership + //IMGUI_DEBUG_LOG("[%05d] Window '%s' steal Viewport %08X from Window '%s'\n", g.FrameCount, window->Name, window->Viewport->ID, window->Viewport->Window->Name); + window->Viewport->Window = window; + window->Viewport->ID = window->ID; + window->Viewport->LastNameHash = 0; + } + else if (!UpdateTryMergeWindowIntoHostViewport(window, g.Viewports[0])) // Merge? + { + // New viewport + window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoFocusOnAppearing); + } + } + // Regular (non-child, non-popup) windows by default are also allowed to protrude + // Child windows are kept contained within their parent. + else if (window->ViewportAllowPlatformMonitorExtend < 0 && (flags & ImGuiWindowFlags_ChildWindow) == 0) + window->ViewportAllowPlatformMonitorExtend = window->Viewport->PlatformMonitor; + + // Update flags + window->ViewportOwned = (window == window->Viewport->Window); + window->ViewportId = window->Viewport->ID; + + // If the OS window has a title bar, hide our imgui title bar + //if (window->ViewportOwned && !(window->Viewport->Flags & ImGuiViewportFlags_NoDecoration)) + // window->Flags |= ImGuiWindowFlags_NoTitleBar; +} + +// Called by user at the end of the main loop, after EndFrame() +// This will handle the creation/update of all OS windows via function defined in the ImGuiPlatformIO api. +void ImGui::UpdatePlatformWindows() +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(g.FrameCountEnded == g.FrameCount && "Forgot to call Render() or EndFrame() before UpdatePlatformWindows()?"); + IM_ASSERT(g.FrameCountPlatformEnded < g.FrameCount); + g.FrameCountPlatformEnded = g.FrameCount; + if (!(g.ConfigFlagsForFrame & ImGuiConfigFlags_ViewportsEnable)) + return; + + // Create/resize/destroy platform windows to match each active viewport. + // Skip the main viewport (index 0), which is always fully handled by the application! + for (int i = 1; i < g.Viewports.Size; i++) + { + ImGuiViewportP* viewport = g.Viewports[i]; + + // Destroy platform window if the viewport hasn't been submitted or if it is hosting a hidden window (the implicit/fallback Debug window will be registered its viewport then be disabled) + bool destroy_platform_window = false; + destroy_platform_window |= (viewport->LastFrameActive < g.FrameCount - 1); + destroy_platform_window |= (viewport->Window && !IsWindowActiveAndVisible(viewport->Window)); + if (destroy_platform_window) + { + DestroyPlatformWindow(viewport); + continue; + } + + // New windows that appears directly in a new viewport won't always have a size on their first frame + if (viewport->LastFrameActive < g.FrameCount || viewport->Size.x <= 0 || viewport->Size.y <= 0) + continue; + + // Create window + bool is_new_platform_window = (viewport->PlatformWindowCreated == false); + if (is_new_platform_window) + { + //IMGUI_DEBUG_LOG("Create Platform Window %08X (%s)\n", viewport->ID, viewport->Window ? viewport->Window->Name : "n/a"); + g.PlatformIO.Platform_CreateWindow(viewport); + if (g.PlatformIO.Renderer_CreateWindow != NULL) + g.PlatformIO.Renderer_CreateWindow(viewport); + viewport->LastNameHash = 0; + viewport->LastPlatformPos = viewport->LastPlatformSize = ImVec2(FLT_MAX, FLT_MAX); // By clearing those we'll enforce a call to Platform_SetWindowPos/Size below, before Platform_ShowWindow (FIXME: Is that necessary?) + viewport->LastRendererSize = viewport->Size; // We don't need to call Renderer_SetWindowSize() as it is expected Renderer_CreateWindow() already did it. + viewport->PlatformWindowCreated = true; + } + + // Apply Position and Size (from ImGui to Platform/Renderer back-ends) + if ((viewport->LastPlatformPos.x != viewport->Pos.x || viewport->LastPlatformPos.y != viewport->Pos.y) && !viewport->PlatformRequestMove) + g.PlatformIO.Platform_SetWindowPos(viewport, viewport->Pos); + if ((viewport->LastPlatformSize.x != viewport->Size.x || viewport->LastPlatformSize.y != viewport->Size.y) && !viewport->PlatformRequestResize) + g.PlatformIO.Platform_SetWindowSize(viewport, viewport->Size); + if ((viewport->LastRendererSize.x != viewport->Size.x || viewport->LastRendererSize.y != viewport->Size.y) && g.PlatformIO.Renderer_SetWindowSize) + g.PlatformIO.Renderer_SetWindowSize(viewport, viewport->Size); + viewport->LastPlatformPos = viewport->Pos; + viewport->LastPlatformSize = viewport->LastRendererSize = viewport->Size; + + // Update title bar (if it changed) + if (ImGuiWindow* window_for_title = GetWindowForTitleDisplay(viewport->Window)) + { + const char* title_begin = window_for_title->Name; + char* title_end = (char*)(intptr_t)FindRenderedTextEnd(title_begin); + const ImGuiID title_hash = ImHashStr(title_begin, title_end - title_begin); + if (viewport->LastNameHash != title_hash) + { + char title_end_backup_c = *title_end; + *title_end = 0; // Cut existing buffer short instead of doing an alloc/free, no small gain. + g.PlatformIO.Platform_SetWindowTitle(viewport, title_begin); + *title_end = title_end_backup_c; + viewport->LastNameHash = title_hash; + } + } + + // Update alpha (if it changed) + if (viewport->LastAlpha != viewport->Alpha && g.PlatformIO.Platform_SetWindowAlpha) + g.PlatformIO.Platform_SetWindowAlpha(viewport, viewport->Alpha); + viewport->LastAlpha = viewport->Alpha; + + // Optional, general purpose call to allow the back-end to perform general book-keeping even if things haven't changed. + if (g.PlatformIO.Platform_UpdateWindow) + g.PlatformIO.Platform_UpdateWindow(viewport); + + if (is_new_platform_window) + { + // On startup ensure new platform window don't steal focus (give it a few frames, as nested contents may lead to viewport being created a few frames late) + if (g.FrameCount < 3) + viewport->Flags |= ImGuiViewportFlags_NoFocusOnAppearing; + + // Show window + g.PlatformIO.Platform_ShowWindow(viewport); + + // Even without focus, we assume the window becomes front-most. + // This is useful for our platform z-order heuristic when io.MouseHoveredViewport is not available. + if (viewport->LastFrontMostStampCount != g.WindowsFrontMostStampCount) + viewport->LastFrontMostStampCount = ++g.WindowsFrontMostStampCount; + } + + // Clear request flags + viewport->ClearRequestFlags(); + } + + // Update our implicit z-order knowledge of platform windows, which is used when the back-end cannot provide io.MouseHoveredViewport. + // When setting Platform_GetWindowFocus, it is expected that the platform back-end can handle calls without crashing if it doesn't have data stored. + if (g.PlatformIO.Platform_GetWindowFocus != NULL) + { + ImGuiViewportP* focused_viewport = NULL; + for (int n = 0; n < g.Viewports.Size && focused_viewport == NULL; n++) + { + ImGuiViewportP* viewport = g.Viewports[n]; + if (n == 0 || viewport->PlatformWindowCreated) + if (g.PlatformIO.Platform_GetWindowFocus(viewport)) + focused_viewport = viewport; + } + if (focused_viewport && g.PlatformLastFocusedViewport != focused_viewport->ID) + { + if (focused_viewport->LastFrontMostStampCount != g.WindowsFrontMostStampCount) + focused_viewport->LastFrontMostStampCount = ++g.WindowsFrontMostStampCount; + g.PlatformLastFocusedViewport = focused_viewport->ID; + } + } +} + //----------------------------------------------------------------------------- // [SECTION] DOCKING From 5ce93bc0cc0ab18078b8c75d3862e33f2bb40d7a Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 7 Mar 2019 18:39:31 +0100 Subject: [PATCH 453/828] Refactor: Move viewport code under other subsystem to simplify merging (4) (moving in multiple commits to make diff/patch behave nicely) --- imgui.cpp | 198 ++++++++++++++++++++++++++---------------------------- 1 file changed, 97 insertions(+), 101 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index ca41697186c7..5fba257dcfc8 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7787,107 +7787,6 @@ ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window) } -//----------------------------------------------------------------------------- -// [SECTION] VIEWPORTS, PLATFORM WINDOWS -//----------------------------------------------------------------------------- - -// This is a default/basic function for performing the rendering/swap of multiple Platform Windows. -// Custom renderers may prefer to not call this function at all, and instead iterate the publicly exposed platform data and handle rendering/sync themselves. -// The Render/Swap functions stored in ImGuiPlatformIO are merely here to allow for this helper to exist, but you can do it yourself: -// -// ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); -// for (int i = 1; i < platform_io.Viewports.Size; i++) -// MyRenderFunction(platform_io.Viewports[i], my_args); -// for (int i = 1; i < platform_io.Viewports.Size; i++) -// MySwapBufferFunction(platform_io.Viewports[i], my_args); -// -void ImGui::RenderPlatformWindowsDefault(void* platform_render_arg, void* renderer_render_arg) -{ - // Skip the main viewport (index 0), which is always fully handled by the application! - ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); - for (int i = 1; i < platform_io.Viewports.Size; i++) - { - ImGuiViewport* viewport = platform_io.Viewports[i]; - if (platform_io.Platform_RenderWindow) platform_io.Platform_RenderWindow(viewport, platform_render_arg); - if (platform_io.Renderer_RenderWindow) platform_io.Renderer_RenderWindow(viewport, renderer_render_arg); - } - for (int i = 1; i < platform_io.Viewports.Size; i++) - { - ImGuiViewport* viewport = platform_io.Viewports[i]; - if (platform_io.Platform_SwapBuffers) platform_io.Platform_SwapBuffers(viewport, platform_render_arg); - if (platform_io.Renderer_SwapBuffers) platform_io.Renderer_SwapBuffers(viewport, renderer_render_arg); - } -} - -static int ImGui::FindPlatformMonitorForPos(const ImVec2& pos) -{ - ImGuiContext& g = *GImGui; - for (int monitor_n = 0; monitor_n < g.PlatformIO.Monitors.Size; monitor_n++) - { - const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[monitor_n]; - if (ImRect(monitor.MainPos, monitor.MainPos + monitor.MainSize).Contains(pos)) - return monitor_n; - } - return -1; -} - -// Search for the monitor with the largest intersection area with the given rectangle -// We generally try to avoid searching loops but the monitor count should be very small here -static int ImGui::FindPlatformMonitorForRect(const ImRect& rect) -{ - ImGuiContext& g = *GImGui; - - // Use a minimum threshold of 1.0f so a zero-sized rect will still find its monitor given its position. - // This is necessary for tooltips which always resize down to zero at first. - const float surface_threshold = ImMax(rect.GetWidth() * rect.GetHeight() * 0.5f, 1.0f); - int best_monitor_n = -1; - float best_monitor_surface = 0.001f; - - for (int monitor_n = 0; monitor_n < g.PlatformIO.Monitors.Size && best_monitor_surface < surface_threshold; monitor_n++) - { - const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[monitor_n]; - const ImRect monitor_rect = ImRect(monitor.MainPos, monitor.MainPos + monitor.MainSize); - if (monitor_rect.Contains(rect)) - return monitor_n; - ImRect overlapping_rect = rect; - overlapping_rect.ClipWithFull(monitor_rect); - float overlapping_surface = overlapping_rect.GetWidth() * overlapping_rect.GetHeight(); - if (overlapping_surface < best_monitor_surface) - continue; - best_monitor_surface = overlapping_surface; - best_monitor_n = monitor_n; - } - return best_monitor_n; -} - -void ImGui::DestroyPlatformWindow(ImGuiViewportP* viewport) -{ - ImGuiContext& g = *GImGui; - if (g.PlatformIO.Renderer_DestroyWindow) - g.PlatformIO.Renderer_DestroyWindow(viewport); - if (g.PlatformIO.Platform_DestroyWindow) - g.PlatformIO.Platform_DestroyWindow(viewport); - IM_ASSERT(viewport->RendererUserData == NULL); - IM_ASSERT(viewport->PlatformUserData == NULL); - viewport->PlatformHandle = NULL; - viewport->RendererUserData = viewport->PlatformHandle = NULL; - viewport->PlatformWindowCreated = false; - viewport->ClearRequestFlags(); -} - -void ImGui::DestroyPlatformWindows() -{ - // We call the destroy window on every viewport (including the main viewport, index 0) to give a chance to the back-end - // to clear any data they may have stored in e.g. PlatformUserData, RendererUserData. - // It is convenient for the platform back-end code to store something in the main viewport, in order for e.g. the mouse handling - // code to operator a consistent manner. - // It is expected that the back-end can handle calls to Renderer_DestroyWindow/Platform_DestroyWindow without - // crashing if it doesn't have data stored. - ImGuiContext& g = *GImGui; - for (int i = 0; i < g.Viewports.Size; i++) - DestroyPlatformWindow(g.Viewports[i]); -} - //----------------------------------------------------------------------------- // [SECTION] KEYBOARD/GAMEPAD NAVIGATION //----------------------------------------------------------------------------- @@ -10613,6 +10512,103 @@ void ImGui::UpdatePlatformWindows() } } +// This is a default/basic function for performing the rendering/swap of multiple Platform Windows. +// Custom renderers may prefer to not call this function at all, and instead iterate the publicly exposed platform data and handle rendering/sync themselves. +// The Render/Swap functions stored in ImGuiPlatformIO are merely here to allow for this helper to exist, but you can do it yourself: +// +// ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); +// for (int i = 1; i < platform_io.Viewports.Size; i++) +// MyRenderFunction(platform_io.Viewports[i], my_args); +// for (int i = 1; i < platform_io.Viewports.Size; i++) +// MySwapBufferFunction(platform_io.Viewports[i], my_args); +// +void ImGui::RenderPlatformWindowsDefault(void* platform_render_arg, void* renderer_render_arg) +{ + // Skip the main viewport (index 0), which is always fully handled by the application! + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + for (int i = 1; i < platform_io.Viewports.Size; i++) + { + ImGuiViewport* viewport = platform_io.Viewports[i]; + if (platform_io.Platform_RenderWindow) platform_io.Platform_RenderWindow(viewport, platform_render_arg); + if (platform_io.Renderer_RenderWindow) platform_io.Renderer_RenderWindow(viewport, renderer_render_arg); + } + for (int i = 1; i < platform_io.Viewports.Size; i++) + { + ImGuiViewport* viewport = platform_io.Viewports[i]; + if (platform_io.Platform_SwapBuffers) platform_io.Platform_SwapBuffers(viewport, platform_render_arg); + if (platform_io.Renderer_SwapBuffers) platform_io.Renderer_SwapBuffers(viewport, renderer_render_arg); + } +} + +static int ImGui::FindPlatformMonitorForPos(const ImVec2& pos) +{ + ImGuiContext& g = *GImGui; + for (int monitor_n = 0; monitor_n < g.PlatformIO.Monitors.Size; monitor_n++) + { + const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[monitor_n]; + if (ImRect(monitor.MainPos, monitor.MainPos + monitor.MainSize).Contains(pos)) + return monitor_n; + } + return -1; +} + +// Search for the monitor with the largest intersection area with the given rectangle +// We generally try to avoid searching loops but the monitor count should be very small here +static int ImGui::FindPlatformMonitorForRect(const ImRect& rect) +{ + ImGuiContext& g = *GImGui; + + // Use a minimum threshold of 1.0f so a zero-sized rect will still find its monitor given its position. + // This is necessary for tooltips which always resize down to zero at first. + const float surface_threshold = ImMax(rect.GetWidth() * rect.GetHeight() * 0.5f, 1.0f); + int best_monitor_n = -1; + float best_monitor_surface = 0.001f; + + for (int monitor_n = 0; monitor_n < g.PlatformIO.Monitors.Size && best_monitor_surface < surface_threshold; monitor_n++) + { + const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[monitor_n]; + const ImRect monitor_rect = ImRect(monitor.MainPos, monitor.MainPos + monitor.MainSize); + if (monitor_rect.Contains(rect)) + return monitor_n; + ImRect overlapping_rect = rect; + overlapping_rect.ClipWithFull(monitor_rect); + float overlapping_surface = overlapping_rect.GetWidth() * overlapping_rect.GetHeight(); + if (overlapping_surface < best_monitor_surface) + continue; + best_monitor_surface = overlapping_surface; + best_monitor_n = monitor_n; + } + return best_monitor_n; +} + +void ImGui::DestroyPlatformWindow(ImGuiViewportP* viewport) +{ + ImGuiContext& g = *GImGui; + if (g.PlatformIO.Renderer_DestroyWindow) + g.PlatformIO.Renderer_DestroyWindow(viewport); + if (g.PlatformIO.Platform_DestroyWindow) + g.PlatformIO.Platform_DestroyWindow(viewport); + IM_ASSERT(viewport->RendererUserData == NULL); + IM_ASSERT(viewport->PlatformUserData == NULL); + viewport->PlatformHandle = NULL; + viewport->RendererUserData = viewport->PlatformHandle = NULL; + viewport->PlatformWindowCreated = false; + viewport->ClearRequestFlags(); +} + +void ImGui::DestroyPlatformWindows() +{ + // We call the destroy window on every viewport (including the main viewport, index 0) to give a chance to the back-end + // to clear any data they may have stored in e.g. PlatformUserData, RendererUserData. + // It is convenient for the platform back-end code to store something in the main viewport, in order for e.g. the mouse handling + // code to operator a consistent manner. + // It is expected that the back-end can handle calls to Renderer_DestroyWindow/Platform_DestroyWindow without + // crashing if it doesn't have data stored. + ImGuiContext& g = *GImGui; + for (int i = 0; i < g.Viewports.Size; i++) + DestroyPlatformWindow(g.Viewports[i]); +} + //----------------------------------------------------------------------------- // [SECTION] DOCKING From ecf7666624cbfa2dad4dba82cd9e8b98c3a1020e Mon Sep 17 00:00:00 2001 From: omar Date: Sun, 10 Mar 2019 22:19:18 +0100 Subject: [PATCH 454/828] Docking: Fixed an issue where removing the last window from a dockspace node that is not a central node without remove the node. (#2414, #2109) --- imgui.cpp | 4 ++-- imgui_internal.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 830ff4ff75f5..90386dcc70d5 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11432,7 +11432,7 @@ static void ImGui::DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window } } - if (node->Windows.Size == 0 && !node->IsCentralNode && window->DockId != node->ID) + if (node->Windows.Size == 0 && !node->IsCentralNode && !node->IsDockSpace() && window->DockId != node->ID) { // Automatic dock node delete themselves if they are not holding at least one tab DockContextRemoveNode(&g, node, true); @@ -11631,7 +11631,7 @@ static void ImGui::DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* nod static void ImGui::DockNodeUpdateVisibleFlag(ImGuiDockNode* node) { // Update visibility flag - bool is_visible = (node->ParentNode == 0) ? node->IsDockSpace() : node->IsCentralNode; + bool is_visible = (node->ParentNode == NULL) ? node->IsDockSpace() : node->IsCentralNode; is_visible |= (node->Windows.Size > 0); is_visible |= (node->ChildNodes[0] && node->ChildNodes[0]->IsVisible); is_visible |= (node->ChildNodes[1] && node->ChildNodes[1]->IsVisible); diff --git a/imgui_internal.h b/imgui_internal.h index 40c8cc1ac2d3..6fdc30bc4c6c 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1670,7 +1670,7 @@ namespace ImGui IMGUI_API void DockBuilderDockWindow(const char* window_name, ImGuiID node_id); IMGUI_API ImGuiDockNode*DockBuilderGetNode(ImGuiID node_id); // Warning: DO NOT HOLD ON ImGuiDockNode* pointer, will be invalided by any split/merge/remove operation. inline ImGuiDockNode* DockBuilderGetCentralNode(ImGuiID node_id) { ImGuiDockNode* node = DockBuilderGetNode(node_id); if (!node) return NULL; return DockNodeGetRootNode(node)->CentralNode; } - IMGUI_API ImGuiID DockBuilderAddNode(ImGuiID node_id, ImGuiDockNodeFlags flags = 0); + IMGUI_API ImGuiID DockBuilderAddNode(ImGuiID node_id, ImGuiDockNodeFlags flags = 0); // Use (flags == ImGuiDockNodeFlags_Dockspace) to create a dockspace, otherwise it'll create a floating node. IMGUI_API void DockBuilderRemoveNode(ImGuiID node_id); // Remove node and all its child, undock all windows IMGUI_API void DockBuilderRemoveNodeDockedWindows(ImGuiID node_id, bool clear_persistent_docking_references = true); IMGUI_API void DockBuilderRemoveNodeChildNodes(ImGuiID node_id); // Remove all split/hierarchy. All remaining docked windows will be re-docked to the root. From 3eedb542a640f5cb71983b388dd81f8cc395b788 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 11 Mar 2019 11:07:23 +0100 Subject: [PATCH 455/828] Viewports: Renamed ConfigViewportsNoParent to ConfigViewportsNoDefaultParent. Fix outdated comments in examples. --- examples/example_glfw_opengl2/main.cpp | 4 ++-- examples/example_glfw_opengl3/main.cpp | 4 ++-- examples/example_glfw_vulkan/main.cpp | 4 ++-- examples/example_sdl_opengl2/main.cpp | 4 ++-- examples/example_sdl_opengl3/main.cpp | 4 ++-- examples/example_win32_directx10/main.cpp | 5 +++-- examples/example_win32_directx11/main.cpp | 12 +++++++----- examples/example_win32_directx12/main.cpp | 5 +++-- examples/example_win32_directx9/main.cpp | 5 +++-- imgui.cpp | 4 ++-- imgui.h | 2 +- imgui_demo.cpp | 4 ++-- 12 files changed, 31 insertions(+), 26 deletions(-) diff --git a/examples/example_glfw_opengl2/main.cpp b/examples/example_glfw_opengl2/main.cpp index ff4ffdbfa854..619e6bf77429 100644 --- a/examples/example_glfw_opengl2/main.cpp +++ b/examples/example_glfw_opengl2/main.cpp @@ -47,8 +47,8 @@ int main(int, char**) //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows - //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoTaskBarIcons; - //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoMerge; + //io.ConfigViewportsNoAutoMerge = true; + //io.ConfigViewportsNoTaskBarIcon = true; // Setup Dear ImGui style ImGui::StyleColorsDark(); diff --git a/examples/example_glfw_opengl3/main.cpp b/examples/example_glfw_opengl3/main.cpp index bb2b1525e6bb..3785d779265a 100644 --- a/examples/example_glfw_opengl3/main.cpp +++ b/examples/example_glfw_opengl3/main.cpp @@ -90,8 +90,8 @@ int main(int, char**) //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows - //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoTaskBarIcons; - //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoMerge; + //io.ConfigViewportsNoAutoMerge = true; + //io.ConfigViewportsNoTaskBarIcon = true; // Setup Dear ImGui style ImGui::StyleColorsDark(); diff --git a/examples/example_glfw_vulkan/main.cpp b/examples/example_glfw_vulkan/main.cpp index b2e902e3e204..74edf5d954e2 100644 --- a/examples/example_glfw_vulkan/main.cpp +++ b/examples/example_glfw_vulkan/main.cpp @@ -358,8 +358,8 @@ int main(int, char**) //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows - //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoTaskBarIcons; - //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoMerge; + //io.ConfigViewportsNoAutoMerge = true; + //io.ConfigViewportsNoTaskBarIcon = true; // Setup Dear ImGui style ImGui::StyleColorsDark(); diff --git a/examples/example_sdl_opengl2/main.cpp b/examples/example_sdl_opengl2/main.cpp index bb3eaca854c9..1d3c26039e01 100644 --- a/examples/example_sdl_opengl2/main.cpp +++ b/examples/example_sdl_opengl2/main.cpp @@ -43,8 +43,8 @@ int main(int, char**) io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows - //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoTaskBarIcons; - //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoMerge; + //io.ConfigViewportsNoAutoMerge = true; + //io.ConfigViewportsNoTaskBarIcon = true; // Setup Dear ImGui style ImGui::StyleColorsDark(); diff --git a/examples/example_sdl_opengl3/main.cpp b/examples/example_sdl_opengl3/main.cpp index 44fa4025fb94..332685c42b3b 100644 --- a/examples/example_sdl_opengl3/main.cpp +++ b/examples/example_sdl_opengl3/main.cpp @@ -83,8 +83,8 @@ int main(int, char**) io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows - //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoTaskBarIcons; - //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoMerge; + //io.ConfigViewportsNoAutoMerge = true; + //io.ConfigViewportsNoTaskBarIcon = true; // Setup Dear ImGui style ImGui::StyleColorsDark(); diff --git a/examples/example_win32_directx10/main.cpp b/examples/example_win32_directx10/main.cpp index 8ef6c7c883e3..bb92c0d09798 100644 --- a/examples/example_win32_directx10/main.cpp +++ b/examples/example_win32_directx10/main.cpp @@ -117,10 +117,11 @@ int main(int, char**) ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows - //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoTaskBarIcons; - //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoMerge; + //io.ConfigViewportsNoAutoMerge = true; + //io.ConfigViewportsNoTaskBarIcon = true; // Setup Dear ImGui style ImGui::StyleColorsDark(); diff --git a/examples/example_win32_directx11/main.cpp b/examples/example_win32_directx11/main.cpp index 5d3efef7b91c..d981c0397229 100644 --- a/examples/example_win32_directx11/main.cpp +++ b/examples/example_win32_directx11/main.cpp @@ -133,16 +133,18 @@ int main(int, char**) ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows - //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoTaskBarIcons; - //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoMerge; - io.ConfigFlags |= ImGuiConfigFlags_DpiEnableScaleFonts; // FIXME-DPI: THIS CURRENTLY DOESN'T WORK AS EXPECTED. DON'T USE IN USER APP! - io.ConfigFlags |= ImGuiConfigFlags_DpiEnableScaleViewports; // FIXME-DPI //io.ConfigViewportsNoAutoMerge = true; - //io.ConfigViewportsNoTaskBarIcon = false; + //io.ConfigViewportsNoTaskBarIcon = true; + //io.ConfigViewportsNoDefaultParent = true; //io.ConfigDockingTabBarOnSingleWindows = true; //io.ConfigDockingTransparentPayload = true; +#if 1 + io.ConfigFlags |= ImGuiConfigFlags_DpiEnableScaleFonts; // FIXME-DPI: THIS CURRENTLY DOESN'T WORK AS EXPECTED. DON'T USE IN USER APP! + io.ConfigFlags |= ImGuiConfigFlags_DpiEnableScaleViewports; // FIXME-DPI +#endif // Setup Dear ImGui style ImGui::StyleColorsDark(); diff --git a/examples/example_win32_directx12/main.cpp b/examples/example_win32_directx12/main.cpp index 54bc752469a4..14af4c2152d3 100644 --- a/examples/example_win32_directx12/main.cpp +++ b/examples/example_win32_directx12/main.cpp @@ -291,10 +291,11 @@ int main(int, char**) ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking //io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows (FIXME: Currently broken in DX12 back-end, need some work!) - //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoTaskBarIcons; - //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoMerge; + //io.ConfigViewportsNoAutoMerge = true; + //io.ConfigViewportsNoTaskBarIcon = true; // Setup Dear ImGui style ImGui::StyleColorsDark(); diff --git a/examples/example_win32_directx9/main.cpp b/examples/example_win32_directx9/main.cpp index 9833ca9354fe..371874542c76 100644 --- a/examples/example_win32_directx9/main.cpp +++ b/examples/example_win32_directx9/main.cpp @@ -115,10 +115,11 @@ int main(int, char**) ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows - //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoTaskBarIcons; - //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoMerge; + //io.ConfigViewportsNoAutoMerge = true; + //io.ConfigViewportsNoTaskBarIcon = true; // Setup Dear ImGui style ImGui::StyleColorsDark(); diff --git a/imgui.cpp b/imgui.cpp index 90386dcc70d5..bf571aa85b9e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1207,7 +1207,7 @@ ImGuiIO::ImGuiIO() ConfigViewportsNoAutoMerge = false; ConfigViewportsNoTaskBarIcon = false; ConfigViewportsNoDecoration = true; - ConfigViewportsNoParent = false; + ConfigViewportsNoDefaultParent = false; // Miscellaneous options MouseDrawCursor = false; @@ -5605,7 +5605,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if (window->WindowClass.ParentViewportId) window->Viewport->ParentViewportId = window->WindowClass.ParentViewportId; else - window->Viewport->ParentViewportId = g.IO.ConfigViewportsNoParent ? 0 : IMGUI_VIEWPORT_DEFAULT_ID; + window->Viewport->ParentViewportId = g.IO.ConfigViewportsNoDefaultParent ? 0 : IMGUI_VIEWPORT_DEFAULT_ID; if (window->WindowClass.ViewportFlagsOverrideMask) viewport_flags = (viewport_flags & ~window->WindowClass.ViewportFlagsOverrideMask) | (window->WindowClass.ViewportFlagsOverrideValue & window->WindowClass.ViewportFlagsOverrideMask); diff --git a/imgui.h b/imgui.h index b385bb02aa04..ee74ded65b9f 100644 --- a/imgui.h +++ b/imgui.h @@ -1400,7 +1400,7 @@ struct ImGuiIO bool ConfigViewportsNoAutoMerge; // = false; // Set to make all floating imgui windows always create their own viewport. Otherwise, they are merged into the main host viewports when overlapping it. bool ConfigViewportsNoTaskBarIcon; // = false // Disable default OS task bar icon flag for secondary viewports. When a viewport doesn't want a task bar icon, ImGuiViewportFlags_NoTaskBarIcon will be set on it. bool ConfigViewportsNoDecoration; // = true // [BETA] Disable default OS window decoration flag for secondary viewports. When a viewport doesn't want window decorations, ImGuiViewportFlags_NoDecoration will be set on it. Enabling decoration can create subsequent issues at OS levels (e.g. minimum window size). - bool ConfigViewportsNoParent; // = false // Disable default OS parenting to main viewport for secondary viewports. By default, viewports are marked with ParentViewportId = , expecting the platform back-end to setup a parent/child relationship between the OS windows (some back-end may ignore this). Set to true if you want the default to be 0, then all viewports will be top-level OS windows. + bool ConfigViewportsNoDefaultParent; // = false // Disable default OS parenting to main viewport for secondary viewports. By default, viewports are marked with ParentViewportId = , expecting the platform back-end to setup a parent/child relationship between the OS windows (some back-end may ignore this). Set to true if you want the default to be 0, then all viewports will be top-level OS windows. // Miscellaneous options bool MouseDrawCursor; // = false // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). Cannot be easily renamed to 'io.ConfigXXX' because this is frequently used by back-end implementations. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 2e55052af4e6..709e86ee2716 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -373,7 +373,7 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::SameLine(); HelpMarker("Toggling this at runtime is normally unsupported (most platform back-ends won't refresh the task bar icon state right away)."); ImGui::Checkbox("io.ConfigViewportsNoDecoration", &io.ConfigViewportsNoDecoration); ImGui::SameLine(); HelpMarker("Toggling this at runtime is normally unsupported (most platform back-ends won't refresh the decoration right away)."); - ImGui::Checkbox("io.ConfigViewportsNoParent", &io.ConfigViewportsNoParent); + ImGui::Checkbox("io.ConfigViewportsNoDefaultParent", &io.ConfigViewportsNoDefaultParent); ImGui::SameLine(); HelpMarker("Toggling this at runtime is normally unsupported (most platform back-ends won't refresh the parenting right away)."); ImGui::Unindent(); } @@ -2802,7 +2802,7 @@ void ImGui::ShowAboutWindow(bool* p_open) if (io.ConfigViewportsNoAutoMerge) ImGui::Text("io.ConfigViewportsNoAutoMerge"); if (io.ConfigViewportsNoTaskBarIcon) ImGui::Text("io.ConfigViewportsNoTaskBarIcon"); if (io.ConfigViewportsNoDecoration) ImGui::Text("io.ConfigViewportsNoDecoration"); - if (io.ConfigViewportsNoParent) ImGui::Text("io.ConfigViewportsNoParent"); + if (io.ConfigViewportsNoDefaultParent) ImGui::Text("io.ConfigViewportsNoDefaultParent"); if (io.ConfigDockingNoSplit) ImGui::Text("io.ConfigDockingNoSplit"); if (io.ConfigDockingWithShift) ImGui::Text("io.ConfigDockingWithShift"); if (io.ConfigDockingTabBarOnSingleWindows) ImGui::Text("io.ConfigDockingTabBarOnSingleWindows"); From cf4fcc473550c8c024a84f480aebe5d4e6aafedc Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 11 Mar 2019 13:10:41 +0100 Subject: [PATCH 456/828] Viewports: Fixed delayed window pos->viewport pos sync leading to monitor not being updated at the time of clamping window position in Begin. (#2415, #1542) --- imgui.cpp | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index bf571aa85b9e..d457f7269f14 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1061,6 +1061,7 @@ static void SetCurrentViewport(ImGuiWindow* window, ImGuiViewportP* static bool GetWindowAlwaysWantOwnViewport(ImGuiWindow* window); static int FindPlatformMonitorForPos(const ImVec2& pos); static int FindPlatformMonitorForRect(const ImRect& r); +static void UpdateViewportPlatformMonitor(ImGuiViewportP* viewport); } @@ -5581,13 +5582,31 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) SetCurrentWindow(window); } + bool viewport_rect_changed = false; if (window->ViewportOwned) { + // Synchronize window --> viewport in most situations // Synchronize viewport -> window in case the platform window has been moved or resized from the OS/WM if (window->Viewport->PlatformRequestMove) window->Pos = window->Viewport->Pos; + else if (memcmp(&window->Viewport->Pos, &window->Pos, sizeof(window->Pos)) != 0) + { + viewport_rect_changed = true; + window->Viewport->Pos = window->Pos; + } + if (window->Viewport->PlatformRequestResize) window->Size = window->SizeFull = window->Viewport->Size; + else if (memcmp(&window->Viewport->Size, &window->Size, sizeof(window->Size)) != 0) + { + viewport_rect_changed = true; + window->Viewport->Size = window->Size; + } + + // The viewport may have changed monitor since the global update in UpdateViewportsNewFrame() + // Either a SetNextWindowPos() call in the current frame or a SetWindowPos() call in the previous frame may have this effect. + if (viewport_rect_changed) + UpdateViewportPlatformMonitor(window->Viewport); // Update common viewport flags ImGuiViewportFlags viewport_flags = (window->Viewport->Flags) & ~(ImGuiViewportFlags_TopMost | ImGuiViewportFlags_NoTaskBarIcon | ImGuiViewportFlags_NoDecoration); @@ -5669,7 +5688,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) UpdateManualResize(window, size_auto_fit, &border_held, resize_grip_count, &resize_grip_col[0]); window->ResizeBorderHeld = (signed char)border_held; - // Synchronize window --> viewport + // Synchronize window --> viewport again and one last time (clamping and manual resize may have affected either) if (window->ViewportOwned) { if (!window->Viewport->PlatformRequestMove) @@ -10115,8 +10134,7 @@ static void ImGui::UpdateViewportsNewFrame() viewport->Size = viewport->LastPlatformSize = g.PlatformIO.Platform_GetWindowSize(viewport); } - // Update monitor (we'll use this info to clamp windows and save windows lost in a removed monitor) - viewport->PlatformMonitor = (short)FindPlatformMonitorForRect(viewport->GetRect()); + UpdateViewportPlatformMonitor(viewport); } // Reset alpha every frame. Users of transparency (docking) needs to request a lower alpha back. @@ -10261,7 +10279,7 @@ ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const viewport->Idx = g.Viewports.Size; viewport->Pos = viewport->LastPos = pos; viewport->Size = size; - viewport->PlatformMonitor = (short)FindPlatformMonitorForRect(viewport->GetRect()); + UpdateViewportPlatformMonitor(viewport); g.Viewports.push_back(viewport); //IMGUI_DEBUG_LOG("Add Viewport %08X (%s)\n", id, window->Name); @@ -10569,11 +10587,12 @@ static int ImGui::FindPlatformMonitorForPos(const ImVec2& pos) // Search for the monitor with the largest intersection area with the given rectangle // We generally try to avoid searching loops but the monitor count should be very small here +// FIXME-OPT: We could test the last monitor used for that viewport first.. static int ImGui::FindPlatformMonitorForRect(const ImRect& rect) { ImGuiContext& g = *GImGui; - // Use a minimum threshold of 1.0f so a zero-sized rect will still find its monitor given its position. + // Use a minimum threshold of 1.0f so a zero-sized rect won't false positive, and will still find the correct monitor given its position. // This is necessary for tooltips which always resize down to zero at first. const float surface_threshold = ImMax(rect.GetWidth() * rect.GetHeight() * 0.5f, 1.0f); int best_monitor_n = -1; @@ -10596,6 +10615,12 @@ static int ImGui::FindPlatformMonitorForRect(const ImRect& rect) return best_monitor_n; } +// Update monitor from viewport rectangle (we'll use this info to clamp windows and save windows lost in a removed monitor) +static void ImGui::UpdateViewportPlatformMonitor(ImGuiViewportP* viewport) +{ + viewport->PlatformMonitor = (short)FindPlatformMonitorForRect(viewport->GetRect()); +} + void ImGui::DestroyPlatformWindow(ImGuiViewportP* viewport) { ImGuiContext& g = *GImGui; From 3ead9820f7dc43d9ccafc51849aab441bd5e17c0 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 11 Mar 2019 16:51:46 +0100 Subject: [PATCH 457/828] Viewport: Popups and Tooltips viewports are correctly parented to the parent window's viewport. (#2409, #1542) --- imgui.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/imgui.cpp b/imgui.cpp index aa858b9adeab..48591b2a0b73 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5623,6 +5623,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // We don't default to the main viewport because. if (window->WindowClass.ParentViewportId) window->Viewport->ParentViewportId = window->WindowClass.ParentViewportId; + else if ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && parent_window_in_stack) + window->Viewport->ParentViewportId = parent_window_in_stack->Viewport->ID; else window->Viewport->ParentViewportId = g.IO.ConfigViewportsNoDefaultParent ? 0 : IMGUI_VIEWPORT_DEFAULT_ID; if (window->WindowClass.ViewportFlagsOverrideMask) From e1acb0b1faaacb0f3b96bfa11b3f992bb51ab06d Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 11 Mar 2019 19:46:37 +0100 Subject: [PATCH 458/828] Docking: Fixed node merging altering window position incorrectly in a way that would make SizeContents incorrect for the next frame (making scrollbar flicker). (#2414, #2109) --- imgui.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 48591b2a0b73..53316f2c065a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11536,8 +11536,8 @@ static void ImGui::DockNodeApplyPosSizeToWindows(ImGuiDockNode* node) { for (int n = 0; n < node->Windows.Size; n++) { - node->Windows[n]->Pos = node->Pos; - node->Windows[n]->SizeFull = node->Size; + SetWindowPos(node->Windows[n], node->Pos, ImGuiCond_Always); // We don't assign directly to Pos because it can break the calculation of SizeContents on next frame + SetWindowSize(node->Windows[n], node->Size, ImGuiCond_Always); } } From e7dca4fec2c4b55731103b7a7a3b2dcb7617a444 Mon Sep 17 00:00:00 2001 From: David Maas Date: Wed, 13 Mar 2019 00:57:15 -0500 Subject: [PATCH 459/828] Fixed main viewport not being marked as created, which broke updating the IME input position for the main viewport. This change also removes the logic scattered about that compensated for PlatformWindowCreated being wrong for the main viewport. --- imgui.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 53316f2c065a..4b1b9f779774 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3790,6 +3790,7 @@ void ImGui::Initialize(ImGuiContext* context) ImGuiViewportP* viewport = IM_NEW(ImGuiViewportP)(); viewport->ID = IMGUI_VIEWPORT_DEFAULT_ID; viewport->Idx = 0; + viewport->PlatformWindowCreated = true; g.Viewports.push_back(viewport); g.PlatformIO.MainViewport = g.Viewports[0]; // Make it accessible in public-facing GetPlatformIO() immediately (before the first call to EndFrame) g.PlatformIO.Viewports.push_back(g.Viewports[0]); @@ -10078,7 +10079,7 @@ static void ImGui::UpdateViewportsNewFrame() for (int n = 0; n < g.Viewports.Size; n++) { ImGuiViewportP* viewport = g.Viewports[n]; - const bool platform_funcs_available = (n == 0 || viewport->PlatformWindowCreated); + const bool platform_funcs_available = viewport->PlatformWindowCreated; if ((g.ConfigFlagsForFrame & ImGuiConfigFlags_ViewportsEnable)) if (g.PlatformIO.Platform_GetWindowMinimized && platform_funcs_available) viewport->PlatformWindowMinimized = g.PlatformIO.Platform_GetWindowMinimized(viewport); @@ -10124,7 +10125,7 @@ static void ImGui::UpdateViewportsNewFrame() continue; } - const bool platform_funcs_available = (n == 0 || viewport->PlatformWindowCreated); + const bool platform_funcs_available = viewport->PlatformWindowCreated; if ((g.ConfigFlagsForFrame & ImGuiConfigFlags_ViewportsEnable)) { // Update Position and Size (from Platform Window to ImGui) if requested. @@ -10535,7 +10536,7 @@ void ImGui::UpdatePlatformWindows() for (int n = 0; n < g.Viewports.Size && focused_viewport == NULL; n++) { ImGuiViewportP* viewport = g.Viewports[n]; - if (n == 0 || viewport->PlatformWindowCreated) + if (viewport->PlatformWindowCreated) if (g.PlatformIO.Platform_GetWindowFocus(viewport)) focused_viewport = viewport; } From c3f20f6b81cd2fd3be2b9073cba089d7c3093740 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 13 Mar 2019 11:27:30 +0100 Subject: [PATCH 460/828] Viewport: DestroyPlatformWindow() skips calling user function if PlatformWindowCreated is set. + Clarified comment about implicit Debug viewport which may be hogging a viewport. --- imgui.cpp | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 4b1b9f779774..89b5535654f0 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10446,7 +10446,8 @@ void ImGui::UpdatePlatformWindows() { ImGuiViewportP* viewport = g.Viewports[i]; - // Destroy platform window if the viewport hasn't been submitted or if it is hosting a hidden window (the implicit/fallback Debug window will be registered its viewport then be disabled) + // Destroy platform window if the viewport hasn't been submitted or if it is hosting a hidden window + // (the implicit/fallback Debug##Default window will be registering its viewport then be disabled, causing a dummy DestroyPlatformWindow to be made each frame) bool destroy_platform_window = false; destroy_platform_window |= (viewport->LastFrameActive < g.FrameCount - 1); destroy_platform_window |= (viewport->Window && !IsWindowActiveAndVisible(viewport->Window)); @@ -10628,15 +10629,20 @@ static void ImGui::UpdateViewportPlatformMonitor(ImGuiViewportP* viewport) void ImGui::DestroyPlatformWindow(ImGuiViewportP* viewport) { ImGuiContext& g = *GImGui; - if (g.PlatformIO.Renderer_DestroyWindow) - g.PlatformIO.Renderer_DestroyWindow(viewport); - if (g.PlatformIO.Platform_DestroyWindow) - g.PlatformIO.Platform_DestroyWindow(viewport); - IM_ASSERT(viewport->RendererUserData == NULL); - IM_ASSERT(viewport->PlatformUserData == NULL); - viewport->PlatformHandle = NULL; - viewport->RendererUserData = viewport->PlatformHandle = NULL; - viewport->PlatformWindowCreated = false; + if (viewport->PlatformWindowCreated) + { + if (g.PlatformIO.Renderer_DestroyWindow) + g.PlatformIO.Renderer_DestroyWindow(viewport); + if (g.PlatformIO.Platform_DestroyWindow) + g.PlatformIO.Platform_DestroyWindow(viewport); + IM_ASSERT(viewport->RendererUserData == NULL && viewport->PlatformUserData == NULL); + viewport->PlatformWindowCreated = false; + } + else + { + IM_ASSERT(viewport->RendererUserData == NULL && viewport->PlatformUserData == NULL && viewport->PlatformHandle == NULL); + } + viewport->RendererUserData = viewport->PlatformUserData = viewport->PlatformHandle = NULL; viewport->ClearRequestFlags(); } From 7ba774a44025f4a2fcc2513296adc8afaf4cb031 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 15 Mar 2019 15:35:46 +0100 Subject: [PATCH 461/828] Viewports: Fixed being unable to refocus windows when ConfigViewportsNoTaskBarIcon + ConfigViewportsNoDecoration are enabled. (#2420, #1542) [@PathogenDavid] + comments. --- imgui.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 4bbb4f4d1c80..3c7ba83df165 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5611,13 +5611,19 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Update common viewport flags ImGuiViewportFlags viewport_flags = (window->Viewport->Flags) & ~(ImGuiViewportFlags_TopMost | ImGuiViewportFlags_NoTaskBarIcon | ImGuiViewportFlags_NoDecoration); + const bool is_short_lived_floating_window = (flags & (ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) != 0; if (flags & ImGuiWindowFlags_Tooltip) viewport_flags |= ImGuiViewportFlags_TopMost; - if (g.IO.ConfigViewportsNoTaskBarIcon || (flags & (ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) != 0) + if (g.IO.ConfigViewportsNoTaskBarIcon || is_short_lived_floating_window) viewport_flags |= ImGuiViewportFlags_NoTaskBarIcon; - if (g.IO.ConfigViewportsNoDecoration || (flags & (ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) != 0) + if (g.IO.ConfigViewportsNoDecoration || is_short_lived_floating_window) viewport_flags |= ImGuiViewportFlags_NoDecoration; - if ((viewport_flags & ImGuiViewportFlags_NoDecoration) && (viewport_flags & ImGuiViewportFlags_NoTaskBarIcon)) + + // For popups and menus that may be protruding out of their parent viewport, we enable _NoFocusOnClick so that clicking on them + // won't steal the OS focus away from their parent window (which may be reflected in OS the title bar decoration). + // Setting _NoFocusOnClick would technically prevent us from bringing back to front in case they are being covered by an OS window from a different app, + // but it shouldn't be much of a problem considering those are already popups that are closed when clicking elsewhere. + if (is_short_lived_floating_window) viewport_flags |= ImGuiViewportFlags_NoFocusOnAppearing | ImGuiViewportFlags_NoFocusOnClick; // We can overwrite viewport flags using ImGuiWindowClass (advanced users) From c7619d4a6afafd38bc0e603c1f883e15522b85cf Mon Sep 17 00:00:00 2001 From: omar Date: Sat, 16 Mar 2019 16:19:09 +0100 Subject: [PATCH 462/828] Docking: Preserve existing docked nodes when setting the ImGuiDockNodeFlags_NoDockingInCentralNode flag. (#2423, #2109) --- imgui.cpp | 11 +++++++---- imgui.h | 6 +++--- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 3c7ba83df165..6ed50ed6b169 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -12873,7 +12873,7 @@ void ImGui::SetWindowDock(ImGuiWindow* window, ImGuiID dock_id, ImGuiCond cond) // Create an explicit dockspace node within an existing window. Also expose dock node flags and creates a CentralNode by default. // The Central Node is always displayed even when empty and shrink/extend according to the requested size of its neighbors. -void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags dockspace_flags, const ImGuiWindowClass* window_class) +void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags flags, const ImGuiWindowClass* window_class) { ImGuiContext* ctx = GImGui; ImGuiContext& g = *ctx; @@ -12881,18 +12881,19 @@ void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags doc if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable)) return; + IM_ASSERT((flags & ImGuiDockNodeFlags_Dockspace) == 0); ImGuiDockNode* node = DockContextFindNodeByID(ctx, id); if (!node) { node = DockContextAddNode(ctx, id); node->IsCentralNode = true; } - node->Flags = dockspace_flags; + node->Flags = flags; node->WindowClass = window_class ? *window_class : ImGuiWindowClass(); // When a Dockspace transitioned form implicit to explicit this may be called a second time // It is possible that the node has already been claimed by a docked window which appeared before the DockSpace() node, so we overwrite IsDockSpace again. - if (node->LastFrameActive == g.FrameCount && !(dockspace_flags & ImGuiDockNodeFlags_KeepAliveOnly)) + if (node->LastFrameActive == g.FrameCount && !(flags & ImGuiDockNodeFlags_KeepAliveOnly)) { IM_ASSERT(node->IsDockSpace() == false && "Cannot call DockSpace() twice a frame with the same ID"); node->Flags |= ImGuiDockNodeFlags_Dockspace; @@ -12901,7 +12902,7 @@ void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags doc node->Flags |= ImGuiDockNodeFlags_Dockspace; // Keep alive mode, this is allow windows docked into this node so stay docked even if they are not visible - if (dockspace_flags & ImGuiDockNodeFlags_KeepAliveOnly) + if (flags & ImGuiDockNodeFlags_KeepAliveOnly) { node->LastFrameAlive = g.FrameCount; return; @@ -13421,12 +13422,14 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) g.NextWindowData.PosUndock = false; } +#if 0 // Undock if the ImGuiDockNodeFlags_NoDockingInCentralNode got set if (node->IsCentralNode && (node->Flags & ImGuiDockNodeFlags_NoDockingInCentralNode)) { DockContextProcessUndockWindow(ctx, window); return; } +#endif // Undock if our dockspace node disappeared // Note how we are testing for LastFrameAlive and NOT LastFrameActive. A DockSpace node can be maintained alive while being inactive with ImGuiDockNodeFlags_KeepAliveOnly. diff --git a/imgui.h b/imgui.h index 95b4a1fc6fc2..57cddcb6bf11 100644 --- a/imgui.h +++ b/imgui.h @@ -594,7 +594,7 @@ namespace ImGui // To dock windows: hold SHIFT anywhere while moving windows (if io.ConfigDockingWithShift == true) or drag windows from their title bar (if io.ConfigDockingWithShift = false) // Use DockSpace() to create an explicit dock node _within_ an existing window. See Docking demo for details. IMGUI_API void DockSpace(ImGuiID id, const ImVec2& size = ImVec2(0, 0), ImGuiDockNodeFlags flags = 0, const ImGuiWindowClass* window_class = NULL); - IMGUI_API ImGuiID DockSpaceOverViewport(ImGuiViewport* viewport = NULL, ImGuiDockNodeFlags dockspace_flags = 0, const ImGuiWindowClass* window_class = NULL); + IMGUI_API ImGuiID DockSpaceOverViewport(ImGuiViewport* viewport = NULL, ImGuiDockNodeFlags flags = 0, const ImGuiWindowClass* window_class = NULL); IMGUI_API void SetNextWindowDockID(ImGuiID dock_id, ImGuiCond cond = 0); // set next window dock id (FIXME-DOCK) IMGUI_API void SetNextWindowClass(const ImGuiWindowClass* window_class); // set next window class (rare/advanced uses: provide hints to the platform back-end via altered viewport flags and parent/child info) IMGUI_API ImGuiID GetWindowDockID(); @@ -882,9 +882,9 @@ enum ImGuiDockNodeFlags_ { ImGuiDockNodeFlags_None = 0, ImGuiDockNodeFlags_KeepAliveOnly = 1 << 0, // Don't display the dockspace node but keep it alive. Windows docked into this dockspace node won't be undocked. - ImGuiDockNodeFlags_NoSplit = 1 << 1, // Disable splitting the node into smaller nodes. Useful e.g. when embedding dockspaces into a main root one (the root one may have splitting disabled to reduce confusion) + ImGuiDockNodeFlags_NoSplit = 1 << 1, // Disable splitting the node into smaller nodes. Useful e.g. when embedding dockspaces into a main root one (the root one may have splitting disabled to reduce confusion). Note: when turned off, existing splits will be preserved. //ImGuiDockNodeFlags_NoCentralNode = 1 << 2, // Disable Central Node (the node which can stay empty) - ImGuiDockNodeFlags_NoDockingInCentralNode = 1 << 3, // Disable docking inside the Central Node, which will be always kept empty. + ImGuiDockNodeFlags_NoDockingInCentralNode = 1 << 3, // Disable docking inside the Central Node, which will be always kept empty. Note: when turned off, existing docked nodes will be preserved. //ImGuiDockNodeFlags_NoLayoutChanges = 1 << 4, // Disable adding/removing nodes interactively. Useful with programatically setup dockspaces. ImGuiDockNodeFlags_NoResize = 1 << 5, // Disable resizing child nodes using the splitter/separators. Useful with programatically setup dockspaces. ImGuiDockNodeFlags_PassthruDockspace = 1 << 6, // Enable passthru dockspace: 1) DockSpace() will render a ImGuiCol_WindowBg background covering everything excepted the Central Node when empty. Meaning the host window should probably use SetNextWindowBgAlpha(0.0f) prior to Begin() when using this. 2) When Central Node is empty: let inputs pass-through + won't display a DockingEmptyBg background. From 7a5196601e96ccc880ea00e5aed0ef3a786edad2 Mon Sep 17 00:00:00 2001 From: omar Date: Sat, 16 Mar 2019 18:13:24 +0100 Subject: [PATCH 463/828] Docking: BeginDocked() doesn't need to rely on tab bar data (will allow removing tab bar). --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 6ed50ed6b169..d928b6a7d025 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -13475,7 +13475,7 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) return; // When the window is selected we mark it as visible. - if (node->TabBar && node->TabBar->VisibleTabId == window->ID) + if (node->VisibleWindow == window) window->DockTabIsVisible = true; // When we are about to select this tab (which will only be visible on the _next frame_), flag it with a non-zero HiddenFramesForResize. From f208fd7ebb55adc0b21c46c3d60309eb47cad95e Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 26 Mar 2019 12:33:58 +0100 Subject: [PATCH 464/828] Docking: Fixed crash with ImGuiDockNodeFlags_AutoHideTabBar flag. (#2423, #2109) --- imgui.cpp | 8 ++++---- imgui_demo.cpp | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 8ee79ed42401..1abffcc442e4 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11915,17 +11915,17 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) if (host_window && node->Windows.Size > 0) { DockNodeUpdateTabBar(node, host_window); - if (node->TabBar->SelectedTabId) - node->SelectedTabID = node->TabBar->SelectedTabId; } else { node->WantCloseAll = false; node->WantCloseTabID = 0; node->IsFocused = false; - if (node->Windows.Size > 0) - node->SelectedTabID = node->Windows[0]->ID; } + if (node->TabBar && node->TabBar->SelectedTabId) + node->SelectedTabID = node->TabBar->SelectedTabId; + else if (node->Windows.Size > 0) + node->SelectedTabID = node->Windows[0]->ID; // Draw payload drop target if (host_window && node->IsVisible) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index ca02f0a329ee..3de9826fa3e4 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -4241,8 +4241,8 @@ static void ShowExampleAppCustomRendering(bool* p_open) void ShowExampleAppDockSpace(bool* p_open) { static bool opt_fullscreen_persistant = true; - static ImGuiDockNodeFlags opt_flags = ImGuiDockNodeFlags_None; bool opt_fullscreen = opt_fullscreen_persistant; + static ImGuiDockNodeFlags dockspace_flags = ImGuiDockNodeFlags_None; // We are using the ImGuiWindowFlags_NoDocking flag to make the parent window not dockable into, // because it would be confusing to have two docking targets within each others. @@ -4260,7 +4260,7 @@ void ShowExampleAppDockSpace(bool* p_open) } // When using ImGuiDockNodeFlags_PassthruDockspace, DockSpace() will render our background and handle the pass-thru hole, so we ask Begin() to not render a background. - if (opt_flags & ImGuiDockNodeFlags_PassthruDockspace) + if (dockspace_flags & ImGuiDockNodeFlags_PassthruDockspace) window_flags |= ImGuiWindowFlags_NoBackground; ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); @@ -4275,7 +4275,7 @@ void ShowExampleAppDockSpace(bool* p_open) if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable) { ImGuiID dockspace_id = ImGui::GetID("MyDockspace"); - ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), opt_flags); + ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags); } else { @@ -4290,11 +4290,11 @@ void ShowExampleAppDockSpace(bool* p_open) // which we can't undo at the moment without finer window depth/z control. //ImGui::MenuItem("Fullscreen", NULL, &opt_fullscreen_persistant); - if (ImGui::MenuItem("Flag: NoSplit", "", (opt_flags & ImGuiDockNodeFlags_NoSplit) != 0)) opt_flags ^= ImGuiDockNodeFlags_NoSplit; - if (ImGui::MenuItem("Flag: NoDockingInCentralNode", "", (opt_flags & ImGuiDockNodeFlags_NoDockingInCentralNode) != 0)) opt_flags ^= ImGuiDockNodeFlags_NoDockingInCentralNode; - if (ImGui::MenuItem("Flag: NoResize", "", (opt_flags & ImGuiDockNodeFlags_NoResize) != 0)) opt_flags ^= ImGuiDockNodeFlags_NoResize; - if (ImGui::MenuItem("Flag: PassthruDockspace", "", (opt_flags & ImGuiDockNodeFlags_PassthruDockspace) != 0)) opt_flags ^= ImGuiDockNodeFlags_PassthruDockspace; - if (ImGui::MenuItem("Flag: AutoHideTabBar", "", (opt_flags & ImGuiDockNodeFlags_AutoHideTabBar) != 0)) opt_flags ^= ImGuiDockNodeFlags_AutoHideTabBar; + if (ImGui::MenuItem("Flag: NoSplit", "", (dockspace_flags & ImGuiDockNodeFlags_NoSplit) != 0)) dockspace_flags ^= ImGuiDockNodeFlags_NoSplit; + if (ImGui::MenuItem("Flag: NoDockingInCentralNode", "", (dockspace_flags & ImGuiDockNodeFlags_NoDockingInCentralNode) != 0)) dockspace_flags ^= ImGuiDockNodeFlags_NoDockingInCentralNode; + if (ImGui::MenuItem("Flag: NoResize", "", (dockspace_flags & ImGuiDockNodeFlags_NoResize) != 0)) dockspace_flags ^= ImGuiDockNodeFlags_NoResize; + if (ImGui::MenuItem("Flag: PassthruDockspace", "", (dockspace_flags & ImGuiDockNodeFlags_PassthruDockspace) != 0)) dockspace_flags ^= ImGuiDockNodeFlags_PassthruDockspace; + if (ImGui::MenuItem("Flag: AutoHideTabBar", "", (dockspace_flags & ImGuiDockNodeFlags_AutoHideTabBar) != 0)) dockspace_flags ^= ImGuiDockNodeFlags_AutoHideTabBar; ImGui::Separator(); if (ImGui::MenuItem("Close DockSpace", NULL, false, p_open != NULL)) *p_open = false; From 26646f2450f32b00fb438e38d027dbc98b5dcc07 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 26 Mar 2019 12:41:50 +0100 Subject: [PATCH 465/828] Docking: Wrapping tab bar creation/destroy to make it easier to debug them. --- imgui.cpp | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 1abffcc442e4..404b91ce517c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10791,6 +10791,8 @@ namespace ImGui static void DockNodeUpdate(ImGuiDockNode* node); static void DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* node); static void DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_window); + static void DockNodeAddTabBar(ImGuiDockNode* node); + static void DockNodeRemoveTabBar(ImGuiDockNode* node); static ImGuiID DockNodeUpdateTabListMenu(ImGuiDockNode* node, ImGuiTabBar* tab_bar); static void DockNodeUpdateVisibleFlag(ImGuiDockNode* node); static void DockNodeStartMouseMovingWindow(ImGuiDockNode* node, ImGuiWindow* window); @@ -11231,7 +11233,7 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) // Create tab bar before we call DockNodeMoveWindows (which would attempt to move the old tab-bar, which would lead us to payload tabs wrongly appearing before target tabs!) if (target_node->Windows.Size > 0 && target_node->TabBar == NULL) { - target_node->TabBar = IM_NEW(ImGuiTabBar)(); + DockNodeAddTabBar(target_node); for (int n = 0; n < target_node->Windows.Size; n++) TabBarAddTab(target_node->TabBar, ImGuiTabItemFlags_None, target_node->Windows[n]); } @@ -11418,7 +11420,7 @@ static void ImGui::DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window, b { if (node->TabBar == NULL) { - node->TabBar = IM_NEW(ImGuiTabBar)(); + DockNodeAddTabBar(node); node->TabBar->SelectedTabId = node->TabBar->NextSelectedTabId = node->SelectedTabID; // Add existing windows @@ -11468,10 +11470,7 @@ static void ImGui::DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window TabBarRemoveTab(node->TabBar, window->ID); const int tab_count_threshold_for_tab_bar = node->IsCentralNode ? 1 : 2; if (node->Windows.Size < tab_count_threshold_for_tab_bar) - { - IM_DELETE(node->TabBar); - node->TabBar = NULL; - } + DockNodeRemoveTabBar(node); } if (node->Windows.Size == 0 && !node->IsCentralNode && !node->IsDockSpace() && window->DockId != node->ID) @@ -11541,8 +11540,7 @@ static void ImGui::DockNodeMoveWindows(ImGuiDockNode* dst_node, ImGuiDockNode* s { if (dst_node->TabBar) dst_node->TabBar->SelectedTabId = src_node->TabBar->SelectedTabId; - IM_DELETE(src_node->TabBar); - src_node->TabBar = NULL; + DockNodeRemoveTabBar(src_node); } } @@ -11571,10 +11569,7 @@ static void ImGui::DockNodeHideHostWindow(ImGuiDockNode* node) } if (node->TabBar) - { - IM_DELETE(node->TabBar); - node->TabBar = NULL; - } + DockNodeRemoveTabBar(node); } struct ImGuiDockNodeUpdateScanResults @@ -12042,7 +12037,10 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w ImGuiTabBar* tab_bar = node->TabBar; bool tab_bar_is_recreated = (tab_bar == NULL); // Tab bar are automatically destroyed when a node gets hidden if (tab_bar == NULL) - tab_bar = node->TabBar = IM_NEW(ImGuiTabBar)(); + { + DockNodeAddTabBar(node); + tab_bar = node->TabBar; + } ImGuiID focus_tab_id = 0; node->IsFocused = is_focused; @@ -12209,6 +12207,20 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w } } +static void ImGui::DockNodeAddTabBar(ImGuiDockNode* node) +{ + IM_ASSERT(node->TabBar == NULL); + node->TabBar = IM_NEW(ImGuiTabBar); +} + +static void ImGui::DockNodeRemoveTabBar(ImGuiDockNode* node) +{ + if (node->TabBar == NULL) + return; + IM_DELETE(node->TabBar); + node->TabBar = NULL; +} + static bool DockNodeIsDropAllowedOne(ImGuiWindow* payload, ImGuiWindow* host_window) { if (host_window->DockNodeAsHost && host_window->DockNodeAsHost->IsDockSpace() && payload->BeginOrderWithinContext < host_window->BeginOrderWithinContext) From f20725eada0adbedc5c137cc22a54ce8e11fc929 Mon Sep 17 00:00:00 2001 From: Tom Watson Date: Wed, 13 Mar 2019 10:02:04 -0700 Subject: [PATCH 466/828] Docking: Fixed an issue where windows docked into a node that's part of their dockspace wouldn't recover their order correctly after init. (#2109) (It only worked on floating dock node for the accidental reason that BeginDocked would generally early out on the first frame) --- imgui.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 404b91ce517c..93f229c38cf9 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -13507,8 +13507,9 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) else window->Flags &= ~ImGuiWindowFlags_NoTitleBar; // Clear the NoTitleBar flag in case the user set it: confusingly enough we need a title bar height so we are correctly offset, but it won't be displayed! - // Save new dock order only if the tab bar is active - if (node->TabBar) + // Save new dock order only if the tab bar has been visible once. + // This allows multiple windows to be created in the same frame and have their respective dock orders preserved. + if (node->TabBar && node->TabBar->CurrFrameVisible != -1) window->DockOrder = (short)DockNodeGetTabOrder(window); if ((node->WantCloseAll || node->WantCloseTabID == window->ID) && p_open != NULL) From 87883abd864567614b2c8ceb8fac3ea065d5a7b1 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 26 Mar 2019 14:15:56 +0100 Subject: [PATCH 467/828] Docking: Tweak and silencing PVS studio static analyzer (back to zero warnings among our selected ones). --- imgui.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 93f229c38cf9..d3c915377091 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11937,14 +11937,14 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) if (node->ChildNodes[1]) DockNodeUpdate(node->ChildNodes[1]); - // End host window - if (beginned_into_host_window) - End(); + // Render outer borders last (after the tab bar) + if (node->IsRootNode()) + RenderOuterBorders(host_window); } - // Render outer borders last (after the tab bar) - if (node->IsRootNode() && host_window) - RenderOuterBorders(host_window); + // End host window + if (beginned_into_host_window) //-V1020 + End(); } // Compare TabItem nodes given the last known DockOrder (will persist in .ini file as hint), used to sort tabs when multiple tabs are added on the same frame. From 04a9ce3a1818aa2b9cf1bb068ef33debbba8f15b Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 27 Mar 2019 15:34:02 +0100 Subject: [PATCH 468/828] Docking: Renamed ImGuiDockNodeFlags_PassthruDockspace to ImGuiDockNodeFlags_PassthruCentralNode. + Comments, shallow tweaks. (#2109) --- docs/TODO.txt | 1 - imgui.cpp | 28 ++++++++++++++++------------ imgui.h | 16 ++++++++-------- imgui_demo.cpp | 14 +++++++++----- imgui_internal.h | 12 ++++++------ 5 files changed, 39 insertions(+), 32 deletions(-) diff --git a/docs/TODO.txt b/docs/TODO.txt index 83795ef3683a..d227a92a1d03 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -133,7 +133,6 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - dock: A~ Unreal style document system (requires low-level controls of dockspace serialization fork/copy/delete). this is mostly working but the DockBuilderXXX api are not exposed/finished. - dock: B: when docking outer, perform size locking on neighbors nodes the same way we do it with splitters, so other nodes are not resized. - dock: B~ central node resizing behavior incorrect. - - dock: B~ central node ID retrieval API? - dock: B: changing title font/style per-window is not supported as dock nodes are created in NewFrame. - dock: B- dock node inside its own viewports creates 1 temporary viewport per window on startup before ditching them (doesn't affect the user nor request platform windows to be created, but unnecessary) - dock: B- resize sibling locking behavior may be less desirable if we merged same-axis sibling in a same node level? diff --git a/imgui.cpp b/imgui.cpp index d3c915377091..a9fed610a74b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2605,7 +2605,7 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) DockNode = DockNodeAsHost = NULL; DockId = 0; - DockTabItemStatusFlags = 0; + DockTabItemStatusFlags = ImGuiItemStatusFlags_None; DockOrder = -1; DockIsActive = DockTabIsVisible = DockTabWantClose = false; } @@ -11343,7 +11343,7 @@ void ImGui::DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node) ImGuiDockNode::ImGuiDockNode(ImGuiID id) { ID = id; - Flags = 0; + Flags = ImGuiDockNodeFlags_None; ParentNode = ChildNodes[0] = ChildNodes[1] = NULL; TabBar = NULL; SplitAxis = ImGuiAxis_None; @@ -11613,7 +11613,7 @@ static void ImGui::DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* nod IM_ASSERT(node->ParentNode == NULL || node->ParentNode->ChildNodes[0] == node || node->ParentNode->ChildNodes[1] == node); // Inherit most flags - ImGuiDockNodeFlags flags_to_inherit = ~0 & ~ImGuiDockNodeFlags_Dockspace; + ImGuiDockNodeFlags flags_to_inherit = ~ImGuiDockNodeFlags_Dockspace; if (node->ParentNode) node->Flags = node->ParentNode->Flags & flags_to_inherit; @@ -11854,9 +11854,9 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) if (g.NavWindow && g.NavWindow->RootWindowDockStop->DockNode && g.NavWindow->RootWindowDockStop->ParentWindow == host_window) node->LastFocusedNodeID = g.NavWindow->RootWindowDockStop->DockNode->ID; - // We need to draw a background if requested by ImGuiDockNodeFlags_PassthruDockspace, but we will only know the correct pos/size after + // We need to draw a background at the root level if requested by ImGuiDockNodeFlags_PassthruCentralNode, but we will only know the correct pos/size after // processing the resizing splitters. So we are using the DrawList channel splitting facility to submit drawing primitives out of order! - const bool render_dockspace_bg = node->IsRootNode() && host_window && (node->Flags & ImGuiDockNodeFlags_PassthruDockspace) != 0; + const bool render_dockspace_bg = node->IsRootNode() && host_window && (node->Flags & ImGuiDockNodeFlags_PassthruCentralNode) != 0; if (render_dockspace_bg) { host_window->DrawList->ChannelsSplit(2); @@ -11865,7 +11865,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) // Register a hit-test hole in the window unless we are currently dragging a window that is compatible with our dockspace const ImGuiDockNode* central_node = node->CentralNode; - const bool central_node_hole = node->IsRootNode() && host_window && (node->Flags & ImGuiDockNodeFlags_PassthruDockspace) != 0 && central_node != NULL && central_node->IsEmpty(); + const bool central_node_hole = node->IsRootNode() && host_window && (node->Flags & ImGuiDockNodeFlags_PassthruCentralNode) != 0 && central_node != NULL && central_node->IsEmpty(); bool central_node_hole_register_hit_test_hole = central_node_hole; if (central_node_hole) if (const ImGuiPayload* payload = ImGui::GetDragDropPayload()) @@ -11892,10 +11892,10 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) } // Draw empty node background (currently can only be the Central Node) - if (host_window && node->IsEmpty() && node->IsVisible && !(node->Flags & ImGuiDockNodeFlags_PassthruDockspace)) + if (host_window && node->IsEmpty() && node->IsVisible && !(node->Flags & ImGuiDockNodeFlags_PassthruCentralNode)) host_window->DrawList->AddRectFilled(node->Pos, node->Pos + node->Size, GetColorU32(ImGuiCol_DockingEmptyBg)); - // Draw whole dockspace background if ImGuiDockNodeFlags_PassthruDockspace if set. + // Draw whole dockspace background if ImGuiDockNodeFlags_PassthruCentralNode if set. if (render_dockspace_bg && node->IsVisible) { host_window->DrawList->ChannelsSetCurrent(0); @@ -12566,6 +12566,7 @@ void ImGui::DockNodeTreeSplit(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImG DockNodeMoveWindows(parent_node->ChildNodes[split_inheritor_child_idx], parent_node); DockNodeTreeUpdatePosSize(parent_node, parent_node->Pos, parent_node->Size); + // Flags transfer child_inheritor->IsCentralNode = parent_node->IsCentralNode; child_inheritor->IsHiddenTabBar = parent_node->IsHiddenTabBar; parent_node->IsCentralNode = false; @@ -12599,9 +12600,11 @@ void ImGui::DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImG DockNodeApplyPosSizeToWindows(parent_node); parent_node->AutorityForPos = parent_node->AutorityForSize = parent_node->AutorityForViewport = ImGuiDataAutority_Auto; parent_node->VisibleWindow = merge_lead_child->VisibleWindow; + parent_node->SizeRef = backup_last_explicit_size; + + // Flags transfer parent_node->IsCentralNode = (child_0 && child_0->IsCentralNode) || (child_1 && child_1->IsCentralNode); parent_node->IsHiddenTabBar = merge_lead_child->IsHiddenTabBar; - parent_node->SizeRef = backup_last_explicit_size; if (child_0) { @@ -12961,7 +12964,7 @@ void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags fla End(); } -// Tips: Use with ImGuiDockNodeFlags_PassthruDockspace! +// Tips: Use with ImGuiDockNodeFlags_PassthruCentralNode! // The limitation with this call is that your window won't have a menu bar. // Even though we could pass window flags, it would also require the user to be able to call BeginMenuBar() somehow meaning we can't Begin/End in a single function. // So if you want a menu bar you need to repeat this code manually ourselves. As with advanced other Docking API, we may change this function signature. @@ -12977,7 +12980,7 @@ ImGuiID ImGui::DockSpaceOverViewport(ImGuiViewport* viewport, ImGuiDockNodeFlags ImGuiWindowFlags host_window_flags = 0; host_window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDocking; host_window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus; - if (dockspace_flags & ImGuiDockNodeFlags_PassthruDockspace) + if (dockspace_flags & ImGuiDockNodeFlags_PassthruCentralNode) host_window_flags |= ImGuiWindowFlags_NoBackground; char label[32]; @@ -14186,6 +14189,7 @@ void ImGui::ShowDockingDebug() { static void NodeDockNode(ImGuiDockNode* node, const char* label) { + ImGuiContext& g = *GImGui; ImGui::SetNextTreeNodeOpen(true, ImGuiCond_Once); bool open; if (node->Windows.Size > 0) @@ -14203,7 +14207,7 @@ void ImGui::ShowDockingDebug() ImGui::BulletText("LastFocusedNodeID: 0x%08X", node->LastFocusedNodeID); ImGui::BulletText("Flags 0x%02X%s%s%s%s", node->Flags, node->IsDockSpace() ? ", IsDockSpace" : "", node->IsCentralNode ? ", IsCentralNode" : "", - (GImGui->FrameCount - node->LastFrameAlive < 2) ? ", IsAlive" : "", (GImGui->FrameCount - node->LastFrameActive < 2) ? ", IsActive" : ""); + (g.FrameCount - node->LastFrameAlive < 2) ? ", IsAlive" : "", (g.FrameCount - node->LastFrameActive < 2) ? ", IsActive" : ""); if (node->ChildNodes[0]) NodeDockNode(node->ChildNodes[0], "Child[0]"); if (node->ChildNodes[1]) diff --git a/imgui.h b/imgui.h index a7aff0badd99..123d395d2fc8 100644 --- a/imgui.h +++ b/imgui.h @@ -591,7 +591,8 @@ namespace ImGui // Docking // [BETA API] Enable with io.ConfigFlags |= ImGuiConfigFlags_DockingEnable. // Note: you DO NOT need to call DockSpace() to use most Docking facilities! - // To dock windows: hold SHIFT anywhere while moving windows (if io.ConfigDockingWithShift == true) or drag windows from their title bar (if io.ConfigDockingWithShift = false) + // To dock windows: if io.ConfigDockingWithShift == false: drag window from their title bar. + // To dock windows: if io.ConfigDockingWithShift == true: hold SHIFT anywhere while moving windows. // Use DockSpace() to create an explicit dock node _within_ an existing window. See Docking demo for details. IMGUI_API void DockSpace(ImGuiID id, const ImVec2& size = ImVec2(0, 0), ImGuiDockNodeFlags flags = 0, const ImGuiWindowClass* window_class = NULL); IMGUI_API ImGuiID DockSpaceOverViewport(ImGuiViewport* viewport = NULL, ImGuiDockNodeFlags flags = 0, const ImGuiWindowClass* window_class = NULL); @@ -882,13 +883,12 @@ enum ImGuiDockNodeFlags_ { ImGuiDockNodeFlags_None = 0, ImGuiDockNodeFlags_KeepAliveOnly = 1 << 0, // Don't display the dockspace node but keep it alive. Windows docked into this dockspace node won't be undocked. - ImGuiDockNodeFlags_NoSplit = 1 << 1, // Disable splitting the node into smaller nodes. Useful e.g. when embedding dockspaces into a main root one (the root one may have splitting disabled to reduce confusion). Note: when turned off, existing splits will be preserved. - //ImGuiDockNodeFlags_NoCentralNode = 1 << 2, // Disable Central Node (the node which can stay empty) - ImGuiDockNodeFlags_NoDockingInCentralNode = 1 << 3, // Disable docking inside the Central Node, which will be always kept empty. Note: when turned off, existing docked nodes will be preserved. - //ImGuiDockNodeFlags_NoLayoutChanges = 1 << 4, // Disable adding/removing nodes interactively. Useful with programatically setup dockspaces. - ImGuiDockNodeFlags_NoResize = 1 << 5, // Disable resizing child nodes using the splitter/separators. Useful with programatically setup dockspaces. - ImGuiDockNodeFlags_PassthruDockspace = 1 << 6, // Enable passthru dockspace: 1) DockSpace() will render a ImGuiCol_WindowBg background covering everything excepted the Central Node when empty. Meaning the host window should probably use SetNextWindowBgAlpha(0.0f) prior to Begin() when using this. 2) When Central Node is empty: let inputs pass-through + won't display a DockingEmptyBg background. - ImGuiDockNodeFlags_AutoHideTabBar = 1 << 7 // Tab bar will automatically hide when there is a single window in the dock node. + //ImGuiDockNodeFlags_NoCentralNode = 1 << 1, // Disable Central Node (the node which can stay empty) + ImGuiDockNodeFlags_NoDockingInCentralNode = 1 << 2, // Disable docking inside the Central Node, which will be always kept empty. Note: when turned off, existing docked nodes will be preserved. + ImGuiDockNodeFlags_NoSplit = 1 << 3, // Disable splitting the node into smaller nodes. Useful e.g. when embedding dockspaces into a main root one (the root one may have splitting disabled to reduce confusion). Note: when turned off, existing splits will be preserved. + ImGuiDockNodeFlags_NoResize = 1 << 4, // Disable resizing child nodes using the splitter/separators. Useful with programatically setup dockspaces. + ImGuiDockNodeFlags_PassthruCentralNode = 1 << 5, // Enable passthru dockspace: 1) DockSpace() will render a ImGuiCol_WindowBg background covering everything excepted the Central Node when empty. Meaning the host window should probably use SetNextWindowBgAlpha(0.0f) prior to Begin() when using this. 2) When Central Node is empty: let inputs pass-through + won't display a DockingEmptyBg background. See demo for details. + ImGuiDockNodeFlags_AutoHideTabBar = 1 << 6 // Tab bar will automatically hide when there is a single window in the dock node. }; // Flags for ImGui::IsWindowFocused() diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 3de9826fa3e4..130d6d52048a 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -4259,8 +4259,8 @@ void ShowExampleAppDockSpace(bool* p_open) window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus; } - // When using ImGuiDockNodeFlags_PassthruDockspace, DockSpace() will render our background and handle the pass-thru hole, so we ask Begin() to not render a background. - if (dockspace_flags & ImGuiDockNodeFlags_PassthruDockspace) + // When using ImGuiDockNodeFlags_PassthruCentralNode, DockSpace() will render our background and handle the pass-thru hole, so we ask Begin() to not render a background. + if (dockspace_flags & ImGuiDockNodeFlags_PassthruCentralNode) window_flags |= ImGuiWindowFlags_NoBackground; ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); @@ -4291,9 +4291,9 @@ void ShowExampleAppDockSpace(bool* p_open) //ImGui::MenuItem("Fullscreen", NULL, &opt_fullscreen_persistant); if (ImGui::MenuItem("Flag: NoSplit", "", (dockspace_flags & ImGuiDockNodeFlags_NoSplit) != 0)) dockspace_flags ^= ImGuiDockNodeFlags_NoSplit; - if (ImGui::MenuItem("Flag: NoDockingInCentralNode", "", (dockspace_flags & ImGuiDockNodeFlags_NoDockingInCentralNode) != 0)) dockspace_flags ^= ImGuiDockNodeFlags_NoDockingInCentralNode; if (ImGui::MenuItem("Flag: NoResize", "", (dockspace_flags & ImGuiDockNodeFlags_NoResize) != 0)) dockspace_flags ^= ImGuiDockNodeFlags_NoResize; - if (ImGui::MenuItem("Flag: PassthruDockspace", "", (dockspace_flags & ImGuiDockNodeFlags_PassthruDockspace) != 0)) dockspace_flags ^= ImGuiDockNodeFlags_PassthruDockspace; + if (ImGui::MenuItem("Flag: NoDockingInCentralNode", "", (dockspace_flags & ImGuiDockNodeFlags_NoDockingInCentralNode) != 0)) dockspace_flags ^= ImGuiDockNodeFlags_NoDockingInCentralNode; + if (ImGui::MenuItem("Flag: PassthruCentralNode", "", (dockspace_flags & ImGuiDockNodeFlags_PassthruCentralNode) != 0)) dockspace_flags ^= ImGuiDockNodeFlags_PassthruCentralNode; if (ImGui::MenuItem("Flag: AutoHideTabBar", "", (dockspace_flags & ImGuiDockNodeFlags_AutoHideTabBar) != 0)) dockspace_flags ^= ImGuiDockNodeFlags_AutoHideTabBar; ImGui::Separator(); if (ImGui::MenuItem("Close DockSpace", NULL, false, p_open != NULL)) @@ -4301,7 +4301,11 @@ void ShowExampleAppDockSpace(bool* p_open) ImGui::EndMenu(); } HelpMarker( - "You can _always_ dock _any_ window into another by holding the SHIFT key while moving a window. Try it now!" "\n" + "When docking is enabled, you can ALWAYS dock MOST window into another! Try it now!" "\n\n" + " > if io.ConfigDockingWithShift==false (default):" "\n" + " drag windows from title bar to dock" "\n" + " > if io.ConfigDockingWithShift==true:" "\n" + " drag windows from anywhere and hold Shift to dock" "\n\n" "This demo app has nothing to do with it!" "\n\n" "This demo app only demonstrate the use of ImGui::DockSpace() which allows you to manually create a docking node _within_ another window. This is useful so you can decorate your main application window (e.g. with a menu bar)." "\n\n" "ImGui::DockSpace() comes with one hard constraint: it needs to be submitted _before_ any window which may be docked into it. Therefore, if you use a dock spot as the central point of your application, you'll probably want it to be part of the very first window you are submitting to imgui every frame." "\n\n" diff --git a/imgui_internal.h b/imgui_internal.h index 19128ad5fc4f..3c2d87975d4e 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -903,12 +903,12 @@ struct ImGuiDockNode ImGuiDockNode(ImGuiID id); ~ImGuiDockNode(); - bool IsRootNode() const { return ParentNode == NULL; } - bool IsDockSpace() const { return (Flags & ImGuiDockNodeFlags_Dockspace) != 0; } - bool IsSplitNode() const { return ChildNodes[0] != NULL; } - bool IsLeafNode() const { return ChildNodes[0] == NULL; } - bool IsEmpty() const { return ChildNodes[0] == NULL && Windows.Size == 0; } - ImRect Rect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } + bool IsRootNode() const { return ParentNode == NULL; } + bool IsDockSpace() const { return (Flags & ImGuiDockNodeFlags_Dockspace) != 0; } + bool IsSplitNode() const { return ChildNodes[0] != NULL; } + bool IsLeafNode() const { return ChildNodes[0] == NULL; } + bool IsEmpty() const { return ChildNodes[0] == NULL && Windows.Size == 0; } + ImRect Rect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } }; //----------------------------------------------------------------------------- From 8d4b5fef1d2efa88812d79515dd5b1c799379188 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 27 Mar 2019 17:25:39 +0100 Subject: [PATCH 469/828] Renamed ImGuiDockNodeFlags_Dockspace to ImGuiDockNodeFlags_DockSpace for consistency. DockBuilderCopyDockspace() to DockBuilderCopyDockSpace(). Made casing consistent elsewhere. (#2109) --- imgui.cpp | 20 ++++++++++---------- imgui_demo.cpp | 17 +++++++---------- imgui_internal.h | 8 ++++---- 3 files changed, 21 insertions(+), 24 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index a9fed610a74b..7c30b218363e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11097,7 +11097,7 @@ static void ImGui::DockContextBuildNodesFromSettings(ImGuiContext* ctx, ImGuiDoc node->SelectedTabID = settings->SelectedTabID; node->SplitAxis = settings->SplitAxis; if (settings->IsDockSpace) - node->Flags |= ImGuiDockNodeFlags_Dockspace; + node->Flags |= ImGuiDockNodeFlags_DockSpace; node->IsCentralNode = settings->IsCentralNode != 0; node->IsHiddenTabBar = settings->IsHiddenTabBar != 0; @@ -11613,7 +11613,7 @@ static void ImGui::DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* nod IM_ASSERT(node->ParentNode == NULL || node->ParentNode->ChildNodes[0] == node || node->ParentNode->ChildNodes[1] == node); // Inherit most flags - ImGuiDockNodeFlags flags_to_inherit = ~ImGuiDockNodeFlags_Dockspace; + ImGuiDockNodeFlags flags_to_inherit = ~ImGuiDockNodeFlags_DockSpace; if (node->ParentNode) node->Flags = node->ParentNode->Flags & flags_to_inherit; @@ -12897,7 +12897,7 @@ void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags fla if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable)) return; - IM_ASSERT((flags & ImGuiDockNodeFlags_Dockspace) == 0); + IM_ASSERT((flags & ImGuiDockNodeFlags_DockSpace) == 0); ImGuiDockNode* node = DockContextFindNodeByID(ctx, id); if (!node) { @@ -12907,15 +12907,15 @@ void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags fla node->Flags = flags; node->WindowClass = window_class ? *window_class : ImGuiWindowClass(); - // When a Dockspace transitioned form implicit to explicit this may be called a second time + // When a DockSpace transitioned form implicit to explicit this may be called a second time // It is possible that the node has already been claimed by a docked window which appeared before the DockSpace() node, so we overwrite IsDockSpace again. if (node->LastFrameActive == g.FrameCount && !(flags & ImGuiDockNodeFlags_KeepAliveOnly)) { IM_ASSERT(node->IsDockSpace() == false && "Cannot call DockSpace() twice a frame with the same ID"); - node->Flags |= ImGuiDockNodeFlags_Dockspace; + node->Flags |= ImGuiDockNodeFlags_DockSpace; return; } - node->Flags |= ImGuiDockNodeFlags_Dockspace; + node->Flags |= ImGuiDockNodeFlags_DockSpace; // Keep alive mode, this is allow windows docked into this node so stay docked even if they are not visible if (flags & ImGuiDockNodeFlags_KeepAliveOnly) @@ -12984,7 +12984,7 @@ ImGuiID ImGui::DockSpaceOverViewport(ImGuiViewport* viewport, ImGuiDockNodeFlags host_window_flags |= ImGuiWindowFlags_NoBackground; char label[32]; - ImFormatString(label, IM_ARRAYSIZE(label), "DockspaceViewport_%08X", viewport->ID); + ImFormatString(label, IM_ARRAYSIZE(label), "DockSpaceViewport_%08X", viewport->ID); PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); @@ -12992,7 +12992,7 @@ ImGuiID ImGui::DockSpaceOverViewport(ImGuiViewport* viewport, ImGuiDockNodeFlags Begin(label, NULL, host_window_flags); PopStyleVar(3); - ImGuiID dockspace_id = GetID("Dockspace"); + ImGuiID dockspace_id = GetID("DockSpace"); DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags, window_class); End(); @@ -13059,7 +13059,7 @@ ImGuiID ImGui::DockBuilderAddNode(ImGuiID id, ImGuiDockNodeFlags flags) { ImGuiContext* ctx = GImGui; ImGuiDockNode* node = NULL; - if (flags & ImGuiDockNodeFlags_Dockspace) + if (flags & ImGuiDockNodeFlags_DockSpace) { DockSpace(id, ImVec2(0, 0), flags | ImGuiDockNodeFlags_KeepAliveOnly); node = DockContextFindNodeByID(ctx, id); @@ -13294,7 +13294,7 @@ void ImGui::DockBuilderCopyWindowSettings(const char* src_name, const char* dst_ } // FIXME: Will probably want to change this signature, in particular how the window remapping pairs are passed. -void ImGui::DockBuilderCopyDockspace(ImGuiID src_dockspace_id, ImGuiID dst_dockspace_id, ImVector* in_window_remap_pairs) +void ImGui::DockBuilderCopyDockSpace(ImGuiID src_dockspace_id, ImGuiID dst_dockspace_id, ImVector* in_window_remap_pairs) { IM_ASSERT(src_dockspace_id != 0); IM_ASSERT(dst_dockspace_id != 0); diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 130d6d52048a..d2498b73ad63 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -4270,11 +4270,11 @@ void ShowExampleAppDockSpace(bool* p_open) if (opt_fullscreen) ImGui::PopStyleVar(2); - // Dockspace + // DockSpace ImGuiIO& io = ImGui::GetIO(); if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable) { - ImGuiID dockspace_id = ImGui::GetID("MyDockspace"); + ImGuiID dockspace_id = ImGui::GetID("MyDockSpace"); ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags); } else @@ -4420,13 +4420,13 @@ void ShowExampleAppDocuments(bool* p_open) { Target_None, Target_Tab, // Create documents as local tab into a local tab bar - Target_DockspaceAndWindow // Create documents as regular windows, and create an embedded dockspace + Target_DockSpaceAndWindow // Create documents as regular windows, and create an embedded dockspace }; static Target opt_target = Target_Tab; static bool opt_reorderable = true; static ImGuiTabBarFlags opt_fitting_flags = ImGuiTabBarFlags_FittingPolicyDefault_; - // When (opt_target == Target_DockspaceAndWindow) there is the possibily that one of our child Document window (e.g. "Eggplant") + // When (opt_target == Target_DockSpaceAndWindow) there is the possibily that one of our child Document window (e.g. "Eggplant") // that we emit gets docked into the same spot as the parent window ("Example: Documents"). // This would create a problematic feedback loop because selecting the "Eggplant" tab would make the "Example: Documents" tab // not visible, which in turn would stop submitting the "Eggplant" window. @@ -4434,7 +4434,7 @@ void ShowExampleAppDocuments(bool* p_open) // Another solution may be to make the "Example: Documents" window use the ImGuiWindowFlags_NoDocking. bool window_contents_visible = ImGui::Begin("Example: Documents", p_open, ImGuiWindowFlags_MenuBar); - if (!window_contents_visible && opt_target != Target_DockspaceAndWindow) + if (!window_contents_visible && opt_target != Target_DockSpaceAndWindow) { ImGui::End(); return; @@ -4486,7 +4486,7 @@ void ShowExampleAppDocuments(bool* p_open) ImGui::PopItemWidth(); bool redock_all = false; if (opt_target == Target_Tab) { ImGui::SameLine(); ImGui::Checkbox("Reorderable Tabs", &opt_reorderable); } - if (opt_target == Target_DockspaceAndWindow) { ImGui::SameLine(); redock_all = ImGui::Button("Redock all"); } + if (opt_target == Target_DockSpaceAndWindow) { ImGui::SameLine(); redock_all = ImGui::Button("Redock all"); } ImGui::Separator(); @@ -4531,7 +4531,7 @@ void ShowExampleAppDocuments(bool* p_open) ImGui::EndTabBar(); } } - else if (opt_target == Target_DockspaceAndWindow) + else if (opt_target == Target_DockSpaceAndWindow) { if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_DockingEnable) { @@ -4548,9 +4548,6 @@ void ShowExampleAppDocuments(bool* p_open) if (!doc->Open) continue; - // FIXME-DOCK: SetNextWindowDock() - //ImGuiID default_dock_id = GetDockspaceRootDocumentDockID(); - //ImGuiID default_dock_id = GetDockspacePreferedDocumentDockID(); ImGui::SetNextWindowDockID(dockspace_id, redock_all ? ImGuiCond_Always : ImGuiCond_FirstUseEver); ImGuiWindowFlags window_flags = (doc->Dirty ? ImGuiWindowFlags_UnsavedDocument : 0); bool visible = ImGui::Begin(doc->Name, &doc->Open, window_flags); diff --git a/imgui_internal.h b/imgui_internal.h index 3c2d87975d4e..1bf90ec5f203 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -851,7 +851,7 @@ struct ImGuiTabBarRef enum ImGuiDockNodeFlagsPrivate_ { - ImGuiDockNodeFlags_Dockspace = 1 << 10 + ImGuiDockNodeFlags_DockSpace = 1 << 10 }; enum ImGuiDataAutority_ @@ -904,7 +904,7 @@ struct ImGuiDockNode ImGuiDockNode(ImGuiID id); ~ImGuiDockNode(); bool IsRootNode() const { return ParentNode == NULL; } - bool IsDockSpace() const { return (Flags & ImGuiDockNodeFlags_Dockspace) != 0; } + bool IsDockSpace() const { return (Flags & ImGuiDockNodeFlags_DockSpace) != 0; } bool IsSplitNode() const { return ChildNodes[0] != NULL; } bool IsLeafNode() const { return ChildNodes[0] == NULL; } bool IsEmpty() const { return ChildNodes[0] == NULL && Windows.Size == 0; } @@ -1671,14 +1671,14 @@ namespace ImGui IMGUI_API void DockBuilderDockWindow(const char* window_name, ImGuiID node_id); IMGUI_API ImGuiDockNode*DockBuilderGetNode(ImGuiID node_id); // Warning: DO NOT HOLD ON ImGuiDockNode* pointer, will be invalided by any split/merge/remove operation. inline ImGuiDockNode* DockBuilderGetCentralNode(ImGuiID node_id) { ImGuiDockNode* node = DockBuilderGetNode(node_id); if (!node) return NULL; return DockNodeGetRootNode(node)->CentralNode; } - IMGUI_API ImGuiID DockBuilderAddNode(ImGuiID node_id, ImGuiDockNodeFlags flags = 0); // Use (flags == ImGuiDockNodeFlags_Dockspace) to create a dockspace, otherwise it'll create a floating node. + IMGUI_API ImGuiID DockBuilderAddNode(ImGuiID node_id, ImGuiDockNodeFlags flags = 0); // Use (flags == ImGuiDockNodeFlags_DockSpace) to create a dockspace, otherwise it'll create a floating node. IMGUI_API void DockBuilderRemoveNode(ImGuiID node_id); // Remove node and all its child, undock all windows IMGUI_API void DockBuilderRemoveNodeDockedWindows(ImGuiID node_id, bool clear_persistent_docking_references = true); IMGUI_API void DockBuilderRemoveNodeChildNodes(ImGuiID node_id); // Remove all split/hierarchy. All remaining docked windows will be re-docked to the root. IMGUI_API void DockBuilderSetNodePos(ImGuiID node_id, ImVec2 pos); IMGUI_API void DockBuilderSetNodeSize(ImGuiID node_id, ImVec2 size); IMGUI_API ImGuiID DockBuilderSplitNode(ImGuiID node_id, ImGuiDir split_dir, float size_ratio_for_node_at_dir, ImGuiID* out_id_dir, ImGuiID* out_id_other); - IMGUI_API void DockBuilderCopyDockspace(ImGuiID src_dockspace_id, ImGuiID dst_dockspace_id, ImVector* in_window_remap_pairs); + IMGUI_API void DockBuilderCopyDockSpace(ImGuiID src_dockspace_id, ImGuiID dst_dockspace_id, ImVector* in_window_remap_pairs); IMGUI_API void DockBuilderCopyNode(ImGuiID src_node_id, ImGuiID dst_node_id, ImVector* out_node_remap_pairs); IMGUI_API void DockBuilderCopyWindowSettings(const char* src_name, const char* dst_name); IMGUI_API void DockBuilderFinish(ImGuiID node_id); From 75e3793f4db622b9a75d4973d05d0b69d035cb55 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 27 Mar 2019 17:30:32 +0100 Subject: [PATCH 470/828] Docking: Fix DockBuilderAddNode() not storing flags when creating floating node. --- imgui.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/imgui.cpp b/imgui.cpp index 7c30b218363e..a149cc49de92 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -13070,6 +13070,7 @@ ImGuiID ImGui::DockBuilderAddNode(ImGuiID id, ImGuiDockNodeFlags flags) node = DockContextFindNodeByID(ctx, id); if (!node) node = DockContextAddNode(ctx, id); + node->Flags = flags; } node->LastFrameAlive = ctx->FrameCount; // Set this otherwise BeginDocked will undock during the same frame. return node->ID; From fd5859ed0453ace718cb210ccd26439c2723dfa6 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 27 Mar 2019 18:48:07 +0100 Subject: [PATCH 471/828] Docking: Separating SharedFlags vs LocalFlags in dock node so settings can be applied to individual nodes. Made _NoResize logic on single node applies as expected. (#2423, #2109) --- imgui.cpp | 63 +++++++++++++++++++++++++++++------------------- imgui.h | 17 +++++++------ imgui_internal.h | 10 +++++--- 3 files changed, 54 insertions(+), 36 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index a149cc49de92..58d91dba15b1 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11097,7 +11097,7 @@ static void ImGui::DockContextBuildNodesFromSettings(ImGuiContext* ctx, ImGuiDoc node->SelectedTabID = settings->SelectedTabID; node->SplitAxis = settings->SplitAxis; if (settings->IsDockSpace) - node->Flags |= ImGuiDockNodeFlags_DockSpace; + node->LocalFlags |= ImGuiDockNodeFlags_DockSpace; node->IsCentralNode = settings->IsCentralNode != 0; node->IsHiddenTabBar = settings->IsHiddenTabBar != 0; @@ -11343,7 +11343,7 @@ void ImGui::DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node) ImGuiDockNode::ImGuiDockNode(ImGuiID id) { ID = id; - Flags = ImGuiDockNodeFlags_None; + SharedFlags = LocalFlags = ImGuiDockNodeFlags_None; ParentNode = ChildNodes[0] = ChildNodes[1] = NULL; TabBar = NULL; SplitAxis = ImGuiAxis_None; @@ -11613,9 +11613,8 @@ static void ImGui::DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* nod IM_ASSERT(node->ParentNode == NULL || node->ParentNode->ChildNodes[0] == node || node->ParentNode->ChildNodes[1] == node); // Inherit most flags - ImGuiDockNodeFlags flags_to_inherit = ~ImGuiDockNodeFlags_DockSpace; if (node->ParentNode) - node->Flags = node->ParentNode->Flags & flags_to_inherit; + node->SharedFlags = node->ParentNode->SharedFlags & ImGuiDockNodeFlags_SharedFlagsInheritMask_; // Recurse into children // There is the possibility that one of our child becoming empty will delete itself and moving its sibling contents into 'node'. @@ -11651,7 +11650,8 @@ static void ImGui::DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* nod } // Auto-hide tab bar option - if (node->WantHiddenTabBarUpdate && node->Windows.Size == 1 && (node->Flags & ImGuiDockNodeFlags_AutoHideTabBar) && !node->IsHiddenTabBar) + ImGuiDockNodeFlags node_flags = node->GetMergedFlags(); + if (node->WantHiddenTabBarUpdate && node->Windows.Size == 1 && (node_flags & ImGuiDockNodeFlags_AutoHideTabBar) && !node->IsHiddenTabBar) node->WantHiddenTabBarToggle = true; node->WantHiddenTabBarUpdate = false; @@ -11856,7 +11856,8 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) // We need to draw a background at the root level if requested by ImGuiDockNodeFlags_PassthruCentralNode, but we will only know the correct pos/size after // processing the resizing splitters. So we are using the DrawList channel splitting facility to submit drawing primitives out of order! - const bool render_dockspace_bg = node->IsRootNode() && host_window && (node->Flags & ImGuiDockNodeFlags_PassthruCentralNode) != 0; + const ImGuiDockNodeFlags node_flags = node->GetMergedFlags(); + const bool render_dockspace_bg = node->IsRootNode() && host_window && (node_flags & ImGuiDockNodeFlags_PassthruCentralNode) != 0; if (render_dockspace_bg) { host_window->DrawList->ChannelsSplit(2); @@ -11865,7 +11866,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) // Register a hit-test hole in the window unless we are currently dragging a window that is compatible with our dockspace const ImGuiDockNode* central_node = node->CentralNode; - const bool central_node_hole = node->IsRootNode() && host_window && (node->Flags & ImGuiDockNodeFlags_PassthruCentralNode) != 0 && central_node != NULL && central_node->IsEmpty(); + const bool central_node_hole = node->IsRootNode() && host_window && (node_flags & ImGuiDockNodeFlags_PassthruCentralNode) != 0 && central_node != NULL && central_node->IsEmpty(); bool central_node_hole_register_hit_test_hole = central_node_hole; if (central_node_hole) if (const ImGuiPayload* payload = ImGui::GetDragDropPayload()) @@ -11892,7 +11893,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) } // Draw empty node background (currently can only be the Central Node) - if (host_window && node->IsEmpty() && node->IsVisible && !(node->Flags & ImGuiDockNodeFlags_PassthruCentralNode)) + if (host_window && node->IsEmpty() && node->IsVisible && !(node_flags & ImGuiDockNodeFlags_PassthruCentralNode)) host_window->DrawList->AddRectFilled(node->Pos, node->Pos + node->Size, GetColorU32(ImGuiCol_DockingEmptyBg)); // Draw whole dockspace background if ImGuiDockNodeFlags_PassthruCentralNode if set. @@ -12369,15 +12370,16 @@ static bool ImGui::DockNodePreviewDockCalc(ImGuiWindow* host_window, ImGuiDockNo data->FutureNode.Size = host_node ? ref_node_for_rect->Size : host_window->Size; // Figure out here we are allowed to dock + ImGuiDockNodeFlags host_node_flags = host_node ? host_node->GetMergedFlags() : 0; const bool src_is_visibly_splitted = root_payload->DockNodeAsHost && root_payload->DockNodeAsHost->IsSplitNode() && (root_payload->DockNodeAsHost->OnlyNodeWithWindows == NULL); data->IsCenterAvailable = !is_outer_docking; if (src_is_visibly_splitted && (!host_node || !host_node->IsEmpty())) data->IsCenterAvailable = false; - if (host_node && (host_node->Flags & ImGuiDockNodeFlags_NoDockingInCentralNode) && host_node->IsCentralNode) + if (host_node && (host_node_flags & ImGuiDockNodeFlags_NoDockingInCentralNode) && host_node->IsCentralNode) data->IsCenterAvailable = false; data->IsSidesAvailable = true; - if ((host_node && (host_node->Flags & ImGuiDockNodeFlags_NoSplit)) || g.IO.ConfigDockingNoSplit) + if ((host_node && (host_node_flags & ImGuiDockNodeFlags_NoSplit)) || g.IO.ConfigDockingNoSplit) data->IsSidesAvailable = false; if (!is_outer_docking && host_node && host_node->ParentNode == NULL && host_node->IsCentralNode) data->IsSidesAvailable = false; @@ -12528,7 +12530,7 @@ static void ImGui::DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDock } // Stop after ImGuiDir_None - if ((host_node && (host_node->Flags & ImGuiDockNodeFlags_NoSplit)) || g.IO.ConfigDockingNoSplit) + if ((host_node && (host_node->GetMergedFlags() & ImGuiDockNodeFlags_NoSplit)) || g.IO.ConfigDockingNoSplit) return; } } @@ -12567,8 +12569,12 @@ void ImGui::DockNodeTreeSplit(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImG DockNodeTreeUpdatePosSize(parent_node, parent_node->Pos, parent_node->Size); // Flags transfer + child_0->SharedFlags = parent_node->SharedFlags & ImGuiDockNodeFlags_SharedFlagsInheritMask_; + child_1->SharedFlags = parent_node->SharedFlags & ImGuiDockNodeFlags_SharedFlagsInheritMask_; + child_inheritor->LocalFlags = parent_node->LocalFlags & ImGuiDockNodeFlags_LocalFlagsTransferMask_; child_inheritor->IsCentralNode = parent_node->IsCentralNode; child_inheritor->IsHiddenTabBar = parent_node->IsHiddenTabBar; + parent_node->LocalFlags &= ~ImGuiDockNodeFlags_LocalFlagsTransferMask_; parent_node->IsCentralNode = false; } @@ -12603,6 +12609,7 @@ void ImGui::DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImG parent_node->SizeRef = backup_last_explicit_size; // Flags transfer + parent_node->LocalFlags = ((child_0 ? child_0->LocalFlags : 0) | (child_1 ? child_1->LocalFlags : 0)) & ImGuiDockNodeFlags_LocalFlagsTransferMask_; parent_node->IsCentralNode = (child_0 && child_0->IsCentralNode) || (child_1 && child_1->IsCentralNode); parent_node->IsHiddenTabBar = merge_lead_child->IsHiddenTabBar; @@ -12719,7 +12726,7 @@ void ImGui::DockNodeTreeUpdateSplitter(ImGuiDockNode* node) bb.Max[axis ^ 1] += child_1->Size[axis ^ 1]; //if (g.IO.KeyCtrl) GetForegroundDrawList(g.CurrentWindow->Viewport)->AddRect(bb.Min, bb.Max, IM_COL32(255,0,255,255)); - if (node->Flags & ImGuiDockNodeFlags_NoResize) + if ((child_0->GetMergedFlags() | child_1->GetMergedFlags()) & ImGuiDockNodeFlags_NoResize) { ImGuiWindow* window = g.CurrentWindow; window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_Separator), g.Style.FrameRounding); @@ -12904,7 +12911,7 @@ void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags fla node = DockContextAddNode(ctx, id); node->IsCentralNode = true; } - node->Flags = flags; + node->SharedFlags = flags; node->WindowClass = window_class ? *window_class : ImGuiWindowClass(); // When a DockSpace transitioned form implicit to explicit this may be called a second time @@ -12912,10 +12919,10 @@ void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags fla if (node->LastFrameActive == g.FrameCount && !(flags & ImGuiDockNodeFlags_KeepAliveOnly)) { IM_ASSERT(node->IsDockSpace() == false && "Cannot call DockSpace() twice a frame with the same ID"); - node->Flags |= ImGuiDockNodeFlags_DockSpace; + node->LocalFlags |= ImGuiDockNodeFlags_DockSpace; return; } - node->Flags |= ImGuiDockNodeFlags_DockSpace; + node->LocalFlags |= ImGuiDockNodeFlags_DockSpace; // Keep alive mode, this is allow windows docked into this node so stay docked even if they are not visible if (flags & ImGuiDockNodeFlags_KeepAliveOnly) @@ -13061,7 +13068,7 @@ ImGuiID ImGui::DockBuilderAddNode(ImGuiID id, ImGuiDockNodeFlags flags) ImGuiDockNode* node = NULL; if (flags & ImGuiDockNodeFlags_DockSpace) { - DockSpace(id, ImVec2(0, 0), flags | ImGuiDockNodeFlags_KeepAliveOnly); + DockSpace(id, ImVec2(0, 0), (flags & ~ImGuiDockNodeFlags_DockSpace) | ImGuiDockNodeFlags_KeepAliveOnly); node = DockContextFindNodeByID(ctx, id); } else @@ -13070,7 +13077,7 @@ ImGuiID ImGui::DockBuilderAddNode(ImGuiID id, ImGuiDockNodeFlags flags) node = DockContextFindNodeByID(ctx, id); if (!node) node = DockContextAddNode(ctx, id); - node->Flags = flags; + node->LocalFlags = flags; } node->LastFrameAlive = ctx->FrameCount; // Set this otherwise BeginDocked will undock during the same frame. return node->ID; @@ -13097,7 +13104,7 @@ void ImGui::DockBuilderRemoveNodeChildNodes(ImGuiID root_id) ImGuiDockNode* root_node = root_id ? DockContextFindNodeByID(ctx, root_id) : NULL; if (root_id && root_node == NULL) return; - bool has_document_root = false; + bool has_central_node = false; ImGuiDataAutority backup_root_node_autority_for_pos = root_node ? root_node->AutorityForPos : ImGuiDataAutority_Auto; ImGuiDataAutority backup_root_node_autority_for_size = root_node ? root_node->AutorityForSize : ImGuiDataAutority_Auto; @@ -13111,7 +13118,7 @@ void ImGui::DockBuilderRemoveNodeChildNodes(ImGuiID root_id) if (want_removal) { if (node->IsCentralNode) - has_document_root = true; + has_central_node = true; if (root_id != 0) DockContextQueueNotifyRemovedNode(ctx, node); if (root_node) @@ -13149,7 +13156,7 @@ void ImGui::DockBuilderRemoveNodeChildNodes(ImGuiID root_id) dc->Nodes.Clear(); dc->Requests.clear(); } - else if (has_document_root) + else if (has_central_node) { root_node->IsCentralNode = true; } @@ -13227,7 +13234,8 @@ static ImGuiDockNode* DockBuilderCopyNodeRec(ImGuiDockNode* src_node, ImGuiID ds { ImGuiContext* ctx = GImGui; ImGuiDockNode* dst_node = ImGui::DockContextAddNode(ctx, dst_node_id_if_known); - dst_node->Flags = src_node->Flags; + dst_node->SharedFlags = src_node->SharedFlags; + dst_node->LocalFlags = src_node->LocalFlags; dst_node->Pos = src_node->Pos; dst_node->Size = src_node->Size; dst_node->SizeRef = src_node->SizeRef; @@ -13488,7 +13496,7 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) g.NextWindowData.PosUndock = false; // Cancel implicit undocking of SetNextWindowPos() window->DockIsActive = true; window->DockTabIsVisible = false; - if (node->Flags & ImGuiDockNodeFlags_KeepAliveOnly) + if (node->SharedFlags & ImGuiDockNodeFlags_KeepAliveOnly) return; // When the window is selected we mark it as visible. @@ -14206,9 +14214,14 @@ void ImGui::ShowDockingDebug() ImGui::BulletText("VisibleWindow: 0x%08X %s", node->VisibleWindow ? node->VisibleWindow->ID : 0, node->VisibleWindow ? node->VisibleWindow->Name : "NULL"); ImGui::BulletText("SelectedTabID: 0x%08X", node->SelectedTabID); ImGui::BulletText("LastFocusedNodeID: 0x%08X", node->LastFocusedNodeID); - ImGui::BulletText("Flags 0x%02X%s%s%s%s", - node->Flags, node->IsDockSpace() ? ", IsDockSpace" : "", node->IsCentralNode ? ", IsCentralNode" : "", - (g.FrameCount - node->LastFrameAlive < 2) ? ", IsAlive" : "", (g.FrameCount - node->LastFrameActive < 2) ? ", IsActive" : ""); + if (ImGui::TreeNode("flags", "SharedFlags 0x%03X NodeFlags 0x%03X%s%s%s%s", + node->SharedFlags, node->LocalFlags, node->IsDockSpace() ? ", IsDockSpace" : "", node->IsCentralNode ? ", IsCentralNode" : "", + (g.FrameCount - node->LastFrameAlive < 2) ? ", IsAlive" : "", (g.FrameCount - node->LastFrameActive < 2) ? ", IsActive" : "")) + { + ImGui::CheckboxFlags("NodeFlags: NoSplit", (unsigned int*)&node->LocalFlags, ImGuiDockNodeFlags_NoSplit); + ImGui::CheckboxFlags("NodeFlags: NoResize", (unsigned int*)&node->LocalFlags, ImGuiDockNodeFlags_NoResize); + ImGui::TreePop(); + } if (node->ChildNodes[0]) NodeDockNode(node->ChildNodes[0], "Child[0]"); if (node->ChildNodes[1]) diff --git a/imgui.h b/imgui.h index 123d395d2fc8..fb483c27a8dc 100644 --- a/imgui.h +++ b/imgui.h @@ -878,17 +878,18 @@ enum ImGuiTabItemFlags_ ImGuiTabItemFlags_NoPushId = 1 << 3 // Don't call PushID(tab->ID)/PopID() on BeginTabItem()/EndTabItem() }; -// Flags for ImGui::DockSpace(), inherited by child nodes. +// Flags for ImGui::DockSpace(), shared/inherited by child nodes. +// (Some flags can be applied to individual nodes directly) enum ImGuiDockNodeFlags_ { ImGuiDockNodeFlags_None = 0, - ImGuiDockNodeFlags_KeepAliveOnly = 1 << 0, // Don't display the dockspace node but keep it alive. Windows docked into this dockspace node won't be undocked. - //ImGuiDockNodeFlags_NoCentralNode = 1 << 1, // Disable Central Node (the node which can stay empty) - ImGuiDockNodeFlags_NoDockingInCentralNode = 1 << 2, // Disable docking inside the Central Node, which will be always kept empty. Note: when turned off, existing docked nodes will be preserved. - ImGuiDockNodeFlags_NoSplit = 1 << 3, // Disable splitting the node into smaller nodes. Useful e.g. when embedding dockspaces into a main root one (the root one may have splitting disabled to reduce confusion). Note: when turned off, existing splits will be preserved. - ImGuiDockNodeFlags_NoResize = 1 << 4, // Disable resizing child nodes using the splitter/separators. Useful with programatically setup dockspaces. - ImGuiDockNodeFlags_PassthruCentralNode = 1 << 5, // Enable passthru dockspace: 1) DockSpace() will render a ImGuiCol_WindowBg background covering everything excepted the Central Node when empty. Meaning the host window should probably use SetNextWindowBgAlpha(0.0f) prior to Begin() when using this. 2) When Central Node is empty: let inputs pass-through + won't display a DockingEmptyBg background. See demo for details. - ImGuiDockNodeFlags_AutoHideTabBar = 1 << 6 // Tab bar will automatically hide when there is a single window in the dock node. + ImGuiDockNodeFlags_KeepAliveOnly = 1 << 0, // Shared // Don't display the dockspace node but keep it alive. Windows docked into this dockspace node won't be undocked. + //ImGuiDockNodeFlags_NoCentralNode = 1 << 1, // Shared // Disable Central Node (the node which can stay empty) + ImGuiDockNodeFlags_NoDockingInCentralNode = 1 << 2, // Shared // Disable docking inside the Central Node, which will be always kept empty. + ImGuiDockNodeFlags_PassthruCentralNode = 1 << 3, // Shared // Enable passthru dockspace: 1) DockSpace() will render a ImGuiCol_WindowBg background covering everything excepted the Central Node when empty. Meaning the host window should probably use SetNextWindowBgAlpha(0.0f) prior to Begin() when using this. 2) When Central Node is empty: let inputs pass-through + won't display a DockingEmptyBg background. See demo for details. + ImGuiDockNodeFlags_NoSplit = 1 << 4, // Shared/Local // Disable splitting the node into smaller nodes. Useful e.g. when embedding dockspaces into a main root one (the root one may have splitting disabled to reduce confusion). Note: when turned off, existing splits will be preserved. + ImGuiDockNodeFlags_NoResize = 1 << 5, // Shared/Local // Disable resizing child nodes using the splitter/separators. Useful with programatically setup dockspaces. + ImGuiDockNodeFlags_AutoHideTabBar = 1 << 6 // Shared/Local // Tab bar will automatically hide when there is a single window in the dock node. }; // Flags for ImGui::IsWindowFocused() diff --git a/imgui_internal.h b/imgui_internal.h index 1bf90ec5f203..3ff3a6885f47 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -851,7 +851,9 @@ struct ImGuiTabBarRef enum ImGuiDockNodeFlagsPrivate_ { - ImGuiDockNodeFlags_DockSpace = 1 << 10 + ImGuiDockNodeFlags_DockSpace = 1 << 10, // Local // A dockspace is a node that occupy space within an existing user window. Otherwise the node is floating and create its own window. + ImGuiDockNodeFlags_SharedFlagsInheritMask_ = ~0, + ImGuiDockNodeFlags_LocalFlagsTransferMask_ = ImGuiDockNodeFlags_NoResize | ImGuiDockNodeFlags_AutoHideTabBar // When splitting those flags are moved to the inheriting child, never duplicated }; enum ImGuiDataAutority_ @@ -865,7 +867,8 @@ enum ImGuiDataAutority_ struct ImGuiDockNode { ImGuiID ID; - ImGuiDockNodeFlags Flags; + ImGuiDockNodeFlags SharedFlags; // Flags shared by all nodes of a same dockspace hierarchy (inherited from the root node) + ImGuiDockNodeFlags LocalFlags; // Flags specific to this node ImGuiDockNode* ParentNode; ImGuiDockNode* ChildNodes[2]; // [Split node only] Child nodes (left/right or top/bottom). Consider switching to an array. ImVector Windows; // Note: unordered list! Iterate TabBar->Tabs for user-order. @@ -904,10 +907,11 @@ struct ImGuiDockNode ImGuiDockNode(ImGuiID id); ~ImGuiDockNode(); bool IsRootNode() const { return ParentNode == NULL; } - bool IsDockSpace() const { return (Flags & ImGuiDockNodeFlags_DockSpace) != 0; } + bool IsDockSpace() const { return (LocalFlags & ImGuiDockNodeFlags_DockSpace) != 0; } bool IsSplitNode() const { return ChildNodes[0] != NULL; } bool IsLeafNode() const { return ChildNodes[0] == NULL; } bool IsEmpty() const { return ChildNodes[0] == NULL && Windows.Size == 0; } + ImGuiDockNodeFlags GetMergedFlags() const { return SharedFlags | LocalFlags; } ImRect Rect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } }; From fc95da8aa3e96a191a471027a8b01a674bd56076 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 27 Mar 2019 20:32:44 +0100 Subject: [PATCH 472/828] Docking: Internals: Moved CentralNode and HiddenTabBar state into LocalFlags for consistency. (#2423, #2109) --- imgui.cpp | 90 +++++++++++++++++++++++------------------------- imgui_internal.h | 10 ++++-- 2 files changed, 51 insertions(+), 49 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 58d91dba15b1..e18d69fd4ff7 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5815,7 +5815,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) } // Docking: Unhide tab bar - if (window->DockNode && window->DockNode->IsHiddenTabBar) + if (window->DockNode && window->DockNode->IsHiddenTabBar()) { float unhide_sz_draw = ImFloor(g.FontSize * 0.70f); float unhide_sz_hit = ImFloor(g.FontSize * 0.55f); @@ -11098,8 +11098,10 @@ static void ImGui::DockContextBuildNodesFromSettings(ImGuiContext* ctx, ImGuiDoc node->SplitAxis = settings->SplitAxis; if (settings->IsDockSpace) node->LocalFlags |= ImGuiDockNodeFlags_DockSpace; - node->IsCentralNode = settings->IsCentralNode != 0; - node->IsHiddenTabBar = settings->IsHiddenTabBar != 0; + if (settings->IsCentralNode) + node->LocalFlags |= ImGuiDockNodeFlags_CentralNode; + if (settings->IsHiddenTabBar) + node->LocalFlags |= ImGuiDockNodeFlags_HiddenTabBar; // Bind host window immediately if it already exist (in case of a rebuild) // This is useful as the RootWindowForTitleBarHighlight links necessary to highlight the currently focused node requires node->HostWindow to be set. @@ -11198,7 +11200,7 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) if (target_node) IM_ASSERT(target_node->LastFrameAlive <= g.FrameCount); if (target_node && target_window && target_node == target_window->DockNodeAsHost) - IM_ASSERT(target_node->Windows.Size > 0 || target_node->IsSplitNode() || target_node->IsCentralNode); + IM_ASSERT(target_node->Windows.Size > 0 || target_node->IsSplitNode() || target_node->IsCentralNode()); // Create new node and add existing window to it if (target_node == NULL) @@ -11226,7 +11228,7 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) new_node->HostWindow = target_node->HostWindow; target_node = new_node; } - target_node->IsHiddenTabBar = false; + target_node->LocalFlags &= ~ImGuiDockNodeFlags_HiddenTabBar; if (target_node != payload_node) { @@ -11256,13 +11258,14 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) DockNodeMoveWindows(visible_node, target_node); DockSettingsRenameNodeReferences(target_node->ID, visible_node->ID); } - if (target_node->IsCentralNode) + if (target_node->IsCentralNode()) { // Central node property needs to be moved to a leaf node, pick the last focused one. + // FIXME-DOCKING: If we had to transfer other flags here, what would the policy be? ImGuiDockNode* last_focused_node = DockContextFindNodeByID(ctx, payload_node->LastFocusedNodeID); IM_ASSERT(last_focused_node != NULL && DockNodeGetRootNode(last_focused_node) == DockNodeGetRootNode(payload_node)); - last_focused_node->IsCentralNode = true; - target_node->IsCentralNode = false; + last_focused_node->LocalFlags |= ImGuiDockNodeFlags_CentralNode; + target_node->LocalFlags &= ~ImGuiDockNodeFlags_CentralNode; } IM_ASSERT(target_node->Windows.Size == 0); @@ -11311,7 +11314,7 @@ void ImGui::DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node) IM_ASSERT(node->IsLeafNode()); IM_ASSERT(node->Windows.Size >= 1); - if (node->IsRootNode() || node->IsCentralNode) + if (node->IsRootNode() || node->IsCentralNode()) { // In the case of a root node or central node, the node will have to stay in place. Create a new node to receive the payload. ImGuiDockNode* new_node = DockContextAddNode(ctx, 0); @@ -11357,7 +11360,7 @@ ImGuiDockNode::ImGuiDockNode(ImGuiID id) AutorityForPos = AutorityForSize = ImGuiDataAutority_DockNode; AutorityForViewport = ImGuiDataAutority_Auto; IsVisible = true; - IsFocused = IsCentralNode = IsHiddenTabBar = HasCloseButton = HasCollapseButton = false; + IsFocused = HasCloseButton = HasCollapseButton = false; WantCloseAll = WantLockSizeOnce = WantMouseMove = WantHiddenTabBarUpdate = WantHiddenTabBarToggle = false; } @@ -11468,19 +11471,19 @@ static void ImGui::DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window if (node->TabBar) { TabBarRemoveTab(node->TabBar, window->ID); - const int tab_count_threshold_for_tab_bar = node->IsCentralNode ? 1 : 2; + const int tab_count_threshold_for_tab_bar = node->IsCentralNode() ? 1 : 2; if (node->Windows.Size < tab_count_threshold_for_tab_bar) DockNodeRemoveTabBar(node); } - if (node->Windows.Size == 0 && !node->IsCentralNode && !node->IsDockSpace() && window->DockId != node->ID) + if (node->Windows.Size == 0 && !node->IsCentralNode() && !node->IsDockSpace() && window->DockId != node->ID) { // Automatic dock node delete themselves if they are not holding at least one tab DockContextRemoveNode(&g, node, true); return; } - if (node->Windows.Size == 1 && !node->IsCentralNode && node->HostWindow) + if (node->Windows.Size == 1 && !node->IsCentralNode() && node->HostWindow) { ImGuiWindow* remaining_window = node->Windows[0]; if (node->HostWindow->ViewportOwned && node->IsRootNode()) @@ -11590,7 +11593,7 @@ static void DockNodeUpdateScanRec(ImGuiDockNode* node, ImGuiDockNodeUpdateScanRe results->FirstNodeWithWindows = node; results->CountNodesWithWindows++; } - if (node->IsCentralNode) + if (node->IsCentralNode()) { IM_ASSERT(results->CentralNode == NULL); // Should be only one IM_ASSERT(node->IsLeafNode() && "If you get this assert: please submit .ini file + repro of actions leading to this."); @@ -11639,7 +11642,7 @@ static void ImGui::DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* nod if (!remove) continue; window->DockTabWantClose = false; - if (node->Windows.Size == 1 && !node->IsCentralNode) + if (node->Windows.Size == 1 && !node->IsCentralNode()) { DockNodeHideHostWindow(node); DockNodeRemoveWindow(node, window, node->ID); // Will delete the node so it'll be invalid on return @@ -11651,15 +11654,15 @@ static void ImGui::DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* nod // Auto-hide tab bar option ImGuiDockNodeFlags node_flags = node->GetMergedFlags(); - if (node->WantHiddenTabBarUpdate && node->Windows.Size == 1 && (node_flags & ImGuiDockNodeFlags_AutoHideTabBar) && !node->IsHiddenTabBar) + if (node->WantHiddenTabBarUpdate && node->Windows.Size == 1 && (node_flags & ImGuiDockNodeFlags_AutoHideTabBar) && !node->IsHiddenTabBar()) node->WantHiddenTabBarToggle = true; node->WantHiddenTabBarUpdate = false; // Apply toggles at a single point of the frame (here!) if (node->Windows.Size > 1) - node->IsHiddenTabBar = false; + node->LocalFlags &= ~ImGuiDockNodeFlags_HiddenTabBar; else if (node->WantHiddenTabBarToggle) - node->IsHiddenTabBar ^= 1; + node->LocalFlags ^= ImGuiDockNodeFlags_HiddenTabBar; node->WantHiddenTabBarToggle = false; DockNodeUpdateVisibleFlag(node); @@ -11668,7 +11671,7 @@ static void ImGui::DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* nod static void ImGui::DockNodeUpdateVisibleFlag(ImGuiDockNode* node) { // Update visibility flag - bool is_visible = (node->ParentNode == NULL) ? node->IsDockSpace() : node->IsCentralNode; + bool is_visible = (node->ParentNode == NULL) ? node->IsDockSpace() : node->IsCentralNode(); is_visible |= (node->Windows.Size > 0); is_visible |= (node->ChildNodes[0] && node->ChildNodes[0]->IsVisible); is_visible |= (node->ChildNodes[1] && node->ChildNodes[1]->IsVisible); @@ -11968,7 +11971,7 @@ static ImGuiID ImGui::DockNodeUpdateTabListMenu(ImGuiDockNode* node, ImGuiTabBar node->IsFocused = true; if (tab_bar->Tabs.Size == 1) { - if (MenuItem("Hide tab bar", NULL, node->IsHiddenTabBar)) + if (MenuItem("Hide tab bar", NULL, node->IsHiddenTabBar())) node->WantHiddenTabBarToggle = true; } else @@ -12008,7 +12011,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w is_focused = true; // Hidden tab bar will show a triangle on the upper-left (in Begin) - if (node->IsHiddenTabBar) + if (node->IsHiddenTabBar()) { node->VisibleWindow = (node->Windows.Size > 0) ? node->Windows[0] : NULL; node->IsFocused = is_focused; @@ -12375,13 +12378,13 @@ static bool ImGui::DockNodePreviewDockCalc(ImGuiWindow* host_window, ImGuiDockNo data->IsCenterAvailable = !is_outer_docking; if (src_is_visibly_splitted && (!host_node || !host_node->IsEmpty())) data->IsCenterAvailable = false; - if (host_node && (host_node_flags & ImGuiDockNodeFlags_NoDockingInCentralNode) && host_node->IsCentralNode) + if (host_node && (host_node_flags & ImGuiDockNodeFlags_NoDockingInCentralNode) && host_node->IsCentralNode()) data->IsCenterAvailable = false; data->IsSidesAvailable = true; if ((host_node && (host_node_flags & ImGuiDockNodeFlags_NoSplit)) || g.IO.ConfigDockingNoSplit) data->IsSidesAvailable = false; - if (!is_outer_docking && host_node && host_node->ParentNode == NULL && host_node->IsCentralNode) + if (!is_outer_docking && host_node && host_node->ParentNode == NULL && host_node->IsCentralNode()) data->IsSidesAvailable = false; // Calculate drop shapes geometry for allowed splitting directions @@ -12470,7 +12473,7 @@ static void ImGui::DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDock ImVec2 tab_pos = tab_bar_rect.Min; if (host_node && host_node->TabBar) { - if (!host_node->IsHiddenTabBar) + if (!host_node->IsHiddenTabBar()) tab_pos.x += host_node->TabBar->OffsetMax + g.Style.ItemInnerSpacing.x; // We don't use OffsetNewTab because when using non-persistent-order tab bar it is incremented with each Tab submission. else tab_pos.x += g.Style.ItemInnerSpacing.x + TabItemCalcSize(host_node->Windows[0]->Name, host_node->Windows[0]->HasCloseButton).x; @@ -12572,10 +12575,7 @@ void ImGui::DockNodeTreeSplit(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImG child_0->SharedFlags = parent_node->SharedFlags & ImGuiDockNodeFlags_SharedFlagsInheritMask_; child_1->SharedFlags = parent_node->SharedFlags & ImGuiDockNodeFlags_SharedFlagsInheritMask_; child_inheritor->LocalFlags = parent_node->LocalFlags & ImGuiDockNodeFlags_LocalFlagsTransferMask_; - child_inheritor->IsCentralNode = parent_node->IsCentralNode; - child_inheritor->IsHiddenTabBar = parent_node->IsHiddenTabBar; parent_node->LocalFlags &= ~ImGuiDockNodeFlags_LocalFlagsTransferMask_; - parent_node->IsCentralNode = false; } void ImGui::DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiDockNode* merge_lead_child) @@ -12610,8 +12610,6 @@ void ImGui::DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImG // Flags transfer parent_node->LocalFlags = ((child_0 ? child_0->LocalFlags : 0) | (child_1 ? child_1->LocalFlags : 0)) & ImGuiDockNodeFlags_LocalFlagsTransferMask_; - parent_node->IsCentralNode = (child_0 && child_0->IsCentralNode) || (child_1 && child_1->IsCentralNode); - parent_node->IsHiddenTabBar = merge_lead_child->IsHiddenTabBar; if (child_0) { @@ -12665,12 +12663,12 @@ void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 si } // 3) If one window is the central node (~ use remaining space, should be made explicit!), use explicit size from the other, and remainder for the central node - else if (child_1->IsCentralNode && child_0->SizeRef[axis] != 0.0f) + else if (child_1->IsCentralNode() && child_0->SizeRef[axis] != 0.0f) { child_0_size[axis] = ImMin(size_avail - size_min_each, child_0->SizeRef[axis]); child_1_size[axis] = (size_avail - child_0_size[axis]); } - else if (child_0->IsCentralNode && child_1->SizeRef[axis] != 0.0f) + else if (child_0->IsCentralNode() && child_1->SizeRef[axis] != 0.0f) { child_1_size[axis] = ImMin(size_avail - size_min_each, child_1->SizeRef[axis]); child_0_size[axis] = (size_avail - child_1_size[axis]); @@ -12909,7 +12907,7 @@ void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags fla if (!node) { node = DockContextAddNode(ctx, id); - node->IsCentralNode = true; + node->LocalFlags |= ImGuiDockNodeFlags_CentralNode; } node->SharedFlags = flags; node->WindowClass = window_class ? *window_class : ImGuiWindowClass(); @@ -13091,8 +13089,8 @@ void ImGui::DockBuilderRemoveNode(ImGuiID node_id) return; DockBuilderRemoveNodeDockedWindows(node_id, true); DockBuilderRemoveNodeChildNodes(node_id); - if (node->IsCentralNode && node->ParentNode) - node->ParentNode->IsCentralNode = true; + if (node->IsCentralNode() && node->ParentNode) + node->ParentNode->LocalFlags = ImGuiDockNodeFlags_CentralNode; DockContextRemoveNode(ctx, node, true); } @@ -13117,7 +13115,7 @@ void ImGui::DockBuilderRemoveNodeChildNodes(ImGuiID root_id) bool want_removal = (root_id == 0) || (node->ID != root_id && DockNodeGetRootNode(node)->ID == root_id); if (want_removal) { - if (node->IsCentralNode) + if (node->IsCentralNode()) has_central_node = true; if (root_id != 0) DockContextQueueNotifyRemovedNode(ctx, node); @@ -13158,7 +13156,7 @@ void ImGui::DockBuilderRemoveNodeChildNodes(ImGuiID root_id) } else if (has_central_node) { - root_node->IsCentralNode = true; + root_node->LocalFlags |= ImGuiDockNodeFlags_CentralNode; } } @@ -13240,7 +13238,6 @@ static ImGuiDockNode* DockBuilderCopyNodeRec(ImGuiDockNode* src_node, ImGuiID ds dst_node->Size = src_node->Size; dst_node->SizeRef = src_node->SizeRef; dst_node->SplitAxis = src_node->SplitAxis; - dst_node->IsCentralNode = src_node->IsCentralNode; out_node_remap_pairs->push_back(src_node->ID); out_node_remap_pairs->push_back(dst_node->ID); @@ -13514,7 +13511,7 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) // Update window flag IM_ASSERT((window->Flags & ImGuiWindowFlags_ChildWindow) == 0); window->Flags |= ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_NoResize; - if (node->IsHiddenTabBar) + if (node->IsHiddenTabBar()) window->Flags |= ImGuiWindowFlags_NoTitleBar; else window->Flags &= ~ImGuiWindowFlags_NoTitleBar; // Clear the NoTitleBar flag in case the user set it: confusingly enough we need a title bar height so we are correctly offset, but it won't be displayed! @@ -13580,7 +13577,7 @@ void ImGui::BeginAsDockableDragDropTarget(ImGuiWindow* window) else allow_null_target_node = true; // Dock into a regular window - const ImRect explicit_target_rect = (target_node && target_node->TabBar && !target_node->IsHiddenTabBar) ? target_node->TabBar->BarRect : ImRect(window->Pos, window->Pos + ImVec2(window->Size.x, GetFrameHeight())); + const ImRect explicit_target_rect = (target_node && target_node->TabBar && !target_node->IsHiddenTabBar()) ? target_node->TabBar->BarRect : ImRect(window->Pos, window->Pos + ImVec2(window->Size.x, GetFrameHeight())); const bool is_explicit_target = g.IO.ConfigDockingWithShift || IsMouseHoveringRect(explicit_target_rect.Min, explicit_target_rect.Max); // Preview docking request and find out split direction/ratio @@ -13590,7 +13587,7 @@ void ImGui::BeginAsDockableDragDropTarget(ImGuiWindow* window) { ImGuiDockPreviewData split_inner, split_outer; ImGuiDockPreviewData* split_data = &split_inner; - if (target_node && (target_node->ParentNode || target_node->IsCentralNode)) + if (target_node && (target_node->ParentNode || target_node->IsCentralNode())) if (ImGuiDockNode* root_node = DockNodeGetRootNode(target_node)) if (DockNodePreviewDockCalc(window, root_node, payload_window, &split_outer, is_explicit_target, true)) split_data = &split_outer; @@ -13714,8 +13711,8 @@ static void DockSettingsHandler_DockNodeToSettings(ImGuiDockContext* dc, ImGuiDo node_settings.SplitAxis = node->IsSplitNode() ? (char)node->SplitAxis : ImGuiAxis_None; node_settings.Depth = (char)depth; node_settings.IsDockSpace = (char)node->IsDockSpace(); - node_settings.IsCentralNode = (char)node->IsCentralNode; - node_settings.IsHiddenTabBar = (char)node->IsHiddenTabBar; + node_settings.IsCentralNode = (char)node->IsCentralNode(); + node_settings.IsHiddenTabBar = (char)node->IsHiddenTabBar(); node_settings.Pos = ImVec2ih((short)node->Pos.x, (short)node->Pos.y); node_settings.Size = ImVec2ih((short)node->Size.x, (short)node->Size.y); node_settings.SizeRef = ImVec2ih((short)node->SizeRef.x, (short)node->SizeRef.y); @@ -14215,11 +14212,12 @@ void ImGui::ShowDockingDebug() ImGui::BulletText("SelectedTabID: 0x%08X", node->SelectedTabID); ImGui::BulletText("LastFocusedNodeID: 0x%08X", node->LastFocusedNodeID); if (ImGui::TreeNode("flags", "SharedFlags 0x%03X NodeFlags 0x%03X%s%s%s%s", - node->SharedFlags, node->LocalFlags, node->IsDockSpace() ? ", IsDockSpace" : "", node->IsCentralNode ? ", IsCentralNode" : "", + node->SharedFlags, node->LocalFlags, node->IsDockSpace() ? ", IsDockSpace" : "", node->IsCentralNode() ? ", IsCentralNode" : "", (g.FrameCount - node->LastFrameAlive < 2) ? ", IsAlive" : "", (g.FrameCount - node->LastFrameActive < 2) ? ", IsActive" : "")) { - ImGui::CheckboxFlags("NodeFlags: NoSplit", (unsigned int*)&node->LocalFlags, ImGuiDockNodeFlags_NoSplit); - ImGui::CheckboxFlags("NodeFlags: NoResize", (unsigned int*)&node->LocalFlags, ImGuiDockNodeFlags_NoResize); + ImGui::CheckboxFlags("LocalFlags: NoSplit", (unsigned int*)&node->LocalFlags, ImGuiDockNodeFlags_NoSplit); + ImGui::CheckboxFlags("LocalFlags: NoResize", (unsigned int*)&node->LocalFlags, ImGuiDockNodeFlags_NoResize); + ImGui::CheckboxFlags("LocalFlags: HiddenTabBar",(unsigned int*)&node->LocalFlags, ImGuiDockNodeFlags_HiddenTabBar); ImGui::TreePop(); } if (node->ChildNodes[0]) @@ -14327,7 +14325,7 @@ void ImGui::ShowDockingDebug() char buf[64] = ""; char* p = buf; ImDrawList* overlay_draw_list = node->HostWindow ? GetForegroundDrawList(node->HostWindow) : GetForegroundDrawList((ImGuiViewportP*)GetMainViewport()); - p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "DockId: %X%s\n", node->ID, node->IsCentralNode ? " *CentralNode*" : ""); + p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "DockId: %X%s\n", node->ID, node->IsCentralNode() ? " *CentralNode*" : ""); p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "Size: (%.0f, %.0f)\n", node->Size.x, node->Size.y); p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "SizeRef: (%.0f, %.0f)\n", node->SizeRef.x, node->SizeRef.y); int depth = DockNodeGetDepth(node); diff --git a/imgui_internal.h b/imgui_internal.h index 3ff3a6885f47..1ed273cc1062 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -851,9 +851,13 @@ struct ImGuiTabBarRef enum ImGuiDockNodeFlagsPrivate_ { + // [Internal] ImGuiDockNodeFlags_DockSpace = 1 << 10, // Local // A dockspace is a node that occupy space within an existing user window. Otherwise the node is floating and create its own window. + ImGuiDockNodeFlags_CentralNode = 1 << 11, // Local + ImGuiDockNodeFlags_HiddenTabBar = 1 << 13, // Local // Tab bar is hidden, with a triangle in the corner to show it again (NB: actual tab-bar instance may be destroyed as this is only used for single-window tab bar) ImGuiDockNodeFlags_SharedFlagsInheritMask_ = ~0, - ImGuiDockNodeFlags_LocalFlagsTransferMask_ = ImGuiDockNodeFlags_NoResize | ImGuiDockNodeFlags_AutoHideTabBar // When splitting those flags are moved to the inheriting child, never duplicated + ImGuiDockNodeFlags_LocalFlagsMask_ = ImGuiDockNodeFlags_NoSplit | ImGuiDockNodeFlags_NoResize | ImGuiDockNodeFlags_AutoHideTabBar | ImGuiDockNodeFlags_DockSpace | ImGuiDockNodeFlags_CentralNode | ImGuiDockNodeFlags_HiddenTabBar, + ImGuiDockNodeFlags_LocalFlagsTransferMask_ = ImGuiDockNodeFlags_LocalFlagsMask_ & ~ImGuiDockNodeFlags_DockSpace // When splitting those flags are moved to the inheriting child, never duplicated }; enum ImGuiDataAutority_ @@ -894,8 +898,6 @@ struct ImGuiDockNode ImGuiDataAutority AutorityForViewport :3; bool IsVisible :1; // Set to false when the node is hidden (usually disabled as it has no active window) bool IsFocused :1; - bool IsCentralNode :1; - bool IsHiddenTabBar :1; bool HasCloseButton :1; bool HasCollapseButton :1; bool WantCloseAll :1; // Set when closing all tabs at once. @@ -908,6 +910,8 @@ struct ImGuiDockNode ~ImGuiDockNode(); bool IsRootNode() const { return ParentNode == NULL; } bool IsDockSpace() const { return (LocalFlags & ImGuiDockNodeFlags_DockSpace) != 0; } + bool IsCentralNode() const { return (LocalFlags & ImGuiDockNodeFlags_CentralNode) != 0; } + bool IsHiddenTabBar() const { return (LocalFlags & ImGuiDockNodeFlags_HiddenTabBar) != 0; } bool IsSplitNode() const { return ChildNodes[0] != NULL; } bool IsLeafNode() const { return ChildNodes[0] == NULL; } bool IsEmpty() const { return ChildNodes[0] == NULL && Windows.Size == 0; } From 5a665e423c5a6dd4d7a327f1a85eaca299d343ef Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 27 Mar 2019 20:44:49 +0100 Subject: [PATCH 473/828] Docking: Added ImGuiDockNodeFlags_NoTabBar (not exposed publicly). (#2423, #2109) --- imgui.cpp | 28 ++++++++++++++++------------ imgui_internal.h | 6 ++++-- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index e18d69fd4ff7..5a86f8f1e3bd 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5814,8 +5814,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border), style.FrameBorderSize); } - // Docking: Unhide tab bar - if (window->DockNode && window->DockNode->IsHiddenTabBar()) + // Docking: Unhide tab bar (small triangle in the corner) + if (window->DockNode && window->DockNode->IsHiddenTabBar() && !window->DockNode->IsNoTabBar()) { float unhide_sz_draw = ImFloor(g.FontSize * 0.70f); float unhide_sz_hit = ImFloor(g.FontSize * 0.55f); @@ -11247,7 +11247,7 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) { if (target_node->Windows.Size > 0) { - // We can dock into a node that already has windows _only_ if our payload is a node tree with a single visible node. + // We can dock a split payload into a node that already has windows _only_ if our payload is a node tree with a single visible node. // In this situation, we move the windows of the target node into the currently visible node of the payload. // This allows us to preserve some of the underlying dock tree settings nicely. IM_ASSERT(payload_node->OnlyNodeWithWindows != NULL); // The docking should have been blocked by DockNodePreviewDockCalc() early on and never submitted. @@ -11725,6 +11725,10 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) } } + // Remove tab bar if not needed + if (node->TabBar && node->IsNoTabBar()) + DockNodeRemoveTabBar(node); + // Early out for hidden root dock nodes (when all DockId references are in inactive windows, or there is only 1 floating window holding on the DockId) if (node->Windows.Size <= 1 && node->IsRootNode() && node->IsLeafNode() && !node->IsDockSpace() && !g.IO.ConfigDockingTabBarOnSingleWindows) { @@ -12011,7 +12015,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w is_focused = true; // Hidden tab bar will show a triangle on the upper-left (in Begin) - if (node->IsHiddenTabBar()) + if (node->IsHiddenTabBar() || node->IsNoTabBar()) { node->VisibleWindow = (node->Windows.Size > 0) ? node->Windows[0] : NULL; node->IsFocused = is_focused; @@ -12473,7 +12477,7 @@ static void ImGui::DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDock ImVec2 tab_pos = tab_bar_rect.Min; if (host_node && host_node->TabBar) { - if (!host_node->IsHiddenTabBar()) + if (!host_node->IsHiddenTabBar() && !host_node->IsNoTabBar()) tab_pos.x += host_node->TabBar->OffsetMax + g.Style.ItemInnerSpacing.x; // We don't use OffsetNewTab because when using non-persistent-order tab bar it is incremented with each Tab submission. else tab_pos.x += g.Style.ItemInnerSpacing.x + TabItemCalcSize(host_node->Windows[0]->Name, host_node->Windows[0]->HasCloseButton).x; @@ -13195,6 +13199,7 @@ void ImGui::DockBuilderRemoveNodeDockedWindows(ImGuiID root_id, bool clear_persi } } +// FIXME-DOCK: We are not exposing nor using split_outer. ImGuiID ImGui::DockBuilderSplitNode(ImGuiID id, ImGuiDir split_dir, float size_ratio_for_node_at_dir, ImGuiID* out_id_at_dir, ImGuiID* out_id_other) { ImGuiContext* ctx = GImGui; @@ -13511,7 +13516,7 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) // Update window flag IM_ASSERT((window->Flags & ImGuiWindowFlags_ChildWindow) == 0); window->Flags |= ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_NoResize; - if (node->IsHiddenTabBar()) + if (node->IsHiddenTabBar() || node->IsNoTabBar()) window->Flags |= ImGuiWindowFlags_NoTitleBar; else window->Flags &= ~ImGuiWindowFlags_NoTitleBar; // Clear the NoTitleBar flag in case the user set it: confusingly enough we need a title bar height so we are correctly offset, but it won't be displayed! @@ -13577,7 +13582,7 @@ void ImGui::BeginAsDockableDragDropTarget(ImGuiWindow* window) else allow_null_target_node = true; // Dock into a regular window - const ImRect explicit_target_rect = (target_node && target_node->TabBar && !target_node->IsHiddenTabBar()) ? target_node->TabBar->BarRect : ImRect(window->Pos, window->Pos + ImVec2(window->Size.x, GetFrameHeight())); + const ImRect explicit_target_rect = (target_node && target_node->TabBar && !target_node->IsHiddenTabBar() && !target_node->IsNoTabBar()) ? target_node->TabBar->BarRect : ImRect(window->Pos, window->Pos + ImVec2(window->Size.x, GetFrameHeight())); const bool is_explicit_target = g.IO.ConfigDockingWithShift || IsMouseHoveringRect(explicit_target_rect.Min, explicit_target_rect.Max); // Preview docking request and find out split direction/ratio @@ -14209,14 +14214,13 @@ void ImGui::ShowDockingDebug() ImGui::BulletText("Pos (%.0f,%.0f), Size (%.0f, %.0f) Ref (%.0f, %.0f)", node->Pos.x, node->Pos.y, node->Size.x, node->Size.y, node->SizeRef.x, node->SizeRef.y); ImGui::BulletText("VisibleWindow: 0x%08X %s", node->VisibleWindow ? node->VisibleWindow->ID : 0, node->VisibleWindow ? node->VisibleWindow->Name : "NULL"); - ImGui::BulletText("SelectedTabID: 0x%08X", node->SelectedTabID); - ImGui::BulletText("LastFocusedNodeID: 0x%08X", node->LastFocusedNodeID); - if (ImGui::TreeNode("flags", "SharedFlags 0x%03X NodeFlags 0x%03X%s%s%s%s", - node->SharedFlags, node->LocalFlags, node->IsDockSpace() ? ", IsDockSpace" : "", node->IsCentralNode() ? ", IsCentralNode" : "", - (g.FrameCount - node->LastFrameAlive < 2) ? ", IsAlive" : "", (g.FrameCount - node->LastFrameActive < 2) ? ", IsActive" : "")) + ImGui::BulletText("SelectedTabID: 0x%08X, LastFocusedNodeID: 0x%08X", node->SelectedTabID, node->LastFocusedNodeID); + ImGui::BulletText("Misc:%s%s%s%s", node->IsDockSpace() ? " IsDockSpace" : "", node->IsCentralNode() ? " IsCentralNode" : "", (g.FrameCount - node->LastFrameAlive < 2) ? " IsAlive" : "", (g.FrameCount - node->LastFrameActive < 2) ? " IsActive" : ""); + if (ImGui::TreeNode("flags", "LocalFlags: 0x%04X SharedFlags: 0x%04X", node->LocalFlags, node->SharedFlags)) { ImGui::CheckboxFlags("LocalFlags: NoSplit", (unsigned int*)&node->LocalFlags, ImGuiDockNodeFlags_NoSplit); ImGui::CheckboxFlags("LocalFlags: NoResize", (unsigned int*)&node->LocalFlags, ImGuiDockNodeFlags_NoResize); + ImGui::CheckboxFlags("LocalFlags: NoTabBar", (unsigned int*)&node->LocalFlags, ImGuiDockNodeFlags_NoTabBar); ImGui::CheckboxFlags("LocalFlags: HiddenTabBar",(unsigned int*)&node->LocalFlags, ImGuiDockNodeFlags_HiddenTabBar); ImGui::TreePop(); } diff --git a/imgui_internal.h b/imgui_internal.h index 1ed273cc1062..1c52d0789ce3 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -854,9 +854,10 @@ enum ImGuiDockNodeFlagsPrivate_ // [Internal] ImGuiDockNodeFlags_DockSpace = 1 << 10, // Local // A dockspace is a node that occupy space within an existing user window. Otherwise the node is floating and create its own window. ImGuiDockNodeFlags_CentralNode = 1 << 11, // Local + ImGuiDockNodeFlags_NoTabBar = 1 << 12, // Local // Tab bar is completely unavailable. No triangle in the corner to enable it back. ImGuiDockNodeFlags_HiddenTabBar = 1 << 13, // Local // Tab bar is hidden, with a triangle in the corner to show it again (NB: actual tab-bar instance may be destroyed as this is only used for single-window tab bar) ImGuiDockNodeFlags_SharedFlagsInheritMask_ = ~0, - ImGuiDockNodeFlags_LocalFlagsMask_ = ImGuiDockNodeFlags_NoSplit | ImGuiDockNodeFlags_NoResize | ImGuiDockNodeFlags_AutoHideTabBar | ImGuiDockNodeFlags_DockSpace | ImGuiDockNodeFlags_CentralNode | ImGuiDockNodeFlags_HiddenTabBar, + ImGuiDockNodeFlags_LocalFlagsMask_ = ImGuiDockNodeFlags_NoSplit | ImGuiDockNodeFlags_NoResize | ImGuiDockNodeFlags_AutoHideTabBar | ImGuiDockNodeFlags_DockSpace | ImGuiDockNodeFlags_CentralNode | ImGuiDockNodeFlags_NoTabBar | ImGuiDockNodeFlags_HiddenTabBar, ImGuiDockNodeFlags_LocalFlagsTransferMask_ = ImGuiDockNodeFlags_LocalFlagsMask_ & ~ImGuiDockNodeFlags_DockSpace // When splitting those flags are moved to the inheriting child, never duplicated }; @@ -911,7 +912,8 @@ struct ImGuiDockNode bool IsRootNode() const { return ParentNode == NULL; } bool IsDockSpace() const { return (LocalFlags & ImGuiDockNodeFlags_DockSpace) != 0; } bool IsCentralNode() const { return (LocalFlags & ImGuiDockNodeFlags_CentralNode) != 0; } - bool IsHiddenTabBar() const { return (LocalFlags & ImGuiDockNodeFlags_HiddenTabBar) != 0; } + bool IsHiddenTabBar() const { return (LocalFlags & ImGuiDockNodeFlags_HiddenTabBar) != 0; } // Hidden tab bar can be shown back by clicking the small triangle + bool IsNoTabBar() const { return (LocalFlags & ImGuiDockNodeFlags_NoTabBar) != 0; } // Never show a tab bar bool IsSplitNode() const { return ChildNodes[0] != NULL; } bool IsLeafNode() const { return ChildNodes[0] == NULL; } bool IsEmpty() const { return ChildNodes[0] == NULL && Windows.Size == 0; } From b6ae8a0dca909c5384afee2a091ade3b28e1d443 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 28 Mar 2019 16:04:00 +0100 Subject: [PATCH 474/828] Docking: Disable SkipItems when directly/programmatically focused (possible generalization of code currently in BeginDocked which relies on tab bar interaction, will remove that code in next commit). (#2453, #2109) --- docs/TODO.txt | 4 +++- imgui.cpp | 9 ++++++++- imgui_internal.h | 1 + 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/docs/TODO.txt b/docs/TODO.txt index 4842ae0cf606..be4ef7cce611 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -130,7 +130,9 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - splitter/separator: formalize the splitter idiom into an official api (we want to handle n-way split) (#319) - dock: merge docking branch (#2109) - - dock: A~ Unreal style document system (requires low-level controls of dockspace serialization fork/copy/delete). this is mostly working but the DockBuilderXXX api are not exposed/finished. + - dock: B~ rework code to be able to lazily create tab bar instance in a single place. The _Unsorted tab flag could be replacing a trailing-counter in DockNode? + - dock: B~ fully track windows/settings reference in dock nodes. perhaps find a representation that allows facilitate use of dock builder functions. + - dock: B~ Unreal style document system (requires low-level controls of dockspace serialization fork/copy/delete). this is mostly working but the DockBuilderXXX api are not exposed/finished. - dock: B: when docking outer, perform size locking on neighbors nodes the same way we do it with splitters, so other nodes are not resized. - dock: B~ central node resizing behavior incorrect. - dock: B: changing title font/style per-window is not supported as dock nodes are created in NewFrame. diff --git a/imgui.cpp b/imgui.cpp index 2661b070053b..267ae67628fb 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2587,6 +2587,7 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX); LastFrameActive = -1; + LastFrameJustFocused = -1; ItemWidthDefault = 0.0f; FontWindowScale = FontDpiScale = 1.0f; SettingsIdx = -1; @@ -6070,7 +6071,12 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) g.NextWindowData.Clear(); if (window->DockIsActive && !window->DockTabIsVisible) - window->HiddenFramesCanSkipItems = 1; + { + if (window->LastFrameJustFocused == g.FrameCount) // This may be a better a generalization for the code in BeginDocked() setting the same field. + window->HiddenFramesCannotSkipItems = 1; + else + window->HiddenFramesCanSkipItems = 1; + } if (flags & ImGuiWindowFlags_ChildWindow) { @@ -6219,6 +6225,7 @@ void ImGui::FocusWindow(ImGuiWindow* window) // Passing NULL allow to disable keyboard focus if (!window) return; + window->LastFrameJustFocused = g.FrameCount; // Select in dock node if (window->DockNode && window->DockNode->TabBar) diff --git a/imgui_internal.h b/imgui_internal.h index e4a4ea225a56..9f735e6bdb07 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1405,6 +1405,7 @@ struct IMGUI_API ImGuiWindow ImVec2ih HitTestHoleSize, HitTestHoleOffset; ImRect ContentsRegionRect; // FIXME: This is currently confusing/misleading. Maximum visible content position ~~ Pos + (SizeContentsExplicit ? SizeContentsExplicit : Size - ScrollbarSizes) - CursorStartPos, per axis int LastFrameActive; // Last frame number the window was Active. + int LastFrameJustFocused; // Last frame number the window was made Focused. float ItemWidthDefault; ImGuiMenuColumns MenuColumns; // Simplified columns storage for menu items ImGuiStorage StateStorage; From 47219dd5c673024107616ecb589fa5250d398b07 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 28 Mar 2019 16:13:06 +0100 Subject: [PATCH 475/828] Docking: Remove code in BeginDocked() to set HiddenFramesCannotSkipItems based on upcoming tab bar selection, solely based on focus (might break something subtle?). Follow-up to c355ed126775f36dfb19202af8526ba2ce183877. (#2453, #2109) --- imgui.cpp | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 267ae67628fb..9bad7a0a38d3 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6070,9 +6070,13 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->BeginCount++; g.NextWindowData.Clear(); + // When we are about to select this tab (which will only be visible on the _next frame_), flag it with a non-zero HiddenFramesCannotSkipItems. + // This will have the important effect of actually returning true in Begin() and not setting SkipItems, allowing an earlier submission of the window contents. + // This is analogous to regular windows being hidden from one frame. + // It is especially important as e.g. nested TabBars would otherwise generate flicker in the form of one empty frame, or focus requests won't be processed. if (window->DockIsActive && !window->DockTabIsVisible) { - if (window->LastFrameJustFocused == g.FrameCount) // This may be a better a generalization for the code in BeginDocked() setting the same field. + if (window->LastFrameJustFocused == g.FrameCount) window->HiddenFramesCannotSkipItems = 1; else window->HiddenFramesCanSkipItems = 1; @@ -13527,14 +13531,6 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) if (node->VisibleWindow == window) window->DockTabIsVisible = true; - // When we are about to select this tab (which will only be visible on the _next frame_), flag it with a non-zero HiddenFramesCannotSkipItems. - // This will have the important effect of actually returning true in Begin() and not setting SkipItems, allowing an earlier submission of the window contents. - // This is analogous to regular windows being hidden from one frame. It is especially important as nested TabBars would otherwise generate flicker in the form - // of one empty frame. - // Note that we set HiddenFramesCannotSkipItems=2 because BeginDocked() is called just before Begin() has a chance to decrement the value. Effectively it'll be a 1 frame thing. - if (!window->DockTabIsVisible && node->TabBar && node->TabBar->NextSelectedTabId == window->ID) - window->HiddenFramesCannotSkipItems = 2; - // Update window flag IM_ASSERT((window->Flags & ImGuiWindowFlags_ChildWindow) == 0); window->Flags |= ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_NoResize; From 5af385ea78d13a8390d95590dd557c8e3385278b Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 28 Mar 2019 18:02:03 +0100 Subject: [PATCH 476/828] Viewport: Renamed member + added note about a Docking issue with restoring focus. --- imgui.cpp | 12 ++++++++---- imgui_internal.h | 4 ++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 9bad7a0a38d3..e54f8e3c4570 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6260,6 +6260,10 @@ void ImGui::FocusPreviousWindowIgnoringOne(ImGuiWindow* ignore_window) if (window != ignore_window && window->WasActive && !(window->Flags & ImGuiWindowFlags_ChildWindow)) if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) { + // FIXME-DOCKING: This is failing (lagging by one frame) for docked windows. + // If A and B are docked into window and B disappear, at the NewFrame() call site window->NavLastChildNavWindow will still point to B. + // We might leverage the tab order implicitly stored in window->DockNodeAsHost->TabBar (essentially the 'most_recently_selected_tab' code in tab bar will do that but on next update) + // to tell which is the "previous" window. Or we may leverage 'LastFrameFocused/LastFrameJustFocused' and have this function handle child window itself? ImGuiWindow* focus_window = NavRestoreLastChildNavWindow(window); FocusWindow(focus_window); return; @@ -10554,8 +10558,8 @@ void ImGui::UpdatePlatformWindows() // Even without focus, we assume the window becomes front-most. // This is useful for our platform z-order heuristic when io.MouseHoveredViewport is not available. - if (viewport->LastFrontMostStampCount != g.WindowsFrontMostStampCount) - viewport->LastFrontMostStampCount = ++g.WindowsFrontMostStampCount; + if (viewport->LastFrontMostStampCount != g.ViewportFrontMostStampCount) + viewport->LastFrontMostStampCount = ++g.ViewportFrontMostStampCount; } // Clear request flags @@ -10576,8 +10580,8 @@ void ImGui::UpdatePlatformWindows() } if (focused_viewport && g.PlatformLastFocusedViewport != focused_viewport->ID) { - if (focused_viewport->LastFrontMostStampCount != g.WindowsFrontMostStampCount) - focused_viewport->LastFrontMostStampCount = ++g.WindowsFrontMostStampCount; + if (focused_viewport->LastFrontMostStampCount != g.ViewportFrontMostStampCount) + focused_viewport->LastFrontMostStampCount = ++g.ViewportFrontMostStampCount; g.PlatformLastFocusedViewport = focused_viewport->ID; } } diff --git a/imgui_internal.h b/imgui_internal.h index 9f735e6bdb07..89d0df5b8818 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -951,7 +951,6 @@ struct ImGuiContext ImVector CurrentWindowStack; ImGuiStorage WindowsById; int WindowsActiveCount; - int WindowsFrontMostStampCount; // Every time the front-most window changes, we stamp its viewport with an incrementing counter ImGuiWindow* CurrentWindow; // Being drawn into ImGuiWindow* HoveredWindow; // Will catch mouse inputs ImGuiWindow* HoveredRootWindow; // Will catch mouse inputs (for focus/move only) @@ -996,6 +995,7 @@ struct ImGuiContext ImGuiViewportP* MouseViewport; ImGuiViewportP* MouseLastHoveredViewport; // Last known viewport that was hovered by mouse (even if we are not hovering any viewport any more) + honoring the _NoInputs flag. ImGuiID PlatformLastFocusedViewport; // Record of last focused platform window/viewport, when this changes we stamp the viewport as front-most + int ViewportFrontMostStampCount; // Every time the front-most window changes, we stamp its viewport with an incrementing counter // Navigation data (for gamepad/keyboard) ImGuiWindow* NavWindow; // Focused window for navigation. Could be called 'FocusWindow' @@ -1141,7 +1141,6 @@ struct ImGuiContext FrameCount = 0; FrameCountEnded = FrameCountPlatformEnded = FrameCountRendered = -1; WindowsActiveCount = 0; - WindowsFrontMostStampCount = 0; CurrentWindow = NULL; HoveredWindow = NULL; HoveredRootWindow = NULL; @@ -1175,6 +1174,7 @@ struct ImGuiContext CurrentViewport = NULL; MouseViewport = MouseLastHoveredViewport = NULL; PlatformLastFocusedViewport = 0; + ViewportFrontMostStampCount = 0; NavWindow = NULL; NavId = NavActivateId = NavActivateDownId = NavActivatePressedId = NavInputId = 0; From 9bf6509c6ef4ead477e5127f5729a55b971f8a4a Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 28 Mar 2019 18:43:27 +0100 Subject: [PATCH 477/828] Docking: Fixed focus restore lagging by a frame when a tab stops being submitted. (#2109) Building on a little build of technical debt there, should transition toward a more general docking-agnostic system (#2304) --- docs/TODO.txt | 4 +++- imgui.cpp | 20 ++++++++++++++------ imgui_internal.h | 1 + imgui_widgets.cpp | 14 ++++++++++++++ 4 files changed, 32 insertions(+), 7 deletions(-) diff --git a/docs/TODO.txt b/docs/TODO.txt index be4ef7cce611..b911e9c89d31 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -9,6 +9,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - doc/test: checklist app to verify binding/integration of imgui (test inputs, rendering, callback, etc.). - doc/tips: tips of the day: website? applet in imgui_club? + - window: preserve/restore relative focus ordering (persistent or not) (#2304) -> also see docking reference to same #. - window: calling SetNextWindowSize() every frame with <= 0 doesn't do anything, may be useful to allow (particularly when used for a single axis). (#690) - window: add a way for very transient windows (non-saved, temporary overlay over hundreds of objects) to "clean" up from the global window list. perhaps a lightweight explicit cleanup pass. - window: auto-fit feedback loop when user relies on any dynamic layout (window width multiplier, column) appears weird to end-user. clarify. @@ -130,6 +131,8 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - splitter/separator: formalize the splitter idiom into an official api (we want to handle n-way split) (#319) - dock: merge docking branch (#2109) + - dock: B: ordering currently held in tab bar should be implicitly held by windows themselves (also see #2304) + - dock: B- tab bar: the order/focus restoring code could be part of TabBar and not DockNode? (#8) - dock: B~ rework code to be able to lazily create tab bar instance in a single place. The _Unsorted tab flag could be replacing a trailing-counter in DockNode? - dock: B~ fully track windows/settings reference in dock nodes. perhaps find a representation that allows facilitate use of dock builder functions. - dock: B~ Unreal style document system (requires low-level controls of dockspace serialization fork/copy/delete). this is mostly working but the DockBuilderXXX api are not exposed/finished. @@ -148,7 +151,6 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - dock: B- dpi: look at interaction with the hi-dpi and multi-dpi stuff. - dock: B- tab bar: appearing on first frame with a dumb layout would do less harm that not appearing? (when behind dynamic branch) or store titles + render in EndTabBar() - dock: B- tab bar: make selected tab always shows its full title? - - dock: B- tab bar: the order/focus restoring code could be part of TabBar and not DockNode? (#8) - dock: B- nav: design interactions so nav controls can dock/undock - dock: B- dockspace: flag to lock the dock tree and/or sizes (ImGuiDockNodeFlags_Locked?) - dock: B- reintroduce collapsing a floating dock node. also collapsing a docked dock node! diff --git a/imgui.cpp b/imgui.cpp index e54f8e3c4570..8d3364b0111e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1040,7 +1040,7 @@ static float NavUpdatePageUpPageDown(int allowed_dir_flags); static inline void NavUpdateAnyRequestFlag(); static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, ImGuiID id); static ImVec2 NavCalcPreferredRefPos(); -static void NavSaveLastChildNavWindow(ImGuiWindow* nav_window); +static void NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window); static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window); // Misc @@ -8143,7 +8143,9 @@ void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags mov } } -static void ImGui::NavSaveLastChildNavWindow(ImGuiWindow* nav_window) +// FIXME: This could be replaced by updating a frame number in each window when (window == NavWindow) and (NavLayer == 0). +// This way we could find the last focused window among our children. It would be much less confusing this way? +static void ImGui::NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window) { ImGuiWindow* parent_window = nav_window; while (parent_window && (parent_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (parent_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0) @@ -8152,10 +8154,16 @@ static void ImGui::NavSaveLastChildNavWindow(ImGuiWindow* nav_window) parent_window->NavLastChildNavWindow = nav_window; } -// Call when we are expected to land on Layer 0 after FocusWindow() +// Restore the last focused child. +// Call when we are expected to land on the Main Layer (0) after FocusWindow() static ImGuiWindow* ImGui::NavRestoreLastChildNavWindow(ImGuiWindow* window) { - return window->NavLastChildNavWindow ? window->NavLastChildNavWindow : window; + if (window->NavLastChildNavWindow && window->NavLastChildNavWindow->WasActive) + return window->NavLastChildNavWindow; + if (window->DockNodeAsHost && window->DockNodeAsHost->TabBar) + if (ImGuiTabItem* tab = TabBarFindMostRecentlySelectedTabForActiveWindow(window->DockNodeAsHost->TabBar)) + return tab->Window; + return window; } static void NavRestoreLayer(ImGuiNavLayer layer) @@ -8380,7 +8388,7 @@ static void ImGui::NavUpdate() // Store our return window (for returning from Layer 1 to Layer 0) and clear it as soon as we step back in our own Layer 0 if (g.NavWindow) - NavSaveLastChildNavWindow(g.NavWindow); + NavSaveLastChildNavWindowIntoParent(g.NavWindow); if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == 0) g.NavWindow->NavLastChildNavWindow = NULL; @@ -8833,7 +8841,7 @@ static void ImGui::NavUpdateWindowing() // Move to parent menu if necessary ImGuiWindow* new_nav_window = g.NavWindow; while (new_nav_window->ParentWindow - && (new_nav_window->DC.NavLayerActiveMask & (1 << 1)) == 0 + && (new_nav_window->DC.NavLayerActiveMask & (1 << ImGuiNavLayer_Menu)) == 0 && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0) new_nav_window = new_nav_window->ParentWindow; diff --git a/imgui_internal.h b/imgui_internal.h index 89d0df5b8818..68c803445952 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1707,6 +1707,7 @@ namespace ImGui // Tab Bars IMGUI_API bool BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& bb, ImGuiTabBarFlags flags, ImGuiDockNode* dock_node); IMGUI_API ImGuiTabItem* TabBarFindTabByID(ImGuiTabBar* tab_bar, ImGuiID tab_id); + IMGUI_API ImGuiTabItem* TabBarFindMostRecentlySelectedTabForActiveWindow(ImGuiTabBar* tab_bar); IMGUI_API void TabBarAddTab(ImGuiTabBar* tab_bar, ImGuiTabItemFlags tab_flags, ImGuiWindow* window); IMGUI_API void TabBarRemoveTab(ImGuiTabBar* tab_bar, ImGuiID tab_id); IMGUI_API void TabBarCloseTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 031a9fd844ff..3dcb50967a67 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6455,6 +6455,20 @@ ImGuiTabItem* ImGui::TabBarFindTabByID(ImGuiTabBar* tab_bar, ImGuiID tab_id) return NULL; } +// FIXME: See references to #2304 in TODO.txt +ImGuiTabItem* ImGui::TabBarFindMostRecentlySelectedTabForActiveWindow(ImGuiTabBar* tab_bar) +{ + ImGuiTabItem* most_recently_selected_tab = NULL; + for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++) + { + ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; + if (most_recently_selected_tab == NULL || most_recently_selected_tab->LastFrameSelected < tab->LastFrameSelected) + if (tab->Window && tab->Window->WasActive) + most_recently_selected_tab = tab; + } + return most_recently_selected_tab; +} + // The purpose of this call is to register tab in advance so we can control their order at the time they appear. // Otherwise calling this is unnecessary as tabs are appending as needed by the BeginTabItem() function. void ImGui::TabBarAddTab(ImGuiTabBar* tab_bar, ImGuiTabItemFlags tab_flags, ImGuiWindow* window) From 9ba64f9fe314ee684f566cd95da15054bd12e039 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 4 Apr 2019 22:02:36 +0200 Subject: [PATCH 478/828] Viewport: Fixed PushClipRectFullScreen() missing out on negative coordinates. Among other things, the outer highlight during CTRL+Tab wouldn't appear in negative coordinates monitors. (~#2176, #1542) --- imgui.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index f2cfc5ec9b53..5db1d8a9d82b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3625,10 +3625,10 @@ void ImGui::NewFrame() g.IO.Fonts->Locked = true; SetCurrentFont(GetDefaultFont()); IM_ASSERT(g.Font->IsLoaded()); - ImVec2 virtual_space_max(0,0); + ImRect virtual_space(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX); for (int n = 0; n < g.Viewports.Size; n++) - virtual_space_max = ImMax(virtual_space_max, g.Viewports[n]->Pos + g.Viewports[n]->Size); - g.DrawListSharedData.ClipRectFullscreen = ImVec4(0.0f, 0.0f, virtual_space_max.x, virtual_space_max.y); + virtual_space.Add(g.Viewports[n]->GetRect()); + g.DrawListSharedData.ClipRectFullscreen = ImVec4(virtual_space.Min.x, virtual_space.Min.y, virtual_space.Max.x, virtual_space.Max.y); g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol; // Mark rendering data as invalid to prevent user who may have a handle on it to use it. @@ -10334,6 +10334,8 @@ ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const // We normally setup for all viewports in NewFrame() but here need to handle the mid-frame creation of a new viewport. // We need to extend the fullscreen clip rect so the OverlayDrawList clip is correct for that the first frame + g.DrawListSharedData.ClipRectFullscreen.x = ImMin(g.DrawListSharedData.ClipRectFullscreen.x, viewport->Pos.x); + g.DrawListSharedData.ClipRectFullscreen.y = ImMin(g.DrawListSharedData.ClipRectFullscreen.y, viewport->Pos.y); g.DrawListSharedData.ClipRectFullscreen.z = ImMax(g.DrawListSharedData.ClipRectFullscreen.z, viewport->Pos.x + viewport->Size.x); g.DrawListSharedData.ClipRectFullscreen.w = ImMax(g.DrawListSharedData.ClipRectFullscreen.w, viewport->Pos.y + viewport->Size.y); From 8ec24036d70d653a3eb8615c7dffc933acf483b8 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 5 Apr 2019 00:00:29 +0200 Subject: [PATCH 479/828] Vulkan: Viewports: Removed redundant field. --- examples/imgui_impl_vulkan.cpp | 20 ++++++++++---------- examples/imgui_impl_vulkan.h | 1 - 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/examples/imgui_impl_vulkan.cpp b/examples/imgui_impl_vulkan.cpp index ae5a17375c76..5956571b337a 100644 --- a/examples/imgui_impl_vulkan.cpp +++ b/examples/imgui_impl_vulkan.cpp @@ -783,7 +783,7 @@ void ImGui_ImplVulkan_NewFrame() { } -// Note: this has no effect in the 'master' branch, but multi-viewports needs this to recreate swap-chains. +// FIXME-VIEWPORT: Need to recreate all swap chains? void ImGui_ImplVulkan_SetSwapChainMinImageCount(int min_image_count) { IM_ASSERT(min_image_count >= 2); @@ -1225,8 +1225,8 @@ static void ImGui_ImplVulkan_RenderWindow(ImGuiViewport* viewport, void*) ImGui_ImplVulkan_InitInfo* v = &g_VulkanInitInfo; VkResult err; + ImGui_ImplVulkanH_Frame* fd = &wd->Frames[wd->FrameIndex]; { - ImGui_ImplVulkanH_Frame* fd = &wd->Frames[wd->FrameIndex]; for (;;) { err = vkWaitForFences(v->Device, 1, &fd->Fence, VK_TRUE, 100); @@ -1235,10 +1235,10 @@ static void ImGui_ImplVulkan_RenderWindow(ImGuiViewport* viewport, void*) check_vk_result(err); } { - err = vkAcquireNextImageKHR(v->Device, wd->Swapchain, UINT64_MAX, fd->ImageAcquiredSemaphore, VK_NULL_HANDLE, &fd->BackbufferCurrentIndex); + err = vkAcquireNextImageKHR(v->Device, wd->Swapchain, UINT64_MAX, fd->ImageAcquiredSemaphore, VK_NULL_HANDLE, &wd->FrameIndex); check_vk_result(err); + fd = &wd->Frames[wd->FrameIndex]; } - IM_ASSERT(wd->FrameIndex == fd->BackbufferCurrentIndex); // FIXME { err = vkResetCommandPool(v->Device, fd->CommandPool, 0); check_vk_result(err); @@ -1255,7 +1255,7 @@ static void ImGui_ImplVulkan_RenderWindow(ImGuiViewport* viewport, void*) VkRenderPassBeginInfo info = {}; info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; info.renderPass = wd->RenderPass; - info.framebuffer = wd->Frames[fd->BackbufferCurrentIndex].Framebuffer; + info.framebuffer = fd->Framebuffer; info.renderArea.extent.width = wd->Width; info.renderArea.extent.height = wd->Height; info.clearValueCount = (viewport->Flags & ImGuiViewportFlags_NoRendererClear) ? 0 : 1; @@ -1264,7 +1264,6 @@ static void ImGui_ImplVulkan_RenderWindow(ImGuiViewport* viewport, void*) } } - ImGui_ImplVulkanH_Frame* fd = &wd->Frames[wd->FrameIndex]; ImGui_ImplVulkan_RenderDrawData(viewport->DrawData, fd->CommandBuffer, &fd->RenderBuffers); { @@ -1298,19 +1297,20 @@ static void ImGui_ImplVulkan_SwapBuffers(ImGuiViewport* viewport, void*) ImGui_ImplVulkan_InitInfo* v = &g_VulkanInitInfo; VkResult err; - uint32_t PresentIndex = wd->FrameIndex; + uint32_t present_index = wd->FrameIndex; - ImGui_ImplVulkanH_Frame* fd = &wd->Frames[PresentIndex]; + ImGui_ImplVulkanH_Frame* fd = &wd->Frames[present_index]; VkPresentInfoKHR info = {}; info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; info.waitSemaphoreCount = 1; info.pWaitSemaphores = &fd->RenderCompleteSemaphore; info.swapchainCount = 1; info.pSwapchains = &wd->Swapchain; - info.pImageIndices = &fd->BackbufferCurrentIndex; + info.pImageIndices = &present_index; err = vkQueuePresentKHR(v->Queue, &info); check_vk_result(err); - wd->FrameIndex = (wd->FrameIndex + 1) % wd->FramesQueueSize; + + wd->FrameIndex = (wd->FrameIndex + 1) % wd->FramesQueueSize; // This is for the next vkWaitForFences() } void ImGui_ImplVulkan_InitPlatformInterface() diff --git a/examples/imgui_impl_vulkan.h b/examples/imgui_impl_vulkan.h index 56c237d636c5..c160b0609f3a 100644 --- a/examples/imgui_impl_vulkan.h +++ b/examples/imgui_impl_vulkan.h @@ -106,7 +106,6 @@ struct ImGui_ImplVulkanH_Frame VkSemaphore RenderCompleteSemaphore; VkImage Backbuffer; VkImageView BackbufferView; - uint32_t BackbufferCurrentIndex; VkFramebuffer Framebuffer; ImGui_ImplVulkan_FrameRenderBuffers RenderBuffers; }; From 01de69de366bac816328a112dce4ffcff59ddee8 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 5 Apr 2019 00:25:42 +0200 Subject: [PATCH 480/828] Vulkan: Note for unsupported feature with multi-viewports. (#2071) --- examples/imgui_impl_vulkan.cpp | 4 +++- examples/imgui_impl_vulkan.h | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/imgui_impl_vulkan.cpp b/examples/imgui_impl_vulkan.cpp index 5956571b337a..43d8d4d96830 100644 --- a/examples/imgui_impl_vulkan.cpp +++ b/examples/imgui_impl_vulkan.cpp @@ -783,10 +783,12 @@ void ImGui_ImplVulkan_NewFrame() { } -// FIXME-VIEWPORT: Need to recreate all swap chains? void ImGui_ImplVulkan_SetSwapChainMinImageCount(int min_image_count) { IM_ASSERT(min_image_count >= 2); + if (g_VulkanInitInfo.MinImageCount == min_image_count) + return; + IM_ASSERT(0); // FIXME-VIEWPORT: Need to recreate all swap chains? g_VulkanInitInfo.MinImageCount = min_image_count; } diff --git a/examples/imgui_impl_vulkan.h b/examples/imgui_impl_vulkan.h index c160b0609f3a..1b156fec177c 100644 --- a/examples/imgui_impl_vulkan.h +++ b/examples/imgui_impl_vulkan.h @@ -61,7 +61,7 @@ IMGUI_IMPL_API void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, V IMGUI_IMPL_API bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer); IMGUI_IMPL_API void ImGui_ImplVulkan_DestroyFontUploadObjects(); IMGUI_IMPL_API void ImGui_ImplVulkan_DestroyFrameRenderBuffers(VkInstance instance, VkDevice device, ImGui_ImplVulkan_FrameRenderBuffers* buffers, const VkAllocationCallbacks* allocator); -IMGUI_IMPL_API void ImGui_ImplVulkan_SetSwapChainMinImageCount(int frames_queue_size); // To override MinImageCount after initialization +IMGUI_IMPL_API void ImGui_ImplVulkan_SetSwapChainMinImageCount(int min_image_count); // To override MinImageCount after initialization // Called by ImGui_ImplVulkan_Init(), might be useful elsewhere. IMGUI_IMPL_API bool ImGui_ImplVulkan_CreateDeviceObjects(); From 1ba79baab5888ce103be05d2270340841812a534 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 5 Apr 2019 16:33:41 +0200 Subject: [PATCH 481/828] Vulkan, Viewports: Fixed ImGui_ImplVulkan_SetWindowSize() not recreating command-buffers, fence etc. (#2472, #2461, #2071) --- examples/imgui_impl_vulkan.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/imgui_impl_vulkan.cpp b/examples/imgui_impl_vulkan.cpp index 43d8d4d96830..8767cb2f6f2b 100644 --- a/examples/imgui_impl_vulkan.cpp +++ b/examples/imgui_impl_vulkan.cpp @@ -1217,7 +1217,7 @@ static void ImGui_ImplVulkan_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) return; ImGui_ImplVulkan_InitInfo* v = &g_VulkanInitInfo; data->Window.ClearEnable = (viewport->Flags & ImGuiViewportFlags_NoRendererClear) ? false : true; - ImGui_ImplVulkanH_CreateWindowSwapChain(v->Instance, v->PhysicalDevice, v->Device, &data->Window, v->Allocator, (int)size.x, (int)size.y, v->MinImageCount); + ImGui_ImplVulkanH_CreateWindow(v->Instance, v->PhysicalDevice, v->Device, &data->Window, v->QueueFamily, v->Allocator, (int)size.x, (int)size.y, v->MinImageCount); } static void ImGui_ImplVulkan_RenderWindow(ImGuiViewport* viewport, void*) From 9acb158990ea32cb138801430423a9d0e715354b Mon Sep 17 00:00:00 2001 From: MindSpunk Date: Fri, 5 Apr 2019 15:50:21 +1100 Subject: [PATCH 482/828] Vulkan, Viewports: Fix for resizing viewport windows crashing. (#2472) --- examples/example_glfw_vulkan/main.cpp | 9 +++--- examples/example_sdl_vulkan/main.cpp | 9 +++--- examples/imgui_impl_vulkan.cpp | 41 ++++++++++++++++++++------- examples/imgui_impl_vulkan.h | 11 +++++-- 4 files changed, 50 insertions(+), 20 deletions(-) diff --git a/examples/example_glfw_vulkan/main.cpp b/examples/example_glfw_vulkan/main.cpp index 0b3bd9555226..bf5414aa0bda 100644 --- a/examples/example_glfw_vulkan/main.cpp +++ b/examples/example_glfw_vulkan/main.cpp @@ -252,7 +252,8 @@ static void FrameRender(ImGui_ImplVulkanH_Window* wd) { VkResult err; - VkSemaphore& image_acquired_semaphore = wd->Frames[wd->FrameIndex].ImageAcquiredSemaphore; + VkSemaphore image_acquired_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].ImageAcquiredSemaphore; + VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore; err = vkAcquireNextImageKHR(g_Device, wd->Swapchain, UINT64_MAX, image_acquired_semaphore, VK_NULL_HANDLE, &wd->FrameIndex); check_vk_result(err); @@ -300,7 +301,7 @@ static void FrameRender(ImGui_ImplVulkanH_Window* wd) info.commandBufferCount = 1; info.pCommandBuffers = &fd->CommandBuffer; info.signalSemaphoreCount = 1; - info.pSignalSemaphores = &fd->RenderCompleteSemaphore; + info.pSignalSemaphores = &render_complete_semaphore; err = vkEndCommandBuffer(fd->CommandBuffer); check_vk_result(err); @@ -311,11 +312,11 @@ static void FrameRender(ImGui_ImplVulkanH_Window* wd) static void FramePresent(ImGui_ImplVulkanH_Window* wd) { - ImGui_ImplVulkanH_Frame* fd = &wd->Frames[wd->FrameIndex]; + VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore; VkPresentInfoKHR info = {}; info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; info.waitSemaphoreCount = 1; - info.pWaitSemaphores = &fd->RenderCompleteSemaphore; + info.pWaitSemaphores = &render_complete_semaphore; info.swapchainCount = 1; info.pSwapchains = &wd->Swapchain; info.pImageIndices = &wd->FrameIndex; diff --git a/examples/example_sdl_vulkan/main.cpp b/examples/example_sdl_vulkan/main.cpp index 37d5ace23fb4..837fc8808cb0 100644 --- a/examples/example_sdl_vulkan/main.cpp +++ b/examples/example_sdl_vulkan/main.cpp @@ -244,7 +244,8 @@ static void FrameRender(ImGui_ImplVulkanH_Window* wd) { VkResult err; - VkSemaphore& image_acquired_semaphore = wd->Frames[wd->FrameIndex].ImageAcquiredSemaphore; + VkSemaphore image_acquired_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].ImageAcquiredSemaphore; + VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore; err = vkAcquireNextImageKHR(g_Device, wd->Swapchain, UINT64_MAX, image_acquired_semaphore, VK_NULL_HANDLE, &wd->FrameIndex); check_vk_result(err); @@ -292,7 +293,7 @@ static void FrameRender(ImGui_ImplVulkanH_Window* wd) info.commandBufferCount = 1; info.pCommandBuffers = &fd->CommandBuffer; info.signalSemaphoreCount = 1; - info.pSignalSemaphores = &fd->RenderCompleteSemaphore; + info.pSignalSemaphores = &render_complete_semaphore; err = vkEndCommandBuffer(fd->CommandBuffer); check_vk_result(err); @@ -303,11 +304,11 @@ static void FrameRender(ImGui_ImplVulkanH_Window* wd) static void FramePresent(ImGui_ImplVulkanH_Window* wd) { - ImGui_ImplVulkanH_Frame* fd = &wd->Frames[wd->FrameIndex]; + VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore; VkPresentInfoKHR info = {}; info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; info.waitSemaphoreCount = 1; - info.pWaitSemaphores = &fd->RenderCompleteSemaphore; + info.pWaitSemaphores = &render_complete_semaphore; info.swapchainCount = 1; info.pSwapchains = &wd->Swapchain; info.pImageIndices = &wd->FrameIndex; diff --git a/examples/imgui_impl_vulkan.cpp b/examples/imgui_impl_vulkan.cpp index 8767cb2f6f2b..88e7e0dc6c64 100644 --- a/examples/imgui_impl_vulkan.cpp +++ b/examples/imgui_impl_vulkan.cpp @@ -65,6 +65,7 @@ static VkBuffer g_UploadBuffer = VK_NULL_HANDLE; // Forward Declarations void ImGui_ImplVulkanH_DestroyFrame(VkInstance instance, VkDevice device, ImGui_ImplVulkanH_Frame* fd, const VkAllocationCallbacks* allocator); +void ImGui_ImplVulkanH_DestroyFrameSemaphores(VkInstance instance, VkDevice device, ImGui_ImplVulkanH_FrameSemaphores* fsd, const VkAllocationCallbacks* allocator); void ImGui_ImplVulkanH_CreateWindowSwapChain(VkInstance instance, VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count); void ImGui_ImplVulkanH_CreateWindowCommandBuffers(VkInstance instance, VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, uint32_t queue_family, const VkAllocationCallbacks* allocator); @@ -902,6 +903,7 @@ void ImGui_ImplVulkanH_CreateWindowCommandBuffers(VkInstance instance, VkPhysica for (uint32_t i = 0; i < wd->FramesQueueSize; i++) { ImGui_ImplVulkanH_Frame* fd = &wd->Frames[i]; + ImGui_ImplVulkanH_FrameSemaphores* fsd = &wd->FrameSemaphores[i]; { VkCommandPoolCreateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; @@ -929,9 +931,9 @@ void ImGui_ImplVulkanH_CreateWindowCommandBuffers(VkInstance instance, VkPhysica { VkSemaphoreCreateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - err = vkCreateSemaphore(device, &info, allocator, &fd->ImageAcquiredSemaphore); + err = vkCreateSemaphore(device, &info, allocator, &fsd->ImageAcquiredSemaphore); check_vk_result(err); - err = vkCreateSemaphore(device, &info, allocator, &fd->RenderCompleteSemaphore); + err = vkCreateSemaphore(device, &info, allocator, &fsd->RenderCompleteSemaphore); check_vk_result(err); } } @@ -960,9 +962,14 @@ void ImGui_ImplVulkanH_CreateWindowSwapChain(VkInstance instance, VkPhysicalDevi // We don't use ImGui_ImplVulkanH_DestroyWindow() because we want to preserve the old swapchain to create the new one. // Destroy old Framebuffer for (uint32_t i = 0; i < wd->FramesQueueSize; i++) + { ImGui_ImplVulkanH_DestroyFrame(instance, device, &wd->Frames[i], allocator); + ImGui_ImplVulkanH_DestroyFrameSemaphores(instance, device, &wd->FrameSemaphores[i], allocator); + } delete[] wd->Frames; + delete[] wd->FrameSemaphores; wd->Frames = NULL; + wd->FrameSemaphores = NULL; wd->FramesQueueSize = 0; if (wd->RenderPass) vkDestroyRenderPass(device, wd->RenderPass, allocator); @@ -1017,7 +1024,9 @@ void ImGui_ImplVulkanH_CreateWindowSwapChain(VkInstance instance, VkPhysicalDevi IM_ASSERT(wd->Frames == NULL); wd->Frames = new ImGui_ImplVulkanH_Frame[wd->FramesQueueSize]; + wd->FrameSemaphores = new ImGui_ImplVulkanH_FrameSemaphores[wd->FramesQueueSize]; memset(wd->Frames, 0, sizeof(wd->Frames[0]) * wd->FramesQueueSize); + memset(wd->FrameSemaphores, 0, sizeof(wd->FrameSemaphores[0]) * wd->FramesQueueSize); for (uint32_t i = 0; i < wd->FramesQueueSize; i++) wd->Frames[i].Backbuffer = backbuffers[i]; } @@ -1115,9 +1124,14 @@ void ImGui_ImplVulkanH_DestroyWindow(VkInstance instance, VkDevice device, ImGui //vkQueueWaitIdle(g_Queue); for (uint32_t i = 0; i < wd->FramesQueueSize; i++) + { ImGui_ImplVulkanH_DestroyFrame(instance, device, &wd->Frames[i], allocator); + ImGui_ImplVulkanH_DestroyFrameSemaphores(instance, device, &wd->FrameSemaphores[i], allocator); + } delete[] wd->Frames; + delete[] wd->FrameSemaphores; wd->Frames = NULL; + wd->FrameSemaphores = NULL; vkDestroyRenderPass(device, wd->RenderPass, allocator); vkDestroySwapchainKHR(device, wd->Swapchain, allocator); vkDestroySurfaceKHR(instance, wd->Surface, allocator); @@ -1125,18 +1139,23 @@ void ImGui_ImplVulkanH_DestroyWindow(VkInstance instance, VkDevice device, ImGui *wd = ImGui_ImplVulkanH_Window(); } +void ImGui_ImplVulkanH_DestroyFrameSemaphores(VkInstance instance, VkDevice device, ImGui_ImplVulkanH_FrameSemaphores* fsd, const VkAllocationCallbacks* allocator) +{ + (void)instance; + vkDestroySemaphore(device, fsd->ImageAcquiredSemaphore, allocator); + vkDestroySemaphore(device, fsd->RenderCompleteSemaphore, allocator); + fsd->ImageAcquiredSemaphore = fsd->RenderCompleteSemaphore = VK_NULL_HANDLE; +} + void ImGui_ImplVulkanH_DestroyFrame(VkInstance instance, VkDevice device, ImGui_ImplVulkanH_Frame* fd, const VkAllocationCallbacks* allocator) { (void)instance; vkDestroyFence(device, fd->Fence, allocator); vkFreeCommandBuffers(device, fd->CommandPool, 1, &fd->CommandBuffer); vkDestroyCommandPool(device, fd->CommandPool, allocator); - vkDestroySemaphore(device, fd->ImageAcquiredSemaphore, allocator); - vkDestroySemaphore(device, fd->RenderCompleteSemaphore, allocator); fd->Fence = VK_NULL_HANDLE; fd->CommandBuffer = VK_NULL_HANDLE; fd->CommandPool = VK_NULL_HANDLE; - fd->ImageAcquiredSemaphore = fd->RenderCompleteSemaphore = VK_NULL_HANDLE; vkDestroyImageView(device, fd->BackbufferView, allocator); vkDestroyFramebuffer(device, fd->Framebuffer, allocator); @@ -1228,6 +1247,7 @@ static void ImGui_ImplVulkan_RenderWindow(ImGuiViewport* viewport, void*) VkResult err; ImGui_ImplVulkanH_Frame* fd = &wd->Frames[wd->FrameIndex]; + ImGui_ImplVulkanH_FrameSemaphores* fsd = &wd->FrameSemaphores[wd->SemaphoreIndex]; { for (;;) { @@ -1237,7 +1257,7 @@ static void ImGui_ImplVulkan_RenderWindow(ImGuiViewport* viewport, void*) check_vk_result(err); } { - err = vkAcquireNextImageKHR(v->Device, wd->Swapchain, UINT64_MAX, fd->ImageAcquiredSemaphore, VK_NULL_HANDLE, &wd->FrameIndex); + err = vkAcquireNextImageKHR(v->Device, wd->Swapchain, UINT64_MAX, fsd->ImageAcquiredSemaphore, VK_NULL_HANDLE, &wd->FrameIndex); check_vk_result(err); fd = &wd->Frames[wd->FrameIndex]; } @@ -1275,12 +1295,12 @@ static void ImGui_ImplVulkan_RenderWindow(ImGuiViewport* viewport, void*) VkSubmitInfo info = {}; info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; info.waitSemaphoreCount = 1; - info.pWaitSemaphores = &fd->ImageAcquiredSemaphore; + info.pWaitSemaphores = &fsd->ImageAcquiredSemaphore; info.pWaitDstStageMask = &wait_stage; info.commandBufferCount = 1; info.pCommandBuffers = &fd->CommandBuffer; info.signalSemaphoreCount = 1; - info.pSignalSemaphores = &fd->RenderCompleteSemaphore; + info.pSignalSemaphores = &fsd->RenderCompleteSemaphore; err = vkEndCommandBuffer(fd->CommandBuffer); check_vk_result(err); @@ -1301,11 +1321,11 @@ static void ImGui_ImplVulkan_SwapBuffers(ImGuiViewport* viewport, void*) VkResult err; uint32_t present_index = wd->FrameIndex; - ImGui_ImplVulkanH_Frame* fd = &wd->Frames[present_index]; + ImGui_ImplVulkanH_FrameSemaphores* fsd = &wd->FrameSemaphores[wd->SemaphoreIndex]; VkPresentInfoKHR info = {}; info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; info.waitSemaphoreCount = 1; - info.pWaitSemaphores = &fd->RenderCompleteSemaphore; + info.pWaitSemaphores = &fsd->RenderCompleteSemaphore; info.swapchainCount = 1; info.pSwapchains = &wd->Swapchain; info.pImageIndices = &present_index; @@ -1313,6 +1333,7 @@ static void ImGui_ImplVulkan_SwapBuffers(ImGuiViewport* viewport, void*) check_vk_result(err); wd->FrameIndex = (wd->FrameIndex + 1) % wd->FramesQueueSize; // This is for the next vkWaitForFences() + wd->SemaphoreIndex = (wd->SemaphoreIndex + 1) % wd->FramesQueueSize; // Now we can use the next set of semaphores } void ImGui_ImplVulkan_InitPlatformInterface() diff --git a/examples/imgui_impl_vulkan.h b/examples/imgui_impl_vulkan.h index 1b156fec177c..c632d62c9c3b 100644 --- a/examples/imgui_impl_vulkan.h +++ b/examples/imgui_impl_vulkan.h @@ -102,14 +102,18 @@ struct ImGui_ImplVulkanH_Frame VkCommandPool CommandPool; VkCommandBuffer CommandBuffer; VkFence Fence; - VkSemaphore ImageAcquiredSemaphore; - VkSemaphore RenderCompleteSemaphore; VkImage Backbuffer; VkImageView BackbufferView; VkFramebuffer Framebuffer; ImGui_ImplVulkan_FrameRenderBuffers RenderBuffers; }; +struct ImGui_ImplVulkanH_FrameSemaphores +{ + VkSemaphore ImageAcquiredSemaphore; + VkSemaphore RenderCompleteSemaphore; +}; + // Helper structure to hold the data needed by one rendering context into one OS window // (Used by example's main.cpp. Used by multi-viewport features. Probably NOT used by your own engine/app.) struct ImGui_ImplVulkanH_Window @@ -125,7 +129,10 @@ struct ImGui_ImplVulkanH_Window VkClearValue ClearValue; uint32_t FrameIndex; // Current frame being rendered to (0 <= FrameIndex < FrameInFlightCount) uint32_t FramesQueueSize; // Number of simultaneous in-flight frames (returned by vkGetSwapchainImagesKHR, usually derived from min_image_count) + uint32_t SemaphoreIndex; // Current set of swapchain wait semaphores we're using (needs to be distinct from per frame data) ImGui_ImplVulkanH_Frame* Frames; + ImGui_ImplVulkanH_FrameSemaphores* FrameSemaphores; + ImGui_ImplVulkanH_Window() { From d61caf57145294fe98445eff3f21e24c71bd58c4 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 5 Apr 2019 18:52:40 +0200 Subject: [PATCH 483/828] Vulkan, Viewports: ImGui_ImplVulkan_RenderDrawData and renderer back-end automatically manage ImGui_ImplVulkanH_WindowRenderBuffers for each viewports so user doesn't have to do it. (#2461, #2348, #2378, #2097) --- examples/imgui_impl_vulkan.cpp | 59 ++++++++++++++++++++++------------ examples/imgui_impl_vulkan.h | 4 --- imgui.cpp | 1 + imgui.h | 3 +- 4 files changed, 42 insertions(+), 25 deletions(-) diff --git a/examples/imgui_impl_vulkan.cpp b/examples/imgui_impl_vulkan.cpp index 1c9e7b51aaf4..2dc93b3ec2d1 100644 --- a/examples/imgui_impl_vulkan.cpp +++ b/examples/imgui_impl_vulkan.cpp @@ -65,6 +65,17 @@ struct ImGui_ImplVulkanH_WindowRenderBuffers ImGui_ImplVulkanH_FrameRenderBuffers* FrameRenderBuffers; }; +// For multi-viewport support +struct ImGuiViewportDataVulkan +{ + bool WindowOwned; + ImGui_ImplVulkanH_Window Window; // Used by secondary viewports only + ImGui_ImplVulkanH_WindowRenderBuffers RenderBuffers; // Used by all viewports + + ImGuiViewportDataVulkan() { WindowOwned = false; memset(&RenderBuffers, 0, sizeof(RenderBuffers)); } + ~ImGuiViewportDataVulkan() { } +}; + // Vulkan data static ImGui_ImplVulkan_InitInfo g_VulkanInitInfo = {}; static VkRenderPass g_RenderPass = VK_NULL_HANDLE; @@ -83,14 +94,14 @@ static VkImageView g_FontView = VK_NULL_HANDLE; static VkDeviceMemory g_UploadBufferMemory = VK_NULL_HANDLE; static VkBuffer g_UploadBuffer = VK_NULL_HANDLE; -// Render buffers -static ImGui_ImplVulkanH_WindowRenderBuffers g_MainWindowRenderBuffers; - // Forward Declarations +bool ImGui_ImplVulkan_CreateDeviceObjects(); +void ImGui_ImplVulkan_DestroyDeviceObjects(); void ImGui_ImplVulkanH_DestroyFrame(VkDevice device, ImGui_ImplVulkanH_Frame* fd, const VkAllocationCallbacks* allocator); void ImGui_ImplVulkanH_DestroyFrameSemaphores(VkDevice device, ImGui_ImplVulkanH_FrameSemaphores* fsd, const VkAllocationCallbacks* allocator); void ImGui_ImplVulkanH_DestroyFrameRenderBuffers(VkDevice device, ImGui_ImplVulkanH_FrameRenderBuffers* buffers, const VkAllocationCallbacks* allocator); void ImGui_ImplVulkanH_DestroyWindowRenderBuffers(VkDevice device, ImGui_ImplVulkanH_WindowRenderBuffers* buffers, const VkAllocationCallbacks* allocator); +void ImGui_ImplVulkanH_DestroyAllViewportsRenderBuffers(VkDevice device, const VkAllocationCallbacks* allocator); void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count); void ImGui_ImplVulkanH_CreateWindowCommandBuffers(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, uint32_t queue_family, const VkAllocationCallbacks* allocator); @@ -274,8 +285,10 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm ImGui_ImplVulkan_InitInfo* v = &g_VulkanInitInfo; - // Allocate array to store enough vertex/index buffers - ImGui_ImplVulkanH_WindowRenderBuffers* wrb = &g_MainWindowRenderBuffers; + // Allocate array to store enough vertex/index buffers. Each unique viewport gets its own storage. + ImGuiViewportDataVulkan* viewport_renderer_data = (ImGuiViewportDataVulkan*)draw_data->OwnerViewport->RendererUserData; + IM_ASSERT(viewport_renderer_data != NULL); + ImGui_ImplVulkanH_WindowRenderBuffers* wrb = &viewport_renderer_data->RenderBuffers; if (wrb->FrameRenderBuffers == NULL) { wrb->Index = 0; @@ -777,7 +790,7 @@ void ImGui_ImplVulkan_DestroyFontUploadObjects() void ImGui_ImplVulkan_DestroyDeviceObjects() { ImGui_ImplVulkan_InitInfo* v = &g_VulkanInitInfo; - ImGui_ImplVulkanH_DestroyWindowRenderBuffers(v->Device, &g_MainWindowRenderBuffers, v->Allocator); + ImGui_ImplVulkanH_DestroyAllViewportsRenderBuffers(v->Device, v->Allocator); ImGui_ImplVulkan_DestroyFontUploadObjects(); if (g_FontView) { vkDestroyImageView(v->Device, g_FontView, v->Allocator); g_FontView = VK_NULL_HANDLE; } @@ -809,6 +822,10 @@ bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info, VkRenderPass rend g_RenderPass = render_pass; ImGui_ImplVulkan_CreateDeviceObjects(); + // Our render function expect RendererUserData to be storing the window render buffer we need (for the main viewport we won't use ->Window) + ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + main_viewport->RendererUserData = IM_NEW(ImGuiViewportDataVulkan)(); + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) ImGui_ImplVulkan_InitPlatformInterface(); @@ -830,11 +847,13 @@ void ImGui_ImplVulkan_SetMinImageCount(uint32_t min_image_count) IM_ASSERT(min_image_count >= 2); if (g_VulkanInitInfo.MinImageCount == min_image_count) return; - IM_ASSERT(0); // FIXME-VIEWPORT: Need to recreate all swap chains? + + IM_ASSERT(0); // FIXME-VIEWPORT: Unsupported. Need to recreate all swap chains! ImGui_ImplVulkan_InitInfo* v = &g_VulkanInitInfo; VkResult err = vkDeviceWaitIdle(v->Device); check_vk_result(err); - ImGui_ImplVulkanH_DestroyWindowRenderBuffers(v->Device, &g_MainWindowRenderBuffers, v->Allocator); + + ImGui_ImplVulkanH_DestroyAllViewportsRenderBuffers(v->Device, v->Allocator); g_VulkanInitInfo.MinImageCount = min_image_count; } @@ -1211,22 +1230,19 @@ void ImGui_ImplVulkanH_DestroyWindowRenderBuffers(VkDevice device, ImGui_ImplVul buffers->Count = 0; } +void ImGui_ImplVulkanH_DestroyAllViewportsRenderBuffers(VkDevice device, const VkAllocationCallbacks* allocator) +{ + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + for (int n = 0; n < platform_io.Viewports.Size; n++) + if (ImGuiViewportDataVulkan* data = (ImGuiViewportDataVulkan*)platform_io.Viewports[n]->RendererUserData) + ImGui_ImplVulkanH_DestroyWindowRenderBuffers(device, &data->RenderBuffers, allocator); +} //-------------------------------------------------------------------------------------------------------- // MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT // This is an _advanced_ and _optional_ feature, allowing the back-end to create and handle multiple viewports simultaneously. // If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first.. //-------------------------------------------------------------------------------------------------------- -// FIXME-PLATFORM: Vulkan support unfinished -//-------------------------------------------------------------------------------------------------------- - -struct ImGuiViewportDataVulkan -{ - ImGui_ImplVulkanH_Window Window; - - ImGuiViewportDataVulkan() { } - ~ImGuiViewportDataVulkan() { } -}; static void ImGui_ImplVulkan_CreateWindow(ImGuiViewport* viewport) { @@ -1263,6 +1279,7 @@ static void ImGui_ImplVulkan_CreateWindow(ImGuiViewport* viewport) // Create SwapChain, RenderPass, Framebuffer, etc. wd->ClearEnable = (viewport->Flags & ImGuiViewportFlags_NoRendererClear) ? false : true; ImGui_ImplVulkanH_CreateWindow(v->Instance, v->PhysicalDevice, v->Device, wd, v->QueueFamily, v->Allocator, (int)viewport->Size.x, (int)viewport->Size.y, v->MinImageCount); + data->WindowOwned = true; } static void ImGui_ImplVulkan_DestroyWindow(ImGuiViewport* viewport) @@ -1271,7 +1288,9 @@ static void ImGui_ImplVulkan_DestroyWindow(ImGuiViewport* viewport) if (ImGuiViewportDataVulkan* data = (ImGuiViewportDataVulkan*)viewport->RendererUserData) { ImGui_ImplVulkan_InitInfo* v = &g_VulkanInitInfo; - ImGui_ImplVulkanH_DestroyWindow(v->Instance, v->Device, &data->Window, v->Allocator); + if (data->WindowOwned) + ImGui_ImplVulkanH_DestroyWindow(v->Instance, v->Device, &data->Window, v->Allocator); + ImGui_ImplVulkanH_DestroyWindowRenderBuffers(v->Device, &data->RenderBuffers, v->Allocator); IM_DELETE(data); } viewport->RendererUserData = NULL; @@ -1380,7 +1399,7 @@ static void ImGui_ImplVulkan_SwapBuffers(ImGuiViewport* viewport, void*) err = vkQueuePresentKHR(v->Queue, &info); check_vk_result(err); - wd->FrameIndex = (wd->FrameIndex + 1) % wd->ImageCount; // This is for the next vkWaitForFences() + wd->FrameIndex = (wd->FrameIndex + 1) % wd->ImageCount; // This is for the next vkWaitForFences() wd->SemaphoreIndex = (wd->SemaphoreIndex + 1) % wd->ImageCount; // Now we can use the next set of semaphores } diff --git a/examples/imgui_impl_vulkan.h b/examples/imgui_impl_vulkan.h index eba29c42d3b6..9ff70c019be6 100644 --- a/examples/imgui_impl_vulkan.h +++ b/examples/imgui_impl_vulkan.h @@ -49,10 +49,6 @@ IMGUI_IMPL_API bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer comm IMGUI_IMPL_API void ImGui_ImplVulkan_DestroyFontUploadObjects(); IMGUI_IMPL_API void ImGui_ImplVulkan_SetMinImageCount(uint32_t min_image_count); // To override MinImageCount after initialization (e.g. if swap chain is recreated) -// Called by ImGui_ImplVulkan_Init(), might be useful elsewhere. -IMGUI_IMPL_API bool ImGui_ImplVulkan_CreateDeviceObjects(); -IMGUI_IMPL_API void ImGui_ImplVulkan_DestroyDeviceObjects(); - //------------------------------------------------------------------------- // Internal / Miscellaneous Vulkan Helpers diff --git a/imgui.cpp b/imgui.cpp index c96b2d8aa4e5..51123a68f48e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4000,6 +4000,7 @@ static void SetupViewportDrawData(ImGuiViewportP* viewport, ImVectorDisplayPos = viewport->Pos; draw_data->DisplaySize = viewport->Size; draw_data->FramebufferScale = ImGui::GetIO().DisplayFramebufferScale; // FIXME-VIEWPORT: This may vary on a per-monitor/viewport basis? + draw_data->OwnerViewport = viewport; for (int n = 0; n < draw_lists->Size; n++) { draw_data->TotalVtxCount += draw_lists->Data[n]->VtxBuffer.Size; diff --git a/imgui.h b/imgui.h index f990e06602ff..fc9d4146ea89 100644 --- a/imgui.h +++ b/imgui.h @@ -2012,11 +2012,12 @@ struct ImDrawData ImVec2 DisplayPos; // Upper-left position of the viewport to render (== upper-left of the orthogonal projection matrix to use) ImVec2 DisplaySize; // Size of the viewport to render (== io.DisplaySize for the main viewport) (DisplayPos + DisplaySize == lower-right of the orthogonal projection matrix to use) ImVec2 FramebufferScale; // Amount of pixels for each unit of DisplaySize. Based on io.DisplayFramebufferScale. Generally (1,1) on normal display, (2,2) on OSX with Retina display. + ImGuiViewport* OwnerViewport; // Viewport carrying the ImDrawData instance, might be of use to the renderer (generally not). // Functions ImDrawData() { Valid = false; Clear(); } ~ImDrawData() { Clear(); } - void Clear() { Valid = false; CmdLists = NULL; CmdListsCount = TotalVtxCount = TotalIdxCount = 0; DisplayPos = DisplaySize = FramebufferScale = ImVec2(0.f, 0.f); } // The ImDrawList are owned by ImGuiContext! + void Clear() { Valid = false; CmdLists = NULL; CmdListsCount = TotalVtxCount = TotalIdxCount = 0; DisplayPos = DisplaySize = FramebufferScale = ImVec2(0.f, 0.f); OwnerViewport = NULL; } // The ImDrawList are owned by ImGuiContext! IMGUI_API void DeIndexAllBuffers(); // Helper to convert all buffers from indexed to non-indexed, in case you cannot render indexed. Note: this is slow and most likely a waste of resources. Always prefer indexed rendering! IMGUI_API void ScaleClipRects(const ImVec2& fb_scale); // Helper to scale the ClipRect field of each ImDrawCmd. Use if your final output buffer is at a different scale than ImGui expects, or if there is a difference between your window resolution and framebuffer resolution. }; From f3110a57cd6f8870e8191f7938365fe03222e899 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 11 Apr 2019 14:51:01 +0200 Subject: [PATCH 484/828] Docking: Fixed an issue where newly created dock node override hosted window pos/size (#2109, #2386) --- imgui.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 636318464b69..03dc2e270f89 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11171,6 +11171,7 @@ static void ImGui::DockContextBuildNodesFromSettings(ImGuiContext* ctx, ImGuiDoc node->Pos = ImVec2(settings->Pos.x, settings->Pos.y); node->Size = ImVec2(settings->Size.x, settings->Size.y); node->SizeRef = ImVec2(settings->SizeRef.x, settings->SizeRef.y); + node->AutorityForPos = node->AutorityForSize = node->AutorityForViewport = ImGuiDataAutority_DockNode; if (node->ParentNode && node->ParentNode->ChildNodes[0] == NULL) node->ParentNode->ChildNodes[0] = node; else if (node->ParentNode && node->ParentNode->ChildNodes[1] == NULL) @@ -11792,7 +11793,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) node->LastFocusedNodeID = results.FirstNodeWithWindows->ID; // Copy the window class from of our first window so it can be used for proper dock filtering. - // When node has mixed windows, prioritize the class with the most constraint (CompatibleWithClassZero = false) as the reference to copy. + // When node has mixed windows, prioritize the class with the most constraint (DockingAllowUnclassed = false) as the reference to copy. // FIXME-DOCK: We don't recurse properly, this code could be reworked to work from DockNodeUpdateScanRec. if (ImGuiDockNode* first_node_with_windows = results.FirstNodeWithWindows) { @@ -11836,7 +11837,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) } DockNodeHideHostWindow(node); - node->AutorityForPos = node->AutorityForSize = node->AutorityForViewport = ImGuiDataAutority_Auto; + node->AutorityForPos = node->AutorityForSize = node->AutorityForViewport = ImGuiDataAutority_Window; node->WantCloseAll = false; node->WantCloseTabID = 0; node->HasCloseButton = node->HasCollapseButton = false; @@ -13516,18 +13517,13 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) if (node == NULL) { node = DockContextAddNode(ctx, window->DockId); + node->AutorityForPos = node->AutorityForSize = node->AutorityForViewport = ImGuiDataAutority_Window; if (auto_dock_node) node->LastFrameAlive = g.FrameCount; } DockNodeAddWindow(node, window, true); IM_ASSERT(node == window->DockNode); - - // Fix an edge case with auto-resizing windows: if they are created on the same frame they are creating their dock node, - // we don't want their initial zero-size to spread to the DockNode. We preserve their size. - SetNextWindowPos(window->Pos); - SetNextWindowSize(window->SizeFull); - g.NextWindowData.PosUndock = false; } #if 0 From c6f1b7b92aa499d508a0ca825da7dbbe6ae7b105 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 12 Apr 2019 19:44:29 +0200 Subject: [PATCH 485/828] Tests: Added hook/tweaks for imgui-test engine. + Fixed warnings. --- imgui.cpp | 7 ++++++- imgui_internal.h | 2 ++ imgui_widgets.cpp | 7 +++++-- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 704ffc1d429c..d4d80d0b95c9 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2899,7 +2899,7 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg) #ifdef IMGUI_ENABLE_TEST_ENGINE if (id != 0) - ImGuiTestEngineHook_ItemAdd(&g, nav_bb_arg ? *nav_bb_arg : bb, id); + IMGUI_TEST_ENGINE_ITEM_ADD(nav_bb_arg ? *nav_bb_arg : bb, id); #endif // Clipping test @@ -6099,6 +6099,11 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DC.LastItemStatusFlags = IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0; window->DC.LastItemRect = title_bar_rect; } + +#ifdef IMGUI_ENABLE_TEST_ENGINE + if (!(window->Flags & ImGuiWindowFlags_NoTitleBar)) + IMGUI_TEST_ENGINE_ITEM_ADD(window->DC.LastItemRect, window->DC.LastItemId); +#endif } else { diff --git a/imgui_internal.h b/imgui_internal.h index b077bc656f53..5e64623f10b1 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1808,8 +1808,10 @@ extern void ImGuiTestEngineHook_PreNewFrame(ImGuiContext* ctx); extern void ImGuiTestEngineHook_PostNewFrame(ImGuiContext* ctx); extern void ImGuiTestEngineHook_ItemAdd(ImGuiContext* ctx, const ImRect& bb, ImGuiID id); extern void ImGuiTestEngineHook_ItemInfo(ImGuiContext* ctx, ImGuiID id, const char* label, ImGuiItemStatusFlags flags); +#define IMGUI_TEST_ENGINE_ITEM_ADD(_BB, _ID) ImGuiTestEngineHook_ItemAdd(&g, _BB, _ID) // Register status flags #define IMGUI_TEST_ENGINE_ITEM_INFO(_ID, _LABEL, _FLAGS) ImGuiTestEngineHook_ItemInfo(&g, _ID, _LABEL, _FLAGS) // Register status flags #else +#define IMGUI_TEST_ENGINE_ITEM_ADD(_BB, _ID) do { } while (0) #define IMGUI_TEST_ENGINE_ITEM_INFO(_ID, _LABEL, _FLAGS) do { } while (0) #endif diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index d75b4184e41b..136eb8e0f807 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -5123,7 +5123,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l SetItemAllowOverlap(); // In this branch, TreeNodeBehavior() cannot toggle the selection so this will never trigger. - if (selected != was_selected) + if (selected != was_selected) //-V547 window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_ToggledSelection; // Render @@ -5371,7 +5371,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl SetItemAllowOverlap(); // In this branch, Selectable() cannot toggle the selection so this will never trigger. - if (selected != was_selected) + if (selected != was_selected) //-V547 window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_ToggledSelection; // Render @@ -5395,6 +5395,8 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl // Automatically close popups if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_DontClosePopups) && !(window->DC.ItemFlags & ImGuiItemFlags_SelectableDontClosePopup)) CloseCurrentPopup(); + + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags); return pressed; } @@ -5744,6 +5746,7 @@ ImGuiMenuColumns::ImGuiMenuColumns() void ImGuiMenuColumns::Update(int count, float spacing, bool clear) { IM_ASSERT(count == IM_ARRAYSIZE(Pos)); + IM_UNUSED(count); Width = NextWidth = 0.0f; Spacing = spacing; if (clear) From 092426bed26dffef59243b2f255ff65b38dc2ece Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 12 Apr 2019 19:44:54 +0200 Subject: [PATCH 486/828] Docking: Hold Shift to force disable docking. (#2109) --- imgui.cpp | 4 ++-- imgui_internal.h | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index d4d80d0b95c9..0fd6d2ecf0a6 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6073,7 +6073,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) { // Docking: Dragging a dockable window (or any of its child) turns it into a drag and drop source. // We need to do this _before_ we overwrite window->DC.LastItemId below because BeginAsDockableDragDropSource() also overwrites it. - if ((g.ActiveId == window->MoveId) && ((g.IO.ConfigDockingWithShift && g.IO.KeyShift) || (!g.IO.ConfigDockingWithShift))) + if ((g.ActiveId == window->MoveId) && (g.IO.ConfigDockingWithShift == g.IO.KeyShift)) if ((window->Flags & ImGuiWindowFlags_NoMove) == 0) if ((window->RootWindow->Flags & (ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDocking)) == 0) BeginAsDockableDragDropSource(window); @@ -10862,7 +10862,6 @@ namespace ImGui static ImGuiDockNode* DockContextAddNode(ImGuiContext* ctx, ImGuiID id); static ImGuiID DockContextGenNodeID(ImGuiContext* ctx); static void DockContextRemoveNode(ImGuiContext* ctx, ImGuiDockNode* node, bool merge_sibling_into_parent_node); - static void DockContextQueueDock(ImGuiContext* ctx, ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, float split_ratio, bool split_outer); static void DockContextQueueNotifyRemovedNode(ImGuiContext* ctx, ImGuiDockNode* node); static void DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req); static void DockContextProcessUndockWindow(ImGuiContext* ctx, ImGuiWindow* window, bool clear_persistent_docking_ref = true); @@ -13635,6 +13634,7 @@ void ImGui::BeginAsDockableDragDropTarget(ImGuiWindow* window) ImGuiContext* ctx = GImGui; ImGuiContext& g = *ctx; + //IM_ASSERT(window->RootWindow == window); // May also be a DockSpace IM_ASSERT((window->Flags & ImGuiWindowFlags_NoDocking) == 0); if (!g.DragDropActive) return; diff --git a/imgui_internal.h b/imgui_internal.h index 5e64623f10b1..948588774a44 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1674,6 +1674,7 @@ namespace ImGui IMGUI_API void DockContextRebuild(ImGuiContext* ctx); IMGUI_API void DockContextNewFrameUpdateUndocking(ImGuiContext* ctx); IMGUI_API void DockContextNewFrameUpdateDocking(ImGuiContext* ctx); + IMGUI_API void DockContextQueueDock(ImGuiContext* ctx, ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, float split_ratio, bool split_outer); IMGUI_API void DockContextQueueUndockWindow(ImGuiContext* ctx, ImGuiWindow* window); IMGUI_API void DockContextQueueUndockNode(ImGuiContext* ctx, ImGuiDockNode* node); inline ImGuiDockNode* DockNodeGetRootNode(ImGuiDockNode* node) { while (node->ParentNode) node = node->ParentNode; return node; } @@ -1809,10 +1810,10 @@ extern void ImGuiTestEngineHook_PostNewFrame(ImGuiContext* ctx); extern void ImGuiTestEngineHook_ItemAdd(ImGuiContext* ctx, const ImRect& bb, ImGuiID id); extern void ImGuiTestEngineHook_ItemInfo(ImGuiContext* ctx, ImGuiID id, const char* label, ImGuiItemStatusFlags flags); #define IMGUI_TEST_ENGINE_ITEM_ADD(_BB, _ID) ImGuiTestEngineHook_ItemAdd(&g, _BB, _ID) // Register status flags -#define IMGUI_TEST_ENGINE_ITEM_INFO(_ID, _LABEL, _FLAGS) ImGuiTestEngineHook_ItemInfo(&g, _ID, _LABEL, _FLAGS) // Register status flags +#define IMGUI_TEST_ENGINE_ITEM_INFO(_ID, _LABEL, _FLAGS) ImGuiTestEngineHook_ItemInfo(&g, _ID, _LABEL, _FLAGS) // Register status flags #else #define IMGUI_TEST_ENGINE_ITEM_ADD(_BB, _ID) do { } while (0) -#define IMGUI_TEST_ENGINE_ITEM_INFO(_ID, _LABEL, _FLAGS) do { } while (0) +#define IMGUI_TEST_ENGINE_ITEM_INFO(_ID, _LABEL, _FLAGS) do { } while (0) #endif #ifdef __clang__ From 224f087a5f354393f2a4502869fe4b59a1469625 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 12 Apr 2019 23:26:46 +0200 Subject: [PATCH 487/828] Docking: Rename typo Autority -> Authority + Rename DockContextNewFrameUpdateDocking -> DockContextUpdateDocking. --- imgui.cpp | 64 ++++++++++++++++++++++++------------------------ imgui_internal.h | 21 ++++++++-------- 2 files changed, 43 insertions(+), 42 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 0fd6d2ecf0a6..3b543b900432 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3734,7 +3734,7 @@ void ImGui::NewFrame() // Undocking // (needs to be before UpdateMouseMovingWindowNewFrame so the window is already offset and following the mouse on the detaching frame) - DockContextNewFrameUpdateUndocking(&g); + DockContextUpdateUndocking(&g); // Find hovered window // (needs to be before UpdateMouseMovingWindowNewFrame so we fill g.HoveredWindowUnderMovingWindow on the mouse release frame) @@ -3810,7 +3810,7 @@ void ImGui::NewFrame() ClosePopupsOverWindow(g.NavWindow); // Docking - DockContextNewFrameUpdateDocking(&g); + DockContextUpdateDocking(&g); // Create implicit/fallback window - which we will only render it if the user has added something to it. // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags. @@ -10979,7 +10979,7 @@ void ImGui::DockContextRebuild(ImGuiContext* ctx) } // Docking context update function, called by NewFrame() -void ImGui::DockContextNewFrameUpdateUndocking(ImGuiContext* ctx) +void ImGui::DockContextUpdateUndocking(ImGuiContext* ctx) { ImGuiContext& g = *ctx; ImGuiDockContext* dc = ctx->DockContext; @@ -11023,7 +11023,7 @@ void ImGui::DockContextNewFrameUpdateUndocking(ImGuiContext* ctx) } // Docking context update function, called by NewFrame() -void ImGui::DockContextNewFrameUpdateDocking(ImGuiContext* ctx) +void ImGui::DockContextUpdateDocking(ImGuiContext* ctx) { ImGuiContext& g = *ctx; ImGuiDockContext* dc = ctx->DockContext; @@ -11181,7 +11181,7 @@ static void ImGui::DockContextBuildNodesFromSettings(ImGuiContext* ctx, ImGuiDoc node->Pos = ImVec2(settings->Pos.x, settings->Pos.y); node->Size = ImVec2(settings->Size.x, settings->Size.y); node->SizeRef = ImVec2(settings->SizeRef.x, settings->SizeRef.y); - node->AutorityForPos = node->AutorityForSize = node->AutorityForViewport = ImGuiDataAutority_DockNode; + node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_DockNode; if (node->ParentNode && node->ParentNode->ChildNodes[0] == NULL) node->ParentNode->ChildNodes[0] = node; else if (node->ParentNode && node->ParentNode->ChildNodes[1] == NULL) @@ -11423,9 +11423,9 @@ void ImGui::DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node) int index_in_parent = (node->ParentNode->ChildNodes[0] == node) ? 0 : 1; node->ParentNode->ChildNodes[index_in_parent] = NULL; DockNodeTreeMerge(ctx, node->ParentNode, node->ParentNode->ChildNodes[index_in_parent ^ 1]); - node->ParentNode->AutorityForViewport = ImGuiDataAutority_Window; // The node that stays in place keeps the viewport, so our newly dragged out node will create a new viewport + node->ParentNode->AuthorityForViewport = ImGuiDataAuthority_Window; // The node that stays in place keeps the viewport, so our newly dragged out node will create a new viewport node->ParentNode = NULL; - node->AutorityForPos = node->AutorityForSize = ImGuiDataAutority_Window; + node->AuthorityForPos = node->AuthorityForSize = ImGuiDataAuthority_Window; node->WantMouseMove = true; } MarkIniSettingsDirty(); @@ -11449,8 +11449,8 @@ ImGuiDockNode::ImGuiDockNode(ImGuiID id) LastFocusedNodeID = 0; SelectedTabID = 0; WantCloseTabID = 0; - AutorityForPos = AutorityForSize = ImGuiDataAutority_DockNode; - AutorityForViewport = ImGuiDataAutority_Auto; + AuthorityForPos = AuthorityForSize = ImGuiDataAuthority_DockNode; + AuthorityForViewport = ImGuiDataAuthority_Auto; IsVisible = true; IsFocused = HasCloseButton = HasCollapseButton = false; WantCloseAll = WantLockSizeOnce = WantMouseMove = WantHiddenTabBarUpdate = WantHiddenTabBarToggle = false; @@ -11502,12 +11502,12 @@ static void ImGui::DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window, b // In particular it is important we init the viewport from the first window so we don't create two viewports and drop one. if (node->HostWindow == NULL && !node->IsDockSpace() && node->IsRootNode()) { - if (node->AutorityForPos == ImGuiDataAutority_Auto) - node->AutorityForPos = ImGuiDataAutority_Window; - if (node->AutorityForSize == ImGuiDataAutority_Auto) - node->AutorityForSize = ImGuiDataAutority_Window; - if (node->AutorityForViewport == ImGuiDataAutority_Auto) - node->AutorityForViewport = ImGuiDataAutority_Window; + if (node->AuthorityForPos == ImGuiDataAuthority_Auto) + node->AuthorityForPos = ImGuiDataAuthority_Window; + if (node->AuthorityForSize == ImGuiDataAuthority_Auto) + node->AuthorityForSize = ImGuiDataAuthority_Window; + if (node->AuthorityForViewport == ImGuiDataAuthority_Auto) + node->AuthorityForViewport = ImGuiDataAuthority_Window; } // Add to tab bar if requested @@ -11847,7 +11847,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) } DockNodeHideHostWindow(node); - node->AutorityForPos = node->AutorityForSize = node->AutorityForViewport = ImGuiDataAutority_Window; + node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_Window; node->WantCloseAll = false; node->WantCloseTabID = 0; node->HasCloseButton = node->HasCollapseButton = false; @@ -11886,23 +11886,23 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) ImGuiWindow* ref_window = (node->Windows.Size > 0) ? node->Windows[0] : NULL; // Sync Pos - if (node->AutorityForPos == ImGuiDataAutority_Window && ref_window) + if (node->AuthorityForPos == ImGuiDataAuthority_Window && ref_window) SetNextWindowPos(ref_window->Pos); - else if (node->AutorityForPos == ImGuiDataAutority_DockNode) + else if (node->AuthorityForPos == ImGuiDataAuthority_DockNode) SetNextWindowPos(node->Pos); // Sync Size - if (node->AutorityForSize == ImGuiDataAutority_Window && ref_window) + if (node->AuthorityForSize == ImGuiDataAuthority_Window && ref_window) SetNextWindowSize(ref_window->SizeFull); - else if (node->AutorityForSize == ImGuiDataAutority_DockNode) + else if (node->AuthorityForSize == ImGuiDataAuthority_DockNode) SetNextWindowSize(node->Size); // Sync Collapsed - if (node->AutorityForSize == ImGuiDataAutority_Window && ref_window) + if (node->AuthorityForSize == ImGuiDataAuthority_Window && ref_window) SetNextWindowCollapsed(ref_window->Collapsed); // Sync Viewport - if (node->AutorityForViewport == ImGuiDataAutority_Window && ref_window) + if (node->AuthorityForViewport == ImGuiDataAuthority_Window && ref_window) SetNextWindowViewport(ref_window->ViewportId); SetNextWindowClass(&node->WindowClass); @@ -11935,12 +11935,12 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) if (node->HostWindow->Appearing) BringWindowToDisplayFront(node->HostWindow); - node->AutorityForPos = node->AutorityForSize = node->AutorityForViewport = ImGuiDataAutority_Auto; + node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_Auto; } else if (node->ParentNode) { node->HostWindow = host_window = node->ParentNode->HostWindow; - node->AutorityForPos = node->AutorityForSize = node->AutorityForViewport = ImGuiDataAutority_Auto; + node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_Auto; } if (node->WantMouseMove && node->HostWindow) DockNodeStartMouseMovingWindow(node, node->HostWindow); @@ -12700,7 +12700,7 @@ void ImGui::DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImG DockSettingsRenameNodeReferences(child_1->ID, parent_node->ID); } DockNodeApplyPosSizeToWindows(parent_node); - parent_node->AutorityForPos = parent_node->AutorityForSize = parent_node->AutorityForViewport = ImGuiDataAutority_Auto; + parent_node->AuthorityForPos = parent_node->AuthorityForSize = parent_node->AuthorityForViewport = ImGuiDataAuthority_Auto; parent_node->VisibleWindow = merge_lead_child->VisibleWindow; parent_node->SizeRef = backup_last_explicit_size; @@ -13141,7 +13141,7 @@ void ImGui::DockBuilderSetNodePos(ImGuiID node_id, ImVec2 pos) if (node == NULL) return; node->Pos = pos; - node->AutorityForPos = ImGuiDataAutority_DockNode; + node->AuthorityForPos = ImGuiDataAuthority_DockNode; } void ImGui::DockBuilderSetNodeSize(ImGuiID node_id, ImVec2 size) @@ -13151,7 +13151,7 @@ void ImGui::DockBuilderSetNodeSize(ImGuiID node_id, ImVec2 size) if (node == NULL) return; node->Size = node->SizeRef = size; - node->AutorityForSize = ImGuiDataAutority_DockNode; + node->AuthorityForSize = ImGuiDataAuthority_DockNode; } // If you create a regular node, both ref_pos/ref_size will position the window. @@ -13200,8 +13200,8 @@ void ImGui::DockBuilderRemoveNodeChildNodes(ImGuiID root_id) return; bool has_central_node = false; - ImGuiDataAutority backup_root_node_autority_for_pos = root_node ? root_node->AutorityForPos : ImGuiDataAutority_Auto; - ImGuiDataAutority backup_root_node_autority_for_size = root_node ? root_node->AutorityForSize : ImGuiDataAutority_Auto; + ImGuiDataAuthority backup_root_node_authority_for_pos = root_node ? root_node->AuthorityForPos : ImGuiDataAuthority_Auto; + ImGuiDataAuthority backup_root_node_authority_for_size = root_node ? root_node->AuthorityForSize : ImGuiDataAuthority_Auto; // Process active windows ImVector nodes_to_remove; @@ -13225,8 +13225,8 @@ void ImGui::DockBuilderRemoveNodeChildNodes(ImGuiID root_id) // Make sure we don't lose our current pos/size. (FIXME-DOCK: Consider tidying up that code in DockNodeAddWindow instead) if (root_node) { - root_node->AutorityForPos = backup_root_node_autority_for_pos; - root_node->AutorityForSize = backup_root_node_autority_for_size; + root_node->AuthorityForPos = backup_root_node_authority_for_pos; + root_node->AuthorityForSize = backup_root_node_authority_for_size; } // Apply to settings @@ -13527,7 +13527,7 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) if (node == NULL) { node = DockContextAddNode(ctx, window->DockId); - node->AutorityForPos = node->AutorityForSize = node->AutorityForViewport = ImGuiDataAutority_Window; + node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_Window; if (auto_dock_node) node->LastFrameAlive = g.FrameCount; } diff --git a/imgui_internal.h b/imgui_internal.h index 948588774a44..635d20817daa 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -84,8 +84,8 @@ struct ImGuiWindowTempData; // Temporary storage for one window (that's struct ImGuiWindowSettings; // Storage for window settings stored in .ini file (we keep one of those even if the actual window wasn't instanced during this session) // Use your programming IDE "Go to definition" facility on the names of the center columns to find the actual flags/enum lists. +typedef int ImGuiDataAuthority; // -> enum ImGuiDataAuthority_ // Enum: for storing the source authority (dock node vs window) of a field typedef int ImGuiLayoutType; // -> enum ImGuiLayoutType_ // Enum: Horizontal or vertical -typedef int ImGuiDataAutority; // -> enum ImGuiDataAutority_ // Enum: for storing the source autority (dock node vs window) of a field typedef int ImGuiButtonFlags; // -> enum ImGuiButtonFlags_ // Flags: for ButtonEx(), ButtonBehavior() typedef int ImGuiDragFlags; // -> enum ImGuiDragFlags_ // Flags: for DragBehavior() typedef int ImGuiItemFlags; // -> enum ImGuiItemFlags_ // Flags: for PushItemFlag() @@ -865,11 +865,12 @@ enum ImGuiDockNodeFlagsPrivate_ ImGuiDockNodeFlags_LocalFlagsTransferMask_ = ImGuiDockNodeFlags_LocalFlagsMask_ & ~ImGuiDockNodeFlags_DockSpace // When splitting those flags are moved to the inheriting child, never duplicated }; -enum ImGuiDataAutority_ +// Store the source authority (dock node vs window) of a field +enum ImGuiDataAuthority_ { - ImGuiDataAutority_Auto, - ImGuiDataAutority_DockNode, - ImGuiDataAutority_Window + ImGuiDataAuthority_Auto, + ImGuiDataAuthority_DockNode, + ImGuiDataAuthority_Window }; // sizeof() 116~160 @@ -898,9 +899,9 @@ struct ImGuiDockNode ImGuiID LastFocusedNodeID; // [Root node only] Which of our child docking node (any ancestor in the hierarchy) was last focused. ImGuiID SelectedTabID; // [Tab node only] Which of our tab is selected. ImGuiID WantCloseTabID; // [Tab node only] Set when closing a specific tab. - ImGuiDataAutority AutorityForPos :3; - ImGuiDataAutority AutorityForSize :3; - ImGuiDataAutority AutorityForViewport :3; + ImGuiDataAuthority AuthorityForPos :3; + ImGuiDataAuthority AuthorityForSize :3; + ImGuiDataAuthority AuthorityForViewport :3; bool IsVisible :1; // Set to false when the node is hidden (usually disabled as it has no active window) bool IsFocused :1; bool HasCloseButton :1; @@ -1672,8 +1673,8 @@ namespace ImGui IMGUI_API void DockContextShutdown(ImGuiContext* ctx); IMGUI_API void DockContextOnLoadSettings(ImGuiContext* ctx); IMGUI_API void DockContextRebuild(ImGuiContext* ctx); - IMGUI_API void DockContextNewFrameUpdateUndocking(ImGuiContext* ctx); - IMGUI_API void DockContextNewFrameUpdateDocking(ImGuiContext* ctx); + IMGUI_API void DockContextUpdateUndocking(ImGuiContext* ctx); + IMGUI_API void DockContextUpdateDocking(ImGuiContext* ctx); IMGUI_API void DockContextQueueDock(ImGuiContext* ctx, ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, float split_ratio, bool split_outer); IMGUI_API void DockContextQueueUndockWindow(ImGuiContext* ctx, ImGuiWindow* window); IMGUI_API void DockContextQueueUndockNode(ImGuiContext* ctx, ImGuiDockNode* node); From 800fb26606fc6abd7b1a92ad10b1b6876f757a59 Mon Sep 17 00:00:00 2001 From: omar Date: Sat, 13 Apr 2019 00:26:40 +0200 Subject: [PATCH 488/828] Docking: Renamed target_node > node in some functions to facilitate debugger watch use across functions. --- imgui.cpp | 91 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 46 insertions(+), 45 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 3b543b900432..15bba324b640 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11272,7 +11272,7 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) ImGuiContext& g = *ctx; ImGuiWindow* payload_window = req->DockPayload; // Optional ImGuiWindow* target_window = req->DockTargetWindow; - ImGuiDockNode* target_node = req->DockTargetNode; + ImGuiDockNode* node = req->DockTargetNode; // Decide which Tab will be selected at the end of the operation ImGuiID next_selected_id = 0; @@ -11289,21 +11289,21 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) // FIXME-DOCK: When we are trying to dock an existing single-window node into a loose window, transfer Node ID as well // When processing an interactive split, usually LastFrameAlive will be < g.FrameCount. But DockBuilder operations can make it ==. - if (target_node) - IM_ASSERT(target_node->LastFrameAlive <= g.FrameCount); - if (target_node && target_window && target_node == target_window->DockNodeAsHost) - IM_ASSERT(target_node->Windows.Size > 0 || target_node->IsSplitNode() || target_node->IsCentralNode()); + if (node) + IM_ASSERT(node->LastFrameAlive <= g.FrameCount); + if (node && target_window && node == target_window->DockNodeAsHost) + IM_ASSERT(node->Windows.Size > 0 || node->IsSplitNode() || node->IsCentralNode()); // Create new node and add existing window to it - if (target_node == NULL) + if (node == NULL) { - target_node = DockContextAddNode(ctx, 0); - target_node->Pos = target_window->Pos; - target_node->Size = target_window->Size; + node = DockContextAddNode(ctx, 0); + node->Pos = target_window->Pos; + node->Size = target_window->Size; if (target_window->DockNodeAsHost == NULL) { - DockNodeAddWindow(target_node, target_window, true); - target_node->TabBar->Tabs[0].Flags &= ~ImGuiTabItemFlags_Unsorted; + DockNodeAddWindow(node, target_window, true); + node->TabBar->Tabs[0].Flags &= ~ImGuiTabItemFlags_Unsorted; target_window->DockIsActive = true; } } @@ -11315,21 +11315,21 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) const ImGuiAxis split_axis = (split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y; const int split_inheritor_child_idx = (split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Up) ? 1 : 0; // Current contents will be moved to the opposite side const float split_ratio = req->DockSplitRatio; - DockNodeTreeSplit(ctx, target_node, split_axis, split_inheritor_child_idx, split_ratio, payload_node); // payload_node may be NULL here! - ImGuiDockNode* new_node = target_node->ChildNodes[split_inheritor_child_idx ^ 1]; - new_node->HostWindow = target_node->HostWindow; - target_node = new_node; + DockNodeTreeSplit(ctx, node, split_axis, split_inheritor_child_idx, split_ratio, payload_node); // payload_node may be NULL here! + ImGuiDockNode* new_node = node->ChildNodes[split_inheritor_child_idx ^ 1]; + new_node->HostWindow = node->HostWindow; + node = new_node; } - target_node->LocalFlags &= ~ImGuiDockNodeFlags_HiddenTabBar; + node->LocalFlags &= ~ImGuiDockNodeFlags_HiddenTabBar; - if (target_node != payload_node) + if (node != payload_node) { // Create tab bar before we call DockNodeMoveWindows (which would attempt to move the old tab-bar, which would lead us to payload tabs wrongly appearing before target tabs!) - if (target_node->Windows.Size > 0 && target_node->TabBar == NULL) + if (node->Windows.Size > 0 && node->TabBar == NULL) { - DockNodeAddTabBar(target_node); - for (int n = 0; n < target_node->Windows.Size; n++) - TabBarAddTab(target_node->TabBar, ImGuiTabItemFlags_None, target_node->Windows[n]); + DockNodeAddTabBar(node); + for (int n = 0; n < node->Windows.Size; n++) + TabBarAddTab(node->TabBar, ImGuiTabItemFlags_None, node->Windows[n]); } if (payload_node != NULL) @@ -11337,7 +11337,7 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) // Transfer full payload node (with 1+ child windows or child nodes) if (payload_node->IsSplitNode()) { - if (target_node->Windows.Size > 0) + if (node->Windows.Size > 0) { // We can dock a split payload into a node that already has windows _only_ if our payload is a node tree with a single visible node. // In this situation, we move the windows of the target node into the currently visible node of the payload. @@ -11346,28 +11346,28 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) ImGuiDockNode* visible_node = payload_node->OnlyNodeWithWindows; if (visible_node->TabBar) IM_ASSERT(visible_node->TabBar->Tabs.Size > 0); - DockNodeMoveWindows(target_node, visible_node); - DockNodeMoveWindows(visible_node, target_node); - DockSettingsRenameNodeReferences(target_node->ID, visible_node->ID); + DockNodeMoveWindows(node, visible_node); + DockNodeMoveWindows(visible_node, node); + DockSettingsRenameNodeReferences(node->ID, visible_node->ID); } - if (target_node->IsCentralNode()) + if (node->IsCentralNode()) { // Central node property needs to be moved to a leaf node, pick the last focused one. // FIXME-DOCKING: If we had to transfer other flags here, what would the policy be? ImGuiDockNode* last_focused_node = DockContextFindNodeByID(ctx, payload_node->LastFocusedNodeID); IM_ASSERT(last_focused_node != NULL && DockNodeGetRootNode(last_focused_node) == DockNodeGetRootNode(payload_node)); last_focused_node->LocalFlags |= ImGuiDockNodeFlags_CentralNode; - target_node->LocalFlags &= ~ImGuiDockNodeFlags_CentralNode; + node->LocalFlags &= ~ImGuiDockNodeFlags_CentralNode; } - IM_ASSERT(target_node->Windows.Size == 0); - DockNodeMoveChildNodes(target_node, payload_node); + IM_ASSERT(node->Windows.Size == 0); + DockNodeMoveChildNodes(node, payload_node); } else { const ImGuiID payload_dock_id = payload_node->ID; - DockNodeMoveWindows(target_node, payload_node); - DockSettingsRenameNodeReferences(payload_dock_id, target_node->ID); + DockNodeMoveWindows(node, payload_node); + DockSettingsRenameNodeReferences(payload_dock_id, node->ID); } DockContextRemoveNode(ctx, payload_node, true); } @@ -11375,15 +11375,15 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) { // Transfer single window const ImGuiID payload_dock_id = payload_window->DockId; - target_node->VisibleWindow = payload_window; - DockNodeAddWindow(target_node, payload_window, true); + node->VisibleWindow = payload_window; + DockNodeAddWindow(node, payload_window, true); if (payload_dock_id != 0) - DockSettingsRenameNodeReferences(payload_dock_id, target_node->ID); + DockSettingsRenameNodeReferences(payload_dock_id, node->ID); } } // Update selection immediately - if (ImGuiTabBar* tab_bar = target_node->TabBar) + if (ImGuiTabBar* tab_bar = node->TabBar) tab_bar->NextSelectedTabId = next_selected_id; MarkIniSettingsDirty(); } @@ -13653,36 +13653,37 @@ void ImGui::BeginAsDockableDragDropTarget(ImGuiWindow* window) ImGuiWindow* payload_window = *(ImGuiWindow**)payload->Data; if (AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_WINDOW, ImGuiDragDropFlags_AcceptBeforeDelivery | ImGuiDragDropFlags_AcceptNoDrawDefaultRect)) { + // Select target node + ImGuiDockNode* node = NULL; bool allow_null_target_node = false; - ImGuiDockNode* target_node = NULL; if (window->DockNodeAsHost) - target_node = DockNodeTreeFindNodeByPos(window->DockNodeAsHost, g.IO.MousePos); + node = DockNodeTreeFindNodeByPos(window->DockNodeAsHost, g.IO.MousePos); else if (window->DockNode) // && window->DockIsActive) - target_node = window->DockNode; + node = window->DockNode; else allow_null_target_node = true; // Dock into a regular window - const ImRect explicit_target_rect = (target_node && target_node->TabBar && !target_node->IsHiddenTabBar() && !target_node->IsNoTabBar()) ? target_node->TabBar->BarRect : ImRect(window->Pos, window->Pos + ImVec2(window->Size.x, GetFrameHeight())); + const ImRect explicit_target_rect = (node && node->TabBar && !node->IsHiddenTabBar() && !node->IsNoTabBar()) ? node->TabBar->BarRect : ImRect(window->Pos, window->Pos + ImVec2(window->Size.x, GetFrameHeight())); const bool is_explicit_target = g.IO.ConfigDockingWithShift || IsMouseHoveringRect(explicit_target_rect.Min, explicit_target_rect.Max); // Preview docking request and find out split direction/ratio //const bool do_preview = true; // Ignore testing for payload->IsPreview() which removes one frame of delay, but breaks overlapping drop targets within the same window. const bool do_preview = payload->IsPreview() || payload->IsDelivery(); - if (do_preview && (target_node != NULL || allow_null_target_node)) + if (do_preview && (node != NULL || allow_null_target_node)) { ImGuiDockPreviewData split_inner, split_outer; ImGuiDockPreviewData* split_data = &split_inner; - if (target_node && (target_node->ParentNode || target_node->IsCentralNode())) - if (ImGuiDockNode* root_node = DockNodeGetRootNode(target_node)) + if (node && (node->ParentNode || node->IsCentralNode())) + if (ImGuiDockNode* root_node = DockNodeGetRootNode(node)) if (DockNodePreviewDockCalc(window, root_node, payload_window, &split_outer, is_explicit_target, true)) split_data = &split_outer; - DockNodePreviewDockCalc(window, target_node, payload_window, &split_inner, is_explicit_target, false); + DockNodePreviewDockCalc(window, node, payload_window, &split_inner, is_explicit_target, false); if (split_data == &split_outer) split_inner.IsDropAllowed = false; // Draw inner then outer, so that previewed tab (in inner data) will be behind the outer drop boxes - DockNodePreviewDockRender(window, target_node, payload_window, &split_inner); - DockNodePreviewDockRender(window, target_node, payload_window, &split_outer); + DockNodePreviewDockRender(window, node, payload_window, &split_inner); + DockNodePreviewDockRender(window, node, payload_window, &split_outer); // Queue docking request if (split_data->IsDropAllowed && payload->IsDelivery()) From 433a7556c7ea3440dd8eba7c0837829b6f493ec3 Mon Sep 17 00:00:00 2001 From: omar Date: Sat, 13 Apr 2019 19:19:56 +0200 Subject: [PATCH 489/828] Docking: Fixed another issue where the resulting node of a split would sometimes recall the pos/size of previous host window. Spent a whole day adding framework for testing more of docking so hopefully we'll heading toward the magical world of less regressions. (#2109) --- imgui.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 2fa446289230..cc715503d81c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11838,6 +11838,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) ImGuiWindow* single_window = node->Windows[0]; node->Pos = single_window->Pos; node->Size = single_window->SizeFull; + node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_Window; // Transfer focus immediately so when we revert to a regular window it is immediately selected if (node->HostWindow && g.NavWindow == node->HostWindow) @@ -11855,7 +11856,6 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) } DockNodeHideHostWindow(node); - node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_Window; node->WantCloseAll = false; node->WantCloseTabID = 0; node->HasCloseButton = node->HasCollapseButton = false; @@ -12664,6 +12664,7 @@ void ImGui::DockNodeTreeSplit(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImG parent_node->ChildNodes[split_inheritor_child_idx]->VisibleWindow = parent_node->VisibleWindow; parent_node->SplitAxis = split_axis; parent_node->VisibleWindow = NULL; + parent_node->AuthorityForPos = parent_node->AuthorityForSize = ImGuiDataAuthority_DockNode; float size_avail = (parent_node->Size[split_axis] - IMGUI_DOCK_SPLITTER_SIZE); size_avail = ImMax(size_avail, g.Style.WindowMinSize[split_axis] * 2.0f); From f70eacee8e4b95571e636cc72874453e2a7d49b3 Mon Sep 17 00:00:00 2001 From: omar Date: Sat, 13 Apr 2019 19:50:30 +0200 Subject: [PATCH 490/828] Docking: Internal: Added helper for automation to process docking at the mouse level. --- imgui.cpp | 45 ++++++++++++++++++++++++++++++++++----------- imgui_internal.h | 1 + 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index cc715503d81c..67812dd69bcc 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10896,11 +10896,11 @@ namespace ImGui static void DockNodeUpdateVisibleFlag(ImGuiDockNode* node); static void DockNodeStartMouseMovingWindow(ImGuiDockNode* node, ImGuiWindow* window); static bool DockNodeIsDropAllowed(ImGuiWindow* host_window, ImGuiWindow* payload_window); - static bool DockNodePreviewDockCalc(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* payload_window, ImGuiDockPreviewData* preview_data, bool is_explicit_target, bool is_outer_docking); + static void DockNodePreviewDockCalc(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* payload_window, ImGuiDockPreviewData* preview_data, bool is_explicit_target, bool is_outer_docking); static void DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* payload_window, const ImGuiDockPreviewData* preview_data); static ImRect DockNodeCalcTabBarRect(const ImGuiDockNode* node); static void DockNodeCalcSplitRects(ImVec2& pos_old, ImVec2& size_old, ImVec2& pos_new, ImVec2& size_new, ImGuiDir dir, ImVec2 size_new_desired); - static bool DockNodeCalcDropRects(const ImRect& parent, ImGuiDir dir, ImRect& out_draw, bool outer_docking); + static bool DockNodeCalcDropRectsAndTestMousePos(const ImRect& parent, ImGuiDir dir, ImRect& out_draw, bool outer_docking, ImVec2* test_mouse_pos); static const char* DockNodeGetHostWindowTitle(ImGuiDockNode* node, char* buf, int buf_size) { ImFormatString(buf, buf_size, "##DockNode_%02X", node->ID); return buf; } static int DockNodeGetDepth(const ImGuiDockNode* node) { int depth = 0; while (node->ParentNode) { node = node->ParentNode; depth++; } return depth; } static int DockNodeGetTabOrder(ImGuiWindow* window); @@ -11439,6 +11439,25 @@ void ImGui::DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node) MarkIniSettingsDirty(); } +// This is mostly used for automation. +bool ImGui::DockContextCalcDropPosForDocking(ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, bool split_outer, ImVec2* out_pos) +{ + if (split_outer) + { + IM_ASSERT(0); + } + else + { + ImGuiDockPreviewData split_data; + DockNodePreviewDockCalc(target, target_node, payload, &split_data, false, split_outer); + if (split_data.DropRectsDraw[split_dir+1].IsInverted()) + return false; + *out_pos = split_data.DropRectsDraw[split_dir+1].GetCenter(); + return true; + } + return false; +} + //----------------------------------------------------------------------------- // Docking: ImGuiDockNode //----------------------------------------------------------------------------- @@ -12408,7 +12427,7 @@ void ImGui::DockNodeCalcSplitRects(ImVec2& pos_old, ImVec2& size_old, ImVec2& po } // Retrieve the drop rectangles for a given direction or for the center + perform hit testing. -bool ImGui::DockNodeCalcDropRects(const ImRect& parent, ImGuiDir dir, ImRect& out_r, bool outer_docking) +bool ImGui::DockNodeCalcDropRectsAndTestMousePos(const ImRect& parent, ImGuiDir dir, ImRect& out_r, bool outer_docking, ImVec2* test_mouse_pos) { ImGuiContext& g = *GImGui; @@ -12440,12 +12459,15 @@ bool ImGui::DockNodeCalcDropRects(const ImRect& parent, ImGuiDir dir, ImRect& ou else if (dir == ImGuiDir_Left) { out_r = ImRect(c.x - off.x - hs_h, c.y - hs_w, c.x - off.x + hs_h, c.y + hs_w); } else if (dir == ImGuiDir_Right) { out_r = ImRect(c.x + off.x - hs_h, c.y - hs_w, c.x + off.x + hs_h, c.y + hs_w); } + if (test_mouse_pos == NULL) + return false; + ImRect hit_r = out_r; if (!outer_docking) { // Custom hit testing for the 5-way selection, designed to reduce flickering when moving diagonally between sides hit_r.Expand(ImFloor(hs_w * 0.30f)); - ImVec2 mouse_delta = (g.IO.MousePos - c); + ImVec2 mouse_delta = (*test_mouse_pos - c); float mouse_delta_len2 = ImLengthSqr(mouse_delta); float r_threshold_center = hs_w * 1.4f; float r_threshold_sides = hs_w * (1.4f + 1.2f); @@ -12454,14 +12476,13 @@ bool ImGui::DockNodeCalcDropRects(const ImRect& parent, ImGuiDir dir, ImRect& ou if (mouse_delta_len2 < r_threshold_sides * r_threshold_sides) return (dir == ImGetDirQuadrantFromDelta(mouse_delta.x, mouse_delta.y)); } - return hit_r.Contains(g.IO.MousePos); + return hit_r.Contains(*test_mouse_pos); } // host_node may be NULL if the window doesn't have a DockNode already. -static bool ImGui::DockNodePreviewDockCalc(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* root_payload, ImGuiDockPreviewData* data, bool is_explicit_target, bool is_outer_docking) +static void ImGui::DockNodePreviewDockCalc(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* root_payload, ImGuiDockPreviewData* data, bool is_explicit_target, bool is_outer_docking) { ImGuiContext& g = *GImGui; - IM_ASSERT(g.CurrentWindow == host_window); // Because we rely on font size to calculate tab sizes // There is an edge case when docking into a dockspace which only has inactive nodes. // In this case DockNodeTreeFindNodeByPos() will have selected a leaf node which is inactive. @@ -12503,7 +12524,7 @@ static bool ImGui::DockNodePreviewDockCalc(ImGuiWindow* host_window, ImGuiDockNo continue; if (dir != ImGuiDir_None && !data->IsSidesAvailable) continue; - if (DockNodeCalcDropRects(data->FutureNode.Rect(), (ImGuiDir)dir, data->DropRectsDraw[dir+1], is_outer_docking)) + if (DockNodeCalcDropRectsAndTestMousePos(data->FutureNode.Rect(), (ImGuiDir)dir, data->DropRectsDraw[dir+1], is_outer_docking, &g.IO.MousePos)) { data->SplitDir = (ImGuiDir)dir; data->IsSplitDirExplicit = true; @@ -12531,13 +12552,12 @@ static bool ImGui::DockNodePreviewDockCalc(ImGuiWindow* host_window, ImGuiDockNo data->FutureNode.Size = size_new; data->SplitRatio = (split_dir == ImGuiDir_Right || split_dir == ImGuiDir_Down) ? (1.0f - split_ratio) : (split_ratio); } - - return data->IsSplitDirExplicit; } static void ImGui::DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* root_payload, const ImGuiDockPreviewData* data) { ImGuiContext& g = *GImGui; + IM_ASSERT(g.CurrentWindow == host_window); // Because we rely on font size to calculate tab sizes // With this option, we only display the preview on the target viewport, and the payload viewport is made transparent. // To compensate for the single layer obstructed by the payload, we'll increase the alpha of the preview nodes. @@ -13684,8 +13704,11 @@ void ImGui::BeginAsDockableDragDropTarget(ImGuiWindow* window) ImGuiDockPreviewData* split_data = &split_inner; if (node && (node->ParentNode || node->IsCentralNode())) if (ImGuiDockNode* root_node = DockNodeGetRootNode(node)) - if (DockNodePreviewDockCalc(window, root_node, payload_window, &split_outer, is_explicit_target, true)) + { + DockNodePreviewDockCalc(window, root_node, payload_window, &split_outer, is_explicit_target, true); + if (split_outer.IsSplitDirExplicit) split_data = &split_outer; + } DockNodePreviewDockCalc(window, node, payload_window, &split_inner, is_explicit_target, false); if (split_data == &split_outer) split_inner.IsDropAllowed = false; diff --git a/imgui_internal.h b/imgui_internal.h index 86736088180b..d4692d4eb734 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1678,6 +1678,7 @@ namespace ImGui IMGUI_API void DockContextQueueDock(ImGuiContext* ctx, ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, float split_ratio, bool split_outer); IMGUI_API void DockContextQueueUndockWindow(ImGuiContext* ctx, ImGuiWindow* window); IMGUI_API void DockContextQueueUndockNode(ImGuiContext* ctx, ImGuiDockNode* node); + IMGUI_API bool DockContextCalcDropPosForDocking(ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, bool split_outer, ImVec2* out_pos); inline ImGuiDockNode* DockNodeGetRootNode(ImGuiDockNode* node) { while (node->ParentNode) node = node->ParentNode; return node; } IMGUI_API void BeginDocked(ImGuiWindow* window, bool* p_open); IMGUI_API void BeginAsDockableDragDropSource(ImGuiWindow* window); From 20f0cb02816832644a5e05b0909e3fad71355e3d Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 19 Apr 2019 20:28:43 +0200 Subject: [PATCH 491/828] Docking: Fixed an issue where DockBuilderSplitNode() wouldn't update the CentralNode shortcut immediately, which was problematic for immediately following DockBuilderDockWindow(). (#2109) --- imgui.cpp | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index f8ffeef791ae..48cbad135a37 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11421,9 +11421,11 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) // Central node property needs to be moved to a leaf node, pick the last focused one. // FIXME-DOCKING: If we had to transfer other flags here, what would the policy be? ImGuiDockNode* last_focused_node = DockContextFindNodeByID(ctx, payload_node->LastFocusedNodeID); - IM_ASSERT(last_focused_node != NULL && DockNodeGetRootNode(last_focused_node) == DockNodeGetRootNode(payload_node)); + ImGuiDockNode* last_focused_root_node = DockNodeGetRootNode(last_focused_node); + IM_ASSERT(last_focused_node != NULL && last_focused_root_node == DockNodeGetRootNode(payload_node)); last_focused_node->LocalFlags |= ImGuiDockNodeFlags_CentralNode; node->LocalFlags &= ~ImGuiDockNodeFlags_CentralNode; + last_focused_root_node->CentralNode = last_focused_node; } IM_ASSERT(node->Windows.Size == 0); @@ -11752,17 +11754,18 @@ static void ImGui::DockNodeHideHostWindow(ImGuiDockNode* node) DockNodeRemoveTabBar(node); } -struct ImGuiDockNodeUpdateScanResults +// Search function called once by root node in DockNodeUpdate() +struct ImGuiDockNodeFindInfoResults { ImGuiDockNode* CentralNode; ImGuiDockNode* FirstNodeWithWindows; int CountNodesWithWindows; //ImGuiWindowClass WindowClassForMerges; - ImGuiDockNodeUpdateScanResults() { CentralNode = FirstNodeWithWindows = NULL; CountNodesWithWindows = 0; } + ImGuiDockNodeFindInfoResults() { CentralNode = FirstNodeWithWindows = NULL; CountNodesWithWindows = 0; } }; -static void DockNodeUpdateScanRec(ImGuiDockNode* node, ImGuiDockNodeUpdateScanResults* results) +static void DockNodeFindInfo(ImGuiDockNode* node, ImGuiDockNodeFindInfoResults* results) { if (node->Windows.Size > 0) { @@ -11779,9 +11782,9 @@ static void DockNodeUpdateScanRec(ImGuiDockNode* node, ImGuiDockNodeUpdateScanRe if (results->CountNodesWithWindows > 1 && results->CentralNode != NULL) return; if (node->ChildNodes[0]) - DockNodeUpdateScanRec(node->ChildNodes[0], results); + DockNodeFindInfo(node->ChildNodes[0], results); if (node->ChildNodes[1]) - DockNodeUpdateScanRec(node->ChildNodes[1], results); + DockNodeFindInfo(node->ChildNodes[1], results); } // - Remove inactive windows/nodes. @@ -11880,8 +11883,8 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) // FIXME-DOCK: Merge this scan into the one above. // - Setup central node pointers // - Find if there's only a single visible window in the hierarchy (in which case we need to display a regular title bar -> FIXME-DOCK: that last part is not done yet!) - ImGuiDockNodeUpdateScanResults results; - DockNodeUpdateScanRec(node, &results); + ImGuiDockNodeFindInfoResults results; + DockNodeFindInfo(node, &results); node->CentralNode = results.CentralNode; node->OnlyNodeWithWindows = (results.CountNodesWithWindows == 1) ? results.FirstNodeWithWindows : NULL; if (node->LastFocusedNodeID == 0 && results.FirstNodeWithWindows != NULL) @@ -12754,11 +12757,13 @@ void ImGui::DockNodeTreeSplit(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImG DockNodeMoveWindows(parent_node->ChildNodes[split_inheritor_child_idx], parent_node); DockNodeTreeUpdatePosSize(parent_node, parent_node->Pos, parent_node->Size); - // Flags transfer + // Flags transfer (e.g. this is where we transfer the ImGuiDockNodeFlags_CentralNode property) child_0->SharedFlags = parent_node->SharedFlags & ImGuiDockNodeFlags_SharedFlagsInheritMask_; child_1->SharedFlags = parent_node->SharedFlags & ImGuiDockNodeFlags_SharedFlagsInheritMask_; child_inheritor->LocalFlags = parent_node->LocalFlags & ImGuiDockNodeFlags_LocalFlagsTransferMask_; parent_node->LocalFlags &= ~ImGuiDockNodeFlags_LocalFlagsTransferMask_; + if (child_inheritor->IsCentralNode()) + DockNodeGetRootNode(parent_node)->CentralNode = child_inheritor; } void ImGui::DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiDockNode* merge_lead_child) @@ -13062,10 +13067,15 @@ void ImGui::SetWindowDock(ImGuiWindow* window, ImGuiID dock_id, ImGuiCond cond) // Policy: Find central node or latest focused node. We first move back to our root node. new_node = DockNodeGetRootNode(new_node); if (new_node->CentralNode) + { + IM_ASSERT(new_node->CentralNode->IsCentralNode()); dock_id = new_node->CentralNode->ID; + } else + { dock_id = new_node->LastFocusedNodeID; } + } if (window->DockId == dock_id) return; @@ -13340,6 +13350,7 @@ void ImGui::DockBuilderRemoveNodeChildNodes(ImGuiID root_id) else if (has_central_node) { root_node->LocalFlags |= ImGuiDockNodeFlags_CentralNode; + root_node->CentralNode = root_node; } } From d0fb547dc1daecf7338e064abc6a2269540ae00d Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 22 Apr 2019 11:46:04 +0200 Subject: [PATCH 492/828] Viewports: Avoid rendering/swapping secondary viewports that are minimized. (#1542, #2496) --- imgui.cpp | 35 ++++++++++++++++++++++++----------- imgui.h | 5 +++-- imgui_internal.h | 3 +-- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 48cbad135a37..d2d7a472793e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5622,7 +5622,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->Pos = FindBestWindowPosForPopup(window); // Late create viewport if we don't fit within our current host viewport. - if (window->ViewportAllowPlatformMonitorExtend >= 0 && !window->ViewportOwned && !window->Viewport->PlatformWindowMinimized) + if (window->ViewportAllowPlatformMonitorExtend >= 0 && !window->ViewportOwned && !(window->Viewport->Flags & ImGuiViewportFlags_Minimized)) if (!window->Viewport->GetRect().Contains(window->Rect())) { // This is based on the assumption that the DPI will be known ahead (same as the DPI of the selection done in UpdateSelectWindowViewport) @@ -10171,7 +10171,7 @@ static bool ImGui::GetWindowAlwaysWantOwnViewport(ImGuiWindow* window) static bool ImGui::UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* viewport) { ImGuiContext& g = *GImGui; - if (!(viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) || window->Viewport == viewport || viewport->PlatformWindowMinimized) + if (!(viewport->Flags & (ImGuiViewportFlags_CanHostOtherWindows | ImGuiViewportFlags_Minimized)) || window->Viewport == viewport) return false; if (!viewport->GetRect().Contains(window->Rect())) return false; @@ -10226,7 +10226,7 @@ static ImGuiViewportP* FindViewportHoveredFromPlatformWindowStack(const ImVec2 m for (int n = 0; n < g.Viewports.Size; n++) { ImGuiViewportP* viewport = g.Viewports[n]; - if (!(viewport->Flags & ImGuiViewportFlags_NoInputs) && !viewport->PlatformWindowMinimized && viewport->GetRect().Contains(mouse_platform_pos)) + if (!(viewport->Flags & (ImGuiViewportFlags_NoInputs | ImGuiViewportFlags_Minimized)) && viewport->GetRect().Contains(mouse_platform_pos)) if (best_candidate == NULL || best_candidate->LastFrontMostStampCount < viewport->LastFrontMostStampCount) best_candidate = viewport; } @@ -10245,7 +10245,13 @@ static void ImGui::UpdateViewportsNewFrame() const bool platform_funcs_available = viewport->PlatformWindowCreated; if ((g.ConfigFlagsForFrame & ImGuiConfigFlags_ViewportsEnable)) if (g.PlatformIO.Platform_GetWindowMinimized && platform_funcs_available) - viewport->PlatformWindowMinimized = g.PlatformIO.Platform_GetWindowMinimized(viewport); + { + bool minimized = g.PlatformIO.Platform_GetWindowMinimized(viewport); + if (minimized) + viewport->Flags |= ImGuiViewportFlags_Minimized; + else + viewport->Flags &= ~ImGuiViewportFlags_Minimized; + } } // Create/update main viewport with current platform position and size @@ -10255,7 +10261,7 @@ static void ImGui::UpdateViewportsNewFrame() ImVec2 main_viewport_platform_pos = ImVec2(0.0f, 0.0f); ImVec2 main_viewport_platform_size = g.IO.DisplaySize; if (g.ConfigFlagsForFrame & ImGuiConfigFlags_ViewportsEnable) - main_viewport_platform_pos = main_viewport->PlatformWindowMinimized ? main_viewport->Pos : g.PlatformIO.Platform_GetWindowPos(main_viewport); + main_viewport_platform_pos = (main_viewport->Flags & ImGuiViewportFlags_Minimized) ? main_viewport->Pos : g.PlatformIO.Platform_GetWindowPos(main_viewport); AddUpdateViewport(NULL, IMGUI_VIEWPORT_DEFAULT_ID, main_viewport_platform_pos, main_viewport_platform_size, ImGuiViewportFlags_CanHostOtherWindows); g.CurrentViewport = NULL; @@ -10293,7 +10299,7 @@ static void ImGui::UpdateViewportsNewFrame() { // Update Position and Size (from Platform Window to ImGui) if requested. // We do it early in the frame instead of waiting for UpdatePlatformWindows() to avoid a frame of lag when moving/resizing using OS facilities. - if (!viewport->PlatformWindowMinimized && platform_funcs_available) + if (!(viewport->Flags & ImGuiViewportFlags_Minimized) && platform_funcs_available) { if (viewport->PlatformRequestMove) viewport->Pos = viewport->LastPlatformPos = g.PlatformIO.Platform_GetWindowPos(viewport); @@ -10437,6 +10443,7 @@ ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const viewport->Pos = pos; if (!viewport->PlatformRequestResize) viewport->Size = size; + viewport->Flags = flags | (viewport->Flags & ImGuiViewportFlags_Minimized); // Preserve existing flags } else { @@ -10446,6 +10453,7 @@ ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const viewport->Idx = g.Viewports.Size; viewport->Pos = viewport->LastPos = pos; viewport->Size = size; + viewport->Flags = flags; UpdateViewportPlatformMonitor(viewport); g.Viewports.push_back(viewport); //IMGUI_DEBUG_LOG("Add Viewport %08X (%s)\n", id, window->Name); @@ -10464,7 +10472,6 @@ ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const } viewport->Window = window; - viewport->Flags = flags; viewport->LastFrameActive = g.FrameCount; IM_ASSERT(window == NULL || viewport->ID == window->ID); @@ -10721,9 +10728,11 @@ void ImGui::UpdatePlatformWindows() // // ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); // for (int i = 1; i < platform_io.Viewports.Size; i++) -// MyRenderFunction(platform_io.Viewports[i], my_args); +// if ((platform_io.Viewports[i]->Flags & ImGuiViewportFlags_Minimized) == 0) +// MyRenderFunction(platform_io.Viewports[i], my_args); // for (int i = 1; i < platform_io.Viewports.Size; i++) -// MySwapBufferFunction(platform_io.Viewports[i], my_args); +// if ((platform_io.Viewports[i]->Flags & ImGuiViewportFlags_Minimized) == 0) +// MySwapBufferFunction(platform_io.Viewports[i], my_args); // void ImGui::RenderPlatformWindowsDefault(void* platform_render_arg, void* renderer_render_arg) { @@ -10732,12 +10741,16 @@ void ImGui::RenderPlatformWindowsDefault(void* platform_render_arg, void* render for (int i = 1; i < platform_io.Viewports.Size; i++) { ImGuiViewport* viewport = platform_io.Viewports[i]; + if (viewport->Flags & ImGuiViewportFlags_Minimized) + continue; if (platform_io.Platform_RenderWindow) platform_io.Platform_RenderWindow(viewport, platform_render_arg); if (platform_io.Renderer_RenderWindow) platform_io.Renderer_RenderWindow(viewport, renderer_render_arg); } for (int i = 1; i < platform_io.Viewports.Size; i++) { ImGuiViewport* viewport = platform_io.Viewports[i]; + if (viewport->Flags & ImGuiViewportFlags_Minimized) + continue; if (platform_io.Platform_SwapBuffers) platform_io.Platform_SwapBuffers(viewport, platform_render_arg); if (platform_io.Renderer_SwapBuffers) platform_io.Renderer_SwapBuffers(viewport, renderer_render_arg); } @@ -14072,7 +14085,7 @@ static void RenderViewportThumbnail(ImDrawList* draw_list, ImGuiViewportP* viewp ImVec2 scale = bb.GetSize() / viewport->Size; ImVec2 off = bb.Min - viewport->Pos * scale; - float alpha_mul = viewport->PlatformWindowMinimized ? 0.30f : 1.00f; + float alpha_mul = (viewport->Flags & ImGuiViewportFlags_Minimized) ? 0.30f : 1.00f; window->DrawList->AddRectFilled(bb.Min, bb.Max, ImGui::GetColorU32(ImGuiCol_Border, alpha_mul * 0.40f)); for (int i = 0; i != g.Windows.Size; i++) { @@ -14287,7 +14300,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::BulletText("Flags: 0x%04X =%s%s%s%s%s%s", viewport->Flags, (flags & ImGuiViewportFlags_CanHostOtherWindows) ? " CanHostOtherWindows" : "", (flags & ImGuiViewportFlags_NoDecoration) ? " NoDecoration" : "", (flags & ImGuiViewportFlags_NoFocusOnAppearing) ? " NoFocusOnAppearing" : "", (flags & ImGuiViewportFlags_NoInputs) ? " NoInputs" : "", - (flags & ImGuiViewportFlags_NoRendererClear) ? " NoRendererClear" : "", viewport->PlatformWindowMinimized ? ", PlatformWindowMinimized" : ""); + (flags & ImGuiViewportFlags_NoRendererClear) ? " NoRendererClear" : "", (flags & ImGuiViewportFlags_Minimized) ? " Minimized" : ""); for (int layer_i = 0; layer_i < IM_ARRAYSIZE(viewport->DrawDataBuilder.Layers); layer_i++) for (int draw_list_i = 0; draw_list_i < viewport->DrawDataBuilder.Layers[layer_i].Size; draw_list_i++) Funcs::NodeDrawList(NULL, viewport, viewport->DrawDataBuilder.Layers[layer_i][draw_list_i], "DrawList"); diff --git a/imgui.h b/imgui.h index 0a08d6c87531..263ab9e672fb 100644 --- a/imgui.h +++ b/imgui.h @@ -726,7 +726,7 @@ namespace ImGui IMGUI_API ImGuiPlatformIO& GetPlatformIO(); // platform/renderer functions, for back-end to setup + viewports list. IMGUI_API ImGuiViewport* GetMainViewport(); // main viewport. same as GetPlatformIO().MainViewport == GetPlatformIO().Viewports[0]. IMGUI_API void UpdatePlatformWindows(); // call in main loop. will call CreateWindow/ResizeWindow/etc. platform functions for each secondary viewport, and DestroyWindow for each inactive viewport. - IMGUI_API void RenderPlatformWindowsDefault(void* platform_arg = NULL, void* renderer_arg = NULL); // call in main loop. will call RenderWindow/SwapBuffers platform functions for each secondary viewport. may be reimplemented by user for custom rendering needs. + IMGUI_API void RenderPlatformWindowsDefault(void* platform_arg = NULL, void* renderer_arg = NULL); // call in main loop. will call RenderWindow/SwapBuffers platform functions for each secondary viewport which doesn't have the ImGuiViewportFlags_Minimized flag set. May be reimplemented by user for custom rendering needs. IMGUI_API void DestroyPlatformWindows(); // call DestroyWindow platform functions for all viewports. call from back-end Shutdown() if you need to close platform windows before imgui shutdown. otherwise will be called by DestroyContext(). IMGUI_API ImGuiViewport* FindViewportByID(ImGuiID id); // this is a helper for back-ends. IMGUI_API ImGuiViewport* FindViewportByPlatformHandle(void* platform_handle); // this is a helper for back-ends. the type platform_handle is decided by the back-end (e.g. HWND, MyWindow*, GLFWwindow* etc.) @@ -2347,7 +2347,8 @@ enum ImGuiViewportFlags_ ImGuiViewportFlags_NoFocusOnClick = 1 << 3, // Platform Window: Don't take focus when clicked on. ImGuiViewportFlags_NoInputs = 1 << 4, // Platform Window: Make mouse pass through so we can drag this window while peaking behind it. ImGuiViewportFlags_NoRendererClear = 1 << 5, // Platform Window: Renderer doesn't need to clear the framebuffer ahead (because we will fill it entirely). - ImGuiViewportFlags_TopMost = 1 << 6 // Platform Window: Display on top (for tooltips only) + ImGuiViewportFlags_TopMost = 1 << 6, // Platform Window: Display on top (for tooltips only) + ImGuiViewportFlags_Minimized = 1 << 7 // Platform Window: Window is minimized, can skip render. When minimized we tend to avoid using the viewport pos/size for clipping window or testing if they are contained in the viewport. }; // The viewports created and managed by imgui. The role of the platform back-end is to create the platform/OS windows corresponding to each viewport. diff --git a/imgui_internal.h b/imgui_internal.h index 4c3ea3f7fbcb..39bcc0577a8b 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -756,7 +756,6 @@ struct ImGuiViewportP : public ImGuiViewport float LastAlpha; short PlatformMonitor; bool PlatformWindowCreated; - bool PlatformWindowMinimized; // When minimized we tend to avoid using the viewport pos/size for clipping window or testing if they are contained in the viewport ImGuiWindow* Window; // Set when the viewport is owned by a window (and ImGuiViewportFlags_CanHostOtherWindows is NOT set) ImDrawList* DrawLists[2]; // Convenience background (0) and foreground (1) draw lists. We use them to draw software mouser cursor when io.MouseDrawCursor is set and to draw most debug overlays. ImDrawData DrawDataP; @@ -765,7 +764,7 @@ struct ImGuiViewportP : public ImGuiViewport ImVec2 LastPlatformSize; ImVec2 LastRendererSize; - ImGuiViewportP() { Idx = -1; LastFrameActive = LastFrameDrawLists[0] = LastFrameDrawLists[1] = LastFrontMostStampCount = -1; LastNameHash = 0; Alpha = LastAlpha = 1.0f; PlatformMonitor = -1; PlatformWindowCreated = PlatformWindowMinimized = false; Window = NULL; DrawLists[0] = DrawLists[1] = NULL; LastPlatformPos = LastPlatformSize = LastRendererSize = ImVec2(FLT_MAX, FLT_MAX); } + ImGuiViewportP() { Idx = -1; LastFrameActive = LastFrameDrawLists[0] = LastFrameDrawLists[1] = LastFrontMostStampCount = -1; LastNameHash = 0; Alpha = LastAlpha = 1.0f; PlatformMonitor = -1; PlatformWindowCreated = false; Window = NULL; DrawLists[0] = DrawLists[1] = NULL; LastPlatformPos = LastPlatformSize = LastRendererSize = ImVec2(FLT_MAX, FLT_MAX); } ~ImGuiViewportP() { if (DrawLists[0]) IM_DELETE(DrawLists[0]); if (DrawLists[1]) IM_DELETE(DrawLists[1]); } ImRect GetRect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } ImVec2 GetCenter() const { return ImVec2(Pos.x + Size.x * 0.5f, Pos.y + Size.y * 0.5f); } From a649d904d7c6274584a38a18702c340c1ecdf567 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 26 Apr 2019 00:28:28 +0200 Subject: [PATCH 493/828] Examples: Emscripten: Fixed not enabling Docking and Nav by default. (#2494) --- examples/example_emscripten/main.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/example_emscripten/main.cpp b/examples/example_emscripten/main.cpp index 574e6e24921e..900557189b63 100644 --- a/examples/example_emscripten/main.cpp +++ b/examples/example_emscripten/main.cpp @@ -61,8 +61,9 @@ int main(int, char**) IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls - //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking // For an Emscripten build we are disabling file-system access, so let's not attempt to do a fopen() of the imgui.ini file. // You may manually call LoadIniSettingsFromMemory() to load settings from your own storage. From 4f22a45cb58a40b92724c6fb29e1c1b574ea5a37 Mon Sep 17 00:00:00 2001 From: ibachar Date: Sat, 4 May 2019 15:54:02 +0300 Subject: [PATCH 494/828] Removed git merge leftovers --- examples/imgui_impl_dx11.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/imgui_impl_dx11.cpp b/examples/imgui_impl_dx11.cpp index 80ea99591c44..dfc32b49e8c4 100644 --- a/examples/imgui_impl_dx11.cpp +++ b/examples/imgui_impl_dx11.cpp @@ -92,7 +92,6 @@ static void ImGui_ImplDX11_SetupRenderState(ImDrawData* draw_data, ID3D11DeviceC ctx->OMSetDepthStencilState(g_pDepthStencilState, 0); ctx->RSSetState(g_pRasterizerState); } ->>>>>>> master // Render function // (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop) From 9ddb8493d5ee832fde957ec0a389ea9b083ebc6a Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 6 May 2019 12:07:29 +0200 Subject: [PATCH 495/828] Examples: DirectX9: Fixes for multi-viewports, destroying all swap chains. (#2520, #2502) --- examples/imgui_impl_dx9.cpp | 33 +++++++++++++++++++++++++++++---- examples/imgui_impl_dx9.h | 2 +- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/examples/imgui_impl_dx9.cpp b/examples/imgui_impl_dx9.cpp index c5d0fc4fd1fd..b601c892a145 100644 --- a/examples/imgui_impl_dx9.cpp +++ b/examples/imgui_impl_dx9.cpp @@ -11,7 +11,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2018-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. +// 2019-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. // 2019-04-30: DirectX9: Added support for special ImDrawCallback_ResetRenderState callback to reset render state. // 2019-03-29: Misc: Fixed erroneous assert in ImGui_ImplDX9_InvalidateDeviceObjects(). // 2019-01-16: Misc: Disabled fog before drawing UI's. Fixes issue #2288. @@ -48,6 +48,8 @@ struct CUSTOMVERTEX // Forward Declarations static void ImGui_ImplDX9_InitPlatformInterface(); static void ImGui_ImplDX9_ShutdownPlatformInterface(); +static void ImGui_ImplDX9_CreateDeviceObjectsForPlatformWindows(); +static void ImGui_ImplDX9_InvalidateDeviceObjectsForPlatformWindows(); static void ImGui_ImplDX9_SetupRenderState(ImDrawData* draw_data) { @@ -272,6 +274,7 @@ bool ImGui_ImplDX9_CreateDeviceObjects() return false; if (!ImGui_ImplDX9_CreateFontsTexture()) return false; + ImGui_ImplDX9_CreateDeviceObjectsForPlatformWindows(); return true; } @@ -282,6 +285,7 @@ void ImGui_ImplDX9_InvalidateDeviceObjects() if (g_pVB) { g_pVB->Release(); g_pVB = NULL; } if (g_pIB) { g_pIB->Release(); g_pIB = NULL; } if (g_FontTexture) { g_FontTexture->Release(); g_FontTexture = NULL; ImGui::GetIO().Fonts->TexID = NULL; } // We copied g_pFontTextureView to io.Fonts->TexID so let's clear that as well. + ImGui_ImplDX9_InvalidateDeviceObjectsForPlatformWindows(); } void ImGui_ImplDX9_NewFrame() @@ -316,13 +320,16 @@ static void ImGui_ImplDX9_CreateWindow(ImGuiViewport* viewport) ZeroMemory(&data->d3dpp, sizeof(D3DPRESENT_PARAMETERS)); data->d3dpp.Windowed = TRUE; data->d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; + data->d3dpp.BackBufferWidth = (UINT)viewport->Size.x; + data->d3dpp.BackBufferHeight = (UINT)viewport->Size.y; data->d3dpp.BackBufferFormat = D3DFMT_UNKNOWN; data->d3dpp.hDeviceWindow = hWnd; data->d3dpp.EnableAutoDepthStencil = TRUE; data->d3dpp.AutoDepthStencilFormat = D3DFMT_D16; data->d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; // Present without vsync - g_pd3dDevice->CreateAdditionalSwapChain(&data->d3dpp, &data->SwapChain); + HRESULT hr = g_pd3dDevice->CreateAdditionalSwapChain(&data->d3dpp, &data->SwapChain); IM_UNUSED(hr); + IM_ASSERT(hr == D3D_OK); IM_ASSERT(data->SwapChain != NULL); } @@ -349,7 +356,8 @@ static void ImGui_ImplDX9_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) data->SwapChain = NULL; data->d3dpp.BackBufferWidth = (UINT)size.x; data->d3dpp.BackBufferHeight = (UINT)size.y; - g_pd3dDevice->CreateAdditionalSwapChain(&data->d3dpp, &data->SwapChain); + HRESULT hr = g_pd3dDevice->CreateAdditionalSwapChain(&data->d3dpp, &data->SwapChain); IM_UNUSED(hr); + IM_ASSERT(hr == D3D_OK); } } @@ -381,7 +389,8 @@ static void ImGui_ImplDX9_RenderWindow(ImGuiViewport* viewport, void*) static void ImGui_ImplDX9_SwapBuffers(ImGuiViewport* viewport, void*) { ImGuiViewportDataDx9* data = (ImGuiViewportDataDx9*)viewport->RendererUserData; - data->SwapChain->Present(NULL, NULL, data->d3dpp.hDeviceWindow, NULL, NULL); + HRESULT hr = data->SwapChain->Present(NULL, NULL, data->d3dpp.hDeviceWindow, NULL, NULL); + IM_ASSERT(hr == D3D_OK); } static void ImGui_ImplDX9_InitPlatformInterface() @@ -398,3 +407,19 @@ static void ImGui_ImplDX9_ShutdownPlatformInterface() { ImGui::DestroyPlatformWindows(); } + +static void ImGui_ImplDX9_CreateDeviceObjectsForPlatformWindows() +{ + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + for (int i = 1; i < platform_io.Viewports.Size; i++) + if (!platform_io.Viewports[i]->RendererUserData) + ImGui_ImplDX9_CreateWindow(platform_io.Viewports[i]); +} + +static void ImGui_ImplDX9_InvalidateDeviceObjectsForPlatformWindows() +{ + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + for (int i = 1; i < platform_io.Viewports.Size; i++) + if (platform_io.Viewports[i]->RendererUserData) + ImGui_ImplDX9_DestroyWindow(platform_io.Viewports[i]); +} diff --git a/examples/imgui_impl_dx9.h b/examples/imgui_impl_dx9.h index 8695368418ba..bff71b08406e 100644 --- a/examples/imgui_impl_dx9.h +++ b/examples/imgui_impl_dx9.h @@ -19,5 +19,5 @@ IMGUI_IMPL_API void ImGui_ImplDX9_NewFrame(); IMGUI_IMPL_API void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data); // Use if you want to reset your rendering device without losing ImGui state. -IMGUI_IMPL_API void ImGui_ImplDX9_InvalidateDeviceObjects(); IMGUI_IMPL_API bool ImGui_ImplDX9_CreateDeviceObjects(); +IMGUI_IMPL_API void ImGui_ImplDX9_InvalidateDeviceObjects(); From e6c982509d899d2c276711e5d0999d59672d03c2 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 6 May 2019 12:12:32 +0200 Subject: [PATCH 496/828] Examples: DirectX9: Fixes for multi-viewports. Avoid using a depth/stencil target for secondary viewport. (#2520, #2502) --- examples/imgui_impl_dx9.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/examples/imgui_impl_dx9.cpp b/examples/imgui_impl_dx9.cpp index b601c892a145..5abc68c614fe 100644 --- a/examples/imgui_impl_dx9.cpp +++ b/examples/imgui_impl_dx9.cpp @@ -324,7 +324,7 @@ static void ImGui_ImplDX9_CreateWindow(ImGuiViewport* viewport) data->d3dpp.BackBufferHeight = (UINT)viewport->Size.y; data->d3dpp.BackBufferFormat = D3DFMT_UNKNOWN; data->d3dpp.hDeviceWindow = hWnd; - data->d3dpp.EnableAutoDepthStencil = TRUE; + data->d3dpp.EnableAutoDepthStencil = FALSE; data->d3dpp.AutoDepthStencilFormat = D3DFMT_D16; data->d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; // Present without vsync @@ -368,22 +368,27 @@ static void ImGui_ImplDX9_RenderWindow(ImGuiViewport* viewport, void*) LPDIRECT3DSURFACE9 render_target = NULL; LPDIRECT3DSURFACE9 last_render_target = NULL; + LPDIRECT3DSURFACE9 last_depth_stencil = NULL; data->SwapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &render_target); g_pd3dDevice->GetRenderTarget(0, &last_render_target); + g_pd3dDevice->GetDepthStencilSurface(&last_depth_stencil); g_pd3dDevice->SetRenderTarget(0, render_target); + g_pd3dDevice->SetDepthStencilSurface(NULL); if (!(viewport->Flags & ImGuiViewportFlags_NoRendererClear)) { D3DCOLOR clear_col_dx = D3DCOLOR_RGBA((int)(clear_color.x*255.0f), (int)(clear_color.y*255.0f), (int)(clear_color.z*255.0f), (int)(clear_color.w*255.0f)); - g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, clear_col_dx, 1.0f, 0); + g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET, clear_col_dx, 1.0f, 0); } ImGui_ImplDX9_RenderDrawData(viewport->DrawData); // Restore render target g_pd3dDevice->SetRenderTarget(0, last_render_target); + g_pd3dDevice->SetDepthStencilSurface(last_depth_stencil); render_target->Release(); last_render_target->Release(); + if (last_depth_stencil) last_depth_stencil->Release(); } static void ImGui_ImplDX9_SwapBuffers(ImGuiViewport* viewport, void*) From 9bf3f910c8be3d2aaebcc61e61eb2d0dbfc944a9 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 8 May 2019 18:20:13 +0200 Subject: [PATCH 497/828] Viewports: Fix to avoid SetNextWindowViewport being overrided by creation of a standalone viewport. (#2544, #1542) --- imgui.cpp | 63 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 35 insertions(+), 28 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 5ba23fd951da..b5565e0b4fbc 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10552,11 +10552,13 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) } } + bool lock_viewport = false; if (g.NextWindowData.ViewportCond) { // Code explicitly request a viewport window->Viewport = (ImGuiViewportP*)FindViewportByID(g.NextWindowData.ViewportId); window->ViewportId = g.NextWindowData.ViewportId; // Store ID even if Viewport isn't resolved yet. + lock_viewport = true; } else if ((flags & ImGuiWindowFlags_ChildWindow) || (flags & ImGuiWindowFlags_ChildMenu)) { @@ -10590,40 +10592,45 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) window->Viewport = main_viewport; // Mark window as allowed to protrude outside of its viewport and into the current monitor - if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) - { - // We need to take account of the possibility that mouse may become invalid. - // Popups/Tooltip always set ViewportAllowPlatformMonitorExtend so GetWindowAllowedExtentRect() will return full monitor bounds. - ImVec2 mouse_ref = (flags & ImGuiWindowFlags_Tooltip) ? g.IO.MousePos : g.BeginPopupStack.back().OpenMousePos; - bool use_mouse_ref = (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow); - bool mouse_valid = IsMousePosValid(&mouse_ref); - if ((window->Appearing || (flags & ImGuiWindowFlags_Tooltip)) && (!use_mouse_ref || mouse_valid)) - window->ViewportAllowPlatformMonitorExtend = FindPlatformMonitorForPos((use_mouse_ref && mouse_valid) ? mouse_ref : NavCalcPreferredRefPos()); - else - window->ViewportAllowPlatformMonitorExtend = window->Viewport->PlatformMonitor; - } - else if (window->Viewport && window != window->Viewport->Window && window->Viewport->Window && !(flags & ImGuiWindowFlags_ChildWindow)) + if (!lock_viewport) { - // When called from Begin() we don't have access to a proper version of the Hidden flag yet, so we replicate this code. - const bool will_be_visible = (window->DockIsActive && !window->DockTabIsVisible) ? false : true; - if ((window->Flags & ImGuiWindowFlags_DockNodeHost) && window->Viewport->LastFrameActive < g.FrameCount && will_be_visible) + if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) { - // Steal/transfer ownership - //IMGUI_DEBUG_LOG("[%05d] Window '%s' steal Viewport %08X from Window '%s'\n", g.FrameCount, window->Name, window->Viewport->ID, window->Viewport->Window->Name); - window->Viewport->Window = window; - window->Viewport->ID = window->ID; - window->Viewport->LastNameHash = 0; + // We need to take account of the possibility that mouse may become invalid. + // Popups/Tooltip always set ViewportAllowPlatformMonitorExtend so GetWindowAllowedExtentRect() will return full monitor bounds. + ImVec2 mouse_ref = (flags & ImGuiWindowFlags_Tooltip) ? g.IO.MousePos : g.BeginPopupStack.back().OpenMousePos; + bool use_mouse_ref = (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow); + bool mouse_valid = IsMousePosValid(&mouse_ref); + if ((window->Appearing || (flags & ImGuiWindowFlags_Tooltip)) && (!use_mouse_ref || mouse_valid)) + window->ViewportAllowPlatformMonitorExtend = FindPlatformMonitorForPos((use_mouse_ref && mouse_valid) ? mouse_ref : NavCalcPreferredRefPos()); + else + window->ViewportAllowPlatformMonitorExtend = window->Viewport->PlatformMonitor; } - else if (!UpdateTryMergeWindowIntoHostViewport(window, g.Viewports[0])) // Merge? + else if (window->Viewport && window != window->Viewport->Window && window->Viewport->Window && !(flags & ImGuiWindowFlags_ChildWindow)) { - // New viewport - window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoFocusOnAppearing); + // When called from Begin() we don't have access to a proper version of the Hidden flag yet, so we replicate this code. + const bool will_be_visible = (window->DockIsActive && !window->DockTabIsVisible) ? false : true; + if ((window->Flags & ImGuiWindowFlags_DockNodeHost) && window->Viewport->LastFrameActive < g.FrameCount && will_be_visible) + { + // Steal/transfer ownership + //IMGUI_DEBUG_LOG("[%05d] Window '%s' steal Viewport %08X from Window '%s'\n", g.FrameCount, window->Name, window->Viewport->ID, window->Viewport->Window->Name); + window->Viewport->Window = window; + window->Viewport->ID = window->ID; + window->Viewport->LastNameHash = 0; + } + else if (!UpdateTryMergeWindowIntoHostViewport(window, g.Viewports[0])) // Merge? + { + // New viewport + window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoFocusOnAppearing); + } + } + else if (window->ViewportAllowPlatformMonitorExtend < 0 && (flags & ImGuiWindowFlags_ChildWindow) == 0) + { + // Regular (non-child, non-popup) windows by default are also allowed to protrude + // Child windows are kept contained within their parent. + window->ViewportAllowPlatformMonitorExtend = window->Viewport->PlatformMonitor; } } - // Regular (non-child, non-popup) windows by default are also allowed to protrude - // Child windows are kept contained within their parent. - else if (window->ViewportAllowPlatformMonitorExtend < 0 && (flags & ImGuiWindowFlags_ChildWindow) == 0) - window->ViewportAllowPlatformMonitorExtend = window->Viewport->PlatformMonitor; // Update flags window->ViewportOwned = (window == window->Viewport->Window); From 239c8732d72f147f9dc4124c73cca472f3cd6f0b Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 8 May 2019 18:22:56 +0200 Subject: [PATCH 498/828] Viewports: Minor tweaks. (#2471) --- imgui.cpp | 11 +++++++++-- imgui.h | 3 ++- imgui_internal.h | 5 ----- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index b5565e0b4fbc..8cde9de959bf 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1099,6 +1099,7 @@ static void UpdateViewportsNewFrame(); static void UpdateViewportsEndFrame(); static void UpdateSelectWindowViewport(ImGuiWindow* window); static bool UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* host_viewport); +static bool UpdateTryMergeWindowIntoHostViewports(ImGuiWindow* window); static void SetCurrentViewport(ImGuiWindow* window, ImGuiViewportP* viewport); static bool GetWindowAlwaysWantOwnViewport(ImGuiWindow* window); static int FindPlatformMonitorForPos(const ImVec2& pos); @@ -10233,6 +10234,12 @@ static bool ImGui::UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImG return true; } +static bool ImGui::UpdateTryMergeWindowIntoHostViewports(ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; + return UpdateTryMergeWindowIntoHostViewport(window, g.Viewports[0]); +} + // Scale all windows (position, size). Use when e.g. changing DPI. (This is a lossy operation!) void ImGui::ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale) { @@ -10584,7 +10591,7 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) // We cannot test window->ViewportOwned as it set lower in the function. bool try_to_merge_into_host_viewport = (window->Viewport && window == window->Viewport->Window && g.ActiveId == 0); if (try_to_merge_into_host_viewport) - UpdateTryMergeWindowIntoHostViewport(window, g.Viewports[0]); + UpdateTryMergeWindowIntoHostViewports(window); } // Fallback to default viewport @@ -10618,7 +10625,7 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) window->Viewport->ID = window->ID; window->Viewport->LastNameHash = 0; } - else if (!UpdateTryMergeWindowIntoHostViewport(window, g.Viewports[0])) // Merge? + else if (!UpdateTryMergeWindowIntoHostViewports(window)) // Merge? { // New viewport window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoFocusOnAppearing); diff --git a/imgui.h b/imgui.h index ae549e5d449a..895c6acd8357 100644 --- a/imgui.h +++ b/imgui.h @@ -2358,7 +2358,8 @@ enum ImGuiViewportFlags_ ImGuiViewportFlags_NoInputs = 1 << 4, // Platform Window: Make mouse pass through so we can drag this window while peaking behind it. ImGuiViewportFlags_NoRendererClear = 1 << 5, // Platform Window: Renderer doesn't need to clear the framebuffer ahead (because we will fill it entirely). ImGuiViewportFlags_TopMost = 1 << 6, // Platform Window: Display on top (for tooltips only) - ImGuiViewportFlags_Minimized = 1 << 7 // Platform Window: Window is minimized, can skip render. When minimized we tend to avoid using the viewport pos/size for clipping window or testing if they are contained in the viewport. + ImGuiViewportFlags_Minimized = 1 << 7, // Platform Window: Window is minimized, can skip render. When minimized we tend to avoid using the viewport pos/size for clipping window or testing if they are contained in the viewport. + ImGuiViewportFlags_CanHostOtherWindows = 1 << 8 // Main viewport: can host multiple imgui windows (secondary viewports are associated to a single window) }; // The viewports created and managed by imgui. The role of the platform back-end is to create the platform/OS windows corresponding to each viewport. diff --git a/imgui_internal.h b/imgui_internal.h index 2ec597555601..743ede2093b2 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -748,11 +748,6 @@ struct ImDrawDataBuilder IMGUI_API void FlattenIntoSingleLayer(); }; -enum ImGuiViewportFlagsPrivate_ -{ - ImGuiViewportFlags_CanHostOtherWindows = 1 << 10 // Normal viewports are associated to a single window. The main viewport can host multiple windows. -}; - // ImGuiViewport Private/Internals fields (cardinal sin: we are using inheritance!) // Note that every instance of ImGuiViewport is in fact a ImGuiViewportP. struct ImGuiViewportP : public ImGuiViewport From b668726a3852ca82c8016166cd67871d6f75b7f5 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 10 May 2019 22:58:24 +0200 Subject: [PATCH 499/828] Fixed a PVS Studio static analyzer warning. --- imgui.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index e123be0e47b5..00fafdc8100a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11524,8 +11524,9 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) // Central node property needs to be moved to a leaf node, pick the last focused one. // FIXME-DOCKING: If we had to transfer other flags here, what would the policy be? ImGuiDockNode* last_focused_node = DockContextFindNodeByID(ctx, payload_node->LastFocusedNodeID); + IM_ASSERT(last_focused_node != NULL); ImGuiDockNode* last_focused_root_node = DockNodeGetRootNode(last_focused_node); - IM_ASSERT(last_focused_node != NULL && last_focused_root_node == DockNodeGetRootNode(payload_node)); + IM_ASSERT(last_focused_root_node == DockNodeGetRootNode(payload_node)); last_focused_node->LocalFlags |= ImGuiDockNodeFlags_CentralNode; node->LocalFlags &= ~ImGuiDockNodeFlags_CentralNode; last_focused_root_node->CentralNode = last_focused_node; From 02d6d2d487e33e6a7d81b1560374f93b5d881872 Mon Sep 17 00:00:00 2001 From: Alzathar Date: Sat, 11 May 2019 04:54:56 -0400 Subject: [PATCH 500/828] Platform Binding for GLFW updated with the release of GLFW 3.3 (#2547) * With the release of GLFW 3.3, it is now possible to detect correctly monitors working area (see GLFW_HAS_MONITOR_WORK_AREA). GLFW 3.3 also introduced the window hint GLFW_FOCUS_ON_SHOW. This fixed the case where a new created window (viewport) takes the focus even if not visible. * Disable a GLFW 3.2 windows hack when GLFW 3.3 is detected (related to window focused when shown). --- examples/imgui_impl_glfw.cpp | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index 37a33cd44720..a6aee9bb5f51 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -44,12 +44,14 @@ #define GLFW_EXPOSE_NATIVE_WIN32 #include // for glfwGetWin32Window #endif -#define GLFW_HAS_WINDOW_TOPMOST (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ GLFW_FLOATING -#define GLFW_HAS_WINDOW_HOVERED (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ GLFW_HOVERED -#define GLFW_HAS_WINDOW_ALPHA (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ glfwSetWindowOpacity -#define GLFW_HAS_PER_MONITOR_DPI (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ glfwGetMonitorContentScale -#define GLFW_HAS_VULKAN (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ glfwCreateWindowSurface -#define GLFW_HAS_FOCUS_WINDOW (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ glfwFocusWindow +#define GLFW_HAS_WINDOW_TOPMOST (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ GLFW_FLOATING +#define GLFW_HAS_WINDOW_HOVERED (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ GLFW_HOVERED +#define GLFW_HAS_WINDOW_ALPHA (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ glfwSetWindowOpacity +#define GLFW_HAS_PER_MONITOR_DPI (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ glfwGetMonitorContentScale +#define GLFW_HAS_VULKAN (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ glfwCreateWindowSurface +#define GLFW_HAS_FOCUS_WINDOW (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ glfwFocusWindow +#define GLFW_HAS_FOCUS_ON_SHOW (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ GLFW_FOCUS_ON_SHOW +#define GLFW_HAS_MONITOR_WORK_AREA (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ glfwGetMonitorWorkarea // Data enum GlfwClientApi @@ -428,8 +430,12 @@ static void ImGui_ImplGlfw_CreateWindow(ImGuiViewport* viewport) viewport->PlatformUserData = data; // GLFW 3.2 unfortunately always set focus on glfwCreateWindow() if GLFW_VISIBLE is set, regardless of GLFW_FOCUSED + // With GLFW 3.3, the hint GLFW_FOCUS_ON_SHOW fixes this problem glfwWindowHint(GLFW_VISIBLE, false); glfwWindowHint(GLFW_FOCUSED, false); +#if GLFW_HAS_FOCUS_ON_SHOW + glfwWindowHint(GLFW_FOCUS_ON_SHOW, false); + #endif glfwWindowHint(GLFW_DECORATED, (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? false : true); #if GLFW_HAS_WINDOW_TOPMOST glfwWindowHint(GLFW_FLOATING, (viewport->Flags & ImGuiViewportFlags_TopMost) ? true : false); @@ -515,6 +521,7 @@ static void ImGui_ImplGlfw_ShowWindow(ImGuiViewport* viewport) ::SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)WndProcNoInputs); #endif +#if !GLFW_HAS_FOCUS_ON_SHOW // GLFW hack: GLFW 3.2 has a bug where glfwShowWindow() also activates/focus the window. // The fix was pushed to GLFW repository on 2018/01/09 and should be included in GLFW 3.3 via a GLFW_FOCUS_ON_SHOW window attribute. // See https://github.com/glfw/glfw/issues/1189 @@ -524,6 +531,7 @@ static void ImGui_ImplGlfw_ShowWindow(ImGuiViewport* viewport) ::ShowWindow(hwnd, SW_SHOWNA); return; } +#endif #endif glfwShowWindow(data->Window); @@ -678,8 +686,17 @@ static void ImGui_ImplGlfw_UpdateMonitors() int x, y; glfwGetMonitorPos(glfw_monitors[n], &x, &y); const GLFWvidmode* vid_mode = glfwGetVideoMode(glfw_monitors[n]); +#if GLFW_HAS_MONITOR_WORK_AREA + monitor.MainPos = ImVec2((float)x, (float)y); + monitor.MainSize = ImVec2((float)vid_mode->width, (float)vid_mode->height); + int w, h; + glfwGetMonitorWorkarea(glfw_monitors[n], &x, &y, &w, &h); + monitor.WorkPos = ImVec2((float)x, (float)y);; + monitor.WorkSize = ImVec2((float)w, (float)h); +#else monitor.MainPos = monitor.WorkPos = ImVec2((float)x, (float)y); monitor.MainSize = monitor.WorkSize = ImVec2((float)vid_mode->width, (float)vid_mode->height); +#endif #if GLFW_HAS_PER_MONITOR_DPI // Warning: the validity of monitor DPI information on Windows depends on the application DPI awareness settings, which generally needs to be set in the manifest or at runtime. float x_scale, y_scale; From b955e485f11c31a7005a3c88b73fe2143753a0d0 Mon Sep 17 00:00:00 2001 From: Chris Savoie Date: Sun, 12 May 2019 08:53:08 -0700 Subject: [PATCH 501/828] Fixed unused variables warnings when asserts are compiled out. (#2551) --- imgui.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 00fafdc8100a..a668395441ba 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3623,6 +3623,7 @@ void ImGui::NewFrame() for (int monitor_n = 0; monitor_n < g.PlatformIO.Monitors.Size; monitor_n++) { ImGuiPlatformMonitor& mon = g.PlatformIO.Monitors[monitor_n]; + IM_UNUSED(mon); IM_ASSERT(mon.MainSize.x > 0.0f && mon.MainSize.y > 0.0f && "Monitor bounds not setup properly."); IM_ASSERT(mon.WorkSize.x > 0.0f && mon.WorkSize.y > 0.0f && "Monitor bounds not setup properly. If you don't have work area information, just copy Min/Max into them."); IM_ASSERT(mon.DpiScale != 0.0f); @@ -9242,7 +9243,8 @@ void ImGui::PushColumnsBackground() ImGuiColumns* columns = window->DC.CurrentColumns; window->DrawList->ChannelsSetCurrent(0); int cmd_size = window->DrawList->CmdBuffer.Size; - PushClipRect(columns->HostClipRect.Min, columns->HostClipRect.Max, false); + PushClipRect(columns->HostClipRect.Min, columns->HostClipRect.Max, false); + IM_UNUSED(cmd_size); IM_ASSERT(cmd_size == window->DrawList->CmdBuffer.Size); // Being in channel 0 this should not have created an ImDrawCmd } @@ -11130,6 +11132,7 @@ void ImGui::DockContextOnLoadSettings(ImGuiContext* ctx) void ImGui::DockContextClearNodes(ImGuiContext* ctx, ImGuiID root_id, bool clear_persistent_docking_references) { + IM_UNUSED(ctx); IM_ASSERT(ctx == GImGui); DockBuilderRemoveNodeDockedWindows(root_id, clear_persistent_docking_references); DockBuilderRemoveNodeChildNodes(root_id); @@ -11439,6 +11442,8 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) IM_ASSERT(req->DockTargetWindow != NULL || req->DockTargetNode != NULL); ImGuiContext& g = *ctx; + IM_UNUSED(g); + ImGuiWindow* payload_window = req->DockPayload; // Optional ImGuiWindow* target_window = req->DockTargetWindow; ImGuiDockNode* node = req->DockTargetNode; From 2e5860b5a0c14412ae209819ca19891ba3f7276d Mon Sep 17 00:00:00 2001 From: omar Date: Sat, 18 May 2019 13:00:00 +0200 Subject: [PATCH 502/828] Docking: Fixed incomplete merge of 36e714a leading to undocking. #2109 --- imgui.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 14a33004b183..26b39a9891fd 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -13724,8 +13724,7 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) // Calling SetNextWindowPos() undock windows by default (by setting PosUndock) bool want_undock = false; want_undock |= (window->Flags & ImGuiWindowFlags_NoDocking) != 0; - want_undock |= (g.NextWindowData.PosCond && (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) && g.NextWindowData.PosUndock); - g.NextWindowData.PosUndock = false; + want_undock |= (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) && (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) && g.NextWindowData.PosUndock; if (want_undock) { DockContextProcessUndockWindow(ctx, window); From 7e772f6a51d0db5d00d9e9b4649cbe4cb0da79b9 Mon Sep 17 00:00:00 2001 From: omar Date: Sat, 18 May 2019 18:42:59 +0200 Subject: [PATCH 503/828] Docking: Fixed undocking whole node (from collapse/docking menu button) from losing its size/pos. Made collapose/docking menu id easier to compute for testing. --- imgui.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 26b39a9891fd..e64b6d335dfa 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11609,6 +11609,7 @@ void ImGui::DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node) DockSettingsRenameNodeReferences(node->ID, new_node->ID); for (int n = 0; n < new_node->Windows.Size; n++) UpdateWindowParentAndRootLinks(new_node->Windows[n], new_node->Windows[n]->Flags, NULL); + new_node->AuthorityForPos = new_node->AuthorityForSize = ImGuiDataAuthority_Window; new_node->WantMouseMove = true; } else @@ -12348,7 +12349,10 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w host_window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Menu); } - PushID(node->ID); + // Use PushOverrideID() instead of PushID() to use the node id _without_ the host window ID. + // This is to facilitate computing those ID from the outside, and will affect more or less only the ID of the collapse button, popup and tabs, + // as docked windows themselves will override the stack with their own root ID. + PushOverrideID(node->ID); ImGuiTabBar* tab_bar = node->TabBar; bool tab_bar_is_recreated = (tab_bar == NULL); // Tab bar are automatically destroyed when a node gets hidden if (tab_bar == NULL) @@ -12361,6 +12365,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w node->IsFocused = is_focused; // Collapse button changes shape and display a list + // FIXME-DOCK: Could we recycle popups id? if (IsPopupOpen("#TabListMenu")) { if (ImGuiID tab_id = DockNodeUpdateTabListMenu(node, tab_bar)) From 1575a3fbcdcea3ea85639cf098c7909f33270cae Mon Sep 17 00:00:00 2001 From: omar Date: Sun, 19 May 2019 17:00:17 +0200 Subject: [PATCH 504/828] Docking: Fixed temporarily losing Dockspace flag when merging remaining sibling back into a parent node. (#2563, #2109) Would trigger an assert in the Passthru hole path. Broken by fd5859ed. --- imgui.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index e64b6d335dfa..2d1e0d2a0060 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -12929,7 +12929,9 @@ void ImGui::DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImG parent_node->SizeRef = backup_last_explicit_size; // Flags transfer - parent_node->LocalFlags = ((child_0 ? child_0->LocalFlags : 0) | (child_1 ? child_1->LocalFlags : 0)) & ImGuiDockNodeFlags_LocalFlagsTransferMask_; + parent_node->LocalFlags &= ~ImGuiDockNodeFlags_LocalFlagsTransferMask_; // Preserve Dockspace flag + parent_node->LocalFlags |= (child_0 ? child_0->LocalFlags : 0) & ImGuiDockNodeFlags_LocalFlagsTransferMask_; + parent_node->LocalFlags |= (child_1 ? child_1->LocalFlags : 0) & ImGuiDockNodeFlags_LocalFlagsTransferMask_; if (child_0) { From e5dfa0855f8de98664dfc9502068a3e42da67bb6 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 24 May 2019 21:59:00 +0200 Subject: [PATCH 505/828] Docking: Honor style.WindowMenuButtonPosition setting in docking node. --- imgui.cpp | 46 +++++++++++++++++++++++++++++++++------------- imgui_widgets.cpp | 2 +- 2 files changed, 34 insertions(+), 14 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 1333bc2902d9..cde3c4ab197b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11125,7 +11125,7 @@ namespace ImGui static bool DockNodeIsDropAllowed(ImGuiWindow* host_window, ImGuiWindow* payload_window); static void DockNodePreviewDockCalc(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* payload_window, ImGuiDockPreviewData* preview_data, bool is_explicit_target, bool is_outer_docking); static void DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* payload_window, const ImGuiDockPreviewData* preview_data); - static ImRect DockNodeCalcTabBarRect(const ImGuiDockNode* node); + static void DockNodeCalcTabBarLayout(const ImGuiDockNode* node, ImRect* out_title_rect, ImRect* out_tab_bar_rect, ImVec2* out_collapse_button_pos); static void DockNodeCalcSplitRects(ImVec2& pos_old, ImVec2& size_old, ImVec2& pos_new, ImVec2& size_new, ImGuiDir dir, ImVec2 size_new_desired); static bool DockNodeCalcDropRectsAndTestMousePos(const ImRect& parent, ImGuiDir dir, ImRect& out_draw, bool outer_docking, ImVec2* test_mouse_pos); static const char* DockNodeGetHostWindowTitle(ImGuiDockNode* node, char* buf, int buf_size) { ImFormatString(buf, buf_size, "##DockNode_%02X", node->ID); return buf; } @@ -12322,8 +12322,12 @@ static int IMGUI_CDECL TabItemComparerByDockOrder(const void* lhs, const void* r static ImGuiID ImGui::DockNodeUpdateTabListMenu(ImGuiDockNode* node, ImGuiTabBar* tab_bar) { // Try to position the menu so it is more likely to stays within the same viewport + ImGuiContext& g = *GImGui; ImGuiID ret_tab_id = 0; - SetNextWindowPos(ImVec2(node->Pos.x, node->Pos.y + GetFrameHeight())); + if (g.Style.WindowMenuButtonPosition == ImGuiDir_Left) + SetNextWindowPos(ImVec2(node->Pos.x, node->Pos.y + GetFrameHeight()), ImGuiCond_Always, ImVec2(0.0f, 0.0f)); + else + SetNextWindowPos(ImVec2(node->Pos.x + node->Size.x, node->Pos.y + GetFrameHeight()), ImGuiCond_Always, ImVec2(1.0f, 0.0f)); if (BeginPopup("#TabListMenu")) { node->IsFocused = true; @@ -12419,15 +12423,19 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w is_focused |= node->IsFocused; } + // Layout + ImRect title_bar_rect, tab_bar_rect; + ImVec2 collapse_button_pos; + DockNodeCalcTabBarLayout(node, &title_bar_rect, &tab_bar_rect, &collapse_button_pos); + // Title bar if (is_focused) node->LastFrameFocused = g.FrameCount; - ImRect title_bar_rect = ImRect(node->Pos, node->Pos + ImVec2(node->Size.x, g.FontSize + style.FramePadding.y * 2.0f)); ImU32 title_bar_col = GetColorU32(host_window->Collapsed ? ImGuiCol_TitleBgCollapsed : is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg); host_window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, host_window->WindowRounding, ImDrawCornerFlags_Top); // Collapse button - if (CollapseButton(host_window->GetID("#COLLAPSE"), title_bar_rect.Min, node)) + if (CollapseButton(host_window->GetID("#COLLAPSE"), collapse_button_pos, node)) OpenPopup("#TabListMenu"); if (IsItemActive()) focus_tab_id = tab_bar->SelectedTabId; @@ -12461,7 +12469,6 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = tab_bar->Tabs.back().Window->ID; // Begin tab bar - const ImRect tab_bar_rect = DockNodeCalcTabBarRect(node); ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_Reorderable | ImGuiTabBarFlags_AutoSelectNewTabs; // | ImGuiTabBarFlags_NoTabListScrollingButtons); tab_bar_flags |= ImGuiTabBarFlags_SaveSettings | ImGuiTabBarFlags_DockNode; if (!host_window->Collapsed && is_focused) @@ -12621,15 +12628,27 @@ static bool ImGui::DockNodeIsDropAllowed(ImGuiWindow* host_window, ImGuiWindow* return false; } -static ImRect ImGui::DockNodeCalcTabBarRect(const ImGuiDockNode* node) +// FIXME: This is similar to RenderWindowTitleBarContents, may want to share code. +static void ImGui::DockNodeCalcTabBarLayout(const ImGuiDockNode* node, ImRect* out_title_rect, ImRect* out_tab_bar_rect, ImVec2* out_collapse_button_pos) { ImGuiContext& g = *GImGui; - ImRect r = ImRect(node->Pos.x, node->Pos.y, node->Pos.x + node->Size.x, node->Pos.y + (g.FontSize + g.Style.FramePadding.y * 2.0f)); - if (node->HasCollapseButton) - r.Min.x += g.Style.FramePadding.x + g.FontSize; // + g.Style.ItemInnerSpacing.x; // <-- Adding ItemInnerSpacing makes the title text moves slightly when in a tab bar. Instead we adjusted RenderArrowDockMenu() - // In DockNodeUpdateTabBar() we currently display a disabled close button even if there is none. - r.Max.x -= g.Style.FramePadding.x + g.FontSize + 1.0f; - return r; + ImRect r = ImRect(node->Pos.x, node->Pos.y, node->Pos.x + node->Size.x, node->Pos.y + g.FontSize + g.Style.FramePadding.y * 2.0f); + if (out_title_rect) { *out_title_rect = r; } + + ImVec2 collapse_button_pos = r.Min; + r.Max.x -= g.Style.FramePadding.x + g.FontSize;// +1.0f; // In DockNodeUpdateTabBar() we currently display a disabled close button even if there is none. + if (node->HasCollapseButton && g.Style.WindowMenuButtonPosition == ImGuiDir_Left) + { + r.Min.x += g.Style.FramePadding.x + g.FontSize; // + g.Style.ItemInnerSpacing.x; // <-- Adding ItemInnerSpacing makes the title text moves slightly when in a docking tab bar. Instead we adjusted RenderArrowDockMenu() + } + else if (node->HasCollapseButton && g.Style.WindowMenuButtonPosition == ImGuiDir_Right) + { + r.Min.x += g.Style.FramePadding.x; + r.Max.x -= g.FontSize + g.Style.FramePadding.x; + collapse_button_pos = ImVec2(r.Max.x, r.Min.y); + } + if (out_tab_bar_rect) { *out_tab_bar_rect = r; } + if (out_collapse_button_pos) { *out_collapse_button_pos = collapse_button_pos; } } void ImGui::DockNodeCalcSplitRects(ImVec2& pos_old, ImVec2& size_old, ImVec2& pos_new, ImVec2& size_new, ImGuiDir dir, ImVec2 size_new_desired) @@ -12832,7 +12851,8 @@ static void ImGui::DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDock if (data->IsDropAllowed && can_preview_tabs && data->SplitDir == ImGuiDir_None && data->IsCenterAvailable) { // Compute target tab bar geometry so we can locate our preview tabs - ImRect tab_bar_rect = DockNodeCalcTabBarRect(&data->FutureNode); + ImRect tab_bar_rect; + DockNodeCalcTabBarLayout(&data->FutureNode, NULL, &tab_bar_rect, NULL); ImVec2 tab_pos = tab_bar_rect.Min; if (host_node && host_node->TabBar) { diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 5e2acb13f961..22af64917a4c 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -793,7 +793,7 @@ bool ImGui::CollapseButton(ImGuiID id, const ImVec2& pos, ImGuiDockNode* dock_no } else { - ImVec2 backup_active_click_offset = g.ActiveIdClickOffset; + ImVec2 backup_active_click_offset = g.ActiveIdClickOffset + (pos - window->Pos); StartMouseMovingWindow(window); g.ActiveIdClickOffset = backup_active_click_offset; } From 511e32e8ca7dcae9fbca4bf3a899597a13bfbf59 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 27 May 2019 12:12:11 +0200 Subject: [PATCH 506/828] Docking: Clarified terminology of docking/tablist/collapse button into Window Menu button matching master. Added private ImGuiDockNodeFlags_NoWindowMenuButton, ImGuiDockNodeFlags_NoCloseButton flags. (#2583, #2109) --- imgui.cpp | 86 ++++++++++++++++++++++++++++-------------------- imgui.h | 3 +- imgui_internal.h | 7 ++-- 3 files changed, 58 insertions(+), 38 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index cde3c4ab197b..62bf4d94b161 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5438,6 +5438,7 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar } // Render title text, collapse button, close button +// When inside a dock node, this is handled in DockNodeUpdateTabBar() instead. void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open) { ImGuiContext& g = *GImGui; @@ -5447,7 +5448,7 @@ void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& titl const bool has_close_button = (p_open != NULL); const bool has_collapse_button = !(flags & ImGuiWindowFlags_NoCollapse); - // Close & collapse button are on the Menu NavLayer and don't default focus (unless there's nothing else on that layer) + // Close & Collapse button are on the Menu NavLayer and don't default focus (unless there's nothing else on that layer) const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags; window->DC.ItemFlags |= ImGuiItemFlags_NoNavDefaultFocus; window->DC.NavLayerCurrent = ImGuiNavLayer_Menu; @@ -11119,13 +11120,13 @@ namespace ImGui static void DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_window); static void DockNodeAddTabBar(ImGuiDockNode* node); static void DockNodeRemoveTabBar(ImGuiDockNode* node); - static ImGuiID DockNodeUpdateTabListMenu(ImGuiDockNode* node, ImGuiTabBar* tab_bar); + static ImGuiID DockNodeUpdateWindowMenu(ImGuiDockNode* node, ImGuiTabBar* tab_bar); static void DockNodeUpdateVisibleFlag(ImGuiDockNode* node); static void DockNodeStartMouseMovingWindow(ImGuiDockNode* node, ImGuiWindow* window); static bool DockNodeIsDropAllowed(ImGuiWindow* host_window, ImGuiWindow* payload_window); static void DockNodePreviewDockCalc(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* payload_window, ImGuiDockPreviewData* preview_data, bool is_explicit_target, bool is_outer_docking); static void DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* payload_window, const ImGuiDockPreviewData* preview_data); - static void DockNodeCalcTabBarLayout(const ImGuiDockNode* node, ImRect* out_title_rect, ImRect* out_tab_bar_rect, ImVec2* out_collapse_button_pos); + static void DockNodeCalcTabBarLayout(const ImGuiDockNode* node, ImRect* out_title_rect, ImRect* out_tab_bar_rect, ImVec2* out_window_menu_button_pos); static void DockNodeCalcSplitRects(ImVec2& pos_old, ImVec2& size_old, ImVec2& pos_new, ImVec2& size_new, ImGuiDir dir, ImVec2 size_new_desired); static bool DockNodeCalcDropRectsAndTestMousePos(const ImRect& parent, ImGuiDir dir, ImRect& out_draw, bool outer_docking, ImVec2* test_mouse_pos); static const char* DockNodeGetHostWindowTitle(ImGuiDockNode* node, char* buf, int buf_size) { ImFormatString(buf, buf_size, "##DockNode_%02X", node->ID); return buf; } @@ -11713,7 +11714,7 @@ ImGuiDockNode::ImGuiDockNode(ImGuiID id) AuthorityForPos = AuthorityForSize = ImGuiDataAuthority_DockNode; AuthorityForViewport = ImGuiDataAuthority_Auto; IsVisible = true; - IsFocused = HasCloseButton = HasCollapseButton = false; + IsFocused = HasCloseButton = HasWindowMenuButton = EnableCloseButton = false; WantCloseAll = WantLockSizeOnce = WantMouseMove = WantHiddenTabBarUpdate = WantHiddenTabBarToggle = false; } @@ -12112,7 +12113,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) DockNodeHideHostWindow(node); node->WantCloseAll = false; node->WantCloseTabID = 0; - node->HasCloseButton = node->HasCollapseButton = false; + node->HasCloseButton = node->HasWindowMenuButton = node->EnableCloseButton = false; node->LastFrameActive = g.FrameCount; if (node->WantMouseMove && node->Windows.Size == 1) @@ -12120,27 +12121,31 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) return; } + const ImGuiDockNodeFlags node_flags = node->GetMergedFlags(); + ImGuiWindow* host_window = NULL; bool beginned_into_host_window = false; if (node->IsDockSpace()) { // [Explicit root dockspace node] IM_ASSERT(node->HostWindow); - node->HasCloseButton = false; - node->HasCollapseButton = true; + node->EnableCloseButton = false; + node->HasCloseButton = (node_flags & ImGuiDockNodeFlags_NoCloseButton) == 0; + node->HasWindowMenuButton = (node_flags & ImGuiDockNodeFlags_NoWindowMenuButton) == 0; host_window = node->HostWindow; } else { // [Automatic root or child nodes] - node->HasCloseButton = false; - node->HasCollapseButton = (node->Windows.Size > 0); + node->EnableCloseButton = false; + node->HasCloseButton = (node->Windows.Size > 0) && (node_flags & ImGuiDockNodeFlags_NoWindowMenuButton) == 0; + node->HasWindowMenuButton = (node->Windows.Size > 0) && (node_flags & ImGuiDockNodeFlags_NoWindowMenuButton) == 0; for (int window_n = 0; window_n < node->Windows.Size; window_n++) { // FIXME-DOCK: Setting DockIsActive here means that for single active window in a leaf node, DockIsActive will be cleared until the next Begin() call. ImGuiWindow* window = node->Windows[window_n]; window->DockIsActive = (node->Windows.Size > 1); - node->HasCloseButton |= window->HasCloseButton; + node->EnableCloseButton |= window->HasCloseButton; } if (node->IsRootNode() && node->IsVisible) @@ -12217,7 +12222,6 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) // We need to draw a background at the root level if requested by ImGuiDockNodeFlags_PassthruCentralNode, but we will only know the correct pos/size after // processing the resizing splitters. So we are using the DrawList channel splitting facility to submit drawing primitives out of order! - const ImGuiDockNodeFlags node_flags = node->GetMergedFlags(); const bool render_dockspace_bg = node->IsRootNode() && host_window && (node_flags & ImGuiDockNodeFlags_PassthruCentralNode) != 0; if (render_dockspace_bg) { @@ -12319,7 +12323,7 @@ static int IMGUI_CDECL TabItemComparerByDockOrder(const void* lhs, const void* r return (a->BeginOrderWithinContext - b->BeginOrderWithinContext); } -static ImGuiID ImGui::DockNodeUpdateTabListMenu(ImGuiDockNode* node, ImGuiTabBar* tab_bar) +static ImGuiID ImGui::DockNodeUpdateWindowMenu(ImGuiDockNode* node, ImGuiTabBar* tab_bar) { // Try to position the menu so it is more likely to stays within the same viewport ImGuiContext& g = *GImGui; @@ -12328,7 +12332,7 @@ static ImGuiID ImGui::DockNodeUpdateTabListMenu(ImGuiDockNode* node, ImGuiTabBar SetNextWindowPos(ImVec2(node->Pos.x, node->Pos.y + GetFrameHeight()), ImGuiCond_Always, ImVec2(0.0f, 0.0f)); else SetNextWindowPos(ImVec2(node->Pos.x + node->Size.x, node->Pos.y + GetFrameHeight()), ImGuiCond_Always, ImVec2(1.0f, 0.0f)); - if (BeginPopup("#TabListMenu")) + if (BeginPopup("#WindowMenu")) { node->IsFocused = true; if (tab_bar->Tabs.Size == 1) @@ -12414,19 +12418,23 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w ImGuiID focus_tab_id = 0; node->IsFocused = is_focused; - // Collapse button changes shape and display a list - // FIXME-DOCK: Could we recycle popups id? - if (IsPopupOpen("#TabListMenu")) + const ImGuiDockNodeFlags node_flags = node->GetMergedFlags(); + const bool has_window_menu_button = (node_flags & ImGuiDockNodeFlags_NoWindowMenuButton) == 0; + const bool has_close_button = (node_flags & ImGuiDockNodeFlags_NoCloseButton) == 0; + + // In a dock node, the Collapse Button turns into the Window Menu button. + // FIXME-DOCK FIXME-OPT: Could we recycle popups id accross multiple dock nodes? + if (has_window_menu_button && IsPopupOpen("#WindowMenu")) { - if (ImGuiID tab_id = DockNodeUpdateTabListMenu(node, tab_bar)) + if (ImGuiID tab_id = DockNodeUpdateWindowMenu(node, tab_bar)) focus_tab_id = tab_bar->NextSelectedTabId = tab_id; is_focused |= node->IsFocused; } // Layout ImRect title_bar_rect, tab_bar_rect; - ImVec2 collapse_button_pos; - DockNodeCalcTabBarLayout(node, &title_bar_rect, &tab_bar_rect, &collapse_button_pos); + ImVec2 window_menu_button_pos; + DockNodeCalcTabBarLayout(node, &title_bar_rect, &tab_bar_rect, &window_menu_button_pos); // Title bar if (is_focused) @@ -12434,11 +12442,14 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w ImU32 title_bar_col = GetColorU32(host_window->Collapsed ? ImGuiCol_TitleBgCollapsed : is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg); host_window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, host_window->WindowRounding, ImDrawCornerFlags_Top); - // Collapse button - if (CollapseButton(host_window->GetID("#COLLAPSE"), collapse_button_pos, node)) - OpenPopup("#TabListMenu"); - if (IsItemActive()) - focus_tab_id = tab_bar->SelectedTabId; + // Docking/Collapse button + if (has_window_menu_button) + { + if (CollapseButton(host_window->GetID("#COLLAPSE"), window_menu_button_pos, node)) + OpenPopup("#WindowMenu"); + if (IsItemActive()) + focus_tab_id = tab_bar->SelectedTabId; + } // Submit new tabs and apply NavWindow focus back to the tab bar. They will be added as Unsorted and sorted below based on relative DockOrder value. const int tabs_count_old = tab_bar->Tabs.Size; @@ -12515,7 +12526,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w // Close button (after VisibleWindow was updated) // Note that VisibleWindow may have been overrided by CTRL+Tabbing, so VisibleWindow->ID may be != from tab_bar->SelectedTabId - if (node->VisibleWindow) + if (has_close_button && node->VisibleWindow) { if (!node->VisibleWindow->HasCloseButton) { @@ -12628,27 +12639,32 @@ static bool ImGui::DockNodeIsDropAllowed(ImGuiWindow* host_window, ImGuiWindow* return false; } +// window menu button == collapse button when not in a dock node. // FIXME: This is similar to RenderWindowTitleBarContents, may want to share code. -static void ImGui::DockNodeCalcTabBarLayout(const ImGuiDockNode* node, ImRect* out_title_rect, ImRect* out_tab_bar_rect, ImVec2* out_collapse_button_pos) +static void ImGui::DockNodeCalcTabBarLayout(const ImGuiDockNode* node, ImRect* out_title_rect, ImRect* out_tab_bar_rect, ImVec2* out_window_menu_button_pos) { ImGuiContext& g = *GImGui; ImRect r = ImRect(node->Pos.x, node->Pos.y, node->Pos.x + node->Size.x, node->Pos.y + g.FontSize + g.Style.FramePadding.y * 2.0f); if (out_title_rect) { *out_title_rect = r; } - ImVec2 collapse_button_pos = r.Min; - r.Max.x -= g.Style.FramePadding.x + g.FontSize;// +1.0f; // In DockNodeUpdateTabBar() we currently display a disabled close button even if there is none. - if (node->HasCollapseButton && g.Style.WindowMenuButtonPosition == ImGuiDir_Left) + ImVec2 window_menu_button_pos = r.Min; + r.Min.x += g.Style.FramePadding.x; + r.Max.x -= g.Style.FramePadding.x; + if (node->HasCloseButton) + { + r.Max.x -= g.FontSize;// +1.0f; // In DockNodeUpdateTabBar() we currently display a disabled close button even if there is none. + } + if (node->HasWindowMenuButton && g.Style.WindowMenuButtonPosition == ImGuiDir_Left) { - r.Min.x += g.Style.FramePadding.x + g.FontSize; // + g.Style.ItemInnerSpacing.x; // <-- Adding ItemInnerSpacing makes the title text moves slightly when in a docking tab bar. Instead we adjusted RenderArrowDockMenu() + r.Min.x += g.FontSize; // + g.Style.ItemInnerSpacing.x; // <-- Adding ItemInnerSpacing makes the title text moves slightly when in a docking tab bar. Instead we adjusted RenderArrowDockMenu() } - else if (node->HasCollapseButton && g.Style.WindowMenuButtonPosition == ImGuiDir_Right) + else if (node->HasWindowMenuButton && g.Style.WindowMenuButtonPosition == ImGuiDir_Right) { - r.Min.x += g.Style.FramePadding.x; r.Max.x -= g.FontSize + g.Style.FramePadding.x; - collapse_button_pos = ImVec2(r.Max.x, r.Min.y); + window_menu_button_pos = ImVec2(r.Max.x, r.Min.y); } if (out_tab_bar_rect) { *out_tab_bar_rect = r; } - if (out_collapse_button_pos) { *out_collapse_button_pos = collapse_button_pos; } + if (out_window_menu_button_pos) { *out_window_menu_button_pos = window_menu_button_pos; } } void ImGui::DockNodeCalcSplitRects(ImVec2& pos_old, ImVec2& size_old, ImVec2& pos_new, ImVec2& size_new, ImGuiDir dir, ImVec2 size_new_desired) @@ -12751,7 +12767,7 @@ static void ImGui::DockNodePreviewDockCalc(ImGuiWindow* host_window, ImGuiDockNo // Build a tentative future node (reuse same structure because it is practical) data->FutureNode.HasCloseButton = (host_node ? host_node->HasCloseButton : host_window->HasCloseButton) || (root_payload->HasCloseButton); - data->FutureNode.HasCollapseButton = host_node ? true : ((host_window->Flags & ImGuiWindowFlags_NoCollapse) == 0); + data->FutureNode.HasWindowMenuButton = host_node ? true : ((host_window->Flags & ImGuiWindowFlags_NoCollapse) == 0); data->FutureNode.Pos = host_node ? ref_node_for_rect->Pos : host_window->Pos; data->FutureNode.Size = host_node ? ref_node_for_rect->Size : host_window->Size; diff --git a/imgui.h b/imgui.h index 506b8977b9c1..ecd425a2fa9c 100644 --- a/imgui.h +++ b/imgui.h @@ -745,7 +745,7 @@ enum ImGuiWindowFlags_ ImGuiWindowFlags_NoMove = 1 << 2, // Disable user moving the window ImGuiWindowFlags_NoScrollbar = 1 << 3, // Disable scrollbars (window can still scroll with mouse or programmatically) ImGuiWindowFlags_NoScrollWithMouse = 1 << 4, // Disable user vertically scrolling with mouse wheel. On child window, mouse wheel will be forwarded to the parent unless NoScrollbar is also set. - ImGuiWindowFlags_NoCollapse = 1 << 5, // Disable user collapsing window by double-clicking on it + ImGuiWindowFlags_NoCollapse = 1 << 5, // Disable user collapsing window by double-clicking on it. Also referred to as "window menu button" within a docking node. ImGuiWindowFlags_AlwaysAutoResize = 1 << 6, // Resize every window to its content every frame ImGuiWindowFlags_NoBackground = 1 << 7, // Disable drawing background color (WindowBg, etc.) and outside border. Similar as using SetNextWindowBgAlpha(0.0f). ImGuiWindowFlags_NoSavedSettings = 1 << 8, // Never load/save settings in .ini file @@ -886,6 +886,7 @@ enum ImGuiTabItemFlags_ // Flags for ImGui::DockSpace(), shared/inherited by child nodes. // (Some flags can be applied to individual nodes directly) +// FIXME-DOCK: Also see ImGuiDockNodeFlagsPrivate_ which may involve using the WIP and internal DockBuilder api. enum ImGuiDockNodeFlags_ { ImGuiDockNodeFlags_None = 0, diff --git a/imgui_internal.h b/imgui_internal.h index 783b3b0ff8f2..68dc280ebf73 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -891,8 +891,10 @@ enum ImGuiDockNodeFlagsPrivate_ ImGuiDockNodeFlags_CentralNode = 1 << 11, // Local ImGuiDockNodeFlags_NoTabBar = 1 << 12, // Local // Tab bar is completely unavailable. No triangle in the corner to enable it back. ImGuiDockNodeFlags_HiddenTabBar = 1 << 13, // Local // Tab bar is hidden, with a triangle in the corner to show it again (NB: actual tab-bar instance may be destroyed as this is only used for single-window tab bar) + ImGuiDockNodeFlags_NoWindowMenuButton = 1 << 14, // Local // Disable window/docking menu (that one that appears instead of the collapse button) + ImGuiDockNodeFlags_NoCloseButton = 1 << 15, // Local ImGuiDockNodeFlags_SharedFlagsInheritMask_ = ~0, - ImGuiDockNodeFlags_LocalFlagsMask_ = ImGuiDockNodeFlags_NoSplit | ImGuiDockNodeFlags_NoResize | ImGuiDockNodeFlags_AutoHideTabBar | ImGuiDockNodeFlags_DockSpace | ImGuiDockNodeFlags_CentralNode | ImGuiDockNodeFlags_NoTabBar | ImGuiDockNodeFlags_HiddenTabBar, + ImGuiDockNodeFlags_LocalFlagsMask_ = ImGuiDockNodeFlags_NoSplit | ImGuiDockNodeFlags_NoResize | ImGuiDockNodeFlags_AutoHideTabBar | ImGuiDockNodeFlags_DockSpace | ImGuiDockNodeFlags_CentralNode | ImGuiDockNodeFlags_NoTabBar | ImGuiDockNodeFlags_HiddenTabBar | ImGuiDockNodeFlags_NoWindowMenuButton | ImGuiDockNodeFlags_NoCloseButton, ImGuiDockNodeFlags_LocalFlagsTransferMask_ = ImGuiDockNodeFlags_LocalFlagsMask_ & ~ImGuiDockNodeFlags_DockSpace // When splitting those flags are moved to the inheriting child, never duplicated }; @@ -936,7 +938,8 @@ struct ImGuiDockNode bool IsVisible :1; // Set to false when the node is hidden (usually disabled as it has no active window) bool IsFocused :1; bool HasCloseButton :1; - bool HasCollapseButton :1; + bool HasWindowMenuButton :1; + bool EnableCloseButton :1; bool WantCloseAll :1; // Set when closing all tabs at once. bool WantLockSizeOnce :1; bool WantMouseMove :1; // After a node extraction we need to transition toward moving the newly created host window From 7c06d9f04319c3e4e8831263aafbbfcc82d5c525 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 27 May 2019 17:04:33 +0200 Subject: [PATCH 507/828] Docking: Saving the NoTabBar, NoWindowMenuButton, NoCloseButton fields of dock node into the .ini file. Added them to the Metrics window. (#2583, #2423, #2109). --- imgui.cpp | 67 +++++++++++++++++++++++++----------------------- imgui_internal.h | 16 +++++++----- 2 files changed, 44 insertions(+), 39 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 62bf4d94b161..befe4e5c25ed 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11065,18 +11065,16 @@ struct ImGuiDockPreviewData // Persistent Settings data, stored contiguously in SettingsNodes (sizeof() ~32 bytes) struct ImGuiDockNodeSettings { - ImGuiID ID; - ImGuiID ParentID; - ImGuiID SelectedTabID; - signed char SplitAxis; - char Depth; - char IsDockSpace; - char IsCentralNode; - char IsHiddenTabBar; - ImVec2ih Pos; - ImVec2ih Size; - ImVec2ih SizeRef; - ImGuiDockNodeSettings() { ID = ParentID = SelectedTabID = 0; SplitAxis = ImGuiAxis_None; Depth = 0; IsDockSpace = IsCentralNode = IsHiddenTabBar = 0; } + ImGuiID ID; + ImGuiID ParentID; + ImGuiID SelectedTabID; + signed char SplitAxis; + char Depth; + ImGuiDockNodeFlags Flags; // NB: We save individual flags one by one in ascii format (ImGuiDockNodeFlags_SavedFlagsMask_) + ImVec2ih Pos; + ImVec2ih Size; + ImVec2ih SizeRef; + ImGuiDockNodeSettings() { ID = ParentID = SelectedTabID = 0; SplitAxis = ImGuiAxis_None; Depth = 0; Flags = ImGuiDockNodeFlags_None; } }; struct ImGuiDockContext @@ -11394,7 +11392,7 @@ static void ImGui::DockContextPruneUnusedSettingsNodes(ImGuiContext* ctx) ImGuiDockContextPruneNodeData* data_root = (data->RootID == settings->ID) ? data : pool.GetByKey(data->RootID); bool remove = false; - remove |= (data->CountWindows == 1 && settings->ParentID == 0 && data->CountChildNodes == 0 && !settings->IsCentralNode); // Floating root node with only 1 window + remove |= (data->CountWindows == 1 && settings->ParentID == 0 && data->CountChildNodes == 0 && !(settings->Flags & ImGuiDockNodeFlags_CentralNode)); // Floating root node with only 1 window remove |= (data->CountWindows == 0 && settings->ParentID == 0 && data->CountChildNodes == 0); // Leaf nodes with 0 window remove |= (data_root->CountChildWindows == 0); if (remove) @@ -11425,12 +11423,7 @@ static void ImGui::DockContextBuildNodesFromSettings(ImGuiContext* ctx, ImGuiDoc node->ParentNode->ChildNodes[1] = node; node->SelectedTabID = settings->SelectedTabID; node->SplitAxis = settings->SplitAxis; - if (settings->IsDockSpace) - node->LocalFlags |= ImGuiDockNodeFlags_DockSpace; - if (settings->IsCentralNode) - node->LocalFlags |= ImGuiDockNodeFlags_CentralNode; - if (settings->IsHiddenTabBar) - node->LocalFlags |= ImGuiDockNodeFlags_HiddenTabBar; + node->LocalFlags |= (settings->Flags & ImGuiDockNodeFlags_SavedFlagsMask_); // Bind host window immediately if it already exist (in case of a rebuild) // This is useful as the RootWindowForTitleBarHighlight links necessary to highlight the currently focused node requires node->HostWindow to be set. @@ -14076,10 +14069,11 @@ static void ImGui::DockSettingsHandler_ReadLine(ImGuiContext* ctx, ImGuiSettings // Parsing, e.g. // " DockNode ID=0x00000001 Pos=383,193 Size=201,322 Split=Y,0.506 " // " DockNode ID=0x00000002 Parent=0x00000001 " + // Important: this code expect currently fields in a fixed order. ImGuiDockNodeSettings node; line = ImStrSkipBlank(line); if (strncmp(line, "DockNode", 8) == 0) { line = ImStrSkipBlank(line + strlen("DockNode")); } - else if (strncmp(line, "DockSpace", 9) == 0) { line = ImStrSkipBlank(line + strlen("DockSpace")); node.IsDockSpace = true; } + else if (strncmp(line, "DockSpace", 9) == 0) { line = ImStrSkipBlank(line + strlen("DockSpace")); node.Flags |= ImGuiDockNodeFlags_DockSpace; } else return; if (sscanf(line, "ID=0x%08X%n", &node.ID, &r) == 1) { line += r; } else return; if (sscanf(line, " Parent=0x%08X%n", &node.ParentID, &r) == 1) { line += r; if (node.ParentID == 0) return; } @@ -14093,8 +14087,11 @@ static void ImGui::DockSettingsHandler_ReadLine(ImGuiContext* ctx, ImGuiSettings if (sscanf(line, " SizeRef=%i,%i%n", &x, &y, &r) == 2) { line += r; node.SizeRef = ImVec2ih((short)x, (short)y); } } if (sscanf(line, " Split=%c%n", &c, &r) == 1) { line += r; if (c == 'X') node.SplitAxis = ImGuiAxis_X; else if (c == 'Y') node.SplitAxis = ImGuiAxis_Y; } - if (sscanf(line, " CentralNode=%d%n", &x, &r) == 1) { line += r; node.IsCentralNode = (x != 0); } - if (sscanf(line, " HiddenTabBar=%d%n", &x, &r) == 1) { line += r; node.IsHiddenTabBar = (x != 0); } + if (sscanf(line, " CentralNode=%d%n", &x, &r) == 1) { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_CentralNode; } + if (sscanf(line, " NoTabBar=%d%n", &x, &r) == 1) { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoTabBar; } + if (sscanf(line, " HiddenTabBar=%d%n", &x, &r) == 1) { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_HiddenTabBar; } + if (sscanf(line, " NoWindowMenuButton=%d%n", &x, &r) == 1) { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoWindowMenuButton; } + if (sscanf(line, " NoCloseButton=%d%n", &x, &r) == 1) { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoCloseButton; } if (sscanf(line, " SelectedTab=0x%08X%n", &node.SelectedTabID,&r) == 1) { line += r; } ImGuiDockContext* dc = ctx->DockContext; if (node.ParentID != 0) @@ -14112,9 +14109,7 @@ static void DockSettingsHandler_DockNodeToSettings(ImGuiDockContext* dc, ImGuiDo node_settings.SelectedTabID = node->SelectedTabID; node_settings.SplitAxis = node->IsSplitNode() ? (char)node->SplitAxis : ImGuiAxis_None; node_settings.Depth = (char)depth; - node_settings.IsDockSpace = (char)node->IsDockSpace(); - node_settings.IsCentralNode = (char)node->IsCentralNode(); - node_settings.IsHiddenTabBar = (char)node->IsHiddenTabBar(); + node_settings.Flags = node->GetMergedFlags() & ImGuiDockNodeFlags_SavedFlagsMask_; node_settings.Pos = ImVec2ih((short)node->Pos.x, (short)node->Pos.y); node_settings.Size = ImVec2ih((short)node->Size.x, (short)node->Size.y); node_settings.SizeRef = ImVec2ih((short)node->SizeRef.x, (short)node->SizeRef.y); @@ -14151,7 +14146,7 @@ static void ImGui::DockSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettings { const int line_start_pos = buf->size(); (void)line_start_pos; const ImGuiDockNodeSettings* node_settings = &dc->SettingsNodes[node_n]; - buf->appendf("%*s%s%*s", node_settings->Depth * 2, "", node_settings->IsDockSpace ? "DockSpace" : "DockNode ", (max_depth - node_settings->Depth) * 2, ""); // Text align nodes to facilitate looking at .ini file + buf->appendf("%*s%s%*s", node_settings->Depth * 2, "", (node_settings->Flags & ImGuiDockNodeFlags_DockSpace) ? "DockSpace" : "DockNode ", (max_depth - node_settings->Depth) * 2, ""); // Text align nodes to facilitate looking at .ini file buf->appendf(" ID=0x%08X", node_settings->ID); if (node_settings->ParentID) buf->appendf(" Parent=0x%08X SizeRef=%d,%d", node_settings->ParentID, node_settings->SizeRef.x, node_settings->SizeRef.y); @@ -14159,10 +14154,16 @@ static void ImGui::DockSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettings buf->appendf(" Pos=%d,%d Size=%d,%d", node_settings->Pos.x, node_settings->Pos.y, node_settings->Size.x, node_settings->Size.y); if (node_settings->SplitAxis != ImGuiAxis_None) buf->appendf(" Split=%c", (node_settings->SplitAxis == ImGuiAxis_X) ? 'X' : 'Y'); - if (node_settings->IsCentralNode) + if (node_settings->Flags & ImGuiDockNodeFlags_CentralNode) buf->appendf(" CentralNode=1"); - if (node_settings->IsHiddenTabBar) + if (node_settings->Flags & ImGuiDockNodeFlags_NoTabBar) + buf->appendf(" NoTabBar=1"); + if (node_settings->Flags & ImGuiDockNodeFlags_HiddenTabBar) buf->appendf(" HiddenTabBar=1"); + if (node_settings->Flags & ImGuiDockNodeFlags_NoWindowMenuButton) + buf->appendf(" NoWindowMenuButton=1"); + if (node_settings->Flags & ImGuiDockNodeFlags_NoCloseButton) + buf->appendf(" NoCloseButton=1"); if (node_settings->SelectedTabID) buf->appendf(" SelectedTab=0x%08X", node_settings->SelectedTabID); @@ -14700,10 +14701,12 @@ void ImGui::ShowDockingDebug() ImGui::BulletText("Misc:%s%s%s%s", node->IsDockSpace() ? " IsDockSpace" : "", node->IsCentralNode() ? " IsCentralNode" : "", (g.FrameCount - node->LastFrameAlive < 2) ? " IsAlive" : "", (g.FrameCount - node->LastFrameActive < 2) ? " IsActive" : ""); if (ImGui::TreeNode("flags", "LocalFlags: 0x%04X SharedFlags: 0x%04X", node->LocalFlags, node->SharedFlags)) { - ImGui::CheckboxFlags("LocalFlags: NoSplit", (unsigned int*)&node->LocalFlags, ImGuiDockNodeFlags_NoSplit); - ImGui::CheckboxFlags("LocalFlags: NoResize", (unsigned int*)&node->LocalFlags, ImGuiDockNodeFlags_NoResize); - ImGui::CheckboxFlags("LocalFlags: NoTabBar", (unsigned int*)&node->LocalFlags, ImGuiDockNodeFlags_NoTabBar); - ImGui::CheckboxFlags("LocalFlags: HiddenTabBar",(unsigned int*)&node->LocalFlags, ImGuiDockNodeFlags_HiddenTabBar); + ImGui::CheckboxFlags("LocalFlags: NoSplit", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_NoSplit); + ImGui::CheckboxFlags("LocalFlags: NoResize", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_NoResize); + ImGui::CheckboxFlags("LocalFlags: NoTabBar", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_NoTabBar); + ImGui::CheckboxFlags("LocalFlags: HiddenTabBar", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_HiddenTabBar); + ImGui::CheckboxFlags("LocalFlags: NoWindowMenuButton", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_NoWindowMenuButton); + ImGui::CheckboxFlags("LocalFlags: NoCloseButton", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_NoCloseButton); ImGui::TreePop(); } if (node->ChildNodes[0]) diff --git a/imgui_internal.h b/imgui_internal.h index 68dc280ebf73..726a0b2a7085 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -884,18 +884,20 @@ struct ImGuiTabBarRef ImGuiTabBarRef(int index_in_main_pool) { Ptr = NULL; IndexInMainPool = index_in_main_pool; } }; +// Extend ImGuiDockNodeFlags_ enum ImGuiDockNodeFlagsPrivate_ { // [Internal] - ImGuiDockNodeFlags_DockSpace = 1 << 10, // Local // A dockspace is a node that occupy space within an existing user window. Otherwise the node is floating and create its own window. - ImGuiDockNodeFlags_CentralNode = 1 << 11, // Local - ImGuiDockNodeFlags_NoTabBar = 1 << 12, // Local // Tab bar is completely unavailable. No triangle in the corner to enable it back. - ImGuiDockNodeFlags_HiddenTabBar = 1 << 13, // Local // Tab bar is hidden, with a triangle in the corner to show it again (NB: actual tab-bar instance may be destroyed as this is only used for single-window tab bar) - ImGuiDockNodeFlags_NoWindowMenuButton = 1 << 14, // Local // Disable window/docking menu (that one that appears instead of the collapse button) - ImGuiDockNodeFlags_NoCloseButton = 1 << 15, // Local + ImGuiDockNodeFlags_DockSpace = 1 << 10, // Local, Saved // A dockspace is a node that occupy space within an existing user window. Otherwise the node is floating and create its own window. + ImGuiDockNodeFlags_CentralNode = 1 << 11, // Local, Saved // + ImGuiDockNodeFlags_NoTabBar = 1 << 12, // Local, Saved // Tab bar is completely unavailable. No triangle in the corner to enable it back. + ImGuiDockNodeFlags_HiddenTabBar = 1 << 13, // Local, Saved // Tab bar is hidden, with a triangle in the corner to show it again (NB: actual tab-bar instance may be destroyed as this is only used for single-window tab bar) + ImGuiDockNodeFlags_NoWindowMenuButton = 1 << 14, // Local, Saved // Disable window/docking menu (that one that appears instead of the collapse button) + ImGuiDockNodeFlags_NoCloseButton = 1 << 15, // Local, Saved // ImGuiDockNodeFlags_SharedFlagsInheritMask_ = ~0, ImGuiDockNodeFlags_LocalFlagsMask_ = ImGuiDockNodeFlags_NoSplit | ImGuiDockNodeFlags_NoResize | ImGuiDockNodeFlags_AutoHideTabBar | ImGuiDockNodeFlags_DockSpace | ImGuiDockNodeFlags_CentralNode | ImGuiDockNodeFlags_NoTabBar | ImGuiDockNodeFlags_HiddenTabBar | ImGuiDockNodeFlags_NoWindowMenuButton | ImGuiDockNodeFlags_NoCloseButton, - ImGuiDockNodeFlags_LocalFlagsTransferMask_ = ImGuiDockNodeFlags_LocalFlagsMask_ & ~ImGuiDockNodeFlags_DockSpace // When splitting those flags are moved to the inheriting child, never duplicated + ImGuiDockNodeFlags_LocalFlagsTransferMask_ = ImGuiDockNodeFlags_LocalFlagsMask_ & ~ImGuiDockNodeFlags_DockSpace, // When splitting those flags are moved to the inheriting child, never duplicated + ImGuiDockNodeFlags_SavedFlagsMask_ = ImGuiDockNodeFlags_DockSpace | ImGuiDockNodeFlags_CentralNode | ImGuiDockNodeFlags_NoTabBar | ImGuiDockNodeFlags_HiddenTabBar | ImGuiDockNodeFlags_NoWindowMenuButton | ImGuiDockNodeFlags_NoCloseButton }; // Store the source authority (dock node vs window) of a field From 5b0e59d9d5582bb450dc922405d177bdc3e252ea Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 27 May 2019 22:11:21 +0200 Subject: [PATCH 508/828] Docking: Saving local _NoResize flag. (#2583) --- imgui.cpp | 5 ++++- imgui.h | 2 +- imgui_internal.h | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index befe4e5c25ed..c3806d4bc800 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -14087,6 +14087,7 @@ static void ImGui::DockSettingsHandler_ReadLine(ImGuiContext* ctx, ImGuiSettings if (sscanf(line, " SizeRef=%i,%i%n", &x, &y, &r) == 2) { line += r; node.SizeRef = ImVec2ih((short)x, (short)y); } } if (sscanf(line, " Split=%c%n", &c, &r) == 1) { line += r; if (c == 'X') node.SplitAxis = ImGuiAxis_X; else if (c == 'Y') node.SplitAxis = ImGuiAxis_Y; } + if (sscanf(line, " NoResize=%d%n", &x, &r) == 1) { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoResize; } if (sscanf(line, " CentralNode=%d%n", &x, &r) == 1) { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_CentralNode; } if (sscanf(line, " NoTabBar=%d%n", &x, &r) == 1) { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoTabBar; } if (sscanf(line, " HiddenTabBar=%d%n", &x, &r) == 1) { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_HiddenTabBar; } @@ -14109,7 +14110,7 @@ static void DockSettingsHandler_DockNodeToSettings(ImGuiDockContext* dc, ImGuiDo node_settings.SelectedTabID = node->SelectedTabID; node_settings.SplitAxis = node->IsSplitNode() ? (char)node->SplitAxis : ImGuiAxis_None; node_settings.Depth = (char)depth; - node_settings.Flags = node->GetMergedFlags() & ImGuiDockNodeFlags_SavedFlagsMask_; + node_settings.Flags = (node->LocalFlags & ImGuiDockNodeFlags_SavedFlagsMask_); node_settings.Pos = ImVec2ih((short)node->Pos.x, (short)node->Pos.y); node_settings.Size = ImVec2ih((short)node->Size.x, (short)node->Size.y); node_settings.SizeRef = ImVec2ih((short)node->SizeRef.x, (short)node->SizeRef.y); @@ -14154,6 +14155,8 @@ static void ImGui::DockSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettings buf->appendf(" Pos=%d,%d Size=%d,%d", node_settings->Pos.x, node_settings->Pos.y, node_settings->Size.x, node_settings->Size.y); if (node_settings->SplitAxis != ImGuiAxis_None) buf->appendf(" Split=%c", (node_settings->SplitAxis == ImGuiAxis_X) ? 'X' : 'Y'); + if (node_settings->Flags & ImGuiDockNodeFlags_NoResize) + buf->appendf(" NoResize=1"); if (node_settings->Flags & ImGuiDockNodeFlags_CentralNode) buf->appendf(" CentralNode=1"); if (node_settings->Flags & ImGuiDockNodeFlags_NoTabBar) diff --git a/imgui.h b/imgui.h index ecd425a2fa9c..5a18b99b4e85 100644 --- a/imgui.h +++ b/imgui.h @@ -895,7 +895,7 @@ enum ImGuiDockNodeFlags_ ImGuiDockNodeFlags_NoDockingInCentralNode = 1 << 2, // Shared // Disable docking inside the Central Node, which will be always kept empty. ImGuiDockNodeFlags_PassthruCentralNode = 1 << 3, // Shared // Enable passthru dockspace: 1) DockSpace() will render a ImGuiCol_WindowBg background covering everything excepted the Central Node when empty. Meaning the host window should probably use SetNextWindowBgAlpha(0.0f) prior to Begin() when using this. 2) When Central Node is empty: let inputs pass-through + won't display a DockingEmptyBg background. See demo for details. ImGuiDockNodeFlags_NoSplit = 1 << 4, // Shared/Local // Disable splitting the node into smaller nodes. Useful e.g. when embedding dockspaces into a main root one (the root one may have splitting disabled to reduce confusion). Note: when turned off, existing splits will be preserved. - ImGuiDockNodeFlags_NoResize = 1 << 5, // Shared/Local // Disable resizing child nodes using the splitter/separators. Useful with programatically setup dockspaces. + ImGuiDockNodeFlags_NoResize = 1 << 5, // Shared/Local // Disable resizing node using the splitter/separators. Useful with programatically setup dockspaces. ImGuiDockNodeFlags_AutoHideTabBar = 1 << 6 // Shared/Local // Tab bar will automatically hide when there is a single window in the dock node. }; diff --git a/imgui_internal.h b/imgui_internal.h index 726a0b2a7085..30396d2d65b7 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -897,7 +897,7 @@ enum ImGuiDockNodeFlagsPrivate_ ImGuiDockNodeFlags_SharedFlagsInheritMask_ = ~0, ImGuiDockNodeFlags_LocalFlagsMask_ = ImGuiDockNodeFlags_NoSplit | ImGuiDockNodeFlags_NoResize | ImGuiDockNodeFlags_AutoHideTabBar | ImGuiDockNodeFlags_DockSpace | ImGuiDockNodeFlags_CentralNode | ImGuiDockNodeFlags_NoTabBar | ImGuiDockNodeFlags_HiddenTabBar | ImGuiDockNodeFlags_NoWindowMenuButton | ImGuiDockNodeFlags_NoCloseButton, ImGuiDockNodeFlags_LocalFlagsTransferMask_ = ImGuiDockNodeFlags_LocalFlagsMask_ & ~ImGuiDockNodeFlags_DockSpace, // When splitting those flags are moved to the inheriting child, never duplicated - ImGuiDockNodeFlags_SavedFlagsMask_ = ImGuiDockNodeFlags_DockSpace | ImGuiDockNodeFlags_CentralNode | ImGuiDockNodeFlags_NoTabBar | ImGuiDockNodeFlags_HiddenTabBar | ImGuiDockNodeFlags_NoWindowMenuButton | ImGuiDockNodeFlags_NoCloseButton + ImGuiDockNodeFlags_SavedFlagsMask_ = ImGuiDockNodeFlags_NoResize | ImGuiDockNodeFlags_DockSpace | ImGuiDockNodeFlags_CentralNode | ImGuiDockNodeFlags_NoTabBar | ImGuiDockNodeFlags_HiddenTabBar | ImGuiDockNodeFlags_NoWindowMenuButton | ImGuiDockNodeFlags_NoCloseButton }; // Store the source authority (dock node vs window) of a field From c7c1bf177b5bb4a3495a6b82e91c45eed46986f7 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 28 May 2019 00:06:21 +0200 Subject: [PATCH 509/828] Docking: Fixed DockBuilderRemoveNode() from overwriting other parent node flags when trying to move the CentralNode flag. --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index c3806d4bc800..7d50ce609bb4 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -13492,7 +13492,7 @@ void ImGui::DockBuilderRemoveNode(ImGuiID node_id) DockBuilderRemoveNodeDockedWindows(node_id, true); DockBuilderRemoveNodeChildNodes(node_id); if (node->IsCentralNode() && node->ParentNode) - node->ParentNode->LocalFlags = ImGuiDockNodeFlags_CentralNode; + node->ParentNode->LocalFlags |= ImGuiDockNodeFlags_CentralNode; DockContextRemoveNode(ctx, node, true); } From 5cdd788f30d12b1abc5f2464135ea70eed87e77a Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 31 May 2019 23:54:15 +0200 Subject: [PATCH 510/828] Comments (#2599). Moved branch Changelog above 1.71 wip one. Added some missing changelog bits. --- docs/CHANGELOG.txt | 105 ++++++++++++++++++++++++--------------------- imgui.cpp | 1 + imgui.h | 8 ++-- imgui_demo.cpp | 8 +++- 4 files changed, 67 insertions(+), 55 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 1e05b3b064e8..12136481f291 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -28,55 +28,6 @@ HOW TO UPDATE? and API updates have been a little more frequent lately. They are documented below and in imgui.cpp and should not affect all users. - Please report any issue! ------------------------------------------------------------------------ - VERSION 1.71 (In Progress) ------------------------------------------------------------------------ - -Breaking Changes: -- IO: changed AddInputCharacter(unsigned short c) signature to AddInputCharacter(unsigned int c). -- Renamed SetNextTreeNodeOpen() to SetNextItemOpen(). Kept inline redirection function (will obsolete). - -Other Changes: -- Columns: Fixed Separator from creating an extraneous draw command. (#125) -- Columns: Fixed Selectable with SpanAllColumns flag from creating an extraneous draw command. (#125) -- Separator: Revert 1.70 "Declare its thickness (1.0f) to the layout" change. It's not incorrect - but it breaks existing some layout patterns. Will return back to it when we expose Separator flags. -- Fixed InputScalar, InputScalarN, SliderScalarN, DragScalarN with non-visible label from inserting - style.ItemInnerSpacing.x worth of trailing spacing. -- Fixed InputFloatX, SliderFloatX, DragFloatX functions erroneously reporting IsItemEdited() multiple - times when the text input doesn't match the formatted output value (e.g. input "1" shows "1.000"). - It wasn't much of a problem because we typically use the return value instead of IsItemEdited() here. -- Fixed uses of IsItemDeactivated(), IsItemDeactivatedAfterEdit() on multi-components widgets and - after EndGroup(). (#2550, #1875) -- Fixed crash when appending with BeginMainMenuBar() more than once and no other window are showing. (#2567) -- ColorEdit: Fixed the color picker popup only displaying inputs as HSV instead of showing multiple - options. (#2587, broken in 1.69 by #2384). -- CollapsingHeader: Better clipping when a close button is enabled and it overlaps the label. (#600) -- Scrollbar: Very minor bounding box adjustment to cope with various border size. -- Style: Added style.WindowMenuButtonPosition (left/right, defaults to ImGuiDir_Left) to move the - collapsing/docking button to the other side of the title bar. -- Style: Made window close button cross slightly smaller. -- ImDrawList: Added ImDrawCmd::VtxOffset value to support large meshes (64k+ vertices) using 16-bits indices. - The renderer back-end needs to set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset' to enable - this, and honor the ImDrawCmd::VtxOffset field. Otherwise the value will always be zero. - This has the advantage of preserving smaller index buffers and allowing to execute on hardware that do not - support 32-bits indices. Most examples back-ends have been modified to support the VtxOffset field. -- ImDrawList: Added ImDrawCmd::IdxOffset value, equivalent to summing element count for each draw command. - This is provided for convenience and consistency with VtxOffset. -- ImFontAtlas: FreeType: Added RasterizerFlags::Monochrome flag to disable font anti-aliasing. (#2545) - Combine with RasterizerFlags::MonoHinting for best results. -- ImFontGlyphRangesBuilder: Fixed unnecessarily over-sized buffer, which incidentally was also not - fully cleared. Fixed edge-case overflow when adding character 0xFFFF. (#2568). [@NIKE3500] -- Demo: Added full "Dear ImGui" prefix to the title of "Dear ImGui Demo" and "Dear ImGui Metrics" windows. -- Backends: Add native Mac clipboard copy/paste default implementation in core library to match what we are - dealing with Win32, and to facilitate integration in custom engines. (#2546) [@andrewwillmott] -- Backends: OSX: imgui_impl_osx: Added mouse cursor support. (#2585, #1873) [@actboy168] -- Examples/Backends: DirectX9/10/11/12, Metal, Vulkan, OpenGL3 (Desktop GL only): Added support for large meshes - (64k+ vertices) with 16-bits indices, enable 'ImGuiBackendFlags_RendererHasVtxOffset' in those back-ends. -- Examples/Backends: Don't filter characters under 0x10000 before calling io.AddInputCharacter(), - the filtering is done in io.AddInputCharacter() itself. This is in prevision for fuller Unicode - support. (#2538, #2541) - ----------------------------------------------------------------------- DOCKING BRANCH (In Progress) @@ -93,7 +44,10 @@ DOCKING FEATURES - Added GetWindowDockId(), IsWindowDocked() API. - Added ImGuiWindowFlags_NoDocking window flag to disable the possibility for a window to be docked. Popup, Menu and Child windows always have the ImGuiWindowFlags_NoDocking flag set. - - Added io.ConfigDockingWithShift option to configure docking mode. + - Added io.ConfigDockingNoSplit option. + - Added io.ConfigDockingWithShift option. + - Added io.ConfigDockingTabBarOnSingleWindows option. + - Added io.ConfigDockingTransparentPayload option. - Style: Added ImGuiCol_DockingPreview, ImGuiCol_DockingEmptyBg colors. - Demo: Added "DockSpace" example app showcasing use of explicit dockspace nodes. @@ -129,6 +83,7 @@ Other changes: - Added UpdatePlatformWindows(), RenderPlatformWindows(), DestroyPlatformWindows() for usage for application core. - Added FindViewportByID(), FindViewportByPlatformHandle() for usage by back-ends. - Added ImGuiConfigFlags_ViewportsEnable configuration flag and other viewport options. +- Added io.ConfigViewportsNoAutoMerge, io.ConfigViewportsNoTaskBarIcon, io.ConfigViewportsNoDecoration, io.ConfigViewportsNoDefaultParent options. - Added ImGuiBackendFlags_PlatformHasViewports, ImGuiBackendFlags_RendererHasViewports, ImGuiBackendFlags_HasMouseHoveredViewport backend flags. - Added io.MainViewport, io.Viewports, io.MouseHoveredViewport (MouseHoveredViewport is optional _even_ for multi-viewport support). - Added ImGuiViewport structure, ImGuiViewportFlags flags. @@ -142,6 +97,56 @@ Other changes: to make the examples main.cpp easier to read. +----------------------------------------------------------------------- + VERSION 1.71 (In Progress) +----------------------------------------------------------------------- + +Breaking Changes: +- IO: changed AddInputCharacter(unsigned short c) signature to AddInputCharacter(unsigned int c). +- Renamed SetNextTreeNodeOpen() to SetNextItemOpen(). Kept inline redirection function (will obsolete). + +Other Changes: +- Columns: Fixed Separator from creating an extraneous draw command. (#125) +- Columns: Fixed Selectable with SpanAllColumns flag from creating an extraneous draw command. (#125) +- Separator: Revert 1.70 "Declare its thickness (1.0f) to the layout" change. It's not incorrect + but it breaks existing some layout patterns. Will return back to it when we expose Separator flags. +- Fixed InputScalar, InputScalarN, SliderScalarN, DragScalarN with non-visible label from inserting + style.ItemInnerSpacing.x worth of trailing spacing. +- Fixed InputFloatX, SliderFloatX, DragFloatX functions erroneously reporting IsItemEdited() multiple + times when the text input doesn't match the formatted output value (e.g. input "1" shows "1.000"). + It wasn't much of a problem because we typically use the return value instead of IsItemEdited() here. +- Fixed uses of IsItemDeactivated(), IsItemDeactivatedAfterEdit() on multi-components widgets and + after EndGroup(). (#2550, #1875) +- Fixed crash when appending with BeginMainMenuBar() more than once and no other window are showing. (#2567) +- ColorEdit: Fixed the color picker popup only displaying inputs as HSV instead of showing multiple + options. (#2587, broken in 1.69 by #2384). +- CollapsingHeader: Better clipping when a close button is enabled and it overlaps the label. (#600) +- Scrollbar: Very minor bounding box adjustment to cope with various border size. +- Style: Added style.WindowMenuButtonPosition (left/right, defaults to ImGuiDir_Left) to move the + collapsing/docking button to the other side of the title bar. +- Style: Made window close button cross slightly smaller. +- ImDrawList: Added ImDrawCmd::VtxOffset value to support large meshes (64k+ vertices) using 16-bits indices. + The renderer back-end needs to set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset' to enable + this, and honor the ImDrawCmd::VtxOffset field. Otherwise the value will always be zero. + This has the advantage of preserving smaller index buffers and allowing to execute on hardware that do not + support 32-bits indices. Most examples back-ends have been modified to support the VtxOffset field. +- ImDrawList: Added ImDrawCmd::IdxOffset value, equivalent to summing element count for each draw command. + This is provided for convenience and consistency with VtxOffset. +- ImFontAtlas: FreeType: Added RasterizerFlags::Monochrome flag to disable font anti-aliasing. (#2545) + Combine with RasterizerFlags::MonoHinting for best results. +- ImFontGlyphRangesBuilder: Fixed unnecessarily over-sized buffer, which incidentally was also not + fully cleared. Fixed edge-case overflow when adding character 0xFFFF. (#2568). [@NIKE3500] +- Demo: Added full "Dear ImGui" prefix to the title of "Dear ImGui Demo" and "Dear ImGui Metrics" windows. +- Backends: Add native Mac clipboard copy/paste default implementation in core library to match what we are + dealing with Win32, and to facilitate integration in custom engines. (#2546) [@andrewwillmott] +- Backends: OSX: imgui_impl_osx: Added mouse cursor support. (#2585, #1873) [@actboy168] +- Examples/Backends: DirectX9/10/11/12, Metal, Vulkan, OpenGL3 (Desktop GL only): Added support for large meshes + (64k+ vertices) with 16-bits indices, enable 'ImGuiBackendFlags_RendererHasVtxOffset' in those back-ends. +- Examples/Backends: Don't filter characters under 0x10000 before calling io.AddInputCharacter(), + the filtering is done in io.AddInputCharacter() itself. This is in prevision for fuller Unicode + support. (#2538, #2541) + + ----------------------------------------------------------------------- VERSION 1.70 (Released 2019-05-06) ----------------------------------------------------------------------- diff --git a/imgui.cpp b/imgui.cpp index ea1347a7d775..5ba7e30aadb5 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5768,6 +5768,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) flags = window->Flags; // Lock border size and padding for the frame (so that altering them doesn't cause inconsistencies) + // We read Style data after the call to UpdateSelectWindowViewport() which might be swapping the style. if (flags & ImGuiWindowFlags_ChildWindow) window->WindowBorderSize = style.ChildBorderSize; else diff --git a/imgui.h b/imgui.h index f46372c08d14..03b6666329e1 100644 --- a/imgui.h +++ b/imgui.h @@ -597,9 +597,9 @@ namespace ImGui // Docking // [BETA API] Enable with io.ConfigFlags |= ImGuiConfigFlags_DockingEnable. // Note: you DO NOT need to call DockSpace() to use most Docking facilities! - // To dock windows: if io.ConfigDockingWithShift == false: drag window from their title bar. - // To dock windows: if io.ConfigDockingWithShift == true: hold SHIFT anywhere while moving windows. - // Use DockSpace() to create an explicit dock node _within_ an existing window. See Docking demo for details. + // - To dock windows: if io.ConfigDockingWithShift == false (default) drag window from their title bar. + // - To dock windows: if io.ConfigDockingWithShift == true: hold SHIFT anywhere while moving windows. + // - Use DockSpace() to create an explicit dock node _within_ an existing window. See Docking demo for details. IMGUI_API void DockSpace(ImGuiID id, const ImVec2& size = ImVec2(0, 0), ImGuiDockNodeFlags flags = 0, const ImGuiWindowClass* window_class = NULL); IMGUI_API ImGuiID DockSpaceOverViewport(ImGuiViewport* viewport = NULL, ImGuiDockNodeFlags flags = 0, const ImGuiWindowClass* window_class = NULL); IMGUI_API void SetNextWindowDockID(ImGuiID dock_id, ImGuiCond cond = 0); // set next window dock id (FIXME-DOCK) @@ -1052,7 +1052,7 @@ enum ImGuiConfigFlags_ ImGuiConfigFlags_NoMouseCursorChange = 1 << 5, // Instruct back-end to not alter mouse cursor shape and visibility. Use if the back-end cursor changes are interfering with yours and you don't want to use SetMouseCursor() to change mouse cursor. You may want to honor requests from imgui by reading GetMouseCursor() yourself instead. // [BETA] Docking - ImGuiConfigFlags_DockingEnable = 1 << 6, // Docking enable flags. Use SHIFT to dock window into another (or without SHIFT if io.ConfigDockingWithShift = false). + ImGuiConfigFlags_DockingEnable = 1 << 6, // Docking enable flags. // [BETA] Viewports // When using viewports it is recommended that your default value for ImGuiCol_WindowBg is opaque (Alpha=1.0) so transition to a viewport won't be noticeable. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 267439dce4cc..11ae195243cd 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -4326,7 +4326,8 @@ static void ShowExampleAppCustomRendering(bool* p_open) //----------------------------------------------------------------------------- // Demonstrate using DockSpace() to create an explicit docking node within an existing window. -// Note that you already dock windows into each others _without_ a DockSpace() by just holding SHIFT when moving a window. +// Note that you already dock windows into each others _without_ a DockSpace() by just moving windows +// from their title bar (or by holding SHIFT if io.ConfigDockingWithShift is set). // DockSpace() is only useful to construct to a central location for your application. void ShowExampleAppDockSpace(bool* p_open) { @@ -4353,6 +4354,11 @@ void ShowExampleAppDockSpace(bool* p_open) if (dockspace_flags & ImGuiDockNodeFlags_PassthruCentralNode) window_flags |= ImGuiWindowFlags_NoBackground; + // Important: note that we proceed even if Begin() returns false (aka window is collapsed). + // This is because we want to keep our DockSpace() active. If a DockSpace() is inactive, + // all active windows docked into it will lose their parent and become undocked. + // We cannot preserve the docking relationship between an active window and an inactive docking, otherwise + // any change of dockspace/settings would lead to windows being stuck in limbo and never being visible. ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); ImGui::Begin("DockSpace Demo", p_open, window_flags); ImGui::PopStyleVar(); From 63310acd5889c7b56ccdbf51d1acc2d98dce4e6f Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 6 Jun 2019 16:16:18 +0200 Subject: [PATCH 511/828] Synced/merged minor cruft from master branch to minimize drift. Only meaningful change AFAIK is removing ImGuiComboFlags_PopupAlignLeft flag from the tab list combo emitted by TabBar. --- docs/TODO.txt | 1 - examples/example_win32_directx12/main.cpp | 10 +++++----- examples/imgui_impl_dx10.cpp | 4 ++-- examples/imgui_impl_dx11.cpp | 4 ++-- examples/imgui_impl_dx12.cpp | 2 +- examples/imgui_impl_dx9.cpp | 5 +++-- examples/imgui_impl_glfw.cpp | 10 +++++----- examples/imgui_impl_opengl2.cpp | 5 +++-- examples/imgui_impl_opengl3.cpp | 4 ++-- examples/imgui_impl_vulkan.cpp | 6 +++--- imgui.cpp | 8 +++++--- imgui_widgets.cpp | 2 +- 12 files changed, 32 insertions(+), 29 deletions(-) diff --git a/docs/TODO.txt b/docs/TODO.txt index 12f22d361ec1..6d31927aad10 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -349,7 +349,6 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - focus: SetKeyboardFocusHere() on with >= 0 offset could be done on same frame (else latch and modulate on beginning of next frame) - focus: unable to use SetKeyboardFocusHere() on clipped widgets. (#787) - - examples: move ImGui::NewFrame() out of the backend _NewFrame() ? - viewport: make it possible to have no main/hosting viewport - viewport: We set ImGuiViewportFlags_NoFocusOnAppearing in a way that is required for GLFW/SDL binding, but could be handled better without on a custom e.g. Win32 bindings. It prevents newly dragged-out viewports from taking the focus, which makes ALT+F4 more ambiguous. diff --git a/examples/example_win32_directx12/main.cpp b/examples/example_win32_directx12/main.cpp index a904125a95f5..26a16f0f0de4 100644 --- a/examples/example_win32_directx12/main.cpp +++ b/examples/example_win32_directx12/main.cpp @@ -268,10 +268,10 @@ bool CreateDeviceD3D(HWND hWnd) { D3D12_DESCRIPTOR_HEAP_DESC desc = {}; - desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; + desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; desc.NumDescriptors = NUM_BACK_BUFFERS; - desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; - desc.NodeMask = 1; + desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; + desc.NodeMask = 1; if (g_pd3dDevice->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&g_pd3dRtvDescHeap)) != S_OK) return false; @@ -295,8 +295,8 @@ bool CreateDeviceD3D(HWND hWnd) { D3D12_COMMAND_QUEUE_DESC desc = {}; - desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; - desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; + desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; + desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; desc.NodeMask = 1; if (g_pd3dDevice->CreateCommandQueue(&desc, IID_PPV_ARGS(&g_pd3dCommandQueue)) != S_OK) return false; diff --git a/examples/imgui_impl_dx10.cpp b/examples/imgui_impl_dx10.cpp index 380031735092..c5fdaeb4041e 100644 --- a/examples/imgui_impl_dx10.cpp +++ b/examples/imgui_impl_dx10.cpp @@ -152,7 +152,7 @@ void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data) g_pIB->Unmap(); // Setup orthographic projection matrix into our constant buffer - // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayMin is (0,0) for single viewport apps. + // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps. { void* mapped_resource; if (g_pVertexConstantBuffer->Map(D3D10_MAP_WRITE_DISCARD, 0, &mapped_resource) != S_OK) @@ -494,9 +494,9 @@ bool ImGui_ImplDX10_Init(ID3D10Device* device) { // Setup back-end capabilities flags ImGuiIO& io = ImGui::GetIO(); - io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional) io.BackendRendererName = "imgui_impl_dx10"; io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. + io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional) // Get factory from device IDXGIDevice* pDXGIDevice = NULL; diff --git a/examples/imgui_impl_dx11.cpp b/examples/imgui_impl_dx11.cpp index 8b87a89526ea..e43a75af3305 100644 --- a/examples/imgui_impl_dx11.cpp +++ b/examples/imgui_impl_dx11.cpp @@ -154,7 +154,7 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) ctx->Unmap(g_pIB, 0); // Setup orthographic projection matrix into our constant buffer - // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayMin is (0,0) for single viewport apps. + // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps. { D3D11_MAPPED_SUBRESOURCE mapped_resource; if (ctx->Map(g_pVertexConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped_resource) != S_OK) @@ -501,9 +501,9 @@ bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_co { // Setup back-end capabilities flags ImGuiIO& io = ImGui::GetIO(); - io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional) io.BackendRendererName = "imgui_impl_dx11"; io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. + io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional) // Get factory from device IDXGIDevice* pDXGIDevice = NULL; diff --git a/examples/imgui_impl_dx12.cpp b/examples/imgui_impl_dx12.cpp index 5a50194bed79..7dfdb9c69723 100644 --- a/examples/imgui_impl_dx12.cpp +++ b/examples/imgui_impl_dx12.cpp @@ -611,9 +611,9 @@ bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FO { // Setup back-end capabilities flags ImGuiIO& io = ImGui::GetIO(); - io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional) // FIXME-VIEWPORT: Actually unfinished.. io.BackendRendererName = "imgui_impl_dx12"; io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. + io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional) // FIXME-VIEWPORT: Actually unfinished.. g_pd3dDevice = device; g_RTVFormat = rtv_format; diff --git a/examples/imgui_impl_dx9.cpp b/examples/imgui_impl_dx9.cpp index b74bded38955..a8635fe0d49e 100644 --- a/examples/imgui_impl_dx9.cpp +++ b/examples/imgui_impl_dx9.cpp @@ -88,7 +88,7 @@ static void ImGui_ImplDX9_SetupRenderState(ImDrawData* draw_data) g_pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); // Setup orthographic projection matrix - // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayMin is (0,0) for single viewport apps. + // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps. // Being agnostic of whether or can be used, we aren't relying on D3DXMatrixIdentity()/D3DXMatrixOrthoOffCenterLH() or DirectX::XMMatrixIdentity()/DirectX::XMMatrixOrthographicOffCenterLH() { float L = draw_data->DisplayPos.x + 0.5f; @@ -226,10 +226,11 @@ void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data) bool ImGui_ImplDX9_Init(IDirect3DDevice9* device) { + // Setup back-end capabilities flags ImGuiIO& io = ImGui::GetIO(); - io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional) io.BackendRendererName = "imgui_impl_dx9"; io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. + io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional) g_pd3dDevice = device; g_pd3dDevice->AddRef(); diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index 75f8f03baeaa..21da2f69e82c 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -233,12 +233,8 @@ void ImGui_ImplGlfw_Shutdown() static void ImGui_ImplGlfw_UpdateMousePosAndButtons() { - ImGuiIO& io = ImGui::GetIO(); - const ImVec2 mouse_pos_backup = io.MousePos; - io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); - io.MouseHoveredViewport = 0; - // Update buttons + ImGuiIO& io = ImGui::GetIO(); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) { // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame. @@ -246,6 +242,10 @@ static void ImGui_ImplGlfw_UpdateMousePosAndButtons() g_MouseJustPressed[i] = false; } + // Update mouse position + const ImVec2 mouse_pos_backup = io.MousePos; + io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); + io.MouseHoveredViewport = 0; ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); for (int n = 0; n < platform_io.Viewports.Size; n++) { diff --git a/examples/imgui_impl_opengl2.cpp b/examples/imgui_impl_opengl2.cpp index bf6209fafa29..12526bc315bf 100644 --- a/examples/imgui_impl_opengl2.cpp +++ b/examples/imgui_impl_opengl2.cpp @@ -65,8 +65,9 @@ bool ImGui_ImplOpenGL2_Init() { // Setup back-end capabilities flags ImGuiIO& io = ImGui::GetIO(); - io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional) io.BackendRendererName = "imgui_impl_opengl2"; + io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional) + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) ImGui_ImplOpenGL2_InitPlatformInterface(); return true; @@ -109,7 +110,7 @@ static void ImGui_ImplOpenGL2_SetupRenderState(ImDrawData* draw_data, int fb_wid // glUseProgram(last_program) // Setup viewport, orthographic projection matrix - // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayMin is (0,0) for single viewport apps. + // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps. glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height); glMatrixMode(GL_PROJECTION); glPushMatrix(); diff --git a/examples/imgui_impl_opengl3.cpp b/examples/imgui_impl_opengl3.cpp index ee3974e5200d..07b2cc67eb4b 100644 --- a/examples/imgui_impl_opengl3.cpp +++ b/examples/imgui_impl_opengl3.cpp @@ -130,11 +130,11 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version) { // Setup back-end capabilities flags ImGuiIO& io = ImGui::GetIO(); - io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional) io.BackendRendererName = "imgui_impl_opengl3"; #if IMGUI_IMPL_OPENGL_HAS_DRAW_WITH_BASE_VERTEX io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. #endif + io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional) // Store GLSL version string so we can refer to it later in case we recreate shaders. Note: GLSL version is NOT the same as GL version. Leave this to NULL if unsure. #if defined(IMGUI_IMPL_OPENGL_ES2) @@ -189,7 +189,7 @@ static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_wid #endif // Setup viewport, orthographic projection matrix - // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayMin is (0,0) for single viewport apps. + // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps. glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height); float L = draw_data->DisplayPos.x; float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x; diff --git a/examples/imgui_impl_vulkan.cpp b/examples/imgui_impl_vulkan.cpp index 59197c30b12a..16d2f4b620a5 100644 --- a/examples/imgui_impl_vulkan.cpp +++ b/examples/imgui_impl_vulkan.cpp @@ -307,7 +307,7 @@ static void ImGui_ImplVulkan_SetupRenderState(ImDrawData* draw_data, VkCommandBu } // Setup scale and translation: - // Our visible imgui space lies from draw_data->DisplayPps (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayMin is (0,0) for single viewport apps. + // Our visible imgui space lies from draw_data->DisplayPps (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps. { float scale[2]; scale[0] = 2.0f / draw_data->DisplaySize.x; @@ -821,9 +821,9 @@ bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info, VkRenderPass rend { // Setup back-end capabilities flags ImGuiIO& io = ImGui::GetIO(); - io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional) io.BackendRendererName = "imgui_impl_vulkan"; io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. + io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional) IM_ASSERT(info->Instance != VK_NULL_HANDLE); IM_ASSERT(info->PhysicalDevice != VK_NULL_HANDLE); @@ -868,8 +868,8 @@ void ImGui_ImplVulkan_SetMinImageCount(uint32_t min_image_count) ImGui_ImplVulkan_InitInfo* v = &g_VulkanInitInfo; VkResult err = vkDeviceWaitIdle(v->Device); check_vk_result(err); - ImGui_ImplVulkanH_DestroyAllViewportsRenderBuffers(v->Device, v->Allocator); + g_VulkanInitInfo.MinImageCount = min_image_count; } diff --git a/imgui.cpp b/imgui.cpp index fb857ed60a82..33a3173b4880 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5510,7 +5510,7 @@ void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& titl window->DC.ItemFlags = item_flags_backup; // Title bar text (with: horizontal alignment, avoiding collapse/close button, optional "unsaved document" marker) - // FIXME: Refactor text alignment facilities along with RenderText helpers, this is too much code.. + // FIXME: Refactor text alignment facilities along with RenderText helpers, this is WAY too much messy code.. const char* UNSAVED_DOCUMENT_MARKER = "*"; const float marker_size_x = (flags & ImGuiWindowFlags_UnsavedDocument) ? CalcTextSize(UNSAVED_DOCUMENT_MARKER, NULL, false).x : 0.0f; const ImVec2 text_size = CalcTextSize(name, NULL, true) + ImVec2(marker_size_x, 0.0f); @@ -6009,6 +6009,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Save last known viewport position within the window itself (so it can be saved in .ini file and restored) window->ViewportPos = window->Viewport->Pos; + // SCROLLBAR VISIBILITY + // Update scrollbar visibility (based on the Size that was effective during last frame or the auto-resized Size). if (!window->Collapsed) { @@ -10254,11 +10256,11 @@ static void SettingsHandlerWindow_ReadLine(ImGuiContext* ctx, ImGuiSettingsHandl else if (sscanf(line, "ClassId=0x%X", &u1) == 1) { settings->ClassId = u1; } } -static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf) +static void SettingsHandlerWindow_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf) { // Gather data from windows that were active during this session // (if a window wasn't opened in this session we preserve its settings) - ImGuiContext& g = *imgui_ctx; + ImGuiContext& g = *ctx; for (int i = 0; i != g.Windows.Size; i++) { ImGuiWindow* window = g.Windows[i]; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index d60969e001da..778a59fa1944 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6779,7 +6779,7 @@ static ImGuiTabItem* ImGui::TabBarTabListPopupButton(ImGuiTabBar* tab_bar) arrow_col.w *= 0.5f; PushStyleColor(ImGuiCol_Text, arrow_col); PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0)); - bool open = BeginCombo("##v", NULL, ImGuiComboFlags_NoPreview | ImGuiComboFlags_PopupAlignLeft); + bool open = BeginCombo("##v", NULL, ImGuiComboFlags_NoPreview); PopStyleColor(2); ImGuiTabItem* tab_to_select = NULL; From 07d3083279834605131baf3bbdda18a4104647c8 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 13 Jun 2019 10:19:14 +0200 Subject: [PATCH 512/828] Docking: Fixed rendering of outer decoration happening on non-visible docked window (#2623, #2109). Revealed by 0770449. We are actually better than before now, as previously those would get unnecessarily get rendered into a hidden draw list. --- imgui.cpp | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index d59e1ae7a291..3918a576ad7a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6181,24 +6181,29 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha * 0.25f), g.Style.WindowRounding); } + const bool is_undocked_or_docked_visible = !window->DockIsActive || window->DockTabIsVisible; + // Since 1.71, child window can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call. // When using overlapping child windows, this will break the assumption that child z-order is mapped to submission order. // We disable this when the parent window has zero vertices, which is a common pattern leading to laying out multiple overlapping child. // We also disabled this when we have dimming overlay behind this specific one child. // FIXME: More code may rely on explicit sorting of overlapping child window and would need to disable this somehow. Please get in contact if you are affected. - bool render_decorations_in_parent = false; - if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip) - if (window->DrawList->CmdBuffer.back().ElemCount == 0 && parent_window->DrawList->VtxBuffer.Size > 0) - render_decorations_in_parent = true; - if (render_decorations_in_parent) - window->DrawList = parent_window->DrawList; - - const ImGuiWindow* window_to_highlight = g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow; - const bool title_bar_is_highlight = want_focus || (window_to_highlight && (window->RootWindowForTitleBarHighlight == window_to_highlight->RootWindowForTitleBarHighlight || (window->DockNode && window->DockNode == window_to_highlight->DockNode))); - RenderWindowDecorations(window, title_bar_rect, title_bar_is_highlight, handle_borders_and_resize_grips, resize_grip_count, resize_grip_col, resize_grip_draw_size); + if (is_undocked_or_docked_visible) + { + bool render_decorations_in_parent = false; + if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip) + if (window->DrawList->CmdBuffer.back().ElemCount == 0 && parent_window->DrawList->VtxBuffer.Size > 0) + render_decorations_in_parent = true; + if (render_decorations_in_parent) + window->DrawList = parent_window->DrawList; - if (render_decorations_in_parent) - window->DrawList = &window->DrawListInst; + const ImGuiWindow* window_to_highlight = g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow; + const bool title_bar_is_highlight = want_focus || (window_to_highlight && (window->RootWindowForTitleBarHighlight == window_to_highlight->RootWindowForTitleBarHighlight || (window->DockNode && window->DockNode == window_to_highlight->DockNode))); + RenderWindowDecorations(window, title_bar_rect, title_bar_is_highlight, handle_borders_and_resize_grips, resize_grip_count, resize_grip_col, resize_grip_draw_size); + + if (render_decorations_in_parent) + window->DrawList = &window->DrawListInst; + } // Draw navigation selection/windowing rectangle border if (g.NavWindowingTargetAnim == window) From ca43436cd395e52191d557800503852bd343c2ea Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 17 Jun 2019 11:19:34 +0200 Subject: [PATCH 513/828] Fix monitor dpi info not being copied to main viewport when multi-viewports are not enabled. (#2621, #1676) + Tweaks, short path in FindPlatformMonitorForRect(). --- imgui.cpp | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 0e3ced3136e6..b6b2a32c8b34 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10558,17 +10558,20 @@ static ImGuiViewportP* FindViewportHoveredFromPlatformWindowStack(const ImVec2 m return best_candidate; } +// Update viewports and monitor infos +// Note that this is runing even if 'ImGuiConfigFlags_ViewportsEnable' is not set, in order to clear unused viewports (if any) and update monitor info. static void ImGui::UpdateViewportsNewFrame() { ImGuiContext& g = *GImGui; IM_ASSERT(g.PlatformIO.Viewports.Size <= g.Viewports.Size); // Update Minimized status (we need it first in order to decide if we'll apply Pos/Size of the main viewport) - for (int n = 0; n < g.Viewports.Size; n++) + if ((g.ConfigFlagsForFrame & ImGuiConfigFlags_ViewportsEnable)) { - ImGuiViewportP* viewport = g.Viewports[n]; - const bool platform_funcs_available = viewport->PlatformWindowCreated; - if ((g.ConfigFlagsForFrame & ImGuiConfigFlags_ViewportsEnable)) + for (int n = 0; n < g.Viewports.Size; n++) + { + ImGuiViewportP* viewport = g.Viewports[n]; + const bool platform_funcs_available = viewport->PlatformWindowCreated; if (g.PlatformIO.Platform_GetWindowMinimized && platform_funcs_available) { bool minimized = g.PlatformIO.Platform_GetWindowMinimized(viewport); @@ -10577,6 +10580,7 @@ static void ImGui::UpdateViewportsNewFrame() else viewport->Flags &= ~ImGuiViewportFlags_Minimized; } + } } // Create/update main viewport with current platform position and size @@ -10593,10 +10597,10 @@ static void ImGui::UpdateViewportsNewFrame() g.MouseViewport = NULL; for (int n = 0; n < g.Viewports.Size; n++) { - // Erase unused viewports ImGuiViewportP* viewport = g.Viewports[n]; viewport->Idx = n; + // Erase unused viewports if (n > 0 && viewport->LastFrameActive < g.FrameCount - 2) { // Clear references to this viewport in windows (window->ViewportId becomes the master data) @@ -10631,10 +10635,11 @@ static void ImGui::UpdateViewportsNewFrame() if (viewport->PlatformRequestResize) viewport->Size = viewport->LastPlatformSize = g.PlatformIO.Platform_GetWindowSize(viewport); } - - UpdateViewportPlatformMonitor(viewport); } + // Update/copy monitor info + UpdateViewportPlatformMonitor(viewport); + // Reset alpha every frame. Users of transparency (docking) needs to request a lower alpha back. viewport->Alpha = 1.0f; @@ -11102,11 +11107,15 @@ static int ImGui::FindPlatformMonitorForPos(const ImVec2& pos) // Search for the monitor with the largest intersection area with the given rectangle // We generally try to avoid searching loops but the monitor count should be very small here -// FIXME-OPT: We could test the last monitor used for that viewport first.. +// FIXME-OPT: We could test the last monitor used for that viewport first, and early static int ImGui::FindPlatformMonitorForRect(const ImRect& rect) { ImGuiContext& g = *GImGui; + const int monitor_count = g.PlatformIO.Monitors.Size; + if (monitor_count <= 1) + return monitor_count - 1; + // Use a minimum threshold of 1.0f so a zero-sized rect won't false positive, and will still find the correct monitor given its position. // This is necessary for tooltips which always resize down to zero at first. const float surface_threshold = ImMax(rect.GetWidth() * rect.GetHeight() * 0.5f, 1.0f); From 2cbc0f128735e7c7e5fa6474698acbad47b11643 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 18 Jun 2019 23:13:12 +0200 Subject: [PATCH 514/828] Restore SLN which in Docking branch includes more projects. --- examples/imgui_examples.sln | 60 +++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/examples/imgui_examples.sln b/examples/imgui_examples.sln index d4c05ebc4987..d68f69ecb5a6 100644 --- a/examples/imgui_examples.sln +++ b/examples/imgui_examples.sln @@ -9,10 +9,22 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_win32_directx10", " EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_win32_directx11", "example_win32_directx11\example_win32_directx11.vcxproj", "{9F316E83-5AE5-4939-A723-305A94F48005}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_win32_directx12", "example_win32_directx12\example_win32_directx12.vcxproj", "{B4CF9797-519D-4AFE-A8F4-5141A6B521D3}" +EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_glfw_opengl2", "example_glfw_opengl2\example_glfw_opengl2.vcxproj", "{9CDA7840-B7A5-496D-A527-E95571496D18}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_glfw_opengl3", "example_glfw_opengl3\example_glfw_opengl3.vcxproj", "{4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_glfw_vulkan", "example_glfw_vulkan\example_glfw_vulkan.vcxproj", "{57E2DF5A-6FC8-45BB-99DD-91A18C646E80}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_sdl_opengl2", "example_sdl_opengl2\example_sdl_opengl2.vcxproj", "{2AE17FDE-F7F3-4CAC-ADAB-0710EDA4F741}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_sdl_opengl3", "example_sdl_opengl3\example_sdl_opengl3.vcxproj", "{BBAEB705-1669-40F3-8567-04CF6A991F4C}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_sdl_vulkan", "example_sdl_vulkan\example_sdl_vulkan.vcxproj", "{BAE3D0B5-9695-4EB1-AD0F-75890EB4A3B3}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_sdl_directx11", "example_sdl_directx11\example_sdl_directx11.vcxproj", "{9E1987E3-1F19-45CA-B9C9-D31E791836D8}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -45,6 +57,14 @@ Global {9F316E83-5AE5-4939-A723-305A94F48005}.Release|Win32.Build.0 = Release|Win32 {9F316E83-5AE5-4939-A723-305A94F48005}.Release|x64.ActiveCfg = Release|x64 {9F316E83-5AE5-4939-A723-305A94F48005}.Release|x64.Build.0 = Release|x64 + {B4CF9797-519D-4AFE-A8F4-5141A6B521D3}.Debug|Win32.ActiveCfg = Debug|Win32 + {B4CF9797-519D-4AFE-A8F4-5141A6B521D3}.Debug|Win32.Build.0 = Debug|Win32 + {B4CF9797-519D-4AFE-A8F4-5141A6B521D3}.Debug|x64.ActiveCfg = Debug|x64 + {B4CF9797-519D-4AFE-A8F4-5141A6B521D3}.Debug|x64.Build.0 = Debug|x64 + {B4CF9797-519D-4AFE-A8F4-5141A6B521D3}.Release|Win32.ActiveCfg = Release|Win32 + {B4CF9797-519D-4AFE-A8F4-5141A6B521D3}.Release|Win32.Build.0 = Release|Win32 + {B4CF9797-519D-4AFE-A8F4-5141A6B521D3}.Release|x64.ActiveCfg = Release|x64 + {B4CF9797-519D-4AFE-A8F4-5141A6B521D3}.Release|x64.Build.0 = Release|x64 {9CDA7840-B7A5-496D-A527-E95571496D18}.Debug|Win32.ActiveCfg = Debug|Win32 {9CDA7840-B7A5-496D-A527-E95571496D18}.Debug|Win32.Build.0 = Debug|Win32 {9CDA7840-B7A5-496D-A527-E95571496D18}.Debug|x64.ActiveCfg = Debug|x64 @@ -61,6 +81,46 @@ Global {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Release|Win32.Build.0 = Release|Win32 {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Release|x64.ActiveCfg = Release|x64 {4A1FB5EA-22F5-42A8-AB92-1D2DF5D47FB9}.Release|x64.Build.0 = Release|x64 + {57E2DF5A-6FC8-45BB-99DD-91A18C646E80}.Debug|Win32.ActiveCfg = Debug|Win32 + {57E2DF5A-6FC8-45BB-99DD-91A18C646E80}.Debug|Win32.Build.0 = Debug|Win32 + {57E2DF5A-6FC8-45BB-99DD-91A18C646E80}.Debug|x64.ActiveCfg = Debug|x64 + {57E2DF5A-6FC8-45BB-99DD-91A18C646E80}.Debug|x64.Build.0 = Debug|x64 + {57E2DF5A-6FC8-45BB-99DD-91A18C646E80}.Release|Win32.ActiveCfg = Release|Win32 + {57E2DF5A-6FC8-45BB-99DD-91A18C646E80}.Release|Win32.Build.0 = Release|Win32 + {57E2DF5A-6FC8-45BB-99DD-91A18C646E80}.Release|x64.ActiveCfg = Release|x64 + {57E2DF5A-6FC8-45BB-99DD-91A18C646E80}.Release|x64.Build.0 = Release|x64 + {2AE17FDE-F7F3-4CAC-ADAB-0710EDA4F741}.Debug|Win32.ActiveCfg = Debug|Win32 + {2AE17FDE-F7F3-4CAC-ADAB-0710EDA4F741}.Debug|Win32.Build.0 = Debug|Win32 + {2AE17FDE-F7F3-4CAC-ADAB-0710EDA4F741}.Debug|x64.ActiveCfg = Debug|x64 + {2AE17FDE-F7F3-4CAC-ADAB-0710EDA4F741}.Debug|x64.Build.0 = Debug|x64 + {2AE17FDE-F7F3-4CAC-ADAB-0710EDA4F741}.Release|Win32.ActiveCfg = Release|Win32 + {2AE17FDE-F7F3-4CAC-ADAB-0710EDA4F741}.Release|Win32.Build.0 = Release|Win32 + {2AE17FDE-F7F3-4CAC-ADAB-0710EDA4F741}.Release|x64.ActiveCfg = Release|x64 + {2AE17FDE-F7F3-4CAC-ADAB-0710EDA4F741}.Release|x64.Build.0 = Release|x64 + {BBAEB705-1669-40F3-8567-04CF6A991F4C}.Debug|Win32.ActiveCfg = Debug|Win32 + {BBAEB705-1669-40F3-8567-04CF6A991F4C}.Debug|Win32.Build.0 = Debug|Win32 + {BBAEB705-1669-40F3-8567-04CF6A991F4C}.Debug|x64.ActiveCfg = Debug|x64 + {BBAEB705-1669-40F3-8567-04CF6A991F4C}.Debug|x64.Build.0 = Debug|x64 + {BBAEB705-1669-40F3-8567-04CF6A991F4C}.Release|Win32.ActiveCfg = Release|Win32 + {BBAEB705-1669-40F3-8567-04CF6A991F4C}.Release|Win32.Build.0 = Release|Win32 + {BBAEB705-1669-40F3-8567-04CF6A991F4C}.Release|x64.ActiveCfg = Release|x64 + {BBAEB705-1669-40F3-8567-04CF6A991F4C}.Release|x64.Build.0 = Release|x64 + {BAE3D0B5-9695-4EB1-AD0F-75890EB4A3B3}.Debug|Win32.ActiveCfg = Debug|Win32 + {BAE3D0B5-9695-4EB1-AD0F-75890EB4A3B3}.Debug|Win32.Build.0 = Debug|Win32 + {BAE3D0B5-9695-4EB1-AD0F-75890EB4A3B3}.Debug|x64.ActiveCfg = Debug|x64 + {BAE3D0B5-9695-4EB1-AD0F-75890EB4A3B3}.Debug|x64.Build.0 = Debug|x64 + {BAE3D0B5-9695-4EB1-AD0F-75890EB4A3B3}.Release|Win32.ActiveCfg = Release|Win32 + {BAE3D0B5-9695-4EB1-AD0F-75890EB4A3B3}.Release|Win32.Build.0 = Release|Win32 + {BAE3D0B5-9695-4EB1-AD0F-75890EB4A3B3}.Release|x64.ActiveCfg = Release|x64 + {BAE3D0B5-9695-4EB1-AD0F-75890EB4A3B3}.Release|x64.Build.0 = Release|x64 + {9E1987E3-1F19-45CA-B9C9-D31E791836D8}.Debug|Win32.ActiveCfg = Debug|Win32 + {9E1987E3-1F19-45CA-B9C9-D31E791836D8}.Debug|Win32.Build.0 = Debug|Win32 + {9E1987E3-1F19-45CA-B9C9-D31E791836D8}.Debug|x64.ActiveCfg = Debug|x64 + {9E1987E3-1F19-45CA-B9C9-D31E791836D8}.Debug|x64.Build.0 = Debug|x64 + {9E1987E3-1F19-45CA-B9C9-D31E791836D8}.Release|Win32.ActiveCfg = Release|Win32 + {9E1987E3-1F19-45CA-B9C9-D31E791836D8}.Release|Win32.Build.0 = Release|Win32 + {9E1987E3-1F19-45CA-B9C9-D31E791836D8}.Release|x64.ActiveCfg = Release|x64 + {9E1987E3-1F19-45CA-B9C9-D31E791836D8}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 3e8eebfbec4092b69009d7b0ca51eeace114a0a1 Mon Sep 17 00:00:00 2001 From: Vincent Hamm Date: Mon, 17 Jun 2019 21:03:00 -0700 Subject: [PATCH 515/828] Viewport: Added PlatformHandleRaw. Update SDL+DX11 example. (#1542, #2635) --- examples/imgui_impl_dx11.cpp | 8 +++++++- examples/imgui_impl_sdl.cpp | 10 ++++++++++ imgui.h | 3 ++- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/examples/imgui_impl_dx11.cpp b/examples/imgui_impl_dx11.cpp index e43a75af3305..b0aad0e160b7 100644 --- a/examples/imgui_impl_dx11.cpp +++ b/examples/imgui_impl_dx11.cpp @@ -564,7 +564,13 @@ static void ImGui_ImplDX11_CreateWindow(ImGuiViewport* viewport) ImGuiViewportDataDx11* data = IM_NEW(ImGuiViewportDataDx11)(); viewport->RendererUserData = data; - HWND hwnd = (HWND)viewport->PlatformHandle; + // When using SDL, PlatformHandleRaw will be the HWND (because PlatformHandle would be the SDL_Window) + // If not using SDL, PlatformHandleRaw will be null and PlatformHandle will contain the HWND + HWND hwnd = (HWND)viewport->PlatformHandleRaw; + if (hwnd == 0) + { + hwnd = (HWND)viewport->PlatformHandle; + } IM_ASSERT(hwnd != 0); // Create swap chain diff --git a/examples/imgui_impl_sdl.cpp b/examples/imgui_impl_sdl.cpp index 27aa2017fa57..e115e27f021c 100644 --- a/examples/imgui_impl_sdl.cpp +++ b/examples/imgui_impl_sdl.cpp @@ -456,6 +456,16 @@ static void ImGui_ImplSDL2_CreateWindow(ImGuiViewport* viewport) if (use_opengl && backup_context) SDL_GL_MakeCurrent(data->Window, backup_context); viewport->PlatformHandle = (void*)data->Window; + +#if defined(_WIN32) + // save the window handle for render that needs it (directX) + SDL_SysWMinfo info; + SDL_VERSION(&info.version); + if (SDL_GetWindowWMInfo(data->Window, &info)) + { + viewport->PlatformHandleRaw = info.info.win.window; + } +#endif } static void ImGui_ImplSDL2_DestroyWindow(ImGuiViewport* viewport) diff --git a/imgui.h b/imgui.h index 7527222009dd..be07f2fe8fdb 100644 --- a/imgui.h +++ b/imgui.h @@ -2399,11 +2399,12 @@ struct ImGuiViewport void* RendererUserData; // void* to hold custom data structure for the renderer (e.g. swap chain, frame-buffers etc.) void* PlatformUserData; // void* to hold custom data structure for the OS / platform (e.g. windowing info, render context) void* PlatformHandle; // void* for FindViewportByPlatformHandle(). (e.g. suggested to use natural platform handle such as HWND, GlfwWindow*, SDL_Window*) + void* PlatformHandleRaw; // void* to hold the platfor-native windows handle (e.g. the HWND) when using an abstraction layer like SDL (where PlatformHandle would be a SDL_Window*) bool PlatformRequestClose; // Platform window requested closure (e.g. window was moved by the OS / host window manager, e.g. pressing ALT-F4) bool PlatformRequestMove; // Platform window requested move (e.g. window was moved by the OS / host window manager, authoritative position will be OS window position) bool PlatformRequestResize; // Platform window requested resize (e.g. window was resized by the OS / host window manager, authoritative size will be OS window size) - ImGuiViewport() { ID = 0; Flags = 0; DpiScale = 0.0f; DrawData = NULL; ParentViewportId = 0; RendererUserData = PlatformUserData = PlatformHandle = NULL; PlatformRequestClose = PlatformRequestMove = PlatformRequestResize = false; } + ImGuiViewport() { ID = 0; Flags = 0; DpiScale = 0.0f; DrawData = NULL; ParentViewportId = 0; RendererUserData = PlatformUserData = PlatformHandle = PlatformHandleRaw = NULL; PlatformRequestClose = PlatformRequestMove = PlatformRequestResize = false; } ~ImGuiViewport() { IM_ASSERT(PlatformUserData == NULL && RendererUserData == NULL); } }; From adbbd17cb66f5a1635d209b55d8599fcad369e5f Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 18 Jun 2019 23:35:48 +0200 Subject: [PATCH 516/828] Addendum to #2635. Add support for multi-viewports in SDL+DX!! example. making all Win32-centric back-ends handle PlatformHandleRaw. Using the field to use/store the HWND for internal purpose in SDL/GLFW back-ends. (#1542) --- examples/example_sdl_directx11/main.cpp | 21 ++++++++++- examples/imgui_impl_dx10.cpp | 4 ++- examples/imgui_impl_dx11.cpp | 10 ++---- examples/imgui_impl_dx12.cpp | 7 ++-- examples/imgui_impl_dx12.h | 3 +- examples/imgui_impl_dx9.cpp | 8 +++-- examples/imgui_impl_glfw.cpp | 23 ++++++++----- examples/imgui_impl_sdl.cpp | 46 ++++++++++++------------- examples/imgui_impl_vulkan.cpp | 2 +- examples/imgui_impl_win32.cpp | 4 +-- imgui.h | 4 +-- 11 files changed, 79 insertions(+), 53 deletions(-) diff --git a/examples/example_sdl_directx11/main.cpp b/examples/example_sdl_directx11/main.cpp index ae523fe90b9e..3c7ee18e08ba 100644 --- a/examples/example_sdl_directx11/main.cpp +++ b/examples/example_sdl_directx11/main.cpp @@ -51,13 +51,25 @@ int main(int, char**) IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking + io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows + //io.ConfigViewportsNoAutoMerge = true; + //io.ConfigViewportsNoTaskBarIcon = true; // Setup Dear ImGui style ImGui::StyleColorsDark(); //ImGui::StyleColorsClassic(); + // When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones. + ImGuiStyle& style = ImGui::GetStyle(); + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + style.WindowRounding = 0.0f; + style.Colors[ImGuiCol_WindowBg].w = 1.0f; + } + // Setup Platform/Renderer bindings ImGui_ImplSDL2_InitForD3D(window); ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext); @@ -149,6 +161,13 @@ int main(int, char**) g_pd3dDeviceContext->ClearRenderTargetView(g_mainRenderTargetView, (float*)&clear_color); ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData()); + // Update and Render additional Platform Windows + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); + } + g_pSwapChain->Present(1, 0); // Present with vsync //g_pSwapChain->Present(0, 0); // Present without vsync } diff --git a/examples/imgui_impl_dx10.cpp b/examples/imgui_impl_dx10.cpp index c5fdaeb4041e..cf1c1c44b30d 100644 --- a/examples/imgui_impl_dx10.cpp +++ b/examples/imgui_impl_dx10.cpp @@ -553,7 +553,9 @@ static void ImGui_ImplDX10_CreateWindow(ImGuiViewport* viewport) ImGuiViewportDataDx10* data = IM_NEW(ImGuiViewportDataDx10)(); viewport->RendererUserData = data; - HWND hwnd = (HWND)viewport->PlatformHandle; + // PlatformHandleRaw should always be a HWND, whereas PlatformHandle might be a higher-level handle (e.g. GLFWWindow*, SDL_Window*). + // Some back-ends will leave PlatformHandleRaw NULL, in which case we assume PlatformHandle will contain the HWND. + HWND hwnd = viewport->PlatformHandleRaw ? (HWND)viewport->PlatformHandleRaw : (HWND)viewport->PlatformHandle; IM_ASSERT(hwnd != 0); // Create swap chain diff --git a/examples/imgui_impl_dx11.cpp b/examples/imgui_impl_dx11.cpp index b0aad0e160b7..02236296aae7 100644 --- a/examples/imgui_impl_dx11.cpp +++ b/examples/imgui_impl_dx11.cpp @@ -564,13 +564,9 @@ static void ImGui_ImplDX11_CreateWindow(ImGuiViewport* viewport) ImGuiViewportDataDx11* data = IM_NEW(ImGuiViewportDataDx11)(); viewport->RendererUserData = data; - // When using SDL, PlatformHandleRaw will be the HWND (because PlatformHandle would be the SDL_Window) - // If not using SDL, PlatformHandleRaw will be null and PlatformHandle will contain the HWND - HWND hwnd = (HWND)viewport->PlatformHandleRaw; - if (hwnd == 0) - { - hwnd = (HWND)viewport->PlatformHandle; - } + // PlatformHandleRaw should always be a HWND, whereas PlatformHandle might be a higher-level handle (e.g. GLFWWindow*, SDL_Window*). + // Some back-end will leave PlatformHandleRaw NULL, in which case we assume PlatformHandle will contain the HWND. + HWND hwnd = viewport->PlatformHandleRaw ? (HWND)viewport->PlatformHandleRaw : (HWND)viewport->PlatformHandle; IM_ASSERT(hwnd != 0); // Create swap chain diff --git a/examples/imgui_impl_dx12.cpp b/examples/imgui_impl_dx12.cpp index 7dfdb9c69723..04b271138525 100644 --- a/examples/imgui_impl_dx12.cpp +++ b/examples/imgui_impl_dx12.cpp @@ -4,7 +4,8 @@ // Implemented features: // [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. // [X] Renderer: Support for large meshes (64k+ vertices) with 16-bits indices. -// Issues: +// Missing features, issues: +// [ ] Renderer: Missing multi-viewport support. // [ ] 64-bit only for now! (Because sizeof(ImTextureId) == sizeof(void*)). See github.com/ocornut/imgui/pull/301 // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. @@ -680,7 +681,9 @@ static void ImGui_ImplDX12_CreateWindow(ImGuiViewport* viewport) /* // FIXME-PLATFORM - HWND hwnd = (HWND)viewport->PlatformHandle; + // PlatformHandleRaw should always be a HWND, whereas PlatformHandle might be a higher-level handle (e.g. GLFWWindow*, SDL_Window*). + // Some back-ends will leave PlatformHandleRaw NULL, in which case we assume PlatformHandle will contain the HWND. + HWND hwnd = viewport->PlatformHandleRaw ? (HWND)viewport->PlatformHandleRaw : (HWND)viewport->PlatformHandle; IM_ASSERT(hwnd != 0); // Create swap chain diff --git a/examples/imgui_impl_dx12.h b/examples/imgui_impl_dx12.h index 8ae75e532b83..cf34c1448328 100644 --- a/examples/imgui_impl_dx12.h +++ b/examples/imgui_impl_dx12.h @@ -4,7 +4,8 @@ // Implemented features: // [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. // [X] Renderer: Support for large meshes (64k+ vertices) with 16-bits indices. -// Issues: +// Missing features, issues: +// [ ] Renderer: Missing multi-viewport support. // [ ] 64-bit only for now! (Because sizeof(ImTextureId) == sizeof(void*)). See github.com/ocornut/imgui/pull/301 // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. diff --git a/examples/imgui_impl_dx9.cpp b/examples/imgui_impl_dx9.cpp index a8635fe0d49e..2c22885e6930 100644 --- a/examples/imgui_impl_dx9.cpp +++ b/examples/imgui_impl_dx9.cpp @@ -319,8 +319,10 @@ static void ImGui_ImplDX9_CreateWindow(ImGuiViewport* viewport) ImGuiViewportDataDx9* data = IM_NEW(ImGuiViewportDataDx9)(); viewport->RendererUserData = data; - HWND hWnd = (HWND)viewport->PlatformHandle; - IM_ASSERT(hWnd != 0); + // PlatformHandleRaw should always be a HWND, whereas PlatformHandle might be a higher-level handle (e.g. GLFWWindow*, SDL_Window*). + // Some back-ends will leave PlatformHandleRaw NULL, in which case we assume PlatformHandle will contain the HWND. + HWND hwnd = viewport->PlatformHandleRaw ? (HWND)viewport->PlatformHandleRaw : (HWND)viewport->PlatformHandle; + IM_ASSERT(hwnd != 0); ZeroMemory(&data->d3dpp, sizeof(D3DPRESENT_PARAMETERS)); data->d3dpp.Windowed = TRUE; @@ -328,7 +330,7 @@ static void ImGui_ImplDX9_CreateWindow(ImGuiViewport* viewport) data->d3dpp.BackBufferWidth = (UINT)viewport->Size.x; data->d3dpp.BackBufferHeight = (UINT)viewport->Size.y; data->d3dpp.BackBufferFormat = D3DFMT_UNKNOWN; - data->d3dpp.hDeviceWindow = hWnd; + data->d3dpp.hDeviceWindow = hwnd; data->d3dpp.EnableAutoDepthStencil = FALSE; data->d3dpp.AutoDepthStencilFormat = D3DFMT_D16; data->d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; // Present without vsync diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index 21da2f69e82c..c4b602175887 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -202,6 +202,9 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw // Our mouse update function expect PlatformHandle to be filled for the main viewport ImGuiViewport* main_viewport = ImGui::GetMainViewport(); main_viewport->PlatformHandle = (void*)g_Window; +#ifdef _WIN32 + main_viewport->PlatformHandleRaw = glfwGetWin32Window(g_Window); +#endif if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) ImGui_ImplGlfw_InitPlatformInterface(); @@ -444,6 +447,9 @@ static void ImGui_ImplGlfw_CreateWindow(ImGuiViewport* viewport) data->Window = glfwCreateWindow((int)viewport->Size.x, (int)viewport->Size.y, "No Title Yet", NULL, share_window); data->WindowOwned = true; viewport->PlatformHandle = (void*)data->Window; +#ifdef _WIN32 + viewport->PlatformHandleRaw = glfwGetWin32Window(data->Window); +#endif glfwSetWindowPos(data->Window, (int)viewport->Pos.x, (int)viewport->Pos.y); // Install callbacks for secondary viewports @@ -468,7 +474,7 @@ static void ImGui_ImplGlfw_DestroyWindow(ImGuiViewport* viewport) if (data->WindowOwned) { #if GLFW_HAS_GLFW_HOVERED && defined(_WIN32) - HWND hwnd = glfwGetWin32Window(data->Window); + HWND hwnd = (HWND)viewport->PlatformHandleRaw; ::RemovePropA(hwnd, "IMGUI_VIEWPORT"); #endif glfwDestroyWindow(data->Window); @@ -504,7 +510,7 @@ static void ImGui_ImplGlfw_ShowWindow(ImGuiViewport* viewport) #if defined(_WIN32) // GLFW hack: Hide icon from task bar - HWND hwnd = glfwGetWin32Window(data->Window); + HWND hwnd = (HWND)viewport->PlatformHandleRaw; if (viewport->Flags & ImGuiViewportFlags_NoTaskBarIcon) { LONG ex_style = ::GetWindowLong(hwnd, GWL_EXSTYLE); @@ -633,13 +639,12 @@ static void ImGui_ImplGlfw_SwapBuffers(ImGuiViewport* viewport, void*) static void ImGui_ImplWin32_SetImeInputPos(ImGuiViewport* viewport, ImVec2 pos) { COMPOSITIONFORM cf = { CFS_FORCE_POSITION, { (LONG)(pos.x - viewport->Pos.x), (LONG)(pos.y - viewport->Pos.y) }, { 0, 0, 0, 0 } }; - if (ImGuiViewportDataGlfw* data = (ImGuiViewportDataGlfw*)viewport->PlatformUserData) - if (HWND hwnd = glfwGetWin32Window(data->Window)) - if (HIMC himc = ::ImmGetContext(hwnd)) - { - ::ImmSetCompositionWindow(himc, &cf); - ::ImmReleaseContext(hwnd, himc); - } + if (HWND hwnd = (HWND)viewport->PlatformHandleRaw) + if (HIMC himc = ::ImmGetContext(hwnd)) + { + ::ImmSetCompositionWindow(himc, &cf); + ::ImmReleaseContext(hwnd, himc); + } } #else #define HAS_WIN32_IME 0 diff --git a/examples/imgui_impl_sdl.cpp b/examples/imgui_impl_sdl.cpp index e115e27f021c..3ee2b82e43f1 100644 --- a/examples/imgui_impl_sdl.cpp +++ b/examples/imgui_impl_sdl.cpp @@ -200,6 +200,12 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window, void* sdl_gl_context) // Our mouse update function expect PlatformHandle to be filled for the main viewport ImGuiViewport* main_viewport = ImGui::GetMainViewport(); main_viewport->PlatformHandle = (void*)window; +#if defined(_WIN32) + SDL_SysWMinfo info; + SDL_VERSION(&info.version); + if (SDL_GetWindowWMInfo(window, &info)) + main_viewport->PlatformHandleRaw = info.info.win.window; +#endif // We need SDL_CaptureMouse(), SDL_GetGlobalMouseState() from SDL 2.0.4+ to support multiple viewports. // We left the call to ImGui_ImplSDL2_InitPlatformInterface() outside of #ifdef to avoid unused-function warnings. @@ -455,16 +461,13 @@ static void ImGui_ImplSDL2_CreateWindow(ImGuiViewport* viewport) } if (use_opengl && backup_context) SDL_GL_MakeCurrent(data->Window, backup_context); - viewport->PlatformHandle = (void*)data->Window; + viewport->PlatformHandle = (void*)data->Window; #if defined(_WIN32) - // save the window handle for render that needs it (directX) SDL_SysWMinfo info; SDL_VERSION(&info.version); if (SDL_GetWindowWMInfo(data->Window, &info)) - { viewport->PlatformHandleRaw = info.info.win.window; - } #endif } @@ -487,28 +490,23 @@ static void ImGui_ImplSDL2_ShowWindow(ImGuiViewport* viewport) { ImGuiViewportDataSDL2* data = (ImGuiViewportDataSDL2*)viewport->PlatformUserData; #if defined(_WIN32) - SDL_SysWMinfo info; - SDL_VERSION(&info.version); - if (SDL_GetWindowWMInfo(data->Window, &info)) - { - HWND hwnd = info.info.win.window; + HWND hwnd = (HWND)viewport->PlatformHandleRaw; - // SDL hack: Hide icon from task bar - // Note: SDL 2.0.6+ has a SDL_WINDOW_SKIP_TASKBAR flag which is supported under Windows but the way it create the window breaks our seamless transition. - if (viewport->Flags & ImGuiViewportFlags_NoTaskBarIcon) - { - LONG ex_style = ::GetWindowLong(hwnd, GWL_EXSTYLE); - ex_style &= ~WS_EX_APPWINDOW; - ex_style |= WS_EX_TOOLWINDOW; - ::SetWindowLong(hwnd, GWL_EXSTYLE, ex_style); - } + // SDL hack: Hide icon from task bar + // Note: SDL 2.0.6+ has a SDL_WINDOW_SKIP_TASKBAR flag which is supported under Windows but the way it create the window breaks our seamless transition. + if (viewport->Flags & ImGuiViewportFlags_NoTaskBarIcon) + { + LONG ex_style = ::GetWindowLong(hwnd, GWL_EXSTYLE); + ex_style &= ~WS_EX_APPWINDOW; + ex_style |= WS_EX_TOOLWINDOW; + ::SetWindowLong(hwnd, GWL_EXSTYLE, ex_style); + } - // SDL hack: SDL always activate/focus windows :/ - if (viewport->Flags & ImGuiViewportFlags_NoFocusOnAppearing) - { - ::ShowWindow(hwnd, SW_SHOWNA); - return; - } + // SDL hack: SDL always activate/focus windows :/ + if (viewport->Flags & ImGuiViewportFlags_NoFocusOnAppearing) + { + ::ShowWindow(hwnd, SW_SHOWNA); + return; } #endif diff --git a/examples/imgui_impl_vulkan.cpp b/examples/imgui_impl_vulkan.cpp index 16d2f4b620a5..74d111ea8ac9 100644 --- a/examples/imgui_impl_vulkan.cpp +++ b/examples/imgui_impl_vulkan.cpp @@ -3,8 +3,8 @@ // Implemented features: // [X] Renderer: Support for large meshes (64k+ vertices) with 16-bits indices. +// [x] Platform: Multi-viewport / platform windows. With issues (flickering when creating a new viewport). // Missing features: -// [ ] Platform: Multi-viewport / platform windows. // [ ] Renderer: User texture binding. Changes of ImTextureID aren't supported by this binding! See https://github.com/ocornut/imgui/pull/914 // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index e1ae9730ec9b..d1a42a2ffebd 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -73,7 +73,7 @@ bool ImGui_ImplWin32_Init(void* hwnd) // Our mouse update function expect PlatformHandle to be filled for the main viewport g_hWnd = (HWND)hwnd; ImGuiViewport* main_viewport = ImGui::GetMainViewport(); - main_viewport->PlatformHandle = (void*)g_hWnd; + main_viewport->PlatformHandle = main_viewport->PlatformHandleRaw = (void*)g_hWnd; if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) ImGui_ImplWin32_InitPlatformInterface(); @@ -547,7 +547,7 @@ static void ImGui_ImplWin32_CreateWindow(ImGuiViewport* viewport) parent_window, NULL, ::GetModuleHandle(NULL), NULL); // Parent window, Menu, Instance, Param data->HwndOwned = true; viewport->PlatformRequestResize = false; - viewport->PlatformHandle = data->Hwnd; + viewport->PlatformHandle = viewport->PlatformHandleRaw = data->Hwnd; } static void ImGui_ImplWin32_DestroyWindow(ImGuiViewport* viewport) diff --git a/imgui.h b/imgui.h index be07f2fe8fdb..b4539b46eeba 100644 --- a/imgui.h +++ b/imgui.h @@ -2398,8 +2398,8 @@ struct ImGuiViewport void* RendererUserData; // void* to hold custom data structure for the renderer (e.g. swap chain, frame-buffers etc.) void* PlatformUserData; // void* to hold custom data structure for the OS / platform (e.g. windowing info, render context) - void* PlatformHandle; // void* for FindViewportByPlatformHandle(). (e.g. suggested to use natural platform handle such as HWND, GlfwWindow*, SDL_Window*) - void* PlatformHandleRaw; // void* to hold the platfor-native windows handle (e.g. the HWND) when using an abstraction layer like SDL (where PlatformHandle would be a SDL_Window*) + void* PlatformHandle; // void* for FindViewportByPlatformHandle(). (e.g. suggested to use natural platform handle such as HWND, GLFWWindow*, SDL_Window*) + void* PlatformHandleRaw; // void* to hold low-level, platform-native window handle (e.g. the HWND) when using an abstraction layer like GLFW or SDL (where PlatformHandle would be a SDL_Window*) bool PlatformRequestClose; // Platform window requested closure (e.g. window was moved by the OS / host window manager, e.g. pressing ALT-F4) bool PlatformRequestMove; // Platform window requested move (e.g. window was moved by the OS / host window manager, authoritative position will be OS window position) bool PlatformRequestResize; // Platform window requested resize (e.g. window was resized by the OS / host window manager, authoritative size will be OS window size) From 50d421fa19c992301c9e401ef59b6b7de58c446a Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 26 Jun 2019 09:52:25 +0200 Subject: [PATCH 517/828] Docking: Fixed GetBackgroundDrawList(), GetForegroundDrawList() overwriting ImDrawList flags after clear, leading to the AllowVtxOffset flag not being cleared. (#2638) --- imgui.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 2173564b9fab..54e96ae2a5d4 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3285,7 +3285,6 @@ static ImDrawList* GetViewportDrawList(ImGuiViewportP* viewport, size_t drawlist draw_list->Clear(); draw_list->PushTextureID(g.IO.Fonts->TexID); draw_list->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size, false); - draw_list->Flags = (g.Style.AntiAliasedLines ? ImDrawListFlags_AntiAliasedLines : 0) | (g.Style.AntiAliasedFill ? ImDrawListFlags_AntiAliasedFill : 0); viewport->LastFrameDrawLists[drawlist_no] = g.FrameCount; } return draw_list; From dd80db87a634cf095cfc832e582c0778d11b6103 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 15 Jul 2019 13:35:21 -0700 Subject: [PATCH 518/828] Viewport: Added ImGuiViewportFlags_NoAutoMerge to prevent merging into host viewport in a per-window basis via the ImGuiWindowClass override mechanism. (#1542) --- imgui.cpp | 16 +++++++++------- imgui.h | 7 ++++--- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 24fbb7d06945..dcb7c3b7fd2f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10532,12 +10532,13 @@ static void SetWindowViewport(ImGuiWindow* window, ImGuiViewportP* viewport) static bool ImGui::GetWindowAlwaysWantOwnViewport(ImGuiWindow* window) { - // Tooltips and menus are not automatically forced into their own viewport when the NoMerge flag is set, however the multiplication of viewports makes them more likely to protude and create their own. + // Tooltips and menus are not automatically forced into their own viewport when the NoMerge flag is set, however the multiplication of viewports makes them more likely to protrude and create their own. ImGuiContext& g = *GImGui; - if (g.IO.ConfigViewportsNoAutoMerge && (g.ConfigFlagsForFrame & ImGuiConfigFlags_ViewportsEnable)) - if (!window->DockIsActive) - if ((window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) == 0) - return true; + if (g.IO.ConfigViewportsNoAutoMerge || ((window->WindowClass.ViewportFlagsOverrideValue & window->WindowClass.ViewportFlagsOverrideMask) & ImGuiViewportFlags_NoAutoMerge)) + if (g.ConfigFlagsForFrame & ImGuiConfigFlags_ViewportsEnable) + if (!window->DockIsActive) + if ((window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) == 0) + return true; return false; } @@ -14812,10 +14813,11 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGuiWindowFlags flags = viewport->Flags; ImGui::BulletText("Pos: (%.0f,%.0f), Size: (%.0f,%.0f), Monitor: %d, DpiScale: %.0f%%", viewport->Pos.x, viewport->Pos.y, viewport->Size.x, viewport->Size.y, viewport->PlatformMonitor, viewport->DpiScale * 100.0f); if (viewport->Idx > 0) { ImGui::SameLine(); if (ImGui::SmallButton("Reset Pos")) { viewport->Pos = ImVec2(200,200); if (viewport->Window) viewport->Window->Pos = ImVec2(200,200); } } - ImGui::BulletText("Flags: 0x%04X =%s%s%s%s%s%s", viewport->Flags, + ImGui::BulletText("Flags: 0x%04X =%s%s%s%s%s%s%s", viewport->Flags, (flags & ImGuiViewportFlags_CanHostOtherWindows) ? " CanHostOtherWindows" : "", (flags & ImGuiViewportFlags_NoDecoration) ? " NoDecoration" : "", (flags & ImGuiViewportFlags_NoFocusOnAppearing) ? " NoFocusOnAppearing" : "", (flags & ImGuiViewportFlags_NoInputs) ? " NoInputs" : "", - (flags & ImGuiViewportFlags_NoRendererClear) ? " NoRendererClear" : "", (flags & ImGuiViewportFlags_Minimized) ? " Minimized" : ""); + (flags & ImGuiViewportFlags_NoRendererClear) ? " NoRendererClear" : "", (flags & ImGuiViewportFlags_Minimized) ? " Minimized" : "", + (flags & ImGuiViewportFlags_NoAutoMerge) ? " NoAutoMerge" : ""); for (int layer_i = 0; layer_i < IM_ARRAYSIZE(viewport->DrawDataBuilder.Layers); layer_i++) for (int draw_list_i = 0; draw_list_i < viewport->DrawDataBuilder.Layers[layer_i].Size; draw_list_i++) Funcs::NodeDrawList(NULL, viewport, viewport->DrawDataBuilder.Layers[layer_i][draw_list_i], "DrawList"); diff --git a/imgui.h b/imgui.h index 1067787fdd57..d930752953d6 100644 --- a/imgui.h +++ b/imgui.h @@ -1429,7 +1429,7 @@ struct ImGuiIO bool ConfigDockingTransparentPayload;// = false // [BETA] Make window or viewport transparent when docking and only display docking boxes on the target viewport. Useful if rendering of multiple viewport cannot be synced. Best used with ConfigViewportsNoAutoMerge. // Viewport options (when ImGuiConfigFlags_ViewportsEnable is set) - bool ConfigViewportsNoAutoMerge; // = false; // Set to make all floating imgui windows always create their own viewport. Otherwise, they are merged into the main host viewports when overlapping it. + bool ConfigViewportsNoAutoMerge; // = false; // Set to make all floating imgui windows always create their own viewport. Otherwise, they are merged into the main host viewports when overlapping it. May also set ImGuiViewportFlags_NoAutoMerge on individual viewport. bool ConfigViewportsNoTaskBarIcon; // = false // Disable default OS task bar icon flag for secondary viewports. When a viewport doesn't want a task bar icon, ImGuiViewportFlags_NoTaskBarIcon will be set on it. bool ConfigViewportsNoDecoration; // = true // [BETA] Disable default OS window decoration flag for secondary viewports. When a viewport doesn't want window decorations, ImGuiViewportFlags_NoDecoration will be set on it. Enabling decoration can create subsequent issues at OS levels (e.g. minimum window size). bool ConfigViewportsNoDefaultParent; // = false // Disable default OS parenting to main viewport for secondary viewports. By default, viewports are marked with ParentViewportId = , expecting the platform back-end to setup a parent/child relationship between the OS windows (some back-end may ignore this). Set to true if you want the default to be 0, then all viewports will be top-level OS windows. @@ -2383,9 +2383,10 @@ enum ImGuiViewportFlags_ ImGuiViewportFlags_NoFocusOnClick = 1 << 3, // Platform Window: Don't take focus when clicked on. ImGuiViewportFlags_NoInputs = 1 << 4, // Platform Window: Make mouse pass through so we can drag this window while peaking behind it. ImGuiViewportFlags_NoRendererClear = 1 << 5, // Platform Window: Renderer doesn't need to clear the framebuffer ahead (because we will fill it entirely). - ImGuiViewportFlags_TopMost = 1 << 6, // Platform Window: Display on top (for tooltips only) + ImGuiViewportFlags_TopMost = 1 << 6, // Platform Window: Display on top (for tooltips only). ImGuiViewportFlags_Minimized = 1 << 7, // Platform Window: Window is minimized, can skip render. When minimized we tend to avoid using the viewport pos/size for clipping window or testing if they are contained in the viewport. - ImGuiViewportFlags_CanHostOtherWindows = 1 << 8 // Main viewport: can host multiple imgui windows (secondary viewports are associated to a single window) + ImGuiViewportFlags_NoAutoMerge = 1 << 8, // Platform Window: Avoid merging this widow into another host window. This can only be set via ImGuiWindowClass viewport flags override (because we need to now ahead if we are going to create a viewport in the first place!). + ImGuiViewportFlags_CanHostOtherWindows = 1 << 9 // Main viewport: can host multiple imgui windows (secondary viewports are associated to a single window). }; // The viewports created and managed by imgui. The role of the platform back-end is to create the platform/OS windows corresponding to each viewport. From 8bc6d976cbe82611435e4f1c10c405bbd83573e4 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 15 Jul 2019 14:17:01 -0700 Subject: [PATCH 519/828] Docking: Fixed using ImGuiDockNodeFlags_AutoHideTabBar with ConfigDockingTabBarOnSingleWindows. (#2109) --- imgui.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/imgui.cpp b/imgui.cpp index dcb7c3b7fd2f..2751c76b999c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11855,6 +11855,11 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) DockSettingsRenameNodeReferences(payload_dock_id, node->ID); } } + else + { + // When docking a floating single window node we want to reevaluate auto-hiding of the tab bar + node->WantHiddenTabBarUpdate = true; + } // Update selection immediately if (ImGuiTabBar* tab_bar = node->TabBar) From 718e15c7de951a0fd3ef8600e6d7e0129ad7050d Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 16 Jul 2019 11:45:52 -0700 Subject: [PATCH 520/828] Docking: Fix so that an appearing window making a dock node reappear won't have a zero-size on its first frame (because dock node ->Size was 0.0 unlike ->SizeRef) (#2109) Docking: Added ImGuiDockNode to .natvis file. --- imgui.cpp | 38 ++++++++++++++++++++++++++++++++++---- imgui_internal.h | 1 + misc/natvis/imgui.natvis | 4 ++++ 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 2751c76b999c..aa5a31f80d27 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11373,7 +11373,7 @@ namespace ImGui // ImGuiDockNode tree manipulations static void DockNodeTreeSplit(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiAxis split_axis, int split_first_child, float split_ratio, ImGuiDockNode* new_node); static void DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiDockNode* merge_lead_child); - static void DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 size); + static void DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 size, bool only_write_to_marked_nodes = false); static void DockNodeTreeUpdateSplitter(ImGuiDockNode* node); static ImGuiDockNode* DockNodeTreeFindNodeByPos(ImGuiDockNode* node, ImVec2 pos); static ImGuiDockNode* DockNodeTreeFindFallbackLeafNode(ImGuiDockNode* node); @@ -11953,6 +11953,7 @@ ImGuiDockNode::ImGuiDockNode(ImGuiID id) IsVisible = true; IsFocused = HasCloseButton = HasWindowMenuButton = EnableCloseButton = false; WantCloseAll = WantLockSizeOnce = WantMouseMove = WantHiddenTabBarUpdate = WantHiddenTabBarToggle = false; + MarkedForPosSizeWrite = false; } ImGuiDockNode::~ImGuiDockNode() @@ -12286,6 +12287,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) ImGuiContext& g = *GImGui; IM_ASSERT(node->LastFrameActive != g.FrameCount); node->LastFrameAlive = g.FrameCount; + node->MarkedForPosSizeWrite = false; node->CentralNode = node->OnlyNodeWithWindows = NULL; if (node->IsRootNode()) @@ -12533,6 +12535,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) node->LastFrameActive = g.FrameCount; // Recurse into children + // FIXME-DOCK FIXME-OPT: Should not need to recurse into children if (host_window) { if (node->ChildNodes[0]) @@ -13265,10 +13268,17 @@ void ImGui::DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImG } // Update Pos/Size for a node hierarchy (don't affect child Windows yet) -void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 size) +void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 size, bool only_write_to_marked_nodes) { - node->Pos = pos; - node->Size = size; + // During the regular dock node update we write to all nodes. + // 'only_write_to_marked_nodes' is only set when turning a node visible mid-frame and we need its size right-away. + const bool write_to_node = (only_write_to_marked_nodes == false) || (node->MarkedForPosSizeWrite); + if (write_to_node) + { + node->Pos = pos; + node->Size = size; + } + if (node->IsLeafNode()) return; @@ -14082,6 +14092,25 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) node->LastFrameAlive = g.FrameCount; } + // If the node just turned visible, it doesn't have a Size assigned by DockNodeTreeUpdatePosSize() yet, + // so we're forcing a Pos/Size update from the first ancestor that is already visible (often it will be the root node). + // If we don't do this, the window will be assigned a zero-size on its first frame, which won't ideally warm up the layout. + // This is a little wonky because we don't normally update the Pos/Size of visible node mid-frame. + if (!node->IsVisible) + { + ImGuiDockNode* ancestor_node = node; + while (!ancestor_node->IsVisible) + { + ancestor_node->IsVisible = true; + ancestor_node->MarkedForPosSizeWrite = true; + if (ancestor_node->ParentNode) + ancestor_node = ancestor_node->ParentNode; + } + IM_ASSERT(ancestor_node->Size.x > 0.0f && ancestor_node->Size.y > 0.0f); + DockNodeTreeUpdatePosSize(ancestor_node, ancestor_node->Pos, ancestor_node->Size, true); + } + + // Add window to node DockNodeAddWindow(node, window, true); IM_ASSERT(node == window->DockNode); } @@ -14128,6 +14157,7 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) } IM_ASSERT(node->HostWindow); IM_ASSERT(node->IsLeafNode()); + IM_ASSERT(node->Size.x > 0.0f && node->Size.y > 0.0f); // Position window SetNextWindowPos(node->Pos); diff --git a/imgui_internal.h b/imgui_internal.h index 265292aeb1ec..44dff1e74655 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -956,6 +956,7 @@ struct ImGuiDockNode bool WantMouseMove :1; // After a node extraction we need to transition toward moving the newly created host window bool WantHiddenTabBarUpdate :1; bool WantHiddenTabBarToggle :1; + bool MarkedForPosSizeWrite :1; // Update by DockNodeTreeUpdatePosSize() write-filtering ImGuiDockNode(ImGuiID id); ~ImGuiDockNode(); diff --git a/misc/natvis/imgui.natvis b/misc/natvis/imgui.natvis index cc768bfb55f2..f1082a814e1d 100644 --- a/misc/natvis/imgui.natvis +++ b/misc/natvis/imgui.natvis @@ -35,5 +35,9 @@ {{Name {Name,s} Active {(Active||WasActive)?1:0,d} Child {(Flags & 0x01000000)?1:0,d} Popup {(Flags & 0x04000000)?1:0,d} Hidden {(Hidden)?1:0,d}} + + + {{ID {ID,x} Pos=({Pos.x,g} {Pos.y,g}) Size=({Size.x,g} {Size.y,g}) Parent {(ParentNode==0)?0:ParentNode->ID,x} Childs {(ChildNodes[0] != 0)+(ChildNodes[1] != 0)} Windows {Windows.Size} } + \ No newline at end of file From bb2aa5e77062e7e16c59741aab9c48eed1b6722f Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 17 Jul 2019 13:55:00 -0700 Subject: [PATCH 521/828] Docking: Making it possible to undock a node by clicking on the tab bar / title bar for the node. (#2645, #2109) --- imgui.cpp | 45 ++++++++++++++++++++++++++++++++++++++------- imgui_internal.h | 1 + imgui_widgets.cpp | 27 ++------------------------- 3 files changed, 41 insertions(+), 32 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 7ec5d5990378..ce7e3cc7f03f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3403,6 +3403,32 @@ void ImGui::StartMouseMovingWindow(ImGuiWindow* window) g.MovingWindow = window; } +void ImGui::StartMouseDragFromTitleBar(ImGuiWindow* window, ImGuiDockNode* node, bool from_collapse_button) +{ + ImGuiContext& g = *GImGui; + bool can_extract_dock_node = false; + if (node != NULL && node->VisibleWindow && (node->VisibleWindow->Flags & ImGuiWindowFlags_NoMove) == 0) + { + ImGuiDockNode* root_node = DockNodeGetRootNode(node); + if (root_node->OnlyNodeWithWindows != node || (root_node->CentralNode != NULL)) + if (from_collapse_button || root_node->IsDockSpace()) + can_extract_dock_node = true; + } + + const bool clicked = IsMouseClicked(0); + const bool dragging = IsMouseDragging(0, g.IO.MouseDragThreshold * 1.70f); + if (can_extract_dock_node && dragging) + { + DockContextQueueUndockNode(&g, node); + g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - node->Pos; + } + else if (!can_extract_dock_node && (clicked || dragging) && g.MovingWindow != window) + { + StartMouseMovingWindow(window); + g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - window->RootWindow->Pos; + } +} + // Handle mouse moving window // Note: moving window with the navigation keys (Square + d-pad / CTRL+TAB + Arrows) are processed in NavUpdateWindowing() void ImGui::UpdateMouseMovingWindowNewFrame() @@ -12802,15 +12828,20 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w } // When clicking on the title bar outside of tabs, we still focus the selected tab for that node - if (g.HoveredWindow == host_window && g.HoveredId == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max)) + // FIXME: TabItem use AllowItemOverlap so we manually perform a more specific test for now (hovered || held) + ImGuiID title_bar_id = host_window->GetID("#TITLEBAR"); + if (g.HoveredId == 0 || g.HoveredId == title_bar_id || g.ActiveId == title_bar_id) { - if (IsMouseClicked(0)) + bool held; + ButtonBehavior(title_bar_rect, title_bar_id, NULL, &held); + if (held) { - focus_tab_id = tab_bar->SelectedTabId; + if (IsMouseClicked(0)) + focus_tab_id = tab_bar->SelectedTabId; // Forward moving request to selected window - if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, focus_tab_id)) - StartMouseMovingWindow(tab->Window); + if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_bar->SelectedTabId)) + StartMouseDragFromTitleBar(tab->Window, node, false); } } @@ -13418,7 +13449,7 @@ void ImGui::DockNodeTreeUpdateSplitter(ImGuiDockNode* node) /* // [DEBUG] Render limits - ImDrawList* draw_list = node->HostWindow ? GetOverlayDrawList(node->HostWindow) : GetOverlayDrawList((ImGuiViewportP*)GetMainViewport()); + ImDrawList* draw_list = node->HostWindow ? GetForegroundDrawList(node->HostWindow) : GetForegroundDrawList((ImGuiViewportP*)GetMainViewport()); for (int n = 0; n < 2; n++) if (axis == ImGuiAxis_X) draw_list->AddLine(ImVec2(resize_limits[n], node->ChildNodes[n]->Pos.y), ImVec2(resize_limits[n], node->ChildNodes[n]->Pos.y + node->ChildNodes[n]->Size.y), IM_COL32(255, 0, 255, 255), 3.0f); @@ -13446,7 +13477,7 @@ void ImGui::DockNodeTreeUpdateSplitter(ImGuiDockNode* node) for (int touching_node_n = 0; touching_node_n < touching_nodes[side_n].Size; touching_node_n++) { ImGuiDockNode* touching_node = touching_nodes[side_n][touching_node_n]; - //ImDrawList* draw_list = node->HostWindow ? GetOverlayDrawList(node->HostWindow) : GetOverlayDrawList((ImGuiViewportP*)GetMainViewport()); + //ImDrawList* draw_list = node->HostWindow ? GetForegroundDrawList(node->HostWindow) : GetForegroundDrawList((ImGuiViewportP*)GetMainViewport()); //draw_list->AddRect(touching_node->Pos, touching_node->Pos + touching_node->Size, IM_COL32(255, 128, 0, 255)); while (touching_node->ParentNode != node) { diff --git a/imgui_internal.h b/imgui_internal.h index e9907a961326..6392ecf23103 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1661,6 +1661,7 @@ namespace ImGui // NewFrame IMGUI_API void UpdateHoveredWindowAndCaptureFlags(); IMGUI_API void StartMouseMovingWindow(ImGuiWindow* window); + IMGUI_API void StartMouseDragFromTitleBar(ImGuiWindow* window, ImGuiDockNode* node, bool from_collapse_button); IMGUI_API void UpdateMouseMovingWindowNewFrame(); IMGUI_API void UpdateMouseMovingWindowEndFrame(); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 4de236d5ed6d..076b3e80b11a 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -776,30 +776,7 @@ bool ImGui::CollapseButton(ImGuiID id, const ImVec2& pos, ImGuiDockNode* dock_no // Switch to moving the window after mouse is moved beyond the initial drag threshold if (IsItemActive() && IsMouseDragging(0)) - { - bool can_extract_dock_node = false; - if (dock_node != NULL && dock_node->VisibleWindow && !(dock_node->VisibleWindow->Flags & ImGuiWindowFlags_NoMove)) - { - ImGuiDockNode* root_node = DockNodeGetRootNode(dock_node); - if (root_node->OnlyNodeWithWindows != dock_node || (root_node->CentralNode != NULL)) - can_extract_dock_node = true; - } - if (can_extract_dock_node) - { - float threshold_base = g.FontSize; - float threshold_x = (threshold_base * 2.2f); - float threshold_y = (threshold_base * 1.5f); - IM_ASSERT(window->DockNodeAsHost != NULL); - if (g.IO.MouseDragMaxDistanceAbs[0].x > threshold_x || g.IO.MouseDragMaxDistanceAbs[0].y > threshold_y) - DockContextQueueUndockNode(&g, dock_node); - } - else - { - ImVec2 backup_active_click_offset = g.ActiveIdClickOffset + (pos - window->Pos); - StartMouseMovingWindow(window); - g.ActiveIdClickOffset = backup_active_click_offset; - } - } + StartMouseDragFromTitleBar(window, dock_node, true); return pressed; } @@ -7048,7 +7025,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, //float threshold_base = g.IO.ConfigDockingWithShift ? g.FontSize * 0.5f : g.FontSize; float threshold_x = (threshold_base * 2.2f); float threshold_y = (threshold_base * 1.5f) + ImClamp((ImFabs(g.IO.MouseDragMaxDistanceAbs[0].x) - threshold_base * 2.0f) * 0.20f, 0.0f, threshold_base * 4.0f); - //GetOverlayDrawList(window)->AddRect(ImVec2(bb.Min.x - threshold_x, bb.Min.y - threshold_y), ImVec2(bb.Max.x + threshold_x, bb.Max.y + threshold_y), IM_COL32_WHITE); // [DEBUG] + //GetForegroundDrawList()->AddRect(ImVec2(bb.Min.x - threshold_x, bb.Min.y - threshold_y), ImVec2(bb.Max.x + threshold_x, bb.Max.y + threshold_y), IM_COL32_WHITE); // [DEBUG] float distance_from_edge_y = ImMax(bb.Min.y - g.IO.MousePos.y, g.IO.MousePos.y - bb.Max.y); if (distance_from_edge_y >= threshold_y) From 47f5ad32b77a6d0a19f5ac04cb5d816e24da4133 Mon Sep 17 00:00:00 2001 From: omar Date: Sun, 21 Jul 2019 12:05:04 -0700 Subject: [PATCH 522/828] Perform simple check: error if Docking or Viewport are enabled _exactly_ on frame 1 (instead of frame 0 or later), which is a common error leading to loss of .ini data. (#2109) --- imgui.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/imgui.cpp b/imgui.cpp index 113bca25bfa7..437a71964192 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3782,6 +3782,13 @@ void ImGui::NewFrame() if (g.IO.ConfigWindowsResizeFromEdges && !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseCursors)) g.IO.ConfigWindowsResizeFromEdges = false; + // Perform simple check: error if Docking or Viewport are enabled _exactly_ on frame 1 (instead of frame 0 or later), which is a common error leading to loss of .ini data. + const ImGuiColumnsFlags prev_config_flags = g.ConfigFlagsForFrame; + if (g.FrameCount == 1 && (g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable) && (prev_config_flags & ImGuiConfigFlags_DockingEnable) == 0) + IM_ASSERT(0 && "Please DockingEnable before the first call to NewFrame()! Otherwise you will lose your .ini settings!"); + if (g.FrameCount == 1 && (g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) && (prev_config_flags & ImGuiConfigFlags_ViewportsEnable) == 0) + IM_ASSERT(0 && "Please ViewportEnable before the first call to NewFrame()! Otherwise you will lose your .ini settings!"); + // Perform simple checks: multi-viewport and platform windows support if (g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { From 6f8d34768da2c5bf16c67e1385158fcf76ce1598 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 22 Jul 2019 10:54:17 -0700 Subject: [PATCH 523/828] Docking: Removed seemingly unnecessary test in TabItemEx() for undocking tab leading to window move. Added ImGuiDockNode::IsFloatingNode() helper to clarify code intent in various places. --- imgui.cpp | 9 +++++---- imgui.h | 2 +- imgui_internal.h | 1 + imgui_widgets.cpp | 4 +--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 65f11a859afd..c1d5a35115f0 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11216,7 +11216,7 @@ void ImGui::DockContextUpdateDocking(ImGuiContext* ctx) // We can have NULL pointers when we delete nodes, but because ID are recycled this should amortize nicely (and our node count will never be very high) for (int n = 0; n < dc->Nodes.Data.Size; n++) if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p) - if (node->IsRootNode() && !node->IsDockSpace()) + if (node->IsFloatingNode()) DockNodeUpdate(node); } @@ -11702,7 +11702,7 @@ static void ImGui::DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window, b // When reactivating a node with one or two loose window, the window pos/size/viewport are authoritative over the node storage. // In particular it is important we init the viewport from the first window so we don't create two viewports and drop one. - if (node->HostWindow == NULL && !node->IsDockSpace() && node->IsRootNode()) + if (node->HostWindow == NULL && node->IsFloatingNode()) { if (node->AuthorityForPos == ImGuiDataAuthority_Auto) node->AuthorityForPos = ImGuiDataAuthority_Window; @@ -12026,7 +12026,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) DockNodeRemoveTabBar(node); // Early out for hidden root dock nodes (when all DockId references are in inactive windows, or there is only 1 floating window holding on the DockId) - if (node->Windows.Size <= 1 && node->IsRootNode() && node->IsLeafNode() && !node->IsDockSpace() && !g.IO.ConfigDockingTabBarOnSingleWindows) + if (node->Windows.Size <= 1 && node->IsFloatingNode() && node->IsLeafNode() && !g.IO.ConfigDockingTabBarOnSingleWindows) { if (node->Windows.Size == 1) { @@ -13219,6 +13219,7 @@ ImGuiDockNode* ImGui::DockNodeTreeFindNodeByPos(ImGuiDockNode* node, ImVec2 pos) // Docking: Public Functions (SetWindowDock, DockSpace, DockSpaceOverViewport) //----------------------------------------------------------------------------- +// [Internal] Called via SetNextWindowDockID() void ImGui::SetWindowDock(ImGuiWindow* window, ImGuiID dock_id, ImGuiCond cond) { // Test condition (NB: bit 0 is always true) and clear flags for next time @@ -14156,7 +14157,7 @@ static void ImGui::DockSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettings if (ImGuiDockNode* node = DockContextFindNodeByID(ctx, node_settings->ID)) { buf->appendf("%*s", ImMax(2, (line_start_pos + 92) - buf->size()), ""); // Align everything - if (node->IsDockSpace && node->HostWindow && node->HostWindow->ParentWindow) + if (node->IsDockSpace() && node->HostWindow && node->HostWindow->ParentWindow) buf->appendf(" ; in '%s'", node->HostWindow->ParentWindow->Name); int contains_window = 0; for (int window_n = 0; window_n < ctx->SettingsWindows.Size; window_n++) diff --git a/imgui.h b/imgui.h index 3a74d7c1892d..6d488ff3ba54 100644 --- a/imgui.h +++ b/imgui.h @@ -1612,7 +1612,7 @@ struct ImGuiWindowClass ImGuiID ParentViewportId; // Hint for the platform back-end. If non-zero, the platform back-end can create a parent<>child relationship between the platform windows. Not conforming back-ends are free to e.g. parent every viewport to the main viewport or not. ImGuiViewportFlags ViewportFlagsOverrideMask; // Viewport flags to override when a window of this class owns a viewport. This allows you to enforce OS decoration or task bar icon, override the defaults on a per-window basis. ImGuiViewportFlags ViewportFlagsOverrideValue; // Viewport flags values to override when a window of this class owns a viewport. - bool DockingAllowUnclassed; // true = can be docked/merged with an unclassed window + bool DockingAllowUnclassed; // Set to true to allow windows of this class to be docked/merged with an unclassed window. ImGuiWindowClass() { ClassId = 0; ParentViewportId = 0; ViewportFlagsOverrideMask = ViewportFlagsOverrideValue = 0x00; DockingAllowUnclassed = true; } }; diff --git a/imgui_internal.h b/imgui_internal.h index da83467d48a9..bd1f9aadf9c9 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -963,6 +963,7 @@ struct ImGuiDockNode ~ImGuiDockNode(); bool IsRootNode() const { return ParentNode == NULL; } bool IsDockSpace() const { return (LocalFlags & ImGuiDockNodeFlags_DockSpace) != 0; } + bool IsFloatingNode() const { return ParentNode == NULL && (LocalFlags & ImGuiDockNodeFlags_DockSpace) == 0; } bool IsCentralNode() const { return (LocalFlags & ImGuiDockNodeFlags_CentralNode) != 0; } bool IsHiddenTabBar() const { return (LocalFlags & ImGuiDockNodeFlags_HiddenTabBar) != 0; } // Hidden tab bar can be shown back by clicking the small triangle bool IsNoTabBar() const { return (LocalFlags & ImGuiDockNodeFlags_NoTabBar) != 0; } // Never show a tab bar diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index ffd026c0a3d8..cb69dd4b8a4f 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6979,10 +6979,8 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, SetItemAllowOverlap(); // Drag and drop a single floating window node moves it - // FIXME-DOCK: In theory we shouldn't test for the ConfigDockingNodifySingleWindows flag here. - // When our single window node and OnlyNodeWithWindows are working properly we may remove this check here. ImGuiDockNode* node = docked_window ? docked_window->DockNode : NULL; - const bool single_floating_window_node = node && node->IsRootNode() && !node->IsDockSpace() && node->Windows.Size == 1 && g.IO.ConfigDockingTabBarOnSingleWindows; + const bool single_floating_window_node = node && node->IsFloatingNode() && (node->Windows.Size == 1); if (held && single_floating_window_node && IsMouseDragging(0, 0.0f)) { // Move From 0e6a096afdc89219e98aeed49d7875a1ead3ba16 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 22 Jul 2019 11:29:22 -0700 Subject: [PATCH 524/828] Docking: Renamed io.ConfigDockingTabBarOnSingleWindows to io.ConfigDockingAlwaysTabBar. (#2109) Added ImGuiWindowClass::DockingAlwaysTabBar to set on individual windows. --- docs/CHANGELOG.txt | 9 ++++--- examples/example_win32_directx11/main.cpp | 2 +- imgui.cpp | 32 +++++++++++++++++------ imgui.h | 5 ++-- imgui_demo.cpp | 4 +-- imgui_internal.h | 6 +++-- 6 files changed, 39 insertions(+), 19 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index fb921de36ddc..7ea1aec52676 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -38,15 +38,16 @@ DOCKING FEATURES - Added Docking system: [BETA] (#2109, #351) - Added ImGuiConfigFlags_DockingEnable flag to enable Docking. Set with `io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;`. - - Added DockSpace() API. + - Added DockSpace(), DockSpaceOverViewport() API. - Added ImGuiDockNodeFlags flags for DockSpace(). - - Added SetNextWindowDock(), SetNextWindowClass() API. - - Added GetWindowDockId(), IsWindowDocked() API. + - Added SetNextWindowDockID(), SetNextWindowClass() API. + - Added GetWindowDockID(), IsWindowDocked() API. - Added ImGuiWindowFlags_NoDocking window flag to disable the possibility for a window to be docked. Popup, Menu and Child windows always have the ImGuiWindowFlags_NoDocking flag set. + - Added ImGuiWindowClass to specify advanced docking/viewport related flags via SetNextWindowClass(). - Added io.ConfigDockingNoSplit option. - Added io.ConfigDockingWithShift option. - - Added io.ConfigDockingTabBarOnSingleWindows option. + - Added io.ConfigDockingAlwaysTabBar option. - Added io.ConfigDockingTransparentPayload option. - Style: Added ImGuiCol_DockingPreview, ImGuiCol_DockingEmptyBg colors. - Demo: Added "DockSpace" example app showcasing use of explicit dockspace nodes. diff --git a/examples/example_win32_directx11/main.cpp b/examples/example_win32_directx11/main.cpp index e8c1057d5f62..d765ba1b104f 100644 --- a/examples/example_win32_directx11/main.cpp +++ b/examples/example_win32_directx11/main.cpp @@ -55,7 +55,7 @@ int main(int, char**) //io.ConfigViewportsNoAutoMerge = true; //io.ConfigViewportsNoTaskBarIcon = true; //io.ConfigViewportsNoDefaultParent = true; - //io.ConfigDockingTabBarOnSingleWindows = true; + //io.ConfigDockingAlwaysTabBar = true; //io.ConfigDockingTransparentPayload = true; #if 1 io.ConfigFlags |= ImGuiConfigFlags_DpiEnableScaleFonts; // FIXME-DPI: THIS CURRENTLY DOESN'T WORK AS EXPECTED. DON'T USE IN USER APP! diff --git a/imgui.cpp b/imgui.cpp index c1d5a35115f0..97cb014c0241 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1262,7 +1262,7 @@ ImGuiIO::ImGuiIO() // Docking options (when ImGuiConfigFlags_DockingEnable is set) ConfigDockingNoSplit = false; ConfigDockingWithShift = false; - ConfigDockingTabBarOnSingleWindows = false; + ConfigDockingAlwaysTabBar = false; ConfigDockingTransparentPayload = false; // Viewport options (when ImGuiConfigFlags_ViewportsEnable is set) @@ -2731,6 +2731,7 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) SkipItems = false; Appearing = false; Hidden = false; + FallbackWindow = false; HasCloseButton = false; ResizeBorderHeld = -1; BeginCount = 0; @@ -4034,7 +4035,8 @@ void ImGui::NewFrame() // This fallback is particularly important as it avoid ImGui:: calls from crashing. SetNextWindowSize(ImVec2(400,400), ImGuiCond_FirstUseEver); Begin("Debug##Default"); - g.FrameScopePushedImplicitWindow = true; + IM_ASSERT(g.CurrentWindow->FallbackWindow == true); + g.FrameScopePushedFallbackWindow = true; #ifdef IMGUI_ENABLE_TEST_ENGINE ImGuiTestEngineHook_PostNewFrame(&g); @@ -4407,7 +4409,7 @@ void ImGui::EndFrame() } // Hide implicit/fallback "Debug" window if it hasn't been used - g.FrameScopePushedImplicitWindow = false; + g.FrameScopePushedFallbackWindow = false; if (g.CurrentWindow && !g.CurrentWindow->WriteAccessed) g.CurrentWindow->Active = false; End(); @@ -5760,7 +5762,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Find or create ImGuiWindow* window = FindWindowByName(name); const bool window_just_created = (window == NULL); - const bool window_is_fallback = (g.CurrentWindowStack.Size == 0); if (window_just_created) { ImVec2 size_on_first_use = (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize) ? g.NextWindowData.SizeVal : ImVec2(0.0f, 0.0f); // Any condition flag will do since we are creating a new window here. @@ -5776,6 +5777,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) const int current_frame = g.FrameCount; const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame); + window->FallbackWindow = (g.CurrentWindowStack.Size == 0); // Update the Appearing flag bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on @@ -5812,7 +5814,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if (first_begin_of_the_frame) { bool has_dock_node = (window->DockId != 0 || window->DockNode != NULL); - bool new_auto_dock_node = !has_dock_node && g.IO.ConfigDockingTabBarOnSingleWindows && !(flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoDocking)) && !window_is_fallback; + bool new_auto_dock_node = !has_dock_node && GetWindowAlwaysWantOwnTabBar(window); if (has_dock_node || new_auto_dock_node) { BeginDocked(window, p_open); @@ -6556,7 +6558,7 @@ void ImGui::End() { ImGuiContext& g = *GImGui; - if (g.CurrentWindowStack.Size <= 1 && g.FrameScopePushedImplicitWindow) + if (g.CurrentWindowStack.Size <= 1 && g.FrameScopePushedFallbackWindow) { IM_ASSERT(g.CurrentWindowStack.Size > 1 && "Calling End() too many times!"); return; // FIXME-ERRORHANDLING @@ -12026,7 +12028,11 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) DockNodeRemoveTabBar(node); // Early out for hidden root dock nodes (when all DockId references are in inactive windows, or there is only 1 floating window holding on the DockId) - if (node->Windows.Size <= 1 && node->IsFloatingNode() && node->IsLeafNode() && !g.IO.ConfigDockingTabBarOnSingleWindows) + bool want_to_hide_host_window = false; + if (node->Windows.Size <= 1 && node->IsFloatingNode() && node->IsLeafNode()) + if (!g.IO.ConfigDockingAlwaysTabBar && (node->Windows.Size == 0 || !node->Windows[0]->WindowClass.DockingAlwaysTabBar)) + want_to_hide_host_window = true; + if (want_to_hide_host_window) { if (node->Windows.Size == 1) { @@ -13749,12 +13755,22 @@ void ImGui::DockBuilderFinish(ImGuiID root_id) // Docking: Begin/End Functions (called from Begin/End) //----------------------------------------------------------------------------- +bool ImGui::GetWindowAlwaysWantOwnTabBar(ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; + if (g.IO.ConfigDockingAlwaysTabBar || window->WindowClass.DockingAlwaysTabBar) + if ((window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoDocking)) == 0) + if (!window->FallbackWindow) // We don't support AlwaysTabBar on the fallback/implicit window to avoid unused dock-node overhead/noise + return true; + return false; +} + void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) { ImGuiContext* ctx = GImGui; ImGuiContext& g = *ctx; - const bool auto_dock_node = (g.IO.ConfigDockingTabBarOnSingleWindows) && !(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoDocking)); + const bool auto_dock_node = GetWindowAlwaysWantOwnTabBar(window); if (auto_dock_node) { if (window->DockId == 0) diff --git a/imgui.h b/imgui.h index 6d488ff3ba54..ca623aeb5671 100644 --- a/imgui.h +++ b/imgui.h @@ -1425,7 +1425,7 @@ struct ImGuiIO // Docking options (when ImGuiConfigFlags_DockingEnable is set) bool ConfigDockingNoSplit; // = false // Simplified docking mode: disable window splitting, so docking is limited to merging multiple windows together into tab-bars. bool ConfigDockingWithShift; // = false // Enable docking with holding Shift key (reduce visual noise, allows dropping in wider space) - bool ConfigDockingTabBarOnSingleWindows; // = false // [BETA] Make every single floating window display within a docking node. + bool ConfigDockingAlwaysTabBar; // = false // [BETA] [FIXME: This currently creates regression with auto-sizing and general overhead] Make every single floating window display within a docking node. bool ConfigDockingTransparentPayload;// = false // [BETA] Make window or viewport transparent when docking and only display docking boxes on the target viewport. Useful if rendering of multiple viewport cannot be synced. Best used with ConfigViewportsNoAutoMerge. // Viewport options (when ImGuiConfigFlags_ViewportsEnable is set) @@ -1612,9 +1612,10 @@ struct ImGuiWindowClass ImGuiID ParentViewportId; // Hint for the platform back-end. If non-zero, the platform back-end can create a parent<>child relationship between the platform windows. Not conforming back-ends are free to e.g. parent every viewport to the main viewport or not. ImGuiViewportFlags ViewportFlagsOverrideMask; // Viewport flags to override when a window of this class owns a viewport. This allows you to enforce OS decoration or task bar icon, override the defaults on a per-window basis. ImGuiViewportFlags ViewportFlagsOverrideValue; // Viewport flags values to override when a window of this class owns a viewport. + bool DockingAlwaysTabBar; // Set to true to enforce windows of this class always having their own tab (equivalent of setting the global io.ConfigDockingAlwaysTabBar) bool DockingAllowUnclassed; // Set to true to allow windows of this class to be docked/merged with an unclassed window. - ImGuiWindowClass() { ClassId = 0; ParentViewportId = 0; ViewportFlagsOverrideMask = ViewportFlagsOverrideValue = 0x00; DockingAllowUnclassed = true; } + ImGuiWindowClass() { ClassId = 0; ParentViewportId = 0; ViewportFlagsOverrideMask = ViewportFlagsOverrideValue = 0x00; DockingAlwaysTabBar = false; DockingAllowUnclassed = true; } }; //----------------------------------------------------------------------------- diff --git a/imgui_demo.cpp b/imgui_demo.cpp index dd25873bd6da..bb09d02de5fb 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -368,7 +368,7 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::SameLine(); HelpMarker("Simplified docking mode: disable window splitting, so docking is limited to merging multiple windows together into tab-bars."); ImGui::Checkbox("io.ConfigDockingWithShift", &io.ConfigDockingWithShift); ImGui::SameLine(); HelpMarker("Enable docking when holding Shift only (allows to drop in wider space, reduce visual noise)"); - ImGui::Checkbox("io.ConfigDockingTabBarOnSingleWindows", &io.ConfigDockingTabBarOnSingleWindows); + ImGui::Checkbox("io.ConfigDockingAlwaysTabBar", &io.ConfigDockingAlwaysTabBar); ImGui::SameLine(); HelpMarker("Create a docking node and tab-bar on single floating windows."); ImGui::Checkbox("io.ConfigDockingTransparentPayload", &io.ConfigDockingTransparentPayload); ImGui::SameLine(); HelpMarker("Make window or viewport transparent when docking and only display docking boxes on the target viewport. Useful if rendering of multiple viewport cannot be synced. Best used with ConfigViewportsNoAutoMerge."); @@ -3079,7 +3079,7 @@ void ImGui::ShowAboutWindow(bool* p_open) if (io.ConfigViewportsNoDefaultParent) ImGui::Text("io.ConfigViewportsNoDefaultParent"); if (io.ConfigDockingNoSplit) ImGui::Text("io.ConfigDockingNoSplit"); if (io.ConfigDockingWithShift) ImGui::Text("io.ConfigDockingWithShift"); - if (io.ConfigDockingTabBarOnSingleWindows) ImGui::Text("io.ConfigDockingTabBarOnSingleWindows"); + if (io.ConfigDockingAlwaysTabBar) ImGui::Text("io.ConfigDockingAlwaysTabBar"); if (io.ConfigDockingTransparentPayload) ImGui::Text("io.ConfigDockingTransparentPayload"); if (io.ConfigMacOSXBehaviors) ImGui::Text("io.ConfigMacOSXBehaviors"); if (io.ConfigInputTextCursorBlink) ImGui::Text("io.ConfigInputTextCursorBlink"); diff --git a/imgui_internal.h b/imgui_internal.h index bd1f9aadf9c9..3b3d23de46fd 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -982,7 +982,7 @@ struct ImGuiContext { bool Initialized; bool FrameScopeActive; // Set by NewFrame(), cleared by EndFrame() - bool FrameScopePushedImplicitWindow; // Set by NewFrame(), cleared by EndFrame() + bool FrameScopePushedFallbackWindow; // Set by NewFrame(), cleared by EndFrame() bool FontAtlasOwnedByContext; // Io.Fonts-> is owned by the ImGuiContext and will be destructed along with it. ImGuiIO IO; ImGuiPlatformIO PlatformIO; @@ -1195,7 +1195,7 @@ struct ImGuiContext ImGuiContext(ImFontAtlas* shared_font_atlas) { Initialized = false; - FrameScopeActive = FrameScopePushedImplicitWindow = false; + FrameScopeActive = FrameScopePushedFallbackWindow = false; ConfigFlagsForFrame = ImGuiConfigFlags_None; Font = NULL; FontSize = FontBaseSize = 0.0f; @@ -1449,6 +1449,7 @@ struct IMGUI_API ImGuiWindow bool SkipItems; // Set when items can safely be all clipped (e.g. window not visible or collapsed) bool Appearing; // Set during the frame where the window is appearing (or re-appearing) bool Hidden; // Do not display (== (HiddenFrames*** > 0)) + bool FallbackWindow; bool HasCloseButton; // Set when the window has a close button (p_open != NULL) signed char ResizeBorderHeld; // Current border being held for resize (-1: none, otherwise 0-3) short BeginCount; // Number of Begin() during the current frame (generally 0 or 1, 1+ if appending via multiple Begin/End pairs) @@ -1758,6 +1759,7 @@ namespace ImGui IMGUI_API void DockContextQueueUndockNode(ImGuiContext* ctx, ImGuiDockNode* node); IMGUI_API bool DockContextCalcDropPosForDocking(ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, bool split_outer, ImVec2* out_pos); inline ImGuiDockNode* DockNodeGetRootNode(ImGuiDockNode* node) { while (node->ParentNode) node = node->ParentNode; return node; } + IMGUI_API bool GetWindowAlwaysWantOwnTabBar(ImGuiWindow* window); IMGUI_API void BeginDocked(ImGuiWindow* window, bool* p_open); IMGUI_API void BeginAsDockableDragDropSource(ImGuiWindow* window); IMGUI_API void BeginAsDockableDragDropTarget(ImGuiWindow* window); From 75136d3bea66ee8621e3548ccde055de7f4778a4 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 22 Jul 2019 11:43:17 -0700 Subject: [PATCH 525/828] Internals: Removed ShowDockingDemo(), moved into Metrics. Metrics: Added more links to browse window->node, node->window, node->node etc. --- imgui.cpp | 314 ++++++++++++++++++++++++----------------------- imgui_internal.h | 1 - 2 files changed, 158 insertions(+), 157 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 97cb014c0241..7ff5295c6e51 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -14172,11 +14172,11 @@ static void ImGui::DockSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettings // [DEBUG] Include comments in the .ini file to ease debugging if (ImGuiDockNode* node = DockContextFindNodeByID(ctx, node_settings->ID)) { - buf->appendf("%*s", ImMax(2, (line_start_pos + 92) - buf->size()), ""); // Align everything + buf->appendf("%*s", ImMax(2, (line_start_pos + 92) - buf->size()), ""); // Align everything if (node->IsDockSpace() && node->HostWindow && node->HostWindow->ParentWindow) buf->appendf(" ; in '%s'", node->HostWindow->ParentWindow->Name); int contains_window = 0; - for (int window_n = 0; window_n < ctx->SettingsWindows.Size; window_n++) + for (int window_n = 0; window_n < ctx->SettingsWindows.Size; window_n++) // Iterate settings so we can give info about windows that didn't exist during the session. if (ctx->SettingsWindows[window_n].DockId == node_settings->ID) { if (contains_window++ == 0) @@ -14411,6 +14411,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) static bool show_windows_rects = false; static int show_windows_rect_type = WRT_WorkRect; static bool show_drawcmd_clip_rects = true; + static bool show_window_dock_info = false; ImGuiIO& io = ImGui::GetIO(); ImGui::Text("Dear ImGui %s", ImGui::GetVersion()); @@ -14420,6 +14421,14 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::Text("%d active allocations", io.MetricsActiveAllocations); ImGui::Separator(); + // Helper functions to display common structures: + // - NodeDrawList + // - NodeColumns + // - NodeWindow + // - NodeWindows + // - NodeViewport + // - NodeDockNode + // - NodeTabBar struct Funcs { static ImRect GetWindowRect(ImGuiWindow* window, int rect_type) @@ -14519,15 +14528,6 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::TreePop(); } - static void NodeWindows(ImVector& windows, const char* label) - { - if (!ImGui::TreeNode(label, "%s (%d)", label, windows.Size)) - return; - for (int i = 0; i < windows.Size; i++) - Funcs::NodeWindow(windows[i], "Window"); - ImGui::TreePop(); - } - static void NodeWindow(ImGuiWindow* window, const char* label) { if (!ImGui::TreeNode(window, "%s '%s', %d @ 0x%p", label, window->Name, window->Active || window->WasActive, window)) @@ -14550,7 +14550,9 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::BulletText("NavRectRel[0]: "); ImGui::BulletText("Viewport: %d%s, ViewportId: 0x%08X, ViewportPos: (%.1f,%.1f)", window->Viewport ? window->Viewport->Idx : -1, window->ViewportOwned ? " (Owned)" : "", window->ViewportId, window->ViewportPos.x, window->ViewportPos.y); ImGui::BulletText("ViewportMonitor: %d", window->Viewport ? window->Viewport->PlatformMonitor : -1); - ImGui::BulletText("DockId: 0x%04X, DockOrder: %d, %s: 0x%p, Act: %d, Vis: %d", window->DockId, window->DockOrder, window->DockNodeAsHost ? "DockNodeAsHost" : "DockNode", window->DockNodeAsHost ? window->DockNodeAsHost : window->DockNode, window->DockIsActive, window->DockTabIsVisible); + ImGui::BulletText("DockId: 0x%04X, DockOrder: %d, Act: %d, Vis: %d", window->DockId, window->DockOrder, window->DockIsActive, window->DockTabIsVisible); + if (window->DockNode || window->DockNodeAsHost) + NodeDockNode(window->DockNodeAsHost ? window->DockNodeAsHost : window->DockNode, window->DockNodeAsHost ? "DockNodeAsHost" : "DockNode"); if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow"); if (window->RootWindowDockStop != window->RootWindow) NodeWindow(window->RootWindowDockStop, "RootWindowDockStop"); if (window->ParentWindow != NULL) NodeWindow(window->ParentWindow, "ParentWindow"); @@ -14565,6 +14567,15 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::TreePop(); } + static void NodeWindows(ImVector& windows, const char* label) + { + if (!ImGui::TreeNode(label, "%s (%d)", label, windows.Size)) + return; + for (int i = 0; i < windows.Size; i++) + Funcs::NodeWindow(windows[i], "Window"); + ImGui::TreePop(); + } + static void NodeViewport(ImGuiViewportP* viewport) { ImGui::SetNextItemOpen(true, ImGuiCond_Once); @@ -14584,6 +14595,75 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::TreePop(); } } + + static void NodeDockNode(ImGuiDockNode* node, const char* label) + { + ImGuiContext& g = *GImGui; + bool open; + if (node->Windows.Size > 0) + open = ImGui::TreeNode((void*)(intptr_t)node->ID, "%s 0x%04X%s: %d windows (vis: '%s')", label, node->ID, node->IsVisible ? "" : " (hidden)", node->Windows.Size, node->VisibleWindow ? node->VisibleWindow->Name : "NULL"); + else + open = ImGui::TreeNode((void*)(intptr_t)node->ID, "%s 0x%04X%s: %s split (vis: '%s')", label, node->ID, node->IsVisible ? "" : " (hidden)", (node->SplitAxis == ImGuiAxis_X) ? "horizontal" : (node->SplitAxis == ImGuiAxis_Y) ? "vertical" : "n/a", node->VisibleWindow ? node->VisibleWindow->Name : "NULL"); + if (open) + { + IM_ASSERT(node->ChildNodes[0] == NULL || node->ChildNodes[0]->ParentNode == node); + IM_ASSERT(node->ChildNodes[1] == NULL || node->ChildNodes[1]->ParentNode == node); + ImGui::BulletText("Pos (%.0f,%.0f), Size (%.0f, %.0f) Ref (%.0f, %.0f)", + node->Pos.x, node->Pos.y, node->Size.x, node->Size.y, node->SizeRef.x, node->SizeRef.y); + NodeWindow(node->VisibleWindow, "VisibleWindow"); + ImGui::BulletText("SelectedTabID: 0x%08X, LastFocusedNodeID: 0x%08X", node->SelectedTabID, node->LastFocusedNodeID); + ImGui::BulletText("Misc:%s%s%s%s", node->IsDockSpace() ? " IsDockSpace" : "", node->IsCentralNode() ? " IsCentralNode" : "", (g.FrameCount - node->LastFrameAlive < 2) ? " IsAlive" : "", (g.FrameCount - node->LastFrameActive < 2) ? " IsActive" : ""); + if (ImGui::TreeNode("flags", "LocalFlags: 0x%04X SharedFlags: 0x%04X", node->LocalFlags, node->SharedFlags)) + { + ImGui::CheckboxFlags("LocalFlags: NoSplit", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_NoSplit); + ImGui::CheckboxFlags("LocalFlags: NoResize", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_NoResize); + ImGui::CheckboxFlags("LocalFlags: NoTabBar", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_NoTabBar); + ImGui::CheckboxFlags("LocalFlags: HiddenTabBar", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_HiddenTabBar); + ImGui::CheckboxFlags("LocalFlags: NoWindowMenuButton", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_NoWindowMenuButton); + ImGui::CheckboxFlags("LocalFlags: NoCloseButton", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_NoCloseButton); + ImGui::TreePop(); + } + if (node->ParentNode) + NodeDockNode(node->ParentNode, "ParentNode"); + if (node->ChildNodes[0]) + NodeDockNode(node->ChildNodes[0], "Child[0]"); + if (node->ChildNodes[1]) + NodeDockNode(node->ChildNodes[1], "Child[1]"); + if (node->TabBar) + NodeTabBar(node->TabBar); + ImGui::TreePop(); + } + } + + static void NodeTabBar(ImGuiTabBar* tab_bar) + { + // Standalone tab bars (not associated to docking/windows functionality) currently hold no discernible strings. + char buf[256]; + char* p = buf; + const char* buf_end = buf + IM_ARRAYSIZE(buf); + p += ImFormatString(p, buf_end - p, "TabBar (%d tabs)%s", + tab_bar->Tabs.Size, (tab_bar->PrevFrameVisible < ImGui::GetFrameCount() - 2) ? " *Inactive*" : ""); + if (tab_bar->Flags & ImGuiTabBarFlags_DockNode) + { + p += ImFormatString(p, buf_end - p, " { "); + for (int tab_n = 0; tab_n < ImMin(tab_bar->Tabs.Size, 3); tab_n++) + p += ImFormatString(p, buf_end - p, "%s'%s'", tab_n > 0 ? ", " : "", tab_bar->Tabs[tab_n].Window->Name); + p += ImFormatString(p, buf_end - p, (tab_bar->Tabs.Size > 3) ? " ... }" : " } "); + } + if (ImGui::TreeNode(tab_bar, "%s", buf)) + { + for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++) + { + const ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; + ImGui::PushID(tab); + if (ImGui::SmallButton("<")) { TabBarQueueChangeTabOrder(tab_bar, tab, -1); } ImGui::SameLine(0, 2); + if (ImGui::SmallButton(">")) { TabBarQueueChangeTabOrder(tab_bar, tab, +1); } ImGui::SameLine(); + ImGui::Text("%02d%c Tab 0x%08X '%s'", tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID, tab->Window ? tab->Window->Name : "N/A"); + ImGui::PopID(); + } + ImGui::TreePop(); + } + } }; // Access private state, we are going to display the draw lists from last frame @@ -14622,9 +14702,60 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::TreePop(); } - if (ImGui::TreeNode("Docking & Tab Bars")) + if (ImGui::TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.Data.Size)) + { + for (int n = 0; n < g.TabBars.Data.Size; n++) + Funcs::NodeTabBar(g.TabBars.GetByIndex(n)); + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Docking")) { - ShowDockingDebug(); + ImGuiDockContext* dc = g.DockContext; + ImGui::Checkbox("Ctrl shows window dock info", &show_window_dock_info); + + if (ImGui::TreeNode("Dock nodes")) + { + if (ImGui::SmallButton("Clear settings")) { DockContextClearNodes(&g, 0, true); } + ImGui::SameLine(); + if (ImGui::SmallButton("Rebuild all")) { dc->WantFullRebuild = true; } + for (int n = 0; n < dc->Nodes.Data.Size; n++) + if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p) + if (node->IsRootNode()) + Funcs::NodeDockNode(node, "Node"); + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Settings")) + { + if (ImGui::SmallButton("Refresh")) + SaveIniSettingsToMemory(); + ImGui::SameLine(); + if (ImGui::SmallButton("Save to disk")) + SaveIniSettingsToDisk(g.IO.IniFilename); + ImGui::Separator(); + ImGui::Text("Docked Windows:"); + for (int n = 0; n < g.SettingsWindows.Size; n++) + if (g.SettingsWindows[n].DockId != 0) + ImGui::BulletText("Window '%s' -> DockId %08X", g.SettingsWindows[n].Name, g.SettingsWindows[n].DockId); + ImGui::Separator(); + ImGui::Text("Dock Nodes:"); + for (int n = 0; n < dc->SettingsNodes.Size; n++) + { + ImGuiDockNodeSettings* settings = &dc->SettingsNodes[n]; + const char* selected_tab_name = NULL; + if (settings->SelectedTabID) + { + if (ImGuiWindow* window = FindWindowByID(settings->SelectedTabID)) + selected_tab_name = window->Name; + else if (ImGuiWindowSettings* window_settings = FindWindowSettings(settings->SelectedTabID)) + selected_tab_name = window_settings->Name; + } + ImGui::BulletText("Node %08X, Parent %08X, SelectedTab %08X ('%s')", settings->ID, settings->ParentID, settings->SelectedTabID, selected_tab_name ? selected_tab_name : settings->SelectedTabID ? "N/A" : ""); + } + ImGui::TreePop(); + } + ImGui::TreePop(); } @@ -14699,148 +14830,11 @@ void ImGui::ShowMetricsWindow(bool* p_open) } } } - ImGui::End(); -} - -#else - -void ImGui::ShowMetricsWindow(bool*) { } - -#endif - -void ImGui::ShowDockingDebug() -{ - ImGuiContext* ctx = GImGui; - ImGuiContext& g = *ctx; - ImGuiDockContext* dc = ctx->DockContext; - - struct Funcs - { - static void NodeDockNode(ImGuiDockNode* node, const char* label) - { - ImGuiContext& g = *GImGui; - ImGui::SetNextItemOpen(true, ImGuiCond_Once); - bool open; - if (node->Windows.Size > 0) - open = ImGui::TreeNode((void*)(intptr_t)node->ID, "%s 0x%04X%s: %d windows (vis: '%s')", label, node->ID, node->IsVisible ? "" : " (hidden)", node->Windows.Size, node->VisibleWindow ? node->VisibleWindow->Name : "NULL"); - else - open = ImGui::TreeNode((void*)(intptr_t)node->ID, "%s 0x%04X%s: %s split (vis: '%s')", label, node->ID, node->IsVisible ? "" : " (hidden)", (node->SplitAxis == ImGuiAxis_X) ? "horizontal" : (node->SplitAxis == ImGuiAxis_Y) ? "vertical" : "n/a", node->VisibleWindow ? node->VisibleWindow->Name : "NULL"); - if (open) - { - IM_ASSERT(node->ChildNodes[0] == NULL || node->ChildNodes[0]->ParentNode == node); - IM_ASSERT(node->ChildNodes[1] == NULL || node->ChildNodes[1]->ParentNode == node); - ImGui::BulletText("Pos (%.0f,%.0f), Size (%.0f, %.0f) Ref (%.0f, %.0f)", - node->Pos.x, node->Pos.y, node->Size.x, node->Size.y, node->SizeRef.x, node->SizeRef.y); - ImGui::BulletText("VisibleWindow: 0x%08X %s", node->VisibleWindow ? node->VisibleWindow->ID : 0, node->VisibleWindow ? node->VisibleWindow->Name : "NULL"); - ImGui::BulletText("SelectedTabID: 0x%08X, LastFocusedNodeID: 0x%08X", node->SelectedTabID, node->LastFocusedNodeID); - ImGui::BulletText("Misc:%s%s%s%s", node->IsDockSpace() ? " IsDockSpace" : "", node->IsCentralNode() ? " IsCentralNode" : "", (g.FrameCount - node->LastFrameAlive < 2) ? " IsAlive" : "", (g.FrameCount - node->LastFrameActive < 2) ? " IsActive" : ""); - if (ImGui::TreeNode("flags", "LocalFlags: 0x%04X SharedFlags: 0x%04X", node->LocalFlags, node->SharedFlags)) - { - ImGui::CheckboxFlags("LocalFlags: NoSplit", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_NoSplit); - ImGui::CheckboxFlags("LocalFlags: NoResize", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_NoResize); - ImGui::CheckboxFlags("LocalFlags: NoTabBar", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_NoTabBar); - ImGui::CheckboxFlags("LocalFlags: HiddenTabBar", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_HiddenTabBar); - ImGui::CheckboxFlags("LocalFlags: NoWindowMenuButton", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_NoWindowMenuButton); - ImGui::CheckboxFlags("LocalFlags: NoCloseButton", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_NoCloseButton); - ImGui::TreePop(); - } - if (node->ChildNodes[0]) - NodeDockNode(node->ChildNodes[0], "Child[0]"); - if (node->ChildNodes[1]) - NodeDockNode(node->ChildNodes[1], "Child[1]"); - if (node->TabBar) - NodeTabBar(node->TabBar); - ImGui::TreePop(); - } - } - - static void NodeTabBar(ImGuiTabBar* tab_bar) - { - // Standalone tab bars (not associated to docking/windows functionality) currently hold no discernible strings. - char buf[256]; - char* p = buf; - const char* buf_end = buf + IM_ARRAYSIZE(buf); - p += ImFormatString(p, buf_end - p, "TabBar (%d tabs)%s", - tab_bar->Tabs.Size, (tab_bar->PrevFrameVisible < ImGui::GetFrameCount() - 2) ? " *Inactive*" : ""); - if (tab_bar->Flags & ImGuiTabBarFlags_DockNode) - { - p += ImFormatString(p, buf_end - p, " { "); - for (int tab_n = 0; tab_n < ImMin(tab_bar->Tabs.Size, 3); tab_n++) - p += ImFormatString(p, buf_end - p, "%s'%s'", tab_n > 0 ? ", " : "", tab_bar->Tabs[tab_n].Window->Name); - p += ImFormatString(p, buf_end - p, (tab_bar->Tabs.Size > 3) ? " ... }" : " } "); - } - if (ImGui::TreeNode(tab_bar, "%s", buf)) - { - for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++) - { - const ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; - ImGui::PushID(tab); - if (ImGui::SmallButton("<")) { TabBarQueueChangeTabOrder(tab_bar, tab, -1); } ImGui::SameLine(0, 2); - if (ImGui::SmallButton(">")) { TabBarQueueChangeTabOrder(tab_bar, tab, +1); } ImGui::SameLine(); - ImGui::Text("%02d%c Tab 0x%08X '%s'", tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID, tab->Window ? tab->Window->Name : "N/A"); - ImGui::PopID(); - } - ImGui::TreePop(); - } - } - }; - - static bool show_window_dock_info = false; - ImGui::Checkbox("Ctrl shows window dock info", &show_window_dock_info); - - if (ImGui::TreeNode("Dock nodes")) - { - if (ImGui::SmallButton("Clear settings")) { DockContextClearNodes(&g, 0, true); } - ImGui::SameLine(); - if (ImGui::SmallButton("Rebuild all")) { dc->WantFullRebuild = true; } - for (int n = 0; n < dc->Nodes.Data.Size; n++) - if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p) - if (node->IsRootNode()) - Funcs::NodeDockNode(node, "Node"); - ImGui::TreePop(); - } - - if (ImGui::TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.Data.Size)) - { - for (int n = 0; n < g.TabBars.Data.Size; n++) - Funcs::NodeTabBar(g.TabBars.GetByIndex(n)); - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Settings")) - { - if (ImGui::SmallButton("Refresh")) - SaveIniSettingsToMemory(); - ImGui::SameLine(); - if (ImGui::SmallButton("Save to disk")) - SaveIniSettingsToDisk(g.IO.IniFilename); - ImGui::Separator(); - ImGui::Text("Docked Windows:"); - for (int n = 0; n < g.SettingsWindows.Size; n++) - if (g.SettingsWindows[n].DockId != 0) - ImGui::BulletText("Window '%s' -> DockId %08X", g.SettingsWindows[n].Name, g.SettingsWindows[n].DockId); - ImGui::Separator(); - ImGui::Text("Dock Nodes:"); - for (int n = 0; n < dc->SettingsNodes.Size; n++) - { - ImGuiDockNodeSettings* settings = &dc->SettingsNodes[n]; - const char* selected_tab_name = NULL; - if (settings->SelectedTabID) - { - if (ImGuiWindow* window = FindWindowByID(settings->SelectedTabID)) - selected_tab_name = window->Name; - else if (ImGuiWindowSettings* window_settings = FindWindowSettings(settings->SelectedTabID)) - selected_tab_name = window_settings->Name; - } - ImGui::BulletText("Node %08X, Parent %08X, SelectedTab %08X ('%s')", settings->ID, settings->ParentID, settings->SelectedTabID, selected_tab_name ? selected_tab_name : settings->SelectedTabID ? "N/A" : ""); - } - ImGui::TreePop(); - } - if (g.IO.KeyCtrl && show_window_dock_info) + if (show_window_dock_info && g.IO.KeyCtrl) { - for (int n = 0; n < dc->Nodes.Data.Size; n++) - if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p) + for (int n = 0; n < g.DockContext->Nodes.Data.Size; n++) + if (ImGuiDockNode* node = (ImGuiDockNode*)g.DockContext->Nodes.Data[n].val_p) { ImGuiDockNode* root_node = DockNodeGetRootNode(node); if (ImGuiDockNode* hovered_node = DockNodeTreeFindNodeByPos(root_node, g.IO.MousePos)) @@ -14853,14 +14847,22 @@ void ImGui::ShowDockingDebug() p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "Size: (%.0f, %.0f)\n", node->Size.x, node->Size.y); p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "SizeRef: (%.0f, %.0f)\n", node->SizeRef.x, node->SizeRef.y); int depth = DockNodeGetDepth(node); - overlay_draw_list->AddRect(node->Pos + ImVec2(3,3) * (float)depth, node->Pos + node->Size - ImVec2(3,3) * (float)depth, IM_COL32(200, 100, 100, 255)); - ImVec2 pos = node->Pos + ImVec2(3,3) * (float)depth; + overlay_draw_list->AddRect(node->Pos + ImVec2(3, 3) * (float)depth, node->Pos + node->Size - ImVec2(3, 3) * (float)depth, IM_COL32(200, 100, 100, 255)); + ImVec2 pos = node->Pos + ImVec2(3, 3) * (float)depth; overlay_draw_list->AddRectFilled(pos - ImVec2(1, 1), pos + CalcTextSize(buf) + ImVec2(1, 1), IM_COL32(200, 100, 100, 255)); overlay_draw_list->AddText(NULL, 0.0f, pos, IM_COL32(255, 255, 255, 255), buf); } } + + ImGui::End(); } +#else + +void ImGui::ShowMetricsWindow(bool*) { } + +#endif + //----------------------------------------------------------------------------- // Include imgui_user.inl at the end of imgui.cpp to access private data/functions that aren't exposed. diff --git a/imgui_internal.h b/imgui_internal.h index 3b3d23de46fd..621bdd11c30b 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1764,7 +1764,6 @@ namespace ImGui IMGUI_API void BeginAsDockableDragDropSource(ImGuiWindow* window); IMGUI_API void BeginAsDockableDragDropTarget(ImGuiWindow* window); IMGUI_API void SetWindowDock(ImGuiWindow* window, ImGuiID dock_id, ImGuiCond cond); - IMGUI_API void ShowDockingDebug(); // Docking - Builder function needs to be generally called before the DockSpace() node is submitted. IMGUI_API void DockBuilderDockWindow(const char* window_name, ImGuiID node_id); From 81b24bd728a25753fc31c8a0cfe9e94bd50508f8 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 23 Jul 2019 13:37:52 -0700 Subject: [PATCH 526/828] Docking: Moving types in imgui.h --- imgui.h | 60 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/imgui.h b/imgui.h index ea15b7654195..bc155d5de765 100644 --- a/imgui.h +++ b/imgui.h @@ -17,7 +17,7 @@ Index of this file: // ImVector<> // ImGuiStyle // ImGuiIO -// Misc data structures (ImGuiInputTextCallbackData, ImGuiSizeCallbackData, ImGuiPayload, ImGuiWindowClass) +// Misc data structures (ImGuiInputTextCallbackData, ImGuiSizeCallbackData, ImGuiWindowClass, ImGuiPayload) // Obsolete functions // Helpers (ImGuiOnceUponAFrame, ImGuiTextFilter, ImGuiTextBuffer, ImGuiStorage, ImGuiListClipper, ImColor) // Draw List API (ImDrawCallback, ImDrawCmd, ImDrawIdx, ImDrawVert, ImDrawChannel, ImDrawListSplitter, ImDrawListFlags, ImDrawList, ImDrawData) @@ -887,21 +887,6 @@ enum ImGuiTabItemFlags_ ImGuiTabItemFlags_NoPushId = 1 << 3 // Don't call PushID(tab->ID)/PopID() on BeginTabItem()/EndTabItem() }; -// Flags for ImGui::DockSpace(), shared/inherited by child nodes. -// (Some flags can be applied to individual nodes directly) -// FIXME-DOCK: Also see ImGuiDockNodeFlagsPrivate_ which may involve using the WIP and internal DockBuilder api. -enum ImGuiDockNodeFlags_ -{ - ImGuiDockNodeFlags_None = 0, - ImGuiDockNodeFlags_KeepAliveOnly = 1 << 0, // Shared // Don't display the dockspace node but keep it alive. Windows docked into this dockspace node won't be undocked. - //ImGuiDockNodeFlags_NoCentralNode = 1 << 1, // Shared // Disable Central Node (the node which can stay empty) - ImGuiDockNodeFlags_NoDockingInCentralNode = 1 << 2, // Shared // Disable docking inside the Central Node, which will be always kept empty. - ImGuiDockNodeFlags_PassthruCentralNode = 1 << 3, // Shared // Enable passthru dockspace: 1) DockSpace() will render a ImGuiCol_WindowBg background covering everything excepted the Central Node when empty. Meaning the host window should probably use SetNextWindowBgAlpha(0.0f) prior to Begin() when using this. 2) When Central Node is empty: let inputs pass-through + won't display a DockingEmptyBg background. See demo for details. - ImGuiDockNodeFlags_NoSplit = 1 << 4, // Shared/Local // Disable splitting the node into smaller nodes. Useful e.g. when embedding dockspaces into a main root one (the root one may have splitting disabled to reduce confusion). Note: when turned off, existing splits will be preserved. - ImGuiDockNodeFlags_NoResize = 1 << 5, // Shared/Local // Disable resizing node using the splitter/separators. Useful with programatically setup dockspaces. - ImGuiDockNodeFlags_AutoHideTabBar = 1 << 6 // Shared/Local // Tab bar will automatically hide when there is a single window in the dock node. -}; - // Flags for ImGui::IsWindowFocused() enum ImGuiFocusedFlags_ { @@ -930,6 +915,21 @@ enum ImGuiHoveredFlags_ ImGuiHoveredFlags_RootAndChildWindows = ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows }; +// Flags for ImGui::DockSpace(), shared/inherited by child nodes. +// (Some flags can be applied to individual nodes directly) +// FIXME-DOCK: Also see ImGuiDockNodeFlagsPrivate_ which may involve using the WIP and internal DockBuilder api. +enum ImGuiDockNodeFlags_ +{ + ImGuiDockNodeFlags_None = 0, + ImGuiDockNodeFlags_KeepAliveOnly = 1 << 0, // Shared // Don't display the dockspace node but keep it alive. Windows docked into this dockspace node won't be undocked. + //ImGuiDockNodeFlags_NoCentralNode = 1 << 1, // Shared // Disable Central Node (the node which can stay empty) + ImGuiDockNodeFlags_NoDockingInCentralNode = 1 << 2, // Shared // Disable docking inside the Central Node, which will be always kept empty. + ImGuiDockNodeFlags_PassthruCentralNode = 1 << 3, // Shared // Enable passthru dockspace: 1) DockSpace() will render a ImGuiCol_WindowBg background covering everything excepted the Central Node when empty. Meaning the host window should probably use SetNextWindowBgAlpha(0.0f) prior to Begin() when using this. 2) When Central Node is empty: let inputs pass-through + won't display a DockingEmptyBg background. See demo for details. + ImGuiDockNodeFlags_NoSplit = 1 << 4, // Shared/Local // Disable splitting the node into smaller nodes. Useful e.g. when embedding dockspaces into a main root one (the root one may have splitting disabled to reduce confusion). Note: when turned off, existing splits will be preserved. + ImGuiDockNodeFlags_NoResize = 1 << 5, // Shared/Local // Disable resizing node using the splitter/separators. Useful with programatically setup dockspaces. + ImGuiDockNodeFlags_AutoHideTabBar = 1 << 6 // Shared/Local // Tab bar will automatically hide when there is a single window in the dock node. +}; + // Flags for ImGui::BeginDragDropSource(), ImGui::AcceptDragDropPayload() enum ImGuiDragDropFlags_ { @@ -1582,6 +1582,20 @@ struct ImGuiSizeCallbackData ImVec2 DesiredSize; // Read-write. Desired size, based on user's mouse position. Write to this field to restrain resizing. }; +// [BETA] Rarely used / very advanced uses only. Use with SetNextWindowClass() and DockSpace() functions. +// Provide hints to the platform back-end via altered viewport flags (enable/disable OS decoration, OS task bar icons, etc.) and OS level parent/child relationships. +struct ImGuiWindowClass +{ + ImGuiID ClassId; // User data. 0 = Default class (unclassed) + ImGuiID ParentViewportId; // Hint for the platform back-end. If non-zero, the platform back-end can create a parent<>child relationship between the platform windows. Not conforming back-ends are free to e.g. parent every viewport to the main viewport or not. + ImGuiViewportFlags ViewportFlagsOverrideMask; // Viewport flags to override when a window of this class owns a viewport. This allows you to enforce OS decoration or task bar icon, override the defaults on a per-window basis. + ImGuiViewportFlags ViewportFlagsOverrideValue; // Viewport flags values to override when a window of this class owns a viewport. + bool DockingAlwaysTabBar; // Set to true to enforce windows of this class always having their own tab (equivalent of setting the global io.ConfigDockingAlwaysTabBar) + bool DockingAllowUnclassed; // Set to true to allow windows of this class to be docked/merged with an unclassed window. + + ImGuiWindowClass() { ClassId = 0; ParentViewportId = 0; ViewportFlagsOverrideMask = ViewportFlagsOverrideValue = 0x00; DockingAlwaysTabBar = false; DockingAllowUnclassed = true; } +}; + // Data payload for Drag and Drop operations: AcceptDragDropPayload(), GetDragDropPayload() struct ImGuiPayload { @@ -1604,20 +1618,6 @@ struct ImGuiPayload bool IsDelivery() const { return Delivery; } }; -// [BETA] Rarely used / very advanced uses only. Use with SetNextWindowClass() and DockSpace() functions. -// Provide hints to the platform back-end via altered viewport flags (enable/disable OS decoration, OS task bar icons, etc.) and OS level parent/child relationships. -struct ImGuiWindowClass -{ - ImGuiID ClassId; // User data. 0 = Default class (unclassed) - ImGuiID ParentViewportId; // Hint for the platform back-end. If non-zero, the platform back-end can create a parent<>child relationship between the platform windows. Not conforming back-ends are free to e.g. parent every viewport to the main viewport or not. - ImGuiViewportFlags ViewportFlagsOverrideMask; // Viewport flags to override when a window of this class owns a viewport. This allows you to enforce OS decoration or task bar icon, override the defaults on a per-window basis. - ImGuiViewportFlags ViewportFlagsOverrideValue; // Viewport flags values to override when a window of this class owns a viewport. - bool DockingAlwaysTabBar; // Set to true to enforce windows of this class always having their own tab (equivalent of setting the global io.ConfigDockingAlwaysTabBar) - bool DockingAllowUnclassed; // Set to true to allow windows of this class to be docked/merged with an unclassed window. - - ImGuiWindowClass() { ClassId = 0; ParentViewportId = 0; ViewportFlagsOverrideMask = ViewportFlagsOverrideValue = 0x00; DockingAlwaysTabBar = false; DockingAllowUnclassed = true; } -}; - //----------------------------------------------------------------------------- // Obsolete functions (Will be removed! Read 'API BREAKING CHANGES' section in imgui.cpp for details) // Please keep your copy of dear imgui up to date! Occasionally set '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' in imconfig.h to stay ahead. From efc4c0fe9de2fd4c0cd6dde3b92a3a7a0d490138 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 23 Jul 2019 21:26:15 -0700 Subject: [PATCH 527/828] Internals: Made IMGUI_DEBUG_LOG redefinable in imconfig.h. Comments. Fix to allow Metrics's NodeWindow() being called with a NULL window. --- imgui.cpp | 8 +++++++- imgui_internal.h | 7 +++++-- imgui_widgets.cpp | 6 +++++- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 7db3d6e072ef..e6cb324cbc2c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5143,6 +5143,7 @@ ImGuiWindow* ImGui::FindWindowByName(const char* name) static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags) { ImGuiContext& g = *GImGui; + //IMGUI_DEBUG_LOG("CreateNewWindow '%s', flags = 0x%08X\n", name, flags); // Create window the first time ImGuiWindow* window = IM_NEW(ImGuiWindow)(&g, name); @@ -14571,7 +14572,12 @@ void ImGui::ShowMetricsWindow(bool* p_open) static void NodeWindow(ImGuiWindow* window, const char* label) { - if (!ImGui::TreeNode(window, "%s '%s', %d @ 0x%p", label, window->Name, window->Active || window->WasActive, window)) + if (window == NULL) + { + ImGui::BulletText("%s: NULL", label); + return; + } + if (!ImGui::TreeNode(window, "%s '%s', %d @ 0x%p", label, window->Name, (window->Active || window->WasActive), window)) return; ImGuiWindowFlags flags = window->Flags; NodeDrawList(window, window->Viewport, window->DrawList, "DrawList"); diff --git a/imgui_internal.h b/imgui_internal.h index be6a0ca732f1..ddad75f442c3 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -148,12 +148,15 @@ extern IMGUI_API ImGuiContext* GImGui; // Current implicit context pointer #define IM_NEWLINE "\n" #endif #define IM_TABSIZE (4) - -#define IMGUI_DEBUG_LOG(_FMT,...) printf("[%05d] " _FMT, GImGui->FrameCount, __VA_ARGS__) #define IM_STATIC_ASSERT(_COND) typedef char static_assertion_##__line__[(_COND)?1:-1] #define IM_F32_TO_INT8_UNBOUND(_VAL) ((int)((_VAL) * 255.0f + ((_VAL)>=0 ? 0.5f : -0.5f))) // Unsaturated, for display purpose #define IM_F32_TO_INT8_SAT(_VAL) ((int)(ImSaturate(_VAL) * 255.0f + 0.5f)) // Saturated, always output 0..255 +// Debug Logging +#ifndef IMGUI_DEBUG_LOG +#define IMGUI_DEBUG_LOG(_FMT,...) printf("[%05d] " _FMT, GImGui->FrameCount, __VA_ARGS__) +#endif + // Enforce cdecl calling convention for functions called by the standard library, in case compilation settings changed the default to e.g. __vectorcall #ifdef _MSC_VER #define IMGUI_CDECL __cdecl diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 4d9e9942606f..910339935b65 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -5947,6 +5947,10 @@ void ImGui::EndMainMenuBar() End(); } +// FIXME: Provided a rectangle perhaps e.g. a BeginMenuBarEx() could be used anywhere.. +// Currently the main responsibility of this function being to setup clip-rect + horizontal layout + menu navigation layer. +// Ideally we also want this to be responsible for claiming space out of the main window scrolling rectangle, in which case ImGuiWindowFlags_MenuBar will become unnecessary. +// Then later the same system could be used for multiple menu-bars, scrollbars, side-bars. bool ImGui::BeginMenuBar() { ImGuiWindow* window = GetCurrentWindow(); @@ -5956,7 +5960,7 @@ bool ImGui::BeginMenuBar() return false; IM_ASSERT(!window->DC.MenuBarAppending); - BeginGroup(); // Backup position on layer 0 + BeginGroup(); // Backup position on layer 0 // FIXME: Misleading to use a group for that backup/restore PushID("##menubar"); // We don't clip with current window clipping rectangle as it is already set to the area below. However we clip with window full rect. From 969278fc0b4c1744f7223a90d4279569db690086 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 23 Jul 2019 21:29:43 -0700 Subject: [PATCH 528/828] Docking: Fixed dragging/resizing from OS decoration not marking settings as dirty. Internals: Added IMGUI_DEBUG_LOG_DOCKING, IMGUI_DEBUG_LOG_VIEWPORT macros to easily enable/disable a bunch of logging code. --- imgui.cpp | 54 +++++++++++++++++++++++++++++++++++++----------- imgui_internal.h | 4 ++++ 2 files changed, 46 insertions(+), 12 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index e6cb324cbc2c..e4be3dc0127b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6108,7 +6108,10 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Synchronize window --> viewport in most situations // Synchronize viewport -> window in case the platform window has been moved or resized from the OS/WM if (window->Viewport->PlatformRequestMove) + { window->Pos = window->Viewport->Pos; + MarkIniSettingsDirty(window); + } else if (memcmp(&window->Viewport->Pos, &window->Pos, sizeof(window->Pos)) != 0) { viewport_rect_changed = true; @@ -6116,7 +6119,10 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) } if (window->Viewport->PlatformRequestResize) + { window->Size = window->SizeFull = window->Viewport->Size; + MarkIniSettingsDirty(window); + } else if (memcmp(&window->Viewport->Size, &window->Size, sizeof(window->Size)) != 0) { viewport_rect_changed = true; @@ -6453,7 +6459,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) { window->Viewport->PlatformRequestClose = false; g.NavWindowingToggleLayer = false; // Assume user mapped PlatformRequestClose on ALT-F4 so we disable ALT for menu toggle. False positive not an issue. - //IMGUI_DEBUG_LOG("Window '%s' PlatformRequestClose\n", window->Name); + IMGUI_DEBUG_LOG_VIEWPORT("Window '%s' PlatformRequestClose\n", window->Name); *p_open = false; } } @@ -10259,7 +10265,7 @@ void ImGui::SetCurrentViewport(ImGuiWindow* current_window, ImGuiViewportP* view if (g.CurrentViewport == viewport) return; g.CurrentViewport = viewport; - //IMGUI_DEBUG_LOG("SetCurrentViewport changed '%s' 0x%08X\n", current_window ? current_window->Name : NULL, viewport ? viewport->ID : 0); + //IMGUI_DEBUG_LOG_VIEWPORT("SetCurrentViewport changed '%s' 0x%08X\n", current_window ? current_window->Name : NULL, viewport ? viewport->ID : 0); // Notify platform layer of viewport changes // FIXME-DPI: This is only currently used for experimenting with handling of multiple DPI @@ -10414,7 +10420,7 @@ static void ImGui::UpdateViewportsNewFrame() g.Viewports.erase(g.Viewports.Data + n); // Destroy - //IMGUI_DEBUG_LOG("Delete Viewport %08X (%s)\n", viewport->ID, viewport->Window ? viewport->Window->Name : "n/a"); + IMGUI_DEBUG_LOG_VIEWPORT("Delete Viewport %08X (%s)\n", viewport->ID, viewport->Window ? viewport->Window->Name : "n/a"); DestroyPlatformWindow(viewport); // In most circumstances the platform window will already be destroyed here. IM_ASSERT(g.PlatformIO.Viewports.contains(viewport) == false); IM_DELETE(viewport); @@ -10585,7 +10591,7 @@ ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const viewport->Flags = flags; UpdateViewportPlatformMonitor(viewport); g.Viewports.push_back(viewport); - //IMGUI_DEBUG_LOG("Add Viewport %08X (%s)\n", id, window->Name); + IMGUI_DEBUG_LOG_VIEWPORT("Add Viewport %08X (%s)\n", id, window->Name); // We normally setup for all viewports in NewFrame() but here need to handle the mid-frame creation of a new viewport. // We need to extend the fullscreen clip rect so the OverlayDrawList clip is correct for that the first frame @@ -10709,7 +10715,7 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) if ((window->Flags & ImGuiWindowFlags_DockNodeHost) && window->Viewport->LastFrameActive < g.FrameCount && will_be_visible) { // Steal/transfer ownership - //IMGUI_DEBUG_LOG("[%05d] Window '%s' steal Viewport %08X from Window '%s'\n", g.FrameCount, window->Name, window->Viewport->ID, window->Viewport->Window->Name); + IMGUI_DEBUG_LOG_VIEWPORT("Window '%s' steal Viewport %08X from Window '%s'\n", window->Name, window->Viewport->ID, window->Viewport->Window->Name); window->Viewport->Window = window; window->Viewport->ID = window->ID; window->Viewport->LastNameHash = 0; @@ -10773,7 +10779,7 @@ void ImGui::UpdatePlatformWindows() bool is_new_platform_window = (viewport->PlatformWindowCreated == false); if (is_new_platform_window) { - //IMGUI_DEBUG_LOG("Create Platform Window %08X (%s)\n", viewport->ID, viewport->Window ? viewport->Window->Name : "n/a"); + IMGUI_DEBUG_LOG_VIEWPORT("Create Platform Window %08X (%s)\n", viewport->ID, viewport->Window ? viewport->Window->Name : "n/a"); g.PlatformIO.Platform_CreateWindow(viewport); if (g.PlatformIO.Renderer_CreateWindow != NULL) g.PlatformIO.Renderer_CreateWindow(viewport); @@ -11187,7 +11193,7 @@ void ImGui::DockContextClearNodes(ImGuiContext* ctx, ImGuiID root_id, bool clear // This function also acts as a defacto test to make sure we can rebuild from scratch without a glitch void ImGui::DockContextRebuild(ImGuiContext* ctx) { - //IMGUI_DEBUG_LOG("[docking] full rebuild\n"); + IMGUI_DEBUG_LOG_DOCKING("DockContextRebuild()\n"); ImGuiDockContext* dc = ctx->DockContext; SaveIniSettingsToMemory(); ImGuiID root_id = 0; // Rebuild all @@ -11284,6 +11290,7 @@ static ImGuiDockNode* ImGui::DockContextAddNode(ImGuiContext* ctx, ImGuiID id) id = DockContextGenNodeID(ctx); else IM_ASSERT(DockContextFindNodeByID(ctx, id) == NULL); + IMGUI_DEBUG_LOG_DOCKING("DockContextAddNode 0x%08X\n", id); ImGuiDockNode* node = IM_NEW(ImGuiDockNode)(id); ctx->DockContext->Nodes.SetVoidPtr(node->ID, node); return node; @@ -11294,7 +11301,7 @@ static void ImGui::DockContextRemoveNode(ImGuiContext* ctx, ImGuiDockNode* node, ImGuiContext& g = *ctx; ImGuiDockContext* dc = ctx->DockContext; - //printf("[%05d] RemoveNode 0x%04X\n", node->ID); + IMGUI_DEBUG_LOG_DOCKING("DockContextRemoveNode 0x%08X\n", node->ID); IM_ASSERT(DockContextFindNodeByID(ctx, node->ID) == node); IM_ASSERT(node->ChildNodes[0] == NULL && node->ChildNodes[1] == NULL); IM_ASSERT(node->Windows.Size == 0); @@ -11380,6 +11387,7 @@ static void ImGui::DockContextPruneUnusedSettingsNodes(ImGuiContext* ctx) remove |= (data_root->CountChildWindows == 0); if (remove) { + IMGUI_DEBUG_LOG_DOCKING("DockContextPruneUnusedSettingsNodes: Prune 0x%08X\n", settings->ID); DockSettingsRemoveNodeReferences(&settings->ID, 1); settings->ID = 0; } @@ -11488,6 +11496,10 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) ImGuiWindow* payload_window = req->DockPayload; // Optional ImGuiWindow* target_window = req->DockTargetWindow; ImGuiDockNode* node = req->DockTargetNode; + if (payload_window) + IMGUI_DEBUG_LOG_DOCKING("DockContextProcessDock node 0x%08X target '%s' dock window '%s', split_dir %d\n", node ? node->ID : 0, target_window ? target_window->Name : "NULL", payload_window ? payload_window->Name : "NULL", req->DockSplitDir); + else + IMGUI_DEBUG_LOG_DOCKING("DockContextProcessDock node 0x%08X, split_dir %d\n", node ? node->ID : 0, req->DockSplitDir); // Decide which Tab will be selected at the end of the operation ImGuiID next_selected_id = 0; @@ -11726,6 +11738,7 @@ static void ImGui::DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window, b DockNodeRemoveWindow(window->DockNode, window, 0); } IM_ASSERT(window->DockNode == NULL || window->DockNodeAsHost == NULL); + IMGUI_DEBUG_LOG_DOCKING("DockNodeAddWindow node 0x%08X window '%s'\n", node->ID, window->Name); node->Windows.push_back(window); node->WantHiddenTabBarUpdate = true; @@ -11783,6 +11796,7 @@ static void ImGui::DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window //IM_ASSERT(window->RootWindow == node->HostWindow); //IM_ASSERT(window->LastFrameActive < g.FrameCount); // We may call this from Begin() IM_ASSERT(save_dock_id == 0 || save_dock_id == node->ID); + IMGUI_DEBUG_LOG_DOCKING("DockNodeRemoveWindow node 0x%08X window '%s'\n", node->ID, window->Name); window->DockNode = NULL; window->DockIsActive = window->DockTabWantClose = false; @@ -12454,12 +12468,18 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w int tabs_unsorted_start = tab_bar->Tabs.Size; for (int tab_n = tab_bar->Tabs.Size - 1; tab_n >= 0 && (tab_bar->Tabs[tab_n].Flags & ImGuiTabItemFlags_Unsorted); tab_n--) { + // FIXME-DOCKING: Consider only clearing the flag after the tab has been alive for a few consecutive frames, allowing late comers to not break sorting? tab_bar->Tabs[tab_n].Flags &= ~ImGuiTabItemFlags_Unsorted; tabs_unsorted_start = tab_n; } - //printf("[%05d] Sorting %d new appearing tabs\n", g.FrameCount, tab_bar->Tabs.Size - tabs_unsorted_start); + if (tab_bar->Tabs.Size > tabs_unsorted_start) + { + IMGUI_DEBUG_LOG_DOCKING("In node 0x%08X: %d new appearing tabs:%s\n", node->ID, tab_bar->Tabs.Size - tabs_unsorted_start, (tab_bar->Tabs.Size > tabs_unsorted_start + 1) ? " (will sort)" : ""); + for (int tab_n = tabs_unsorted_start; tab_n < tab_bar->Tabs.Size; tab_n++) + IMGUI_DEBUG_LOG_DOCKING(" - Tab '%s' Order %d\n", tab_bar->Tabs[tab_n].Window->Name, tab_bar->Tabs[tab_n].Window->DockOrder); if (tab_bar->Tabs.Size > tabs_unsorted_start + 1) ImQsort(tab_bar->Tabs.Data + tabs_unsorted_start, tab_bar->Tabs.Size - tabs_unsorted_start, sizeof(ImGuiTabItem), TabItemComparerByDockOrder); + } // Selected newly added tabs, or persistent tab ID if the tab bar was just recreated if (tab_bar_is_recreated && TabBarFindTabByID(tab_bar, node->SelectedTabID) != NULL) @@ -12985,6 +13005,7 @@ void ImGui::DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImG IM_ASSERT(parent_node->TabBar == NULL); IM_ASSERT(parent_node->Windows.Size == 0); } + IMGUI_DEBUG_LOG_DOCKING("DockNodeTreeMerge 0x%08X & 0x%08X back into parent 0x%08X\n", child_0 ? child_0->ID : 0, child_1 ? child_1->ID : 0, parent_node->ID); ImVec2 backup_last_explicit_size = parent_node->SizeRef; DockNodeMoveChildNodes(parent_node, merge_lead_child); @@ -13316,9 +13337,12 @@ void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags fla ImGuiDockNode* node = DockContextFindNodeByID(ctx, id); if (!node) { + IMGUI_DEBUG_LOG_DOCKING("DockSpace: dockspace node 0x%08X created\n", id); node = DockContextAddNode(ctx, id); node->LocalFlags |= ImGuiDockNodeFlags_CentralNode; } + if (window_class && window_class->ClassId != node->WindowClass.ClassId) + IMGUI_DEBUG_LOG_DOCKING("DockSpace: dockspace node 0x%08X: setup WindowClass 0x%08X -> 0x%08X\n", id, node->WindowClass.ClassId, window_class->ClassId); node->SharedFlags = flags; node->WindowClass = window_class ? *window_class : ImGuiWindowClass(); @@ -13352,6 +13376,7 @@ void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags fla SetNextWindowSize(node->Size); g.NextWindowData.PosUndock = false; + // FIXME-DOCKING: Why do we need a child window to host a dockspace, could we host it in the existing window? ImGuiWindowFlags window_flags = ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_DockNodeHost; window_flags |= ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar; window_flags |= ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse; @@ -13611,6 +13636,7 @@ ImGuiID ImGui::DockBuilderSplitNode(ImGuiID id, ImGuiDir split_dir, float size_r { ImGuiContext* ctx = GImGui; IM_ASSERT(split_dir != ImGuiDir_None); + IMGUI_DEBUG_LOG_DOCKING("DockBuilderSplitNode node 0x%08X, split_dir %d\n", id, split_dir); ImGuiDockNode* node = DockContextFindNodeByID(ctx, id); if (node == NULL) @@ -13661,7 +13687,7 @@ static ImGuiDockNode* DockBuilderCopyNodeRec(ImGuiDockNode* src_node, ImGuiID ds dst_node->ChildNodes[child_n]->ParentNode = dst_node; } - //IMGUI_DEBUG_LOG("Fork node %08X -> %08X (%d childs)\n", src_node->ID, dst_node->ID, dst_node->IsSplitNode() ? 2 : 0); + IMGUI_DEBUG_LOG_DOCKING("Fork node %08X -> %08X (%d childs)\n", src_node->ID, dst_node->ID, dst_node->IsSplitNode() ? 2 : 0); return dst_node; } @@ -13753,13 +13779,14 @@ void ImGui::DockBuilderCopyDockSpace(ImGuiID src_dockspace_id, ImGuiID dst_docks if (dst_dock_id != 0) { // Docked windows gets redocked into the new node hierarchy. - //IMGUI_DEBUG_LOG("Remap window '%s' %08X -> %08X\n", dst_window_name, src_dock_id, dst_dock_id); + IMGUI_DEBUG_LOG_DOCKING("Remap live window '%s' 0x%08X -> '%s' 0x%08X\n", src_window_name, src_dock_id, dst_window_name, dst_dock_id); DockBuilderDockWindow(dst_window_name, dst_dock_id); } else { // Floating windows gets their settings transferred (regardless of whether the new window already exist or not) // When this is leading to a Copy and not a Move, we would get two overlapping floating windows. Could we possibly dock them together? + IMGUI_DEBUG_LOG_DOCKING("Remap window settings '%s' -> '%s'\n", src_window_name, dst_window_name); DockBuilderCopyWindowSettings(src_window_name, dst_window_name); } } @@ -13778,7 +13805,7 @@ void ImGui::DockBuilderCopyDockSpace(ImGuiID src_dockspace_id, ImGuiID dst_docks continue; // Docked windows gets redocked into the new node hierarchy. - //IMGUI_DEBUG_LOG("Remap window '%s' %08X -> %08X\n", window->Name, src_dock_id, dst_dock_id); + IMGUI_DEBUG_LOG_DOCKING("Remap window '%s' %08X -> %08X\n", window->Name, src_dock_id, dst_dock_id); DockBuilderDockWindow(window->Name, dst_dock_id); } } @@ -14047,6 +14074,7 @@ void ImGui::BeginAsDockableDragDropTarget(ImGuiWindow* window) static void ImGui::DockSettingsRenameNodeReferences(ImGuiID old_node_id, ImGuiID new_node_id) { ImGuiContext& g = *GImGui; + IMGUI_DEBUG_LOG_DOCKING("DockSettingsRenameNodeReferences: from 0x%08X -> to 0x%08X\n", old_node_id, new_node_id); for (int window_n = 0; window_n < g.Windows.Size; window_n++) { ImGuiWindow* window = g.Windows[window_n]; @@ -14586,6 +14614,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) (flags & ImGuiWindowFlags_ChildWindow) ? "Child " : "", (flags & ImGuiWindowFlags_Tooltip) ? "Tooltip " : "", (flags & ImGuiWindowFlags_Popup) ? "Popup " : "", (flags & ImGuiWindowFlags_Modal) ? "Modal " : "", (flags & ImGuiWindowFlags_ChildMenu) ? "ChildMenu " : "", (flags & ImGuiWindowFlags_NoSavedSettings) ? "NoSavedSettings " : "", (flags & ImGuiWindowFlags_NoMouseInputs)? "NoMouseInputs":"", (flags & ImGuiWindowFlags_NoNavInputs) ? "NoNavInputs" : "", (flags & ImGuiWindowFlags_AlwaysAutoResize) ? "AlwaysAutoResize" : ""); + ImGui::BulletText("WindowClassId: 0x%08X", window->WindowClass.ClassId); ImGui::BulletText("Scroll: (%.2f/%.2f,%.2f/%.2f)", window->Scroll.x, window->ScrollMax.x, window->Scroll.y, window->ScrollMax.y); ImGui::BulletText("Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", window->Active, window->WasActive, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1); ImGui::BulletText("Appearing: %d, Hidden: %d (CanSkip %d Cannot %d), SkipItems: %d", window->Appearing, window->Hidden, window->HiddenFramesCanSkipItems, window->HiddenFramesCannotSkipItems, window->SkipItems); @@ -14897,6 +14926,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) char* p = buf; ImDrawList* overlay_draw_list = node->HostWindow ? GetForegroundDrawList(node->HostWindow) : GetForegroundDrawList((ImGuiViewportP*)GetMainViewport()); p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "DockId: %X%s\n", node->ID, node->IsCentralNode() ? " *CentralNode*" : ""); + p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "WindowClass: %08X\n", node->WindowClass.ClassId); p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "Size: (%.0f, %.0f)\n", node->Size.x, node->Size.y); p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "SizeRef: (%.0f, %.0f)\n", node->SizeRef.x, node->SizeRef.y); int depth = DockNodeGetDepth(node); diff --git a/imgui_internal.h b/imgui_internal.h index ddad75f442c3..951f83a8b9d7 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -156,6 +156,10 @@ extern IMGUI_API ImGuiContext* GImGui; // Current implicit context pointer #ifndef IMGUI_DEBUG_LOG #define IMGUI_DEBUG_LOG(_FMT,...) printf("[%05d] " _FMT, GImGui->FrameCount, __VA_ARGS__) #endif +#define IMGUI_DEBUG_LOG_VIEWPORT(...) ((void)0) // Disable log +#define IMGUI_DEBUG_LOG_DOCKING(...) ((void)0) // Disable log +//#define IMGUI_DEBUG_LOG_VIEWPORT IMGUI_DEBUG_LOG // Enable log +//#define IMGUI_DEBUG_LOG_DOCKING IMGUI_DEBUG_LOG // Enable log // Enforce cdecl calling convention for functions called by the standard library, in case compilation settings changed the default to e.g. __vectorcall #ifdef _MSC_VER From 7c183dc6a15f9440536ace71bbb71f9f98454359 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 24 Jul 2019 11:18:13 -0700 Subject: [PATCH 529/828] Docking: Explicitly inhibit constraint when docked for now (#2690, #2109) Added asserts to catch issues. --- imgui.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/imgui.cpp b/imgui.cpp index 55ac8de1ad4a..4c2b8f58778f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5852,6 +5852,9 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) { BeginDocked(window, p_open); flags = window->Flags; + + // Docking currently override constraints + g.NextWindowData.Flags &= ~ImGuiNextWindowDataFlags_HasSizeConstraint; } } @@ -13050,6 +13053,7 @@ void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 si { // During the regular dock node update we write to all nodes. // 'only_write_to_marked_nodes' is only set when turning a node visible mid-frame and we need its size right-away. + IM_ASSERT(size.x > 0.0f && size.y > 0.0f); const bool write_to_node = (only_write_to_marked_nodes == false) || (node->MarkedForPosSizeWrite); if (write_to_node) { @@ -13082,6 +13086,7 @@ void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 si child_0->WantLockSizeOnce = false; child_0_size[axis] = child_0->SizeRef[axis] = child_0->Size[axis]; child_1_size[axis] = child_1->SizeRef[axis] = (size_avail - child_0_size[axis]); + IM_ASSERT(child_0->SizeRef[axis] > 0.0f && child_1->SizeRef[axis] > 0.0f); } else if (child_1->WantLockSizeOnce) @@ -13089,6 +13094,7 @@ void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 si child_1->WantLockSizeOnce = false; child_1_size[axis] = child_1->SizeRef[axis] = child_1->Size[axis]; child_0_size[axis] = child_0->SizeRef[axis] = (size_avail - child_1_size[axis]); + IM_ASSERT(child_0->SizeRef[axis] > 0.0f && child_1->SizeRef[axis] > 0.0f); } // 3) If one window is the central node (~ use remaining space, should be made explicit!), use explicit size from the other, and remainder for the central node @@ -13373,6 +13379,7 @@ void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags fla size.x = ImMax(content_avail.x + size.x, 4.0f); // Arbitrary minimum child size (0.0f causing too much issues) if (size.y <= 0.0f) size.y = ImMax(content_avail.y + size.y, 4.0f); + IM_ASSERT(size.x > 0.0f && size.y > 0.0f); node->Pos = window->DC.CursorPos; node->Size = node->SizeRef = size; @@ -13493,6 +13500,7 @@ void ImGui::DockBuilderSetNodeSize(ImGuiID node_id, ImVec2 size) ImGuiDockNode* node = DockContextFindNodeByID(ctx, node_id); if (node == NULL) return; + IM_ASSERT(size.x > 0.0f && size.y > 0.0f); node->Size = node->SizeRef = size; node->AuthorityForSize = ImGuiDataAuthority_DockNode; } From e5b905481dd2d7c341befa99505487f8f86a62fd Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 24 Jul 2019 13:37:17 -0700 Subject: [PATCH 530/828] Viewport: Refactored ViewportFlagsOverrideMask+ViewportFlagsOverrideValue into ViewportFlagsOverrideSet+ViewportFlagsOverrideClear which appears easier to grasp. (#1542) (cherry picked from commit 9437630872e7ca19065bee78fcafaab54a0d5bf2) --- imgui.cpp | 9 ++++++--- imgui.h | 6 +++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 4c2b8f58778f..3c051ee98701 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6162,8 +6162,10 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->Viewport->ParentViewportId = parent_window_in_stack->Viewport->ID; else window->Viewport->ParentViewportId = g.IO.ConfigViewportsNoDefaultParent ? 0 : IMGUI_VIEWPORT_DEFAULT_ID; - if (window->WindowClass.ViewportFlagsOverrideMask) - viewport_flags = (viewport_flags & ~window->WindowClass.ViewportFlagsOverrideMask) | (window->WindowClass.ViewportFlagsOverrideValue & window->WindowClass.ViewportFlagsOverrideMask); + if (window->WindowClass.ViewportFlagsOverrideSet) + viewport_flags |= window->WindowClass.ViewportFlagsOverrideSet; + if (window->WindowClass.ViewportFlagsOverrideClear) + viewport_flags &= ~window->WindowClass.ViewportFlagsOverrideClear; // We also tell the back-end that clearing the platform window won't be necessary, as our window is filling the viewport and we have disabled BgAlpha viewport_flags |= ImGuiViewportFlags_NoRendererClear; @@ -7437,6 +7439,7 @@ void ImGui::SetNextWindowDockID(ImGuiID id, ImGuiCond cond) void ImGui::SetNextWindowClass(const ImGuiWindowClass* window_class) { ImGuiContext& g = *GImGui; + IM_ASSERT((window_class->ViewportFlagsOverrideSet & window_class->ViewportFlagsOverrideClear) == 0); // Cannot set both set and clear for the same bit g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasWindowClass; g.NextWindowData.WindowClass = *window_class; } @@ -10291,7 +10294,7 @@ static bool ImGui::GetWindowAlwaysWantOwnViewport(ImGuiWindow* window) { // Tooltips and menus are not automatically forced into their own viewport when the NoMerge flag is set, however the multiplication of viewports makes them more likely to protrude and create their own. ImGuiContext& g = *GImGui; - if (g.IO.ConfigViewportsNoAutoMerge || ((window->WindowClass.ViewportFlagsOverrideValue & window->WindowClass.ViewportFlagsOverrideMask) & ImGuiViewportFlags_NoAutoMerge)) + if (g.IO.ConfigViewportsNoAutoMerge || (window->WindowClass.ViewportFlagsOverrideSet & ImGuiViewportFlags_NoAutoMerge)) if (g.ConfigFlagsForFrame & ImGuiConfigFlags_ViewportsEnable) if (!window->DockIsActive) if ((window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) == 0) diff --git a/imgui.h b/imgui.h index bc155d5de765..50b982942389 100644 --- a/imgui.h +++ b/imgui.h @@ -1588,12 +1588,12 @@ struct ImGuiWindowClass { ImGuiID ClassId; // User data. 0 = Default class (unclassed) ImGuiID ParentViewportId; // Hint for the platform back-end. If non-zero, the platform back-end can create a parent<>child relationship between the platform windows. Not conforming back-ends are free to e.g. parent every viewport to the main viewport or not. - ImGuiViewportFlags ViewportFlagsOverrideMask; // Viewport flags to override when a window of this class owns a viewport. This allows you to enforce OS decoration or task bar icon, override the defaults on a per-window basis. - ImGuiViewportFlags ViewportFlagsOverrideValue; // Viewport flags values to override when a window of this class owns a viewport. + ImGuiViewportFlags ViewportFlagsOverrideSet; // Viewport flags to set when a window of this class owns a viewport. This allows you to enforce OS decoration or task bar icon, override the defaults on a per-window basis. + ImGuiViewportFlags ViewportFlagsOverrideClear; // Viewport flags to clear when a window of this class owns a viewport. This allows you to enforce OS decoration or task bar icon, override the defaults on a per-window basis. bool DockingAlwaysTabBar; // Set to true to enforce windows of this class always having their own tab (equivalent of setting the global io.ConfigDockingAlwaysTabBar) bool DockingAllowUnclassed; // Set to true to allow windows of this class to be docked/merged with an unclassed window. - ImGuiWindowClass() { ClassId = 0; ParentViewportId = 0; ViewportFlagsOverrideMask = ViewportFlagsOverrideValue = 0x00; DockingAlwaysTabBar = false; DockingAllowUnclassed = true; } + ImGuiWindowClass() { ClassId = 0; ParentViewportId = 0; ViewportFlagsOverrideSet = ViewportFlagsOverrideClear = 0x00; DockingAlwaysTabBar = false; DockingAllowUnclassed = true; } }; // Data payload for Drag and Drop operations: AcceptDragDropPayload(), GetDragDropPayload() From 949a9fa2cbab18f6d15bf84d3fad438ddcc502ec Mon Sep 17 00:00:00 2001 From: Chris Savoie Date: Mon, 29 Jul 2019 15:52:30 -0700 Subject: [PATCH 531/828] Vulkan: Fix crash when viewports are disabled and memory leak on shutdown. (#2698) --- examples/imgui_impl_vulkan.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/examples/imgui_impl_vulkan.cpp b/examples/imgui_impl_vulkan.cpp index 74d111ea8ac9..b9026e92c728 100644 --- a/examples/imgui_impl_vulkan.cpp +++ b/examples/imgui_impl_vulkan.cpp @@ -850,8 +850,17 @@ bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info, VkRenderPass rend void ImGui_ImplVulkan_Shutdown() { - ImGui_ImplVulkan_ShutdownPlatformInterface(); + // First destroy objects in all viewports ImGui_ImplVulkan_DestroyDeviceObjects(); + + // Manually delete main viewport render data in-case we haven't initialized for viewports + ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + if (ImGuiViewportDataVulkan* data = (ImGuiViewportDataVulkan*)main_viewport->RendererUserData) + IM_DELETE(data); + main_viewport->RendererUserData = NULL; + + // Clean up windows + ImGui_ImplVulkan_ShutdownPlatformInterface(); } void ImGui_ImplVulkan_NewFrame() From cb2de62bb169c033d42959d9d9a09b3a23056c3f Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 29 Jul 2019 15:40:07 -0700 Subject: [PATCH 532/828] Docking: Renaming, comments. --- imgui.cpp | 52 ++++++++++++++++++++++++------------------------ imgui_internal.h | 4 ++-- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 376ded2ef1b0..36f322389443 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11073,15 +11073,15 @@ struct ImGuiDockPreviewData struct ImGuiDockNodeSettings { ImGuiID ID; - ImGuiID ParentID; - ImGuiID SelectedTabID; + ImGuiID ParentNodeID; + ImGuiID SelectedWindowID; signed char SplitAxis; char Depth; ImGuiDockNodeFlags Flags; // NB: We save individual flags one by one in ascii format (ImGuiDockNodeFlags_SavedFlagsMask_) ImVec2ih Pos; ImVec2ih Size; ImVec2ih SizeRef; - ImGuiDockNodeSettings() { ID = ParentID = SelectedTabID = 0; SplitAxis = ImGuiAxis_None; Depth = 0; Flags = ImGuiDockNodeFlags_None; } + ImGuiDockNodeSettings() { ID = ParentNodeID = SelectedWindowID = 0; SplitAxis = ImGuiAxis_None; Depth = 0; Flags = ImGuiDockNodeFlags_None; } }; struct ImGuiDockContext @@ -11374,10 +11374,10 @@ static void ImGui::DockContextPruneUnusedSettingsNodes(ImGuiContext* ctx) for (int settings_n = 0; settings_n < dc->SettingsNodes.Size; settings_n++) { ImGuiDockNodeSettings* settings = &dc->SettingsNodes[settings_n]; - ImGuiDockContextPruneNodeData* parent_data = settings->ParentID ? pool.GetByKey(settings->ParentID) : 0; + ImGuiDockContextPruneNodeData* parent_data = settings->ParentNodeID ? pool.GetByKey(settings->ParentNodeID) : 0; pool.GetOrAddByKey(settings->ID)->RootID = parent_data ? parent_data->RootID : settings->ID; - if (settings->ParentID) - pool.GetOrAddByKey(settings->ParentID)->CountChildNodes++; + if (settings->ParentNodeID) + pool.GetOrAddByKey(settings->ParentNodeID)->CountChildNodes++; } // Count reference to dock ids from window settings @@ -11400,8 +11400,8 @@ static void ImGui::DockContextPruneUnusedSettingsNodes(ImGuiContext* ctx) ImGuiDockContextPruneNodeData* data_root = (data->RootID == settings->ID) ? data : pool.GetByKey(data->RootID); bool remove = false; - remove |= (data->CountWindows == 1 && settings->ParentID == 0 && data->CountChildNodes == 0 && !(settings->Flags & ImGuiDockNodeFlags_CentralNode)); // Floating root node with only 1 window - remove |= (data->CountWindows == 0 && settings->ParentID == 0 && data->CountChildNodes == 0); // Leaf nodes with 0 window + remove |= (data->CountWindows == 1 && settings->ParentNodeID == 0 && data->CountChildNodes == 0 && !(settings->Flags & ImGuiDockNodeFlags_CentralNode)); // Floating root node with only 1 window + remove |= (data->CountWindows == 0 && settings->ParentNodeID == 0 && data->CountChildNodes == 0); // Leaf nodes with 0 window remove |= (data_root->CountChildWindows == 0); if (remove) { @@ -11421,7 +11421,7 @@ static void ImGui::DockContextBuildNodesFromSettings(ImGuiContext* ctx, ImGuiDoc if (settings->ID == 0) continue; ImGuiDockNode* node = DockContextAddNode(ctx, settings->ID); - node->ParentNode = settings->ParentID ? DockContextFindNodeByID(ctx, settings->ParentID) : NULL; + node->ParentNode = settings->ParentNodeID ? DockContextFindNodeByID(ctx, settings->ParentNodeID) : NULL; node->Pos = ImVec2(settings->Pos.x, settings->Pos.y); node->Size = ImVec2(settings->Size.x, settings->Size.y); node->SizeRef = ImVec2(settings->SizeRef.x, settings->SizeRef.y); @@ -11430,7 +11430,7 @@ static void ImGui::DockContextBuildNodesFromSettings(ImGuiContext* ctx, ImGuiDoc node->ParentNode->ChildNodes[0] = node; else if (node->ParentNode && node->ParentNode->ChildNodes[1] == NULL) node->ParentNode->ChildNodes[1] = node; - node->SelectedTabID = settings->SelectedTabID; + node->SelectedTabID = settings->SelectedWindowID; node->SplitAxis = settings->SplitAxis; node->LocalFlags |= (settings->Flags & ImGuiDockNodeFlags_SavedFlagsMask_); @@ -14164,9 +14164,9 @@ static void ImGui::DockSettingsHandler_ReadLine(ImGuiContext* ctx, ImGuiSettings if (strncmp(line, "DockNode", 8) == 0) { line = ImStrSkipBlank(line + strlen("DockNode")); } else if (strncmp(line, "DockSpace", 9) == 0) { line = ImStrSkipBlank(line + strlen("DockSpace")); node.Flags |= ImGuiDockNodeFlags_DockSpace; } else return; - if (sscanf(line, "ID=0x%08X%n", &node.ID, &r) == 1) { line += r; } else return; - if (sscanf(line, " Parent=0x%08X%n", &node.ParentID, &r) == 1) { line += r; if (node.ParentID == 0) return; } - if (node.ParentID == 0) + if (sscanf(line, "ID=0x%08X%n", &node.ID, &r) == 1) { line += r; } else return; + if (sscanf(line, " Parent=0x%08X%n", &node.ParentNodeID, &r) == 1) { line += r; if (node.ParentNodeID == 0) return; } + if (node.ParentNodeID == 0) { if (sscanf(line, " Pos=%i,%i%n", &x, &y, &r) == 2) { line += r; node.Pos = ImVec2ih((short)x, (short)y); } else return; if (sscanf(line, " Size=%i,%i%n", &x, &y, &r) == 2) { line += r; node.Size = ImVec2ih((short)x, (short)y); } else return; @@ -14182,10 +14182,10 @@ static void ImGui::DockSettingsHandler_ReadLine(ImGuiContext* ctx, ImGuiSettings if (sscanf(line, " HiddenTabBar=%d%n", &x, &r) == 1) { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_HiddenTabBar; } if (sscanf(line, " NoWindowMenuButton=%d%n", &x, &r) == 1) { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoWindowMenuButton; } if (sscanf(line, " NoCloseButton=%d%n", &x, &r) == 1) { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoCloseButton; } - if (sscanf(line, " SelectedTab=0x%08X%n", &node.SelectedTabID,&r) == 1) { line += r; } + if (sscanf(line, " Selected=0x%08X%n", &node.SelectedWindowID,&r) == 1) { line += r; } ImGuiDockContext* dc = ctx->DockContext; - if (node.ParentID != 0) - if (ImGuiDockNodeSettings* parent_settings = DockSettingsFindNodeSettings(ctx, node.ParentID)) + if (node.ParentNodeID != 0) + if (ImGuiDockNodeSettings* parent_settings = DockSettingsFindNodeSettings(ctx, node.ParentNodeID)) node.Depth = parent_settings->Depth + 1; dc->SettingsNodes.push_back(node); } @@ -14195,8 +14195,8 @@ static void DockSettingsHandler_DockNodeToSettings(ImGuiDockContext* dc, ImGuiDo ImGuiDockNodeSettings node_settings; IM_ASSERT(depth < (1 << (sizeof(node_settings.Depth) << 3))); node_settings.ID = node->ID; - node_settings.ParentID = node->ParentNode ? node->ParentNode->ID : 0; - node_settings.SelectedTabID = node->SelectedTabID; + node_settings.ParentNodeID = node->ParentNode ? node->ParentNode->ID : 0; + node_settings.SelectedWindowID = node->SelectedTabID; node_settings.SplitAxis = node->IsSplitNode() ? (char)node->SplitAxis : ImGuiAxis_None; node_settings.Depth = (char)depth; node_settings.Flags = (node->LocalFlags & ImGuiDockNodeFlags_SavedFlagsMask_); @@ -14238,8 +14238,8 @@ static void ImGui::DockSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettings const ImGuiDockNodeSettings* node_settings = &dc->SettingsNodes[node_n]; buf->appendf("%*s%s%*s", node_settings->Depth * 2, "", (node_settings->Flags & ImGuiDockNodeFlags_DockSpace) ? "DockSpace" : "DockNode ", (max_depth - node_settings->Depth) * 2, ""); // Text align nodes to facilitate looking at .ini file buf->appendf(" ID=0x%08X", node_settings->ID); - if (node_settings->ParentID) - buf->appendf(" Parent=0x%08X SizeRef=%d,%d", node_settings->ParentID, node_settings->SizeRef.x, node_settings->SizeRef.y); + if (node_settings->ParentNodeID) + buf->appendf(" Parent=0x%08X SizeRef=%d,%d", node_settings->ParentNodeID, node_settings->SizeRef.x, node_settings->SizeRef.y); else buf->appendf(" Pos=%d,%d Size=%d,%d", node_settings->Pos.x, node_settings->Pos.y, node_settings->Size.x, node_settings->Size.y); if (node_settings->SplitAxis != ImGuiAxis_None) @@ -14256,8 +14256,8 @@ static void ImGui::DockSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettings buf->appendf(" NoWindowMenuButton=1"); if (node_settings->Flags & ImGuiDockNodeFlags_NoCloseButton) buf->appendf(" NoCloseButton=1"); - if (node_settings->SelectedTabID) - buf->appendf(" SelectedTab=0x%08X", node_settings->SelectedTabID); + if (node_settings->SelectedWindowID) + buf->appendf(" Selected=0x%08X", node_settings->SelectedWindowID); #if IMGUI_DEBUG_INI_SETTINGS // [DEBUG] Include comments in the .ini file to ease debugging @@ -14844,14 +14844,14 @@ void ImGui::ShowMetricsWindow(bool* p_open) { ImGuiDockNodeSettings* settings = &dc->SettingsNodes[n]; const char* selected_tab_name = NULL; - if (settings->SelectedTabID) + if (settings->SelectedWindowID) { - if (ImGuiWindow* window = FindWindowByID(settings->SelectedTabID)) + if (ImGuiWindow* window = FindWindowByID(settings->SelectedWindowID)) selected_tab_name = window->Name; - else if (ImGuiWindowSettings* window_settings = FindWindowSettings(settings->SelectedTabID)) + else if (ImGuiWindowSettings* window_settings = FindWindowSettings(settings->SelectedWindowID)) selected_tab_name = window_settings->Name; } - ImGui::BulletText("Node %08X, Parent %08X, SelectedTab %08X ('%s')", settings->ID, settings->ParentID, settings->SelectedTabID, selected_tab_name ? selected_tab_name : settings->SelectedTabID ? "N/A" : ""); + ImGui::BulletText("Node %08X, Parent %08X, SelectedTab %08X ('%s')", settings->ID, settings->ParentNodeID, settings->SelectedWindowID, selected_tab_name ? selected_tab_name : settings->SelectedWindowID ? "N/A" : ""); } ImGui::TreePop(); } diff --git a/imgui_internal.h b/imgui_internal.h index bd6a25393ec3..2699a37bbc68 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -950,8 +950,8 @@ struct ImGuiDockNode int LastFrameActive; // Last frame number the node was updated. int LastFrameFocused; // Last frame number the node was focused. ImGuiID LastFocusedNodeID; // [Root node only] Which of our child docking node (any ancestor in the hierarchy) was last focused. - ImGuiID SelectedTabID; // [Tab node only] Which of our tab is selected. - ImGuiID WantCloseTabID; // [Tab node only] Set when closing a specific tab. + ImGuiID SelectedTabID; // [Leaf node only] Which of our tab/window is selected. + ImGuiID WantCloseTabID; // [Leaf node only] Set when closing a specific tab/window. ImGuiDataAuthority AuthorityForPos :3; ImGuiDataAuthority AuthorityForSize :3; ImGuiDataAuthority AuthorityForViewport :3; From 07c52a25ffaf1f7e8ee69386bd9c343030da153e Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 29 Jul 2019 15:40:51 -0700 Subject: [PATCH 533/828] Docking: Recording dockspace parent window so pruning doesn't zealously lose the location of nodes. (#2109) --- imgui.cpp | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 36f322389443..5f0bbe69d143 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11074,6 +11074,7 @@ struct ImGuiDockNodeSettings { ImGuiID ID; ImGuiID ParentNodeID; + ImGuiID ParentWindowID; ImGuiID SelectedWindowID; signed char SplitAxis; char Depth; @@ -11081,7 +11082,7 @@ struct ImGuiDockNodeSettings ImVec2ih Pos; ImVec2ih Size; ImVec2ih SizeRef; - ImGuiDockNodeSettings() { ID = ParentNodeID = SelectedWindowID = 0; SplitAxis = ImGuiAxis_None; Depth = 0; Flags = ImGuiDockNodeFlags_None; } + ImGuiDockNodeSettings() { ID = ParentNodeID = ParentWindowID = SelectedWindowID = 0; SplitAxis = ImGuiAxis_None; Depth = 0; Flags = ImGuiDockNodeFlags_None; } }; struct ImGuiDockContext @@ -11380,6 +11381,18 @@ static void ImGui::DockContextPruneUnusedSettingsNodes(ImGuiContext* ctx) pool.GetOrAddByKey(settings->ParentNodeID)->CountChildNodes++; } + // Count reference to dock ids from dockspaces + // We track the 'auto-DockNode <- manual-Window <- manual-DockSpace' in order to avoid 'auto-DockNode' being ditched by DockContextPruneUnusedSettingsNodes() + for (int settings_n = 0; settings_n < dc->SettingsNodes.Size; settings_n++) + { + ImGuiDockNodeSettings* settings = &dc->SettingsNodes[settings_n]; + if (settings->ParentWindowID != 0) + if (ImGuiWindowSettings* window_settings = FindWindowSettings(settings->ParentWindowID)) + if (window_settings->DockId) + if (ImGuiDockContextPruneNodeData* data = pool.GetByKey(window_settings->DockId)) + data->CountChildNodes++; + } + // Count reference to dock ids from window settings for (int settings_n = 0; settings_n < g.SettingsWindows.Size; settings_n++) if (ImGuiID dock_id = g.SettingsWindows[settings_n].DockId) @@ -14166,6 +14179,7 @@ static void ImGui::DockSettingsHandler_ReadLine(ImGuiContext* ctx, ImGuiSettings else return; if (sscanf(line, "ID=0x%08X%n", &node.ID, &r) == 1) { line += r; } else return; if (sscanf(line, " Parent=0x%08X%n", &node.ParentNodeID, &r) == 1) { line += r; if (node.ParentNodeID == 0) return; } + if (sscanf(line, " Window=0x%08X%n", &node.ParentWindowID, &r) ==1) { line += r; if (node.ParentWindowID == 0) return; } if (node.ParentNodeID == 0) { if (sscanf(line, " Pos=%i,%i%n", &x, &y, &r) == 2) { line += r; node.Pos = ImVec2ih((short)x, (short)y); } else return; @@ -14196,6 +14210,7 @@ static void DockSettingsHandler_DockNodeToSettings(ImGuiDockContext* dc, ImGuiDo IM_ASSERT(depth < (1 << (sizeof(node_settings.Depth) << 3))); node_settings.ID = node->ID; node_settings.ParentNodeID = node->ParentNode ? node->ParentNode->ID : 0; + node_settings.ParentWindowID = (node->IsDockSpace() && node->HostWindow && node->HostWindow->ParentWindow) ? node->HostWindow->ParentWindow->ID : 0; node_settings.SelectedWindowID = node->SelectedTabID; node_settings.SplitAxis = node->IsSplitNode() ? (char)node->SplitAxis : ImGuiAxis_None; node_settings.Depth = (char)depth; @@ -14239,9 +14254,15 @@ static void ImGui::DockSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettings buf->appendf("%*s%s%*s", node_settings->Depth * 2, "", (node_settings->Flags & ImGuiDockNodeFlags_DockSpace) ? "DockSpace" : "DockNode ", (max_depth - node_settings->Depth) * 2, ""); // Text align nodes to facilitate looking at .ini file buf->appendf(" ID=0x%08X", node_settings->ID); if (node_settings->ParentNodeID) + { buf->appendf(" Parent=0x%08X SizeRef=%d,%d", node_settings->ParentNodeID, node_settings->SizeRef.x, node_settings->SizeRef.y); + } else + { + if (node_settings->ParentWindowID) + buf->appendf(" Window=0x%08X", node_settings->ParentWindowID); buf->appendf(" Pos=%d,%d Size=%d,%d", node_settings->Pos.x, node_settings->Pos.y, node_settings->Size.x, node_settings->Size.y); + } if (node_settings->SplitAxis != ImGuiAxis_None) buf->appendf(" Split=%c", (node_settings->SplitAxis == ImGuiAxis_X) ? 'X' : 'Y'); if (node_settings->Flags & ImGuiDockNodeFlags_NoResize) From 967073ba3d2deaf7ef67a01a3bf9f252a491fcac Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 31 Jul 2019 20:08:06 -0700 Subject: [PATCH 534/828] Viewport: Handle case where host window gets moved and resized simultaneous (toggling maximized state). There's no perfect solution there, than using io.ConfigViewportsNoAutoMerge = false. (#1542) --- imgui.cpp | 52 ++++++++++++++++++++++++++++++++---------------- imgui_internal.h | 6 ++++-- 2 files changed, 39 insertions(+), 19 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 0a7e60b3a2c9..efba73012ce5 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3472,7 +3472,7 @@ void ImGui::UpdateMouseMovingWindowNewFrame() { // Try to merge the window back into the main viewport. // This works because MouseViewport should be != MovingWindow->Viewport on release (as per code in UpdateViewports) - if (g.ConfigFlagsForFrame & ImGuiConfigFlags_ViewportsEnable) + if (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable) UpdateTryMergeWindowIntoHostViewport(moving_window, g.MouseViewport); // Restore the mouse viewport so that we don't hover the viewport _under_ the moved window during the frame we released the mouse button. @@ -3821,10 +3821,10 @@ void ImGui::NewFrame() NewFrameSanityChecks(); // Perform simple check: error if Docking or Viewport are enabled _exactly_ on frame 1 (instead of frame 0 or later), which is a common error leading to loss of .ini data. - const ImGuiColumnsFlags prev_config_flags = g.ConfigFlagsForFrame; - if (g.FrameCount == 1 && (g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable) && (prev_config_flags & ImGuiConfigFlags_DockingEnable) == 0) + g.ConfigFlagsLastFrame = g.ConfigFlagsCurrFrame; + if (g.FrameCount == 1 && (g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable) && (g.ConfigFlagsLastFrame & ImGuiConfigFlags_DockingEnable) == 0) IM_ASSERT(0 && "Please DockingEnable before the first call to NewFrame()! Otherwise you will lose your .ini settings!"); - if (g.FrameCount == 1 && (g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) && (prev_config_flags & ImGuiConfigFlags_ViewportsEnable) == 0) + if (g.FrameCount == 1 && (g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) && (g.ConfigFlagsLastFrame & ImGuiConfigFlags_ViewportsEnable) == 0) IM_ASSERT(0 && "Please ViewportEnable before the first call to NewFrame()! Otherwise you will lose your .ini settings!"); // Perform simple checks: multi-viewport and platform windows support @@ -3863,6 +3863,7 @@ void ImGui::NewFrame() IM_ASSERT(mon.DpiScale != 0.0f); } } + g.ConfigFlagsCurrFrame = g.IO.ConfigFlags; // Load settings on first frame (if not explicitly loaded manually before) if (!g.SettingsLoaded) @@ -3892,7 +3893,6 @@ void ImGui::NewFrame() g.FrameCount += 1; g.TooltipOverrideCount = 0; g.WindowsActiveCount = 0; - g.ConfigFlagsForFrame = g.IO.ConfigFlags; UpdateViewportsNewFrame(); @@ -10317,7 +10317,7 @@ static bool ImGui::GetWindowAlwaysWantOwnViewport(ImGuiWindow* window) // Tooltips and menus are not automatically forced into their own viewport when the NoMerge flag is set, however the multiplication of viewports makes them more likely to protrude and create their own. ImGuiContext& g = *GImGui; if (g.IO.ConfigViewportsNoAutoMerge || (window->WindowClass.ViewportFlagsOverrideSet & ImGuiViewportFlags_NoAutoMerge)) - if (g.ConfigFlagsForFrame & ImGuiConfigFlags_ViewportsEnable) + if (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable) if (!window->DockIsActive) if ((window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) == 0) return true; @@ -10362,6 +10362,26 @@ static bool ImGui::UpdateTryMergeWindowIntoHostViewports(ImGuiWindow* window) return UpdateTryMergeWindowIntoHostViewport(window, g.Viewports[0]); } +// Translate imgui windows when a Host Viewport has been moved +// (This additionally keeps windows at the same place when ImGuiConfigFlags_ViewportsEnable is toggled!) +void ImGui::TranslateWindowsInViewport(ImGuiViewportP* viewport, const ImVec2& old_pos, const ImVec2& new_pos) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(viewport->Window == NULL && (viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows)); + + // 1) We test if ImGuiConfigFlags_ViewportsEnable was just toggled, which allows us to conveniently + // translate imgui windows from OS-window-local to absolute coordinates or vice-versa. + // 2) If it's not going to fit into the new size, keep it at same absolute position. + // One problem with this is that most Win32 applications doesn't update their render while dragging, + // and so the window will appear to teleport when releasing the mouse. + const bool translate_all_windows = (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable) != (g.ConfigFlagsLastFrame & ImGuiConfigFlags_ViewportsEnable); + ImRect test_still_fit_rect(old_pos, old_pos + viewport->Size); + ImVec2 delta_pos = new_pos - old_pos; + for (int window_n = 0; window_n < g.Windows.Size; window_n++) // FIXME-OPT + if (translate_all_windows || (g.Windows[window_n]->Viewport == viewport && test_still_fit_rect.Contains(g.Windows[window_n]->Rect()))) + TranslateWindow(g.Windows[window_n], delta_pos); +} + // Scale all windows (position, size). Use when e.g. changing DPI. (This is a lossy operation!) void ImGui::ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale) { @@ -10403,7 +10423,7 @@ static void ImGui::UpdateViewportsNewFrame() IM_ASSERT(g.PlatformIO.Viewports.Size <= g.Viewports.Size); // Update Minimized status (we need it first in order to decide if we'll apply Pos/Size of the main viewport) - if ((g.ConfigFlagsForFrame & ImGuiConfigFlags_ViewportsEnable)) + if ((g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable)) { for (int n = 0; n < g.Viewports.Size; n++) { @@ -10426,7 +10446,7 @@ static void ImGui::UpdateViewportsNewFrame() IM_ASSERT(main_viewport->Window == NULL); ImVec2 main_viewport_platform_pos = ImVec2(0.0f, 0.0f); ImVec2 main_viewport_platform_size = g.IO.DisplaySize; - if (g.ConfigFlagsForFrame & ImGuiConfigFlags_ViewportsEnable) + if (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable) main_viewport_platform_pos = (main_viewport->Flags & ImGuiViewportFlags_Minimized) ? main_viewport->Pos : g.PlatformIO.Platform_GetWindowPos(main_viewport); AddUpdateViewport(NULL, IMGUI_VIEWPORT_DEFAULT_ID, main_viewport_platform_pos, main_viewport_platform_size, ImGuiViewportFlags_CanHostOtherWindows); @@ -10461,7 +10481,7 @@ static void ImGui::UpdateViewportsNewFrame() } const bool platform_funcs_available = viewport->PlatformWindowCreated; - if ((g.ConfigFlagsForFrame & ImGuiConfigFlags_ViewportsEnable)) + if ((g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable)) { // Update Position and Size (from Platform Window to ImGui) if requested. // We do it early in the frame instead of waiting for UpdatePlatformWindows() to avoid a frame of lag when moving/resizing using OS facilities. @@ -10482,11 +10502,9 @@ static void ImGui::UpdateViewportsNewFrame() // Translate imgui windows when a Host Viewport has been moved // (This additionally keeps windows at the same place when ImGuiConfigFlags_ViewportsEnable is toggled!) - ImVec2 viewport_delta = viewport->Pos - viewport->LastPos; - if ((viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) && (viewport_delta.x != 0.0f || viewport_delta.y != 0.0f)) - for (int window_n = 0; window_n < g.Windows.Size; window_n++) - if (g.Windows[window_n]->Viewport == viewport || (g.ConfigFlagsForFrame & ImGuiConfigFlags_ViewportsEnable) == 0) - TranslateWindow(g.Windows[window_n], viewport_delta); + const ImVec2 viewport_delta_pos = viewport->Pos - viewport->LastPos; + if ((viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) && (viewport_delta_pos.x != 0.0f || viewport_delta_pos.y != 0.0f)) + TranslateWindowsInViewport(viewport, viewport->LastPos, viewport->Pos); // Update DPI scale float new_dpi_scale; @@ -10513,7 +10531,7 @@ static void ImGui::UpdateViewportsNewFrame() viewport->DpiScale = new_dpi_scale; } - if (!(g.ConfigFlagsForFrame & ImGuiConfigFlags_ViewportsEnable)) + if (!(g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable)) { g.MouseViewport = main_viewport; return; @@ -10657,7 +10675,7 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) // Restore main viewport if multi-viewport is not supported by the back-end ImGuiViewportP* main_viewport = g.Viewports[0]; - if (!(g.ConfigFlagsForFrame & ImGuiConfigFlags_ViewportsEnable)) + if (!(g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable)) { SetWindowViewport(window, main_viewport); return; @@ -10783,7 +10801,7 @@ void ImGui::UpdatePlatformWindows() IM_ASSERT(g.FrameCountEnded == g.FrameCount && "Forgot to call Render() or EndFrame() before UpdatePlatformWindows()?"); IM_ASSERT(g.FrameCountPlatformEnded < g.FrameCount); g.FrameCountPlatformEnded = g.FrameCount; - if (!(g.ConfigFlagsForFrame & ImGuiConfigFlags_ViewportsEnable)) + if (!(g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable)) return; // Create/resize/destroy platform windows to match each active viewport. diff --git a/imgui_internal.h b/imgui_internal.h index d370fe0dfabb..a13e3633cc1b 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -997,7 +997,8 @@ struct ImGuiContext ImGuiIO IO; ImGuiPlatformIO PlatformIO; ImGuiStyle Style; - ImGuiConfigFlags ConfigFlagsForFrame; // = g.IO.ConfigFlags at the time of NewFrame() + ImGuiConfigFlags ConfigFlagsCurrFrame; // = g.IO.ConfigFlags at the time of NewFrame() + ImGuiConfigFlags ConfigFlagsLastFrame; ImFont* Font; // (Shortcut) == FontStack.empty() ? IO.Font : FontStack.back() float FontSize; // (Shortcut) == FontBaseSize * g.CurrentWindow->FontWindowScale == window->FontSize(). Text height for current window. float FontBaseSize; // (Shortcut) == IO.FontGlobalScale * Font->Scale * Font->FontSize. Base text height. @@ -1209,7 +1210,7 @@ struct ImGuiContext { Initialized = false; FrameScopeActive = FrameScopePushedFallbackWindow = false; - ConfigFlagsForFrame = ImGuiConfigFlags_None; + ConfigFlagsCurrFrame = ImGuiConfigFlags_None; Font = NULL; FontSize = FontBaseSize = 0.0f; FontAtlasOwnedByContext = shared_font_atlas ? false : true; @@ -1683,6 +1684,7 @@ namespace ImGui IMGUI_API void UpdateMouseMovingWindowEndFrame(); // Viewports + IMGUI_API void TranslateWindowsInViewport(ImGuiViewportP* viewport, const ImVec2& old_pos, const ImVec2& new_pos); IMGUI_API void ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale); IMGUI_API void DestroyPlatformWindow(ImGuiViewportP* viewport); IMGUI_API void ShowViewportThumbnails(); From 3aa9aae0be474dfd77281a2aca1f3a69542b9b44 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 1 Aug 2019 15:50:05 -0700 Subject: [PATCH 535/828] Docking: Fix a crash that could occur with a malformed ini file (DockNode Parent value pointing to a missing node) --- imgui.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index efba73012ce5..eaecd43bd16a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11426,13 +11426,14 @@ static void ImGui::DockContextPruneUnusedSettingsNodes(ImGuiContext* ctx) } // Count reference to dock ids from window settings + // We guard against the possibility of an invalid .ini file (RootID may point to a missing node) for (int settings_n = 0; settings_n < g.SettingsWindows.Size; settings_n++) if (ImGuiID dock_id = g.SettingsWindows[settings_n].DockId) if (ImGuiDockContextPruneNodeData* data = pool.GetByKey(dock_id)) { - ImGuiDockContextPruneNodeData* data_root = (data->RootID == dock_id) ? data : pool.GetByKey(data->RootID); data->CountWindows++; - data_root->CountChildWindows++; + if (ImGuiDockContextPruneNodeData* data_root = (data->RootID == dock_id) ? data : pool.GetByKey(data->RootID)) + data_root->CountChildWindows++; } // Prune From 451c756b016ba331106ec2a4d30fc48bf7c34f6e Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 1 Aug 2019 16:23:54 -0700 Subject: [PATCH 536/828] Docking: Modals don't need to set ImGuiViewportFlags_NoFocusOnClick. This also mitigate the common described by #2445, which becomes particularly bad with unfocused modal. (#1542) --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index eaecd43bd16a..cd0773138f44 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6131,7 +6131,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // won't steal the OS focus away from their parent window (which may be reflected in OS the title bar decoration). // Setting _NoFocusOnClick would technically prevent us from bringing back to front in case they are being covered by an OS window from a different app, // but it shouldn't be much of a problem considering those are already popups that are closed when clicking elsewhere. - if (is_short_lived_floating_window) + if (is_short_lived_floating_window && (flags & ImGuiWindowFlags_Modal) == 0) viewport_flags |= ImGuiViewportFlags_NoFocusOnAppearing | ImGuiViewportFlags_NoFocusOnClick; // We can overwrite viewport flags using ImGuiWindowClass (advanced users) From 5d87ee8d82cbf0c24ab766edca3f424288a2857a Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 16 Aug 2019 15:29:58 +0200 Subject: [PATCH 537/828] Internals: Added function index for Viewport and Docking. Renamed a few functions. --- imgui.cpp | 138 +++++++++++++++++++++++++++++++++++++++++++---- imgui_internal.h | 5 +- 2 files changed, 132 insertions(+), 11 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index cd0773138f44..a87899b588e5 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10262,6 +10262,29 @@ static void SettingsHandlerWindow_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandl //----------------------------------------------------------------------------- // [SECTION] VIEWPORTS, PLATFORM WINDOWS //----------------------------------------------------------------------------- +// - GetMainViewport() +// - FindViewportByID() +// - FindViewportByPlatformHandle() +// - SetCurrentViewport() [Internal] +// - SetWindowViewport() [Internal] +// - GetWindowAlwaysWantOwnViewport() [Internal] +// - UpdateTryMergeWindowIntoHostViewport() [Internal] +// - UpdateTryMergeWindowIntoHostViewports() [Internal] +// - TranslateWindowsInViewport() [Internal] +// - ScaleWindowsInViewport() [Internal] +// - FindHoveredViewportFromPlatformWindowStack() [Internal] +// - UpdateViewportsNewFrame() [Internal] +// - UpdateViewportsEndFrame() [Internal] +// - AddUpdateViewport() [Internal] +// - UpdateSelectWindowViewport() [Internal] +// - UpdatePlatformWindows() +// - RenderPlatformWindowsDefault() +// - FindPlatformMonitorForPos() [Internal] +// - FindPlatformMonitorForRect() [Internal] +// - UpdateViewportPlatformMonitor() [Internal] +// - DestroyPlatformWindow() [Internal] +// - DestroyPlatformWindows() +//----------------------------------------------------------------------------- ImGuiViewport* ImGui::GetMainViewport() { @@ -10401,7 +10424,7 @@ void ImGui::ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale) // If the back-end doesn't set MouseLastHoveredViewport or doesn't honor ImGuiViewportFlags_NoInputs, we do a search ourselves. // A) It won't take account of the possibility that non-imgui windows may be in-between our dragged window and our target window. // B) It requires Platform_GetWindowFocus to be implemented by back-end. -static ImGuiViewportP* FindViewportHoveredFromPlatformWindowStack(const ImVec2 mouse_platform_pos) +static ImGuiViewportP* FindHoveredViewportFromPlatformWindowStack(const ImVec2 mouse_platform_pos) { ImGuiContext& g = *GImGui; ImGuiViewportP* best_candidate = NULL; @@ -10416,7 +10439,7 @@ static ImGuiViewportP* FindViewportHoveredFromPlatformWindowStack(const ImVec2 m } // Update viewports and monitor infos -// Note that this is runing even if 'ImGuiConfigFlags_ViewportsEnable' is not set, in order to clear unused viewports (if any) and update monitor info. +// Note that this is running even if 'ImGuiConfigFlags_ViewportsEnable' is not set, in order to clear unused viewports (if any) and update monitor info. static void ImGui::UpdateViewportsNewFrame() { ImGuiContext& g = *GImGui; @@ -10547,7 +10570,7 @@ static void ImGui::UpdateViewportsNewFrame() { // Back-end failed at honoring its contract if it returned a viewport with the _NoInputs flag. IM_ASSERT(0); - viewport_hovered = FindViewportHoveredFromPlatformWindowStack(g.IO.MousePos); + viewport_hovered = FindHoveredViewportFromPlatformWindowStack(g.IO.MousePos); } } else @@ -10555,7 +10578,7 @@ static void ImGui::UpdateViewportsNewFrame() // If the back-end doesn't know how to honor ImGuiViewportFlags_NoInputs, we do a search ourselves. Note that this search: // A) won't take account of the possibility that non-imgui windows may be in-between our dragged window and our target window. // B) uses LastFrameAsRefViewport as a flawed replacement for the last time a window was focused (we could/should fix that by introducing Focus functions in PlatformIO) - viewport_hovered = FindViewportHoveredFromPlatformWindowStack(g.IO.MousePos); + viewport_hovered = FindHoveredViewportFromPlatformWindowStack(g.IO.MousePos); } if (viewport_hovered != NULL) g.MouseLastHoveredViewport = viewport_hovered; @@ -11043,15 +11066,21 @@ void ImGui::DestroyPlatformWindows() // Docking: ImGuiDockContext Docking/Undocking functions // Docking: ImGuiDockNode // Docking: ImGuiDockNode Tree manipulation functions -// Docking: Public Functions (SetWindowDock, DockSpace) +// Docking: Public Functions (SetWindowDock, DockSpace, DockSpaceOverViewport) // Docking: Builder Functions -// Docking: Begin/End Functions (called from Begin/End) +// Docking: Begin/End Support Functions (called from Begin/End) // Docking: Settings //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // Docking: Internal Types //----------------------------------------------------------------------------- +// - ImGuiDockRequestType +// - ImGuiDockRequest +// - ImGuiDockPreviewData +// - ImGuiDockNodeSettings +// - ImGuiDockContext +//----------------------------------------------------------------------------- static float IMGUI_DOCK_SPLITTER_SIZE = 2.0f; @@ -11198,6 +11227,22 @@ namespace ImGui // we can also very easily recreate the nodes from scratch given the settings data (this is what DockContextRebuild() does). // This is convenient as docking reconfiguration can be implemented by mostly poking at the simpler settings data. //----------------------------------------------------------------------------- +// - DockContextInitialize() +// - DockContextShutdown() +// - DockContextOnLoadSettings() +// - DockContextClearNodes() +// - DockContextRebuildNodes() +// - DockContextUpdateUndocking() +// - DockContextUpdateDocking() +// - DockContextFindNodeByID() +// - DockContextGenNodeID() +// - DockContextAddNode() +// - DockContextRemoveNode() +// - ImGuiDockContextPruneNodeData +// - DockContextPruneUnusedSettingsNodes() +// - DockContextBuildNodesFromSettings() +// - DockContextBuildAddWindowsToNodes() +//----------------------------------------------------------------------------- void ImGui::DockContextInitialize(ImGuiContext* ctx) { @@ -11242,7 +11287,7 @@ void ImGui::DockContextClearNodes(ImGuiContext* ctx, ImGuiID root_id, bool clear } // This function also acts as a defacto test to make sure we can rebuild from scratch without a glitch -void ImGui::DockContextRebuild(ImGuiContext* ctx) +void ImGui::DockContextRebuildNodes(ImGuiContext* ctx) { IMGUI_DEBUG_LOG_DOCKING("DockContextRebuild()\n"); ImGuiDockContext* dc = ctx->DockContext; @@ -11282,7 +11327,7 @@ void ImGui::DockContextUpdateUndocking(ImGuiContext* ctx) #endif if (dc->WantFullRebuild) { - DockContextRebuild(ctx); + DockContextRebuildNodes(ctx); dc->WantFullRebuild = false; } @@ -11510,6 +11555,15 @@ void ImGui::DockContextBuildAddWindowsToNodes(ImGuiContext* ctx, ImGuiID root_id //----------------------------------------------------------------------------- // Docking: ImGuiDockContext Docking/Undocking functions //----------------------------------------------------------------------------- +// - DockContextQueueDock() +// - DockContextQueueUndockWindow() +// - DockContextQueueUndockNode() +// - DockContextQueueNotifyRemovedNode() +// - DockContextProcessDock() +// - DockContextProcessUndockWindow() +// - DockContextProcessUndockNode() +// - DockContextCalcDropPosForDocking() +//----------------------------------------------------------------------------- void ImGui::DockContextQueueDock(ImGuiContext* ctx, ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, float split_ratio, bool split_outer) { @@ -11753,6 +11807,31 @@ bool ImGui::DockContextCalcDropPosForDocking(ImGuiWindow* target, ImGuiDockNode* //----------------------------------------------------------------------------- // Docking: ImGuiDockNode //----------------------------------------------------------------------------- +// - DockNodeGetTabOrder() +// - DockNodeAddWindow() +// - DockNodeRemoveWindow() +// - DockNodeMoveChildNodes() +// - DockNodeMoveWindows() +// - DockNodeApplyPosSizeToWindows() +// - DockNodeHideHostWindow() +// - ImGuiDockNodeFindInfoResults +// - DockNodeFindInfo() +// - DockNodeUpdateVisibleFlagAndInactiveChilds() +// - DockNodeUpdateVisibleFlag() +// - DockNodeStartMouseMovingWindow() +// - DockNodeUpdate() +// - DockNodeUpdateWindowMenu() +// - DockNodeUpdateTabBar() +// - DockNodeAddTabBar() +// - DockNodeRemoveTabBar() +// - DockNodeIsDropAllowedOne() +// - DockNodeIsDropAllowed() +// - DockNodeCalcTabBarLayout() +// - DockNodeCalcSplitRects() +// - DockNodeCalcDropRectsAndTestMousePos() +// - DockNodePreviewDockCalc() +// - DockNodePreviewDockRender() +//----------------------------------------------------------------------------- ImGuiDockNode::ImGuiDockNode(ImGuiID id) { @@ -13017,6 +13096,14 @@ static void ImGui::DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDock //----------------------------------------------------------------------------- // Docking: ImGuiDockNode Tree manipulation functions //----------------------------------------------------------------------------- +// - DockNodeTreeSplit() +// - DockNodeTreeMerge() +// - DockNodeTreeUpdatePosSize() +// - DockNodeTreeUpdateSplitterFindTouchingNode() +// - DockNodeTreeUpdateSplitter() +// - DockNodeTreeFindFallbackLeafNode() +// - DockNodeTreeFindNodeByPos() +//----------------------------------------------------------------------------- void ImGui::DockNodeTreeSplit(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiAxis split_axis, int split_inheritor_child_idx, float split_ratio, ImGuiDockNode* new_node) { @@ -13352,6 +13439,10 @@ ImGuiDockNode* ImGui::DockNodeTreeFindNodeByPos(ImGuiDockNode* node, ImVec2 pos) //----------------------------------------------------------------------------- // Docking: Public Functions (SetWindowDock, DockSpace, DockSpaceOverViewport) //----------------------------------------------------------------------------- +// - SetWindowDock() [Internal] +// - DockSpace() +// - DockSpaceOverViewport() +//----------------------------------------------------------------------------- // [Internal] Called via SetNextWindowDockID() void ImGui::SetWindowDock(ImGuiWindow* window, ImGuiID dock_id, ImGuiCond cond) @@ -13511,8 +13602,24 @@ ImGuiID ImGui::DockSpaceOverViewport(ImGuiViewport* viewport, ImGuiDockNodeFlags // Docking: Builder Functions //----------------------------------------------------------------------------- // Very early end-user API to manipulate dock nodes. +// Only available in imgui_internal.h. Expect this API to change/break! // It is expected that those functions are all called _before_ the dockspace node submission. //----------------------------------------------------------------------------- +// - DockBuilderDockWindow() +// - DockBuilderGetNode() +// - DockBuilderSetNodePos() +// - DockBuilderSetNodeSize() +// - DockBuilderAddNode() +// - DockBuilderRemoveNode() +// - DockBuilderRemoveNodeChildNodes() +// - DockBuilderRemoveNodeDockedWindows() +// - DockBuilderSplitNode() +// - DockBuilderCopyNodeRec() +// - DockBuilderCopyNode() +// - DockBuilderCopyWindowSettings() +// - DockBuilderCopyDockSpace() +// - DockBuilderFinish() +//----------------------------------------------------------------------------- void ImGui::DockBuilderDockWindow(const char* window_name, ImGuiID node_id) { @@ -13888,7 +13995,12 @@ void ImGui::DockBuilderFinish(ImGuiID root_id) } //----------------------------------------------------------------------------- -// Docking: Begin/End Functions (called from Begin/End) +// Docking: Begin/End Support Functions (called from Begin/End) +//----------------------------------------------------------------------------- +// - GetWindowAlwaysWantOwnTabBar() +// - BeginDocked() +// - BeginAsDockableDragDropSource() +// - BeginAsDockableDragDropTarget() //----------------------------------------------------------------------------- bool ImGui::GetWindowAlwaysWantOwnTabBar(ImGuiWindow* window) @@ -14139,6 +14251,14 @@ void ImGui::BeginAsDockableDragDropTarget(ImGuiWindow* window) //----------------------------------------------------------------------------- // Docking: Settings //----------------------------------------------------------------------------- +// - DockSettingsRenameNodeReferences() +// - DockSettingsRemoveNodeReferences() +// - DockSettingsFindNodeSettings() +// - DockSettingsHandler_ReadOpen() +// - DockSettingsHandler_ReadLine() +// - DockSettingsHandler_DockNodeToSettings() +// - DockSettingsHandler_WriteAll() +//----------------------------------------------------------------------------- static void ImGui::DockSettingsRenameNodeReferences(ImGuiID old_node_id, ImGuiID new_node_id) { diff --git a/imgui_internal.h b/imgui_internal.h index a13e3633cc1b..0f6366aa9a70 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1774,7 +1774,7 @@ namespace ImGui IMGUI_API void DockContextInitialize(ImGuiContext* ctx); IMGUI_API void DockContextShutdown(ImGuiContext* ctx); IMGUI_API void DockContextOnLoadSettings(ImGuiContext* ctx); - IMGUI_API void DockContextRebuild(ImGuiContext* ctx); + IMGUI_API void DockContextRebuildNodes(ImGuiContext* ctx); IMGUI_API void DockContextUpdateUndocking(ImGuiContext* ctx); IMGUI_API void DockContextUpdateDocking(ImGuiContext* ctx); IMGUI_API void DockContextQueueDock(ImGuiContext* ctx, ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, float split_ratio, bool split_outer); @@ -1789,8 +1789,9 @@ namespace ImGui IMGUI_API void SetWindowDock(ImGuiWindow* window, ImGuiID dock_id, ImGuiCond cond); // Docking - Builder function needs to be generally called before the DockSpace() node is submitted. + // Important: do not hold on ImGuiDockNode* pointers. They may be invalidated by any split/merge/remove operation and every frame. IMGUI_API void DockBuilderDockWindow(const char* window_name, ImGuiID node_id); - IMGUI_API ImGuiDockNode*DockBuilderGetNode(ImGuiID node_id); // Warning: DO NOT HOLD ON ImGuiDockNode* pointer, will be invalided by any split/merge/remove operation. + IMGUI_API ImGuiDockNode*DockBuilderGetNode(ImGuiID node_id); inline ImGuiDockNode* DockBuilderGetCentralNode(ImGuiID node_id) { ImGuiDockNode* node = DockBuilderGetNode(node_id); if (!node) return NULL; return DockNodeGetRootNode(node)->CentralNode; } IMGUI_API ImGuiID DockBuilderAddNode(ImGuiID node_id, ImGuiDockNodeFlags flags = 0); // Use (flags == ImGuiDockNodeFlags_DockSpace) to create a dockspace, otherwise it'll create a floating node. IMGUI_API void DockBuilderRemoveNode(ImGuiID node_id); // Remove node and all its child, undock all windows From 76ccbb899dfa744208d3f808ab77824e89f23f5a Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 19 Aug 2019 11:21:33 +0200 Subject: [PATCH 538/828] Viewport: Fix modal/popup window being stuck in unowned hidden viewport associated to fallback window without stealing it back. (#1542) Viewport: Fix modal reference viewport when opened outside of another window. + Comments --- imgui.cpp | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index a87899b588e5..67f5376f663a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4519,9 +4519,9 @@ void ImGui::Render() if (g.FrameCountEnded != g.FrameCount) EndFrame(); g.FrameCountRendered = g.FrameCount; - - // Gather ImDrawList to render (for each active window) g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = g.IO.MetricsRenderWindows = 0; + + // Add background ImDrawList (for each active viewport) for (int n = 0; n != g.Viewports.Size; n++) { ImGuiViewportP* viewport = g.Viewports[n]; @@ -4530,9 +4530,10 @@ void ImGui::Render() AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[0], GetBackgroundDrawList(viewport)); } + // Add ImDrawList to render (for each active window) ImGuiWindow* windows_to_render_top_most[2]; windows_to_render_top_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindow : NULL; - windows_to_render_top_most[1] = g.NavWindowingTarget ? g.NavWindowingList : NULL; + windows_to_render_top_most[1] = (g.NavWindowingTarget ? g.NavWindowingList : NULL); for (int n = 0; n != g.Windows.Size; n++) { ImGuiWindow* window = g.Windows[n]; @@ -4553,8 +4554,11 @@ void ImGui::Render() { ImGuiViewportP* viewport = g.Viewports[n]; viewport->DrawDataBuilder.FlattenIntoSingleLayer(); + + // Add foreground ImDrawList (for each active viewport) if (viewport->DrawLists[1] != NULL) AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[0], GetForegroundDrawList(viewport)); + SetupViewportDrawData(viewport, &viewport->DrawDataBuilder.Layers[0]); g.IO.MetricsRenderVertices += viewport->DrawData->TotalVtxCount; g.IO.MetricsRenderIndices += viewport->DrawData->TotalIdxCount; @@ -8298,7 +8302,10 @@ bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags fla // Center modal windows by default // FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window. if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) == 0) - SetNextWindowPos(window->Viewport->GetCenter(), ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); + { + ImGuiViewportP* viewport = window->WasActive ? window->Viewport : (ImGuiViewportP*)GetMainViewport(); // FIXME-VIEWPORT: What may be our reference viewport? + SetNextWindowPos(viewport->GetCenter(), ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); + } flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoDocking; const bool is_open = Begin(name, p_open, flags); @@ -10342,8 +10349,9 @@ static bool ImGui::GetWindowAlwaysWantOwnViewport(ImGuiWindow* window) if (g.IO.ConfigViewportsNoAutoMerge || (window->WindowClass.ViewportFlagsOverrideSet & ImGuiViewportFlags_NoAutoMerge)) if (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable) if (!window->DockIsActive) - if ((window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) == 0) - return true; + if ((window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip)) == 0) + if ((window->Flags & ImGuiWindowFlags_Popup) == 0 || (window->Flags & ImGuiWindowFlags_Modal) != 0) + return true; return false; } @@ -10715,7 +10723,7 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasViewport) == 0) { // By default inherit from parent window - if (window->Viewport == NULL && window->ParentWindow) + if (window->Viewport == NULL && window->ParentWindow && !window->ParentWindow->FallbackWindow) window->Viewport = window->ParentWindow->Viewport; // Attempt to restore saved viewport id (= window that hasn't been activated yet), try to restore the viewport based on saved 'window->ViewportPos' restored from .ini file From 72090b646f00b53bb9e0adc0bff7b8d37388d1bf Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 22 Aug 2019 11:34:17 +0200 Subject: [PATCH 539/828] Fixed incorrect assignment of IsFallbackWindow which would tag dock node host windows created in NewFrame() as such, messing with popup viewport inheritance. --- imgui.cpp | 12 ++++++------ imgui_internal.h | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index a2ba3d8d6934..e02578b80263 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2733,7 +2733,7 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) SkipItems = false; Appearing = false; Hidden = false; - FallbackWindow = false; + IsFallbackWindow = false; HasCloseButton = false; ResizeBorderHeld = -1; BeginCount = 0; @@ -4062,10 +4062,10 @@ void ImGui::NewFrame() // Create implicit/fallback window - which we will only render it if the user has added something to it. // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags. // This fallback is particularly important as it avoid ImGui:: calls from crashing. + g.FrameScopePushedFallbackWindow = true; SetNextWindowSize(ImVec2(400,400), ImGuiCond_FirstUseEver); Begin("Debug##Default"); - IM_ASSERT(g.CurrentWindow->FallbackWindow == true); - g.FrameScopePushedFallbackWindow = true; + IM_ASSERT(g.CurrentWindow->IsFallbackWindow == true); #ifdef IMGUI_ENABLE_TEST_ENGINE ImGuiTestEngineHook_PostNewFrame(&g); @@ -5796,7 +5796,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) const int current_frame = g.FrameCount; const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame); - window->FallbackWindow = (g.CurrentWindowStack.Size == 0); + window->IsFallbackWindow = (g.CurrentWindowStack.Size == 0 && g.FrameScopePushedFallbackWindow); // Update the Appearing flag bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on @@ -10731,7 +10731,7 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasViewport) == 0) { // By default inherit from parent window - if (window->Viewport == NULL && window->ParentWindow && !window->ParentWindow->FallbackWindow) + if (window->Viewport == NULL && window->ParentWindow && !window->ParentWindow->IsFallbackWindow) window->Viewport = window->ParentWindow->Viewport; // Attempt to restore saved viewport id (= window that hasn't been activated yet), try to restore the viewport based on saved 'window->ViewportPos' restored from .ini file @@ -14024,7 +14024,7 @@ bool ImGui::GetWindowAlwaysWantOwnTabBar(ImGuiWindow* window) ImGuiContext& g = *GImGui; if (g.IO.ConfigDockingAlwaysTabBar || window->WindowClass.DockingAlwaysTabBar) if ((window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoDocking)) == 0) - if (!window->FallbackWindow) // We don't support AlwaysTabBar on the fallback/implicit window to avoid unused dock-node overhead/noise + if (!window->IsFallbackWindow) // We don't support AlwaysTabBar on the fallback/implicit window to avoid unused dock-node overhead/noise return true; return false; } diff --git a/imgui_internal.h b/imgui_internal.h index 91bab1daa430..ea1709ff264a 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1465,7 +1465,7 @@ struct IMGUI_API ImGuiWindow bool SkipItems; // Set when items can safely be all clipped (e.g. window not visible or collapsed) bool Appearing; // Set during the frame where the window is appearing (or re-appearing) bool Hidden; // Do not display (== (HiddenFrames*** > 0)) - bool FallbackWindow; + bool IsFallbackWindow; bool HasCloseButton; // Set when the window has a close button (p_open != NULL) signed char ResizeBorderHeld; // Current border being held for resize (-1: none, otherwise 0-3) short BeginCount; // Number of Begin() during the current frame (generally 0 or 1, 1+ if appending via multiple Begin/End pairs) From d8f9f6ba2abb6cfc00f704b887264ac9472cf9f7 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 22 Aug 2019 13:50:55 +0200 Subject: [PATCH 540/828] Viewport: Fixed issue where resize grip would display hovered (before of extruded hit rectangle) while mouse is still off the OS bounds so click would miss it and focus the OS window behind expected one. (#1542) --- imgui.cpp | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index e02578b80263..3c6c5a66445c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5398,6 +5398,15 @@ static bool ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au ImVec2 pos_target(FLT_MAX, FLT_MAX); ImVec2 size_target(FLT_MAX, FLT_MAX); + // Clip mouse interaction rectangles within the viewport (in practice the narrowing is going to happen most of the time). + // - Not narrowing would mostly benefit the situation where OS windows _without_ decoration have a threshold for hovering when outside their limits. + // This is however not the case with current back-ends under Win32, but a custom borderless window implementation would benefit from it. + // - When decoration are enabled we typically benefit from that distance, but then our resize elements would be conflicting with OS resize elements, so we also narrow. + // - Note that we are unable to tell if the platform setup allows hovering with a distance threshold (on Win32, decorated window have such threshold). + ImRect clip_viewport_rect(-FLT_MAX, -FLT_MAX, +FLT_MAX, +FLT_MAX); + if (!(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport) || (g.IO.MouseHoveredViewport != window->ViewportId) || !(window->Viewport->Flags & ImGuiViewportFlags_NoDecoration)) + clip_viewport_rect = window->Viewport->GetRect(); + // Resize grips and borders are on layer 1 window->DC.NavLayerCurrent = ImGuiNavLayer_Menu; window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Menu); @@ -5413,6 +5422,7 @@ static bool ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au ImRect resize_rect(corner - grip.InnerDir * grip_hover_outer_size, corner + grip.InnerDir * grip_hover_inner_size); if (resize_rect.Min.x > resize_rect.Max.x) ImSwap(resize_rect.Min.x, resize_rect.Max.x); if (resize_rect.Min.y > resize_rect.Max.y) ImSwap(resize_rect.Min.y, resize_rect.Max.y); + resize_rect.ClipWith(clip_viewport_rect); bool hovered, held; ButtonBehavior(resize_rect, window->GetID((void*)(intptr_t)resize_grip_n), &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus); //GetForegroundDrawList(window)->AddRect(resize_rect.Min, resize_rect.Max, IM_COL32(255, 255, 0, 255)); @@ -5440,8 +5450,9 @@ static bool ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au { bool hovered, held; ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_inner_size, WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); + border_rect.ClipWith(clip_viewport_rect); ButtonBehavior(border_rect, window->GetID((void*)(intptr_t)(border_n + 4)), &hovered, &held, ImGuiButtonFlags_FlattenChildren); - //GetForegroundDrawLists(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255)); + //GetForegroundDrawList(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255)); if ((hovered && g.HoveredIdTimer > WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER) || held) { g.MouseCursor = (border_n & 1) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS; @@ -5461,6 +5472,10 @@ static bool ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au } PopID(); + // Resize nav layer + window->DC.NavLayerCurrent = ImGuiNavLayer_Main; + window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Main); + // Navigation resize (keyboard/gamepad) if (g.NavWindowingTarget && g.NavWindowingTarget->RootWindow == window) { @@ -5493,10 +5508,6 @@ static bool ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au MarkIniSettingsDirty(window); } - // Resize nav layer - window->DC.NavLayerCurrent = ImGuiNavLayer_Main; - window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Main); - window->Size = window->SizeFull; return ret_auto_fit; } From 27431dcc6b8101ddcdbcdb3777765cb9e8f50013 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 20 Aug 2019 19:24:23 +0200 Subject: [PATCH 541/828] Docking: fix BeginDocked() path that creates node so that SetNextWindowDockID() doesn't immediately discard the node..(#2109) Amend 515ecbddc270f3129642e9f1ab8d95e3778a0b95, not sure at this point if the (auto_dock_node) flag was needed at all. Comments. Exposed DockContextGenNodeID() in imgui_internal.h --- imgui.cpp | 18 ++++++++++-------- imgui_internal.h | 3 ++- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 3c6c5a66445c..09688b126f21 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3823,9 +3823,9 @@ void ImGui::NewFrame() // Perform simple check: error if Docking or Viewport are enabled _exactly_ on frame 1 (instead of frame 0 or later), which is a common error leading to loss of .ini data. g.ConfigFlagsLastFrame = g.ConfigFlagsCurrFrame; if (g.FrameCount == 1 && (g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable) && (g.ConfigFlagsLastFrame & ImGuiConfigFlags_DockingEnable) == 0) - IM_ASSERT(0 && "Please DockingEnable before the first call to NewFrame()! Otherwise you will lose your .ini settings!"); + IM_ASSERT(0 && "Please set DockingEnable before the first call to NewFrame()! Otherwise you will lose your .ini settings!"); if (g.FrameCount == 1 && (g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) && (g.ConfigFlagsLastFrame & ImGuiConfigFlags_ViewportsEnable) == 0) - IM_ASSERT(0 && "Please ViewportEnable before the first call to NewFrame()! Otherwise you will lose your .ini settings!"); + IM_ASSERT(0 && "Please set ViewportsEnable before the first call to NewFrame()! Otherwise you will lose your .ini settings!"); // Perform simple checks: multi-viewport and platform windows support if (g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) @@ -11190,7 +11190,6 @@ namespace ImGui { // ImGuiDockContext static ImGuiDockNode* DockContextAddNode(ImGuiContext* ctx, ImGuiID id); - static ImGuiID DockContextGenNodeID(ImGuiContext* ctx); static void DockContextRemoveNode(ImGuiContext* ctx, ImGuiDockNode* node, bool merge_sibling_into_parent_node); static void DockContextQueueNotifyRemovedNode(ImGuiContext* ctx, ImGuiDockNode* node); static void DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req); @@ -11396,7 +11395,7 @@ static ImGuiDockNode* ImGui::DockContextFindNodeByID(ImGuiContext* ctx, ImGuiID return (ImGuiDockNode*)ctx->DockContext->Nodes.GetVoidPtr(id); } -static ImGuiID ImGui::DockContextGenNodeID(ImGuiContext* ctx) +ImGuiID ImGui::DockContextGenNodeID(ImGuiContext* ctx) { // Generate an ID for new node (the exact ID value doesn't matter as long as it is not already used) // FIXME-OPT FIXME-DOCKING: This is suboptimal, even if the node count is small enough not to be a worry. We should poke in ctx->Nodes to find a suitable ID faster. @@ -11413,6 +11412,8 @@ static ImGuiDockNode* ImGui::DockContextAddNode(ImGuiContext* ctx, ImGuiID id) id = DockContextGenNodeID(ctx); else IM_ASSERT(DockContextFindNodeByID(ctx, id) == NULL); + + // We don't set node->LastFrameAlive on construction. Nodes are always created at all time to reflect .ini settings! IMGUI_DEBUG_LOG_DOCKING("DockContextAddNode 0x%08X\n", id); ImGuiDockNode* node = IM_NEW(ImGuiDockNode)(id); ctx->DockContext->Nodes.SetVoidPtr(node->ID, node); @@ -11917,8 +11918,8 @@ static void ImGui::DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window, b window->DockIsActive = (node->Windows.Size > 1); window->DockTabWantClose = false; - // If 2+ windows appeared on the same frame, creating a new DockNode+TabBar from the second window, - // then we need to hide the first one after the fact otherwise it would be visible as a standalone window for one frame. + // If more than 2 windows appeared on the same frame, we'll create a new hosting DockNode from the point of the second window submission. + // Then we need to hide the first window (after its been output) otherwise it would be visible as a standalone window for one frame. if (node->HostWindow == NULL && node->Windows.Size == 2 && node->Windows[0]->WasActive == false) { node->Windows[0]->Hidden = true; @@ -12464,6 +12465,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) if (node->IsRootNode() && (g.MovingWindow == NULL || g.MovingWindow->RootWindow != host_window)) BeginAsDockableDragDropTarget(host_window); + // We update this after DockNodeUpdateTabBar() node->LastFrameActive = g.FrameCount; // Recurse into children @@ -14087,8 +14089,7 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) { node = DockContextAddNode(ctx, window->DockId); node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_Window; - if (auto_dock_node) - node->LastFrameAlive = g.FrameCount; + node->LastFrameAlive = g.FrameCount; } // If the node just turned visible, it doesn't have a Size assigned by DockNodeTreeUpdatePosSize() yet, @@ -14913,6 +14914,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) IM_ASSERT(node->ChildNodes[1] == NULL || node->ChildNodes[1]->ParentNode == node); ImGui::BulletText("Pos (%.0f,%.0f), Size (%.0f, %.0f) Ref (%.0f, %.0f)", node->Pos.x, node->Pos.y, node->Size.x, node->Size.y, node->SizeRef.x, node->SizeRef.y); + NodeWindow(node->HostWindow, "HostWindow"); NodeWindow(node->VisibleWindow, "VisibleWindow"); ImGui::BulletText("SelectedTabID: 0x%08X, LastFocusedNodeID: 0x%08X", node->SelectedTabID, node->LastFocusedNodeID); ImGui::BulletText("Misc:%s%s%s%s", node->IsDockSpace() ? " IsDockSpace" : "", node->IsCentralNode() ? " IsCentralNode" : "", (g.FrameCount - node->LastFrameAlive < 2) ? " IsAlive" : "", (g.FrameCount - node->LastFrameActive < 2) ? " IsActive" : ""); diff --git a/imgui_internal.h b/imgui_internal.h index ea1709ff264a..effdda285831 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1606,7 +1606,7 @@ struct ImGuiTabBar { ImVector Tabs; ImGuiID ID; // Zero for tab-bars used by docking - ImGuiID SelectedTabId; // Selected tab + ImGuiID SelectedTabId; // Selected tab/window ImGuiID NextSelectedTabId; ImGuiID VisibleTabId; // Can occasionally be != SelectedTabId (e.g. when previewing contents for CTRL+TAB preview) int CurrFrameVisible; @@ -1777,6 +1777,7 @@ namespace ImGui IMGUI_API void DockContextRebuildNodes(ImGuiContext* ctx); IMGUI_API void DockContextUpdateUndocking(ImGuiContext* ctx); IMGUI_API void DockContextUpdateDocking(ImGuiContext* ctx); + IMGUI_API ImGuiID DockContextGenNodeID(ImGuiContext* ctx); IMGUI_API void DockContextQueueDock(ImGuiContext* ctx, ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, float split_ratio, bool split_outer); IMGUI_API void DockContextQueueUndockWindow(ImGuiContext* ctx, ImGuiWindow* window); IMGUI_API void DockContextQueueUndockNode(ImGuiContext* ctx, ImGuiDockNode* node); From 10a202422a8e03d2871946c5d4131e145f987f30 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 20 Aug 2019 19:24:37 +0200 Subject: [PATCH 542/828] Docking: Extracted some of BeginDocked() into a DockContextBindNodeToWindow() function. Moved one of the undocking blurb to favor fast path. (Commit intended to have no functional side effects) --- imgui.cpp | 107 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 62 insertions(+), 45 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 09688b126f21..eb4682e090f9 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11197,6 +11197,7 @@ namespace ImGui static void DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node); static void DockContextPruneUnusedSettingsNodes(ImGuiContext* ctx); static ImGuiDockNode* DockContextFindNodeByID(ImGuiContext* ctx, ImGuiID id); + static ImGuiDockNode* DockContextBindNodeToWindow(ImGuiContext* ctx, ImGuiWindow* window); static void DockContextClearNodes(ImGuiContext* ctx, ImGuiID root_id, bool clear_persistent_docking_refs); // Use root_id==0 to clear all static void DockContextBuildNodesFromSettings(ImGuiContext* ctx, ImGuiDockNodeSettings* node_settings_array, int node_settings_count); static void DockContextBuildAddWindowsToNodes(ImGuiContext* ctx, ImGuiID root_id); // Use root_id==0 to add all @@ -11208,6 +11209,7 @@ namespace ImGui static void DockNodeApplyPosSizeToWindows(ImGuiDockNode* node); static void DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window, ImGuiID save_dock_id); static void DockNodeHideHostWindow(ImGuiDockNode* node); + static ImGuiWindow* DockNodeFindWindowByID(ImGuiDockNode* node, ImGuiID id); static void DockNodeUpdate(ImGuiDockNode* node); static void DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* node); static void DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_window); @@ -11261,6 +11263,7 @@ namespace ImGui // - DockContextUpdateUndocking() // - DockContextUpdateDocking() // - DockContextFindNodeByID() +// - DockContextBindNodeToWindow() // - DockContextGenNodeID() // - DockContextAddNode() // - DockContextRemoveNode() @@ -11800,7 +11803,7 @@ void ImGui::DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node) } else { - // Otherwise delete the previous node by merging the other sibling back into the parent node. + // Otherwise extract our node and merging our sibling back into the parent node. IM_ASSERT(node->ParentNode->ChildNodes[0] == node || node->ParentNode->ChildNodes[1] == node); int index_in_parent = (node->ParentNode->ChildNodes[0] == node) ? 0 : 1; node->ParentNode->ChildNodes[index_in_parent] = NULL; @@ -12295,6 +12298,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) const ImGuiDockNodeFlags node_flags = node->GetMergedFlags(); + // Bind or create host window ImGuiWindow* host_window = NULL; bool beginned_into_host_window = false; if (node->IsDockSpace()) @@ -14042,6 +14046,51 @@ bool ImGui::GetWindowAlwaysWantOwnTabBar(ImGuiWindow* window) return false; } +static ImGuiDockNode* ImGui::DockContextBindNodeToWindow(ImGuiContext* ctx, ImGuiWindow* window) +{ + ImGuiContext& g = *ctx; + ImGuiDockNode* node = DockContextFindNodeByID(ctx, window->DockId); + IM_ASSERT(window->DockNode == NULL); + + // We should not be docking into a split node (SetWindowDock should avoid this) + if (node && node->IsSplitNode()) + { + DockContextProcessUndockWindow(ctx, window); + return NULL; + } + + // Create node + if (node == NULL) + { + node = DockContextAddNode(ctx, window->DockId); + node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_Window; + node->LastFrameAlive = g.FrameCount; + } + + // If the node just turned visible and is part of a hierarchy, it doesn't have a Size assigned by DockNodeTreeUpdatePosSize() yet, + // so we're forcing a Pos/Size update from the first ancestor that is already visible (often it will be the root node). + // If we don't do this, the window will be assigned a zero-size on its first frame, which won't ideally warm up the layout. + // This is a little wonky because we don't normally update the Pos/Size of visible node mid-frame. + if (!node->IsVisible) + { + ImGuiDockNode* ancestor_node = node; + while (!ancestor_node->IsVisible) + { + ancestor_node->IsVisible = true; + ancestor_node->MarkedForPosSizeWrite = true; + if (ancestor_node->ParentNode) + ancestor_node = ancestor_node->ParentNode; + } + IM_ASSERT(ancestor_node->Size.x > 0.0f && ancestor_node->Size.y > 0.0f); + DockNodeTreeUpdatePosSize(ancestor_node, ancestor_node->Pos, ancestor_node->Size, true); + } + + // Add window to node + DockNodeAddWindow(node, window, true); + IM_ASSERT(node == window->DockNode); + return node; +} + void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) { ImGuiContext* ctx = GImGui; @@ -14075,44 +14124,9 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) IM_ASSERT(window->DockId == node->ID); if (window->DockId != 0 && node == NULL) { - node = DockContextFindNodeByID(ctx, window->DockId); - - // We should not be docking into a split node (SetWindowDock should avoid this) - if (node && node->IsSplitNode()) - { - DockContextProcessUndockWindow(ctx, window); - return; - } - - // Create node + node = DockContextBindNodeToWindow(ctx, window); if (node == NULL) - { - node = DockContextAddNode(ctx, window->DockId); - node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_Window; - node->LastFrameAlive = g.FrameCount; - } - - // If the node just turned visible, it doesn't have a Size assigned by DockNodeTreeUpdatePosSize() yet, - // so we're forcing a Pos/Size update from the first ancestor that is already visible (often it will be the root node). - // If we don't do this, the window will be assigned a zero-size on its first frame, which won't ideally warm up the layout. - // This is a little wonky because we don't normally update the Pos/Size of visible node mid-frame. - if (!node->IsVisible) - { - ImGuiDockNode* ancestor_node = node; - while (!ancestor_node->IsVisible) - { - ancestor_node->IsVisible = true; - ancestor_node->MarkedForPosSizeWrite = true; - if (ancestor_node->ParentNode) - ancestor_node = ancestor_node->ParentNode; - } - IM_ASSERT(ancestor_node->Size.x > 0.0f && ancestor_node->Size.y > 0.0f); - DockNodeTreeUpdatePosSize(ancestor_node, ancestor_node->Pos, ancestor_node->Size, true); - } - - // Add window to node - DockNodeAddWindow(node, window, true); - IM_ASSERT(node == window->DockNode); + return; } #if 0 @@ -14142,23 +14156,26 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) return; } - // Undock if we are submitted earlier than the host window - if (node->HostWindow && window->BeginOrderWithinContext < node->HostWindow->BeginOrderWithinContext) - { - DockContextProcessUndockWindow(ctx, window); - return; - } - + // Fast path return. It is common for windows to hold on a persistent DockId but be the only visible window, + // and never create neither a host window neither a tab bar. // FIXME-DOCK: replace ->HostWindow NULL compare with something more explicit (~was initially intended as a first frame test) if (node->HostWindow == NULL) { window->DockTabIsVisible = false; return; } + IM_ASSERT(node->HostWindow); IM_ASSERT(node->IsLeafNode()); IM_ASSERT(node->Size.x > 0.0f && node->Size.y > 0.0f); + // Undock if we are submitted earlier than the host window + if (window->BeginOrderWithinContext < node->HostWindow->BeginOrderWithinContext) + { + DockContextProcessUndockWindow(ctx, window); + return; + } + // Position window SetNextWindowPos(node->Pos); SetNextWindowSize(node->Size); From 483534b525063a242d63f4481e55c88cf0c5eba9 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 23 Aug 2019 12:31:14 +0200 Subject: [PATCH 543/828] Internals: Using simpler ImVec2ih construct + fixed misnamed member. --- imgui.cpp | 28 ++++++++++++++-------------- imgui_internal.h | 11 ++++++----- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 6faec2bb60b6..7e3977e40da7 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5182,7 +5182,7 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFl if (settings->ViewportId) { window->ViewportId = settings->ViewportId; - window->ViewportPos = ImVec2(settings->ViewportPosh.x, settings->ViewportPosh.y); + window->ViewportPos = ImVec2(settings->ViewportPos.x, settings->ViewportPos.y); } else { @@ -7304,8 +7304,8 @@ void ImGui::SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond co static void SetWindowHitTestHole(ImGuiWindow* window, const ImVec2& pos, const ImVec2& size) { IM_ASSERT(window->HitTestHoleSize.x == 0); // We don't support multiple holes/hit test filters - window->HitTestHoleSize = ImVec2ih((short)size.x, (short)size.y); - window->HitTestHoleOffset = ImVec2ih((short)(pos.x - window->Pos.x), (short)(pos.y - window->Pos.y)); + window->HitTestHoleSize = ImVec2ih(size); + window->HitTestHoleOffset = ImVec2ih(pos - window->Pos); } void ImGui::SetWindowCollapsed(bool collapsed, ImGuiCond cond) @@ -10217,7 +10217,7 @@ static void SettingsHandlerWindow_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, if (sscanf(line, "Pos=%i,%i", &x, &y) == 2) { settings->Pos = ImVec2ih((short)x, (short)y); } else if (sscanf(line, "Size=%i,%i", &x, &y) == 2) { settings->Size = ImVec2ih((short)x, (short)y); } else if (sscanf(line, "ViewportId=0x%08X", &u1) == 1) { settings->ViewportId = u1; } - else if (sscanf(line, "ViewportPos=%i,%i", &x, &y) == 2) { settings->ViewportPosh = ImVec2ih((short)x, (short)y); } + else if (sscanf(line, "ViewportPos=%i,%i", &x, &y) == 2) { settings->ViewportPos = ImVec2ih((short)x, (short)y); } else if (sscanf(line, "Collapsed=%d", &i) == 1) { settings->Collapsed = (i != 0); } else if (sscanf(line, "DockId=0x%X,%d", &u1, &i) == 2) { settings->DockId = u1; settings->DockOrder = (short)i; } else if (sscanf(line, "DockId=0x%X", &u1) == 1) { settings->DockId = u1; settings->DockOrder = -1; } @@ -10242,10 +10242,10 @@ static void SettingsHandlerWindow_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandl window->SettingsIdx = g.SettingsWindows.index_from_ptr(settings); } IM_ASSERT(settings->ID == window->ID); - settings->Pos = ImVec2ih((short)(window->Pos.y - window->ViewportPos.y), (short)(window->Pos.y - window->ViewportPos.y)); - settings->Size = ImVec2ih((short)window->SizeFull.x, (short)window->SizeFull.y); + settings->Pos = ImVec2ih(window->Pos - window->ViewportPos); + settings->Size = ImVec2ih(window->SizeFull); settings->ViewportId = window->ViewportId; - settings->ViewportPosh = ImVec2ih((short)window->ViewportPos.x, (short)window->ViewportPos.y); + settings->ViewportPos = ImVec2ih(window->ViewportPos); IM_ASSERT(window->DockNode == NULL || window->DockNode->ID == window->DockId); settings->DockId = window->DockId; settings->ClassId = window->WindowClass.ClassId; @@ -10261,7 +10261,7 @@ static void SettingsHandlerWindow_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandl buf->appendf("[%s][%s]\n", handler->TypeName, settings->Name); if (settings->ViewportId != 0 && settings->ViewportId != ImGui::IMGUI_VIEWPORT_DEFAULT_ID) { - buf->appendf("ViewportPos=%d,%d\n", settings->ViewportPosh.x, settings->ViewportPosh.y); + buf->appendf("ViewportPos=%d,%d\n", settings->ViewportPos.x, settings->ViewportPos.y); buf->appendf("ViewportId=0x%08X\n", settings->ViewportId); } if (settings->Pos.x != 0 || settings->Pos.y != 0 || settings->ViewportId == ImGui::IMGUI_VIEWPORT_DEFAULT_ID) @@ -13929,10 +13929,10 @@ void ImGui::DockBuilderCopyWindowSettings(const char* src_name, const char* dst_ } else if (ImGuiWindowSettings* dst_settings = FindOrCreateWindowSettings(dst_name)) { - ImVec2ih window_pos_2ih = ImVec2ih((short)src_window->Pos.x, (short)src_window->Pos.y); + ImVec2ih window_pos_2ih = ImVec2ih(src_window->Pos); if (src_window->ViewportId != 0 && src_window->ViewportId != IMGUI_VIEWPORT_DEFAULT_ID) { - dst_settings->ViewportPosh = window_pos_2ih; + dst_settings->ViewportPos = window_pos_2ih; dst_settings->ViewportId = src_window->ViewportId; dst_settings->Pos = ImVec2ih(0, 0); } @@ -13940,7 +13940,7 @@ void ImGui::DockBuilderCopyWindowSettings(const char* src_name, const char* dst_ { dst_settings->Pos = window_pos_2ih; } - dst_settings->Size = ImVec2ih((short)src_window->SizeFull.x, (short)src_window->SizeFull.y); + dst_settings->Size = ImVec2ih(src_window->SizeFull); dst_settings->Collapsed = src_window->Collapsed; } } @@ -14412,9 +14412,9 @@ static void DockSettingsHandler_DockNodeToSettings(ImGuiDockContext* dc, ImGuiDo node_settings.SplitAxis = node->IsSplitNode() ? (char)node->SplitAxis : ImGuiAxis_None; node_settings.Depth = (char)depth; node_settings.Flags = (node->LocalFlags & ImGuiDockNodeFlags_SavedFlagsMask_); - node_settings.Pos = ImVec2ih((short)node->Pos.x, (short)node->Pos.y); - node_settings.Size = ImVec2ih((short)node->Size.x, (short)node->Size.y); - node_settings.SizeRef = ImVec2ih((short)node->SizeRef.x, (short)node->SizeRef.y); + node_settings.Pos = ImVec2ih(node->Pos); + node_settings.Size = ImVec2ih(node->Size); + node_settings.SizeRef = ImVec2ih(node->SizeRef); dc->SettingsNodes.push_back(node_settings); if (node->ChildNodes[0]) DockSettingsHandler_DockNodeToSettings(dc, node->ChildNodes[0], depth + 1); diff --git a/imgui_internal.h b/imgui_internal.h index eb01cb67c304..aad0bb77eaf5 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -544,8 +544,9 @@ struct ImVec1 struct ImVec2ih { short x, y; - ImVec2ih() { x = y = 0; } - ImVec2ih(short _x, short _y) { x = _x; y = _y; } + ImVec2ih() { x = y = 0; } + ImVec2ih(short _x, short _y) { x = _x; y = _y; } + explicit ImVec2ih(const ImVec2& rhs) { x = (short)rhs.x; y = (short)rhs.y; } }; // 2D axis aligned bounding-box @@ -677,14 +678,14 @@ struct ImGuiWindowSettings ImGuiID ID; ImVec2ih Pos; // NB: Settings position are stored RELATIVE to the viewport! Whereas runtime ones are absolute positions. ImVec2ih Size; - ImVec2ih ViewportPosh; + ImVec2ih ViewportPos; ImGuiID ViewportId; ImGuiID DockId; // ID of last known DockNode (even if the DockNode is invisible because it has only 1 active window), or 0 if none. ImGuiID ClassId; // ID of window class if specified short DockOrder; // Order of the last time the window was visible within its DockNode. This is used to reorder windows that are reappearing on the same frame. Same value between windows that were active and windows that were none are possible. bool Collapsed; - ImGuiWindowSettings() { Name = NULL; ID = 0; Pos = Size = ViewportPosh = ImVec2ih(0, 0); ViewportId = DockId = ClassId = 0; DockOrder = -1; Collapsed = false; } + ImGuiWindowSettings() { Name = NULL; ID = 0; Pos = Size = ViewportPos = ImVec2ih(0, 0); ViewportId = DockId = ClassId = 0; DockOrder = -1; Collapsed = false; } }; struct ImGuiSettingsHandler @@ -1210,7 +1211,7 @@ struct ImGuiContext { Initialized = false; FrameScopeActive = FrameScopePushedFallbackWindow = false; - ConfigFlagsCurrFrame = ImGuiConfigFlags_None; + ConfigFlagsCurrFrame = ConfigFlagsLastFrame = ImGuiConfigFlags_None; Font = NULL; FontSize = FontBaseSize = 0.0f; FontAtlasOwnedByContext = shared_font_atlas ? false : true; From a01d149369ac2a91c4838509fdc08cc05ddc1d88 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 28 Aug 2019 10:52:17 +0200 Subject: [PATCH 544/828] Fixed context popup windows from not having the NoDocking flag. (#2763) --- imgui.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 7e3977e40da7..f2f4f3fdd2cd 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -8276,7 +8276,7 @@ void ImGui::CloseCurrentPopup() window->DC.NavHideHighlightOneFrame = true; } -bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags) +bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags flags) { ImGuiContext& g = *GImGui; if (!IsPopupOpen(id)) @@ -8286,12 +8286,13 @@ bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags) } char name[20]; - if (extra_flags & ImGuiWindowFlags_ChildMenu) + if (flags & ImGuiWindowFlags_ChildMenu) ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.BeginPopupStack.Size); // Recycle windows based on depth else ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame - bool is_open = Begin(name, NULL, extra_flags | ImGuiWindowFlags_Popup); + flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_NoDocking; + bool is_open = Begin(name, NULL, flags); if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display) EndPopup(); @@ -8306,7 +8307,7 @@ bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags) g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values return false; } - flags |= ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoDocking; + flags |= ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings; return BeginPopupEx(g.CurrentWindow->GetID(str_id), flags); } From 9e294be5c56848b9da7d55d67228edbe602affc0 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 21 Aug 2019 15:07:28 +0200 Subject: [PATCH 545/828] Docking: Fix for node created at the same time as windows that are still resizing (typically with io.ConfigDockingAlwaysTabBar) to not be zero/min sized. (#2109) The fix delay their visibility by one frame, which is not ideal but not very problematic as the .ini data gets populated after that --- imgui.cpp | 41 +++++++++++++++++++++++++++++++++++++++++ imgui_internal.h | 9 +++++++++ 2 files changed, 50 insertions(+) diff --git a/imgui.cpp b/imgui.cpp index f2f4f3fdd2cd..4b324a6743d6 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11206,6 +11206,7 @@ namespace ImGui static void DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window, bool add_to_tab_bar); static void DockNodeMoveWindows(ImGuiDockNode* dst_node, ImGuiDockNode* src_node); static void DockNodeMoveChildNodes(ImGuiDockNode* dst_node, ImGuiDockNode* src_node); + static ImGuiWindow* DockNodeFindWindowByID(ImGuiDockNode* node, ImGuiID id); static void DockNodeApplyPosSizeToWindows(ImGuiDockNode* node); static void DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window, ImGuiID save_dock_id); static void DockNodeHideHostWindow(ImGuiDockNode* node); @@ -11846,6 +11847,7 @@ bool ImGui::DockContextCalcDropPosForDocking(ImGuiWindow* target, ImGuiDockNode* // - DockNodeHideHostWindow() // - ImGuiDockNodeFindInfoResults // - DockNodeFindInfo() +// - DockNodeFindWindowByID() // - DockNodeUpdateVisibleFlagAndInactiveChilds() // - DockNodeUpdateVisibleFlag() // - DockNodeStartMouseMovingWindow() @@ -11871,6 +11873,7 @@ ImGuiDockNode::ImGuiDockNode(ImGuiID id) TabBar = NULL; SplitAxis = ImGuiAxis_None; + State = ImGuiDockNodeState_Unknown; HostWindow = VisibleWindow = NULL; CentralNode = OnlyNodeWithWindows = NULL; LastFrameAlive = LastFrameActive = LastFrameFocused = -1; @@ -12131,6 +12134,15 @@ static void DockNodeFindInfo(ImGuiDockNode* node, ImGuiDockNodeFindInfoResults* DockNodeFindInfo(node->ChildNodes[1], results); } +static ImGuiWindow* ImGui::DockNodeFindWindowByID(ImGuiDockNode* node, ImGuiID id) +{ + IM_ASSERT(id != 0); + for (int n = 0; n < node->Windows.Size; n++) + if (node->Windows[n]->ID == id) + return node->Windows[n]; + return NULL; +} + // - Remove inactive windows/nodes. // - Update visibility flag. static void ImGui::DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* node) @@ -12169,6 +12181,7 @@ static void ImGui::DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* nod if (node->Windows.Size == 1 && !node->IsCentralNode()) { DockNodeHideHostWindow(node); + node->State = ImGuiDockNodeState_HostWindowHiddenBecauseSingleWindow; DockNodeRemoveWindow(node, window, node->ID); // Will delete the node so it'll be invalid on return return; } @@ -12285,6 +12298,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) } DockNodeHideHostWindow(node); + node->State = ImGuiDockNodeState_HostWindowHiddenBecauseSingleWindow; node->WantCloseAll = false; node->WantCloseTabID = 0; node->HasCloseButton = node->HasWindowMenuButton = node->EnableCloseButton = false; @@ -12295,6 +12309,31 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) return; } + // In some circumstance we will defer creating the host window (so everything will be kept hidden), + // while the expected visible window is resizing itself. + // This is important for first-time (no ini settings restored) single window when io.ConfigDockingAlwaysTabBar is enabled, + // otherwise the node ends up using the minimum window size. Effectively those windows will take an extra frame to show up: + // N+0: Begin(): window created (with no known size), node is created + // N+1: DockNodeUpdate(): node skip creating host window / Begin(): window size applied, not visible + // N+2: DockNodeUpdate(): node can create host window / Begin(): window becomes visible + // We could remove this frame if we could reliably calculate the expected window size during node update, before the Begin() code. + // It would require a generalization of CalcWindowExpectedSize(), probably extracting code away from Begin(). + // In reality it isn't very important as user quickly ends up with size data in .ini file. + if (node->IsVisible && node->HostWindow == NULL && node->IsFloatingNode() && node->IsLeafNode()) + { + IM_ASSERT(node->Windows.Size > 0); + ImGuiWindow* ref_window = NULL; + if (node->SelectedTabID != 0) // Note that we prune single-window-node settings on .ini loading, so this is generally 0 for them! + ref_window = DockNodeFindWindowByID(node, node->SelectedTabID); + if (ref_window == NULL) + ref_window = node->Windows[0]; + if (ref_window->AutoFitFramesX > 0 || ref_window->AutoFitFramesY > 0) + { + node->State = ImGuiDockNodeState_HostWindowHiddenBecauseWindowsAreResizing; + return; + } + } + const ImGuiDockNodeFlags node_flags = node->GetMergedFlags(); // Bind or create host window @@ -14161,6 +14200,7 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) // FIXME-DOCK: replace ->HostWindow NULL compare with something more explicit (~was initially intended as a first frame test) if (node->HostWindow == NULL) { + window->DockIsActive = (node->State == ImGuiDockNodeState_HostWindowHiddenBecauseWindowsAreResizing); window->DockTabIsVisible = false; return; } @@ -14168,6 +14208,7 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) IM_ASSERT(node->HostWindow); IM_ASSERT(node->IsLeafNode()); IM_ASSERT(node->Size.x > 0.0f && node->Size.y > 0.0f); + node->State = ImGuiDockNodeState_HostWindowVisible; // Undock if we are submitted earlier than the host window if (window->BeginOrderWithinContext < node->HostWindow->BeginOrderWithinContext) diff --git a/imgui_internal.h b/imgui_internal.h index aad0bb77eaf5..54c5bc51b55d 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -929,6 +929,14 @@ enum ImGuiDataAuthority_ ImGuiDataAuthority_Window }; +enum ImGuiDockNodeState +{ + ImGuiDockNodeState_Unknown, + ImGuiDockNodeState_HostWindowHiddenBecauseSingleWindow, + ImGuiDockNodeState_HostWindowHiddenBecauseWindowsAreResizing, + ImGuiDockNodeState_HostWindowVisible +}; + // sizeof() 116~160 struct ImGuiDockNode { @@ -945,6 +953,7 @@ struct ImGuiDockNode int SplitAxis; // [Split node only] Split axis (X or Y) ImGuiWindowClass WindowClass; + ImGuiDockNodeState State; ImGuiWindow* HostWindow; ImGuiWindow* VisibleWindow; // Generally point to window which is ID is == SelectedTabID, but when CTRL+Tabbing this can be a different window. ImGuiDockNode* CentralNode; // [Root node only] Pointer to central node. From 09780b8b3dfca9cad9959a697a9c240f99ce1ab2 Mon Sep 17 00:00:00 2001 From: Rokas Kupstys <19151258+rokups@users.noreply.github.com> Date: Thu, 29 Aug 2019 14:02:42 +0300 Subject: [PATCH 546/828] Viewport: Fix setting window size on macos (glfw). (#2767, #2117) MacOS positions windows by their bottom-left corner why the rest of the world (including imgui) position windows by the top-left corner. This created an issue where collapsing imgui window would cause window header to remain at the bottom the full window rect. Likewise resizing window by using sizing handle caused window to grow upwards when we tried to expand window downwards. This workaround moves window to the opposite direction by the delta of size change creating an illusion that windows are positioned by their top-left corner. --- examples/imgui_impl_glfw.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index cb5e88b9997d..dc2d981922a4 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -570,6 +570,16 @@ static ImVec2 ImGui_ImplGlfw_GetWindowSize(ImGuiViewport* viewport) static void ImGui_ImplGlfw_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) { ImGuiViewportDataGlfw* data = (ImGuiViewportDataGlfw*)viewport->PlatformUserData; +#if __APPLE__ + // Native OS windows are positioned from the bottom-left corner on macOS, whereas on other platforms they are + // positioned from the upper-left corner. GLFW makes an effort to convert macOS style coordinates, however it + // doesn't handle it when changing size. We are manually moving the window in order for changes of size to be based + // on the upper-left corner. + int x, y, width, height; + glfwGetWindowPos(data->Window, &x, &y); + glfwGetWindowSize(data->Window, &width, &height); + glfwSetWindowPos(data->Window, x, y - height + size.y); +#endif glfwSetWindowSize(data->Window, (int)size.x, (int)size.y); } From a89a3cd2f15b09a396428d7fcba433e0acc6880e Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Fri, 23 Aug 2019 16:12:06 +0300 Subject: [PATCH 547/828] Viewports, GLFW: Fix window having incorrect size after uncollapse. Issue manifests on Linux when window is in it's own viewport. (#2756, #2117) --- examples/imgui_impl_glfw.cpp | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index dc2d981922a4..59bd16d0dfd3 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -406,8 +406,9 @@ struct ImGuiViewportDataGlfw { GLFWwindow* Window; bool WindowOwned; + bool IgnoreSetWindowSizeEvent; - ImGuiViewportDataGlfw() { Window = NULL; WindowOwned = false; } + ImGuiViewportDataGlfw() { Window = NULL; WindowOwned = false; IgnoreSetWindowSizeEvent = false; } ~ImGuiViewportDataGlfw() { IM_ASSERT(Window == NULL); } }; @@ -426,7 +427,21 @@ static void ImGui_ImplGlfw_WindowPosCallback(GLFWwindow* window, int, int) static void ImGui_ImplGlfw_WindowSizeCallback(GLFWwindow* window, int, int) { if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle(window)) + { + if (ImGuiViewportDataGlfw* data = (ImGuiViewportDataGlfw*)viewport->PlatformUserData) + { + if (data->IgnoreSetWindowSizeEvent) + { + // GLFW will dispatch window size event. ImGui expects no such event sent when library explicitly requests setting + // window size. Depending on the platform this callback may be invoked during glfwSetWindowSize() call or queued + // for the next frame and invoked during glfwPollEvents() call. When latter happens - restoring collapsed window + // would have incorrect size. + data->IgnoreSetWindowSizeEvent = false; + return; + } + } viewport->PlatformRequestResize = true; + } } static void ImGui_ImplGlfw_CreateWindow(ImGuiViewport* viewport) @@ -569,6 +584,8 @@ static ImVec2 ImGui_ImplGlfw_GetWindowSize(ImGuiViewport* viewport) static void ImGui_ImplGlfw_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) { + if (ImGuiViewportDataGlfw* data = (ImGuiViewportDataGlfw*)viewport->PlatformUserData) + data->IgnoreSetWindowSizeEvent = true; ImGuiViewportDataGlfw* data = (ImGuiViewportDataGlfw*)viewport->PlatformUserData; #if __APPLE__ // Native OS windows are positioned from the bottom-left corner on macOS, whereas on other platforms they are @@ -630,7 +647,7 @@ static void ImGui_ImplGlfw_RenderWindow(ImGuiViewport* viewport, void*) static void ImGui_ImplGlfw_SwapBuffers(ImGuiViewport* viewport, void*) { ImGuiViewportDataGlfw* data = (ImGuiViewportDataGlfw*)viewport->PlatformUserData; - if (g_ClientApi == GlfwClientApi_OpenGL) + if (g_ClientApi == GlfwClientApi_OpenGL) { glfwMakeContextCurrent(data->Window); glfwSwapBuffers(data->Window); From a4af3cc814f2540e279c166e64f4d2687851bb00 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 29 Aug 2019 15:53:33 +0200 Subject: [PATCH 548/828] Viewport, GLFW: Fix for #2756 under Windows. --- examples/imgui_impl_glfw.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index 59bd16d0dfd3..166f06a191e9 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -1,7 +1,7 @@ // dear imgui: Platform Binding for GLFW // This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan..) // (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) -// (Requires: GLFW 3.1+) +// (Requires: GLFW 3.1+. Prefer GLFW 3.3+ for full feature support.) // Implemented features: // [X] Platform: Clipboard support. @@ -406,9 +406,9 @@ struct ImGuiViewportDataGlfw { GLFWwindow* Window; bool WindowOwned; - bool IgnoreSetWindowSizeEvent; + int IgnoreWindowSizeEventFrame; - ImGuiViewportDataGlfw() { Window = NULL; WindowOwned = false; IgnoreSetWindowSizeEvent = false; } + ImGuiViewportDataGlfw() { Window = NULL; WindowOwned = false; IgnoreWindowSizeEventFrame = -1; } ~ImGuiViewportDataGlfw() { IM_ASSERT(Window == NULL); } }; @@ -430,15 +430,16 @@ static void ImGui_ImplGlfw_WindowSizeCallback(GLFWwindow* window, int, int) { if (ImGuiViewportDataGlfw* data = (ImGuiViewportDataGlfw*)viewport->PlatformUserData) { - if (data->IgnoreSetWindowSizeEvent) - { - // GLFW will dispatch window size event. ImGui expects no such event sent when library explicitly requests setting - // window size. Depending on the platform this callback may be invoked during glfwSetWindowSize() call or queued - // for the next frame and invoked during glfwPollEvents() call. When latter happens - restoring collapsed window - // would have incorrect size. - data->IgnoreSetWindowSizeEvent = false; + // GLFW may dispatch window size event after calling glfwSetWindowSize(). + // However depending on the platform the callback may be invoked at different time: on Windows it + // appears to be called within the glfwSetWindowSize() call whereas on Linux it is queued and invoked + // during glfwPollEvents(). + // Because the event doesn't always fire on glfwSetWindowSize() we use a frame counter tag to only + // ignore recent glfwSetWindowSize() calls. + bool ignore_event = (ImGui::GetFrameCount() <= data->IgnoreWindowSizeEventFrame + 1); + data->IgnoreWindowSizeEventFrame = -1; + if (ignore_event) return; - } } viewport->PlatformRequestResize = true; } @@ -584,8 +585,6 @@ static ImVec2 ImGui_ImplGlfw_GetWindowSize(ImGuiViewport* viewport) static void ImGui_ImplGlfw_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) { - if (ImGuiViewportDataGlfw* data = (ImGuiViewportDataGlfw*)viewport->PlatformUserData) - data->IgnoreSetWindowSizeEvent = true; ImGuiViewportDataGlfw* data = (ImGuiViewportDataGlfw*)viewport->PlatformUserData; #if __APPLE__ // Native OS windows are positioned from the bottom-left corner on macOS, whereas on other platforms they are @@ -597,6 +596,7 @@ static void ImGui_ImplGlfw_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) glfwGetWindowSize(data->Window, &width, &height); glfwSetWindowPos(data->Window, x, y - height + size.y); #endif + data->IgnoreWindowSizeEventFrame = ImGui::GetFrameCount(); glfwSetWindowSize(data->Window, (int)size.x, (int)size.y); } From d049a7988c9c569ff240533e96d8500f3a715cab Mon Sep 17 00:00:00 2001 From: omar Date: Sat, 31 Aug 2019 16:51:12 +0200 Subject: [PATCH 549/828] Docking: comments for DockBuilder API. --- imgui.cpp | 18 ++++++++++++------ imgui_internal.h | 10 +++++++--- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 4b324a6743d6..e7ddd574ed45 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -13740,8 +13740,12 @@ void ImGui::DockBuilderSetNodeSize(ImGuiID node_id, ImVec2 size) node->AuthorityForSize = ImGuiDataAuthority_DockNode; } -// If you create a regular node, both ref_pos/ref_size will position the window. -// If you create a dockspace node: ref_pos won't be used, ref_size is useful on the first frame to... +// Make sure to use the ImGuiDockNodeFlags_DockSpace flag to create a dockspace node! Otherwise this will create a floating node! +// - Floating node: you can then call DockBuilderSetNodePos()/DockBuilderSetNodeSize() to position and size the floating node. +// - Dockspace node: calling DockBuilderSetNodePos() is unnecessary. +// - If you intend to split a node immediately after creation using DockBuilderSplitNode(), make sure to call DockBuilderSetNodeSize() beforehand! +// For various reason, the splitting code currently needs a base size otherwise space may not be allocated as precisely as you would expect. +// - Use (id == 0) to let the system allocate a node identifier. ImGuiID ImGui::DockBuilderAddNode(ImGuiID id, ImGuiDockNodeFlags flags) { ImGuiContext* ctx = GImGui; @@ -13878,8 +13882,10 @@ void ImGui::DockBuilderRemoveNodeDockedWindows(ImGuiID root_id, bool clear_persi } } +// If 'out_id_at_dir' or 'out_id_at_opposite_dir' are non NULL, the function will write out the ID of the two new nodes created. +// Return value is ID of the node at the specified direction, so same as (*out_id_at_dir) if that pointer is set. // FIXME-DOCK: We are not exposing nor using split_outer. -ImGuiID ImGui::DockBuilderSplitNode(ImGuiID id, ImGuiDir split_dir, float size_ratio_for_node_at_dir, ImGuiID* out_id_at_dir, ImGuiID* out_id_other) +ImGuiID ImGui::DockBuilderSplitNode(ImGuiID id, ImGuiDir split_dir, float size_ratio_for_node_at_dir, ImGuiID* out_id_at_dir, ImGuiID* out_id_at_opposite_dir) { ImGuiContext* ctx = GImGui; IM_ASSERT(split_dir != ImGuiDir_None); @@ -13905,11 +13911,11 @@ ImGuiID ImGui::DockBuilderSplitNode(ImGuiID id, ImGuiDir split_dir, float size_r DockContextProcessDock(ctx, &req); ImGuiID id_at_dir = node->ChildNodes[(split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Up) ? 0 : 1]->ID; - ImGuiID id_other = node->ChildNodes[(split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Up) ? 1 : 0]->ID; + ImGuiID id_at_opposite_dir = node->ChildNodes[(split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Up) ? 1 : 0]->ID; if (out_id_at_dir) *out_id_at_dir = id_at_dir; - if (out_id_other) - *out_id_other = id_other; + if (out_id_at_opposite_dir) + *out_id_at_opposite_dir = id_at_opposite_dir; return id_at_dir; } diff --git a/imgui_internal.h b/imgui_internal.h index 54c5bc51b55d..bca9c3dc7827 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1800,17 +1800,21 @@ namespace ImGui IMGUI_API void SetWindowDock(ImGuiWindow* window, ImGuiID dock_id, ImGuiCond cond); // Docking - Builder function needs to be generally called before the DockSpace() node is submitted. - // Important: do not hold on ImGuiDockNode* pointers. They may be invalidated by any split/merge/remove operation and every frame. + // - The DockBuilderXXX functions are designed to _eventually_ become a public API, but it is too early to expose it and guarantee stability. + // - You can create dockspace _or_ floating nodes with this API. To create a dockspace node, make sure to set the ImGuiDockNodeFlags_DockSpace flag. + // - If you intend to split the node immediately after creation using DockBuilderSplitNode(), make sure to call DockBuilderSetNodeSize() beforehand. + // - Call DockBuilderFinish() after you are done. + // - Important: do not hold on ImGuiDockNode* pointers! They may be invalidated by any split/merge/remove operation and every frame. IMGUI_API void DockBuilderDockWindow(const char* window_name, ImGuiID node_id); IMGUI_API ImGuiDockNode*DockBuilderGetNode(ImGuiID node_id); inline ImGuiDockNode* DockBuilderGetCentralNode(ImGuiID node_id) { ImGuiDockNode* node = DockBuilderGetNode(node_id); if (!node) return NULL; return DockNodeGetRootNode(node)->CentralNode; } - IMGUI_API ImGuiID DockBuilderAddNode(ImGuiID node_id, ImGuiDockNodeFlags flags = 0); // Use (flags == ImGuiDockNodeFlags_DockSpace) to create a dockspace, otherwise it'll create a floating node. + IMGUI_API ImGuiID DockBuilderAddNode(ImGuiID node_id = 0, ImGuiDockNodeFlags flags = 0); IMGUI_API void DockBuilderRemoveNode(ImGuiID node_id); // Remove node and all its child, undock all windows IMGUI_API void DockBuilderRemoveNodeDockedWindows(ImGuiID node_id, bool clear_persistent_docking_references = true); IMGUI_API void DockBuilderRemoveNodeChildNodes(ImGuiID node_id); // Remove all split/hierarchy. All remaining docked windows will be re-docked to the root. IMGUI_API void DockBuilderSetNodePos(ImGuiID node_id, ImVec2 pos); IMGUI_API void DockBuilderSetNodeSize(ImGuiID node_id, ImVec2 size); - IMGUI_API ImGuiID DockBuilderSplitNode(ImGuiID node_id, ImGuiDir split_dir, float size_ratio_for_node_at_dir, ImGuiID* out_id_dir, ImGuiID* out_id_other); + IMGUI_API ImGuiID DockBuilderSplitNode(ImGuiID node_id, ImGuiDir split_dir, float size_ratio_for_node_at_dir, ImGuiID* out_id_at_dir, ImGuiID* out_id_at_opposite_dir); IMGUI_API void DockBuilderCopyDockSpace(ImGuiID src_dockspace_id, ImGuiID dst_dockspace_id, ImVector* in_window_remap_pairs); IMGUI_API void DockBuilderCopyNode(ImGuiID src_node_id, ImGuiID dst_node_id, ImVector* out_node_remap_pairs); IMGUI_API void DockBuilderCopyWindowSettings(const char* src_name, const char* dst_name); From 1ca6ff974ccc0359cd58925fd9635fe1d028d5c6 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 17 Sep 2019 18:02:58 +0200 Subject: [PATCH 550/828] Viewport: fix to allow multiple shutdown / calls to DestroyPlatformWindows(). (#2769) --- imgui.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 70914d2bc942..5048bc5b2657 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11173,7 +11173,11 @@ void ImGui::DestroyPlatformWindow(ImGuiViewportP* viewport) if (g.PlatformIO.Platform_DestroyWindow) g.PlatformIO.Platform_DestroyWindow(viewport); IM_ASSERT(viewport->RendererUserData == NULL && viewport->PlatformUserData == NULL); - viewport->PlatformWindowCreated = false; + + // Don't clear PlatformWindowCreated for the main viewport, as we initially set that up to true in Initialize() + // The right-er way may be to leave it to the back-end to set this flag all-together, and made the flag public. + if (viewport->ID != IMGUI_VIEWPORT_DEFAULT_ID) + viewport->PlatformWindowCreated = false; } else { From cf982908732ee4c7e3faa3db47feab2eec7da00d Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 17 Sep 2019 18:32:26 +0200 Subject: [PATCH 551/828] Backends: DirectX9: Workaround for windows not refreshing when main viewport has no draw call. (#2560) --- examples/imgui_impl_dx9.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/examples/imgui_impl_dx9.cpp b/examples/imgui_impl_dx9.cpp index 2c22885e6930..a9df2e8a5504 100644 --- a/examples/imgui_impl_dx9.cpp +++ b/examples/imgui_impl_dx9.cpp @@ -214,6 +214,11 @@ void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data) global_vtx_offset += cmd_list->VtxBuffer.Size; } + // When using multi-viewports, it appears that there's an odd logic in DirectX9 which prevent subsequent windows + // from rendering until the first window submits at least one draw call, even once. That's our workaround. (see #2560) + if (global_vtx_offset == 0) + g_pd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 0, 0, 0); + // Restore the DX9 transform g_pd3dDevice->SetTransform(D3DTS_WORLD, &last_world); g_pd3dDevice->SetTransform(D3DTS_VIEW, &last_view); From 13f00331dacbfc6ff14e41fc359c4234100fa6b9 Mon Sep 17 00:00:00 2001 From: omar Date: Sat, 21 Sep 2019 14:50:02 +0200 Subject: [PATCH 552/828] Docking: Added ImGuiDockNodeFlags_NoDocking flag. (#2109) --- imgui.cpp | 3 +++ imgui_internal.h | 5 +++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index cb278944afed..cb6c3d006133 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -13122,6 +13122,8 @@ static void ImGui::DockNodePreviewDockCalc(ImGuiWindow* host_window, ImGuiDockNo data->IsCenterAvailable = !is_outer_docking; if (src_is_visibly_splitted && (!host_node || !host_node->IsEmpty())) data->IsCenterAvailable = false; + if (host_node && (host_node_flags & ImGuiDockNodeFlags_NoDocking)) + data->IsCenterAvailable = false; if (host_node && (host_node_flags & ImGuiDockNodeFlags_NoDockingInCentralNode) && host_node->IsCentralNode()) data->IsCenterAvailable = false; @@ -15102,6 +15104,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::BulletText("Misc:%s%s%s%s", node->IsDockSpace() ? " IsDockSpace" : "", node->IsCentralNode() ? " IsCentralNode" : "", (g.FrameCount - node->LastFrameAlive < 2) ? " IsAlive" : "", (g.FrameCount - node->LastFrameActive < 2) ? " IsActive" : ""); if (ImGui::TreeNode("flags", "LocalFlags: 0x%04X SharedFlags: 0x%04X", node->LocalFlags, node->SharedFlags)) { + ImGui::CheckboxFlags("LocalFlags: NoDocking", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_NoDocking); ImGui::CheckboxFlags("LocalFlags: NoSplit", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_NoSplit); ImGui::CheckboxFlags("LocalFlags: NoResize", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_NoResize); ImGui::CheckboxFlags("LocalFlags: NoTabBar", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_NoTabBar); diff --git a/imgui_internal.h b/imgui_internal.h index a22e2b83a354..86c3e0ad1c40 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -916,10 +916,11 @@ enum ImGuiDockNodeFlagsPrivate_ ImGuiDockNodeFlags_HiddenTabBar = 1 << 13, // Local, Saved // Tab bar is hidden, with a triangle in the corner to show it again (NB: actual tab-bar instance may be destroyed as this is only used for single-window tab bar) ImGuiDockNodeFlags_NoWindowMenuButton = 1 << 14, // Local, Saved // Disable window/docking menu (that one that appears instead of the collapse button) ImGuiDockNodeFlags_NoCloseButton = 1 << 15, // Local, Saved // + ImGuiDockNodeFlags_NoDocking = 1 << 16, // Local, Saved // Disable any form of docking in this dockspace or individual node. (On a whole dockspace, this pretty much defeat the purpose of using a dockspace at all). Note: when turned on, existing docked nodes will be preserved. ImGuiDockNodeFlags_SharedFlagsInheritMask_ = ~0, - ImGuiDockNodeFlags_LocalFlagsMask_ = ImGuiDockNodeFlags_NoSplit | ImGuiDockNodeFlags_NoResize | ImGuiDockNodeFlags_AutoHideTabBar | ImGuiDockNodeFlags_DockSpace | ImGuiDockNodeFlags_CentralNode | ImGuiDockNodeFlags_NoTabBar | ImGuiDockNodeFlags_HiddenTabBar | ImGuiDockNodeFlags_NoWindowMenuButton | ImGuiDockNodeFlags_NoCloseButton, + ImGuiDockNodeFlags_LocalFlagsMask_ = ImGuiDockNodeFlags_NoSplit | ImGuiDockNodeFlags_NoResize | ImGuiDockNodeFlags_AutoHideTabBar | ImGuiDockNodeFlags_DockSpace | ImGuiDockNodeFlags_CentralNode | ImGuiDockNodeFlags_NoTabBar | ImGuiDockNodeFlags_HiddenTabBar | ImGuiDockNodeFlags_NoWindowMenuButton | ImGuiDockNodeFlags_NoCloseButton | ImGuiDockNodeFlags_NoDocking, ImGuiDockNodeFlags_LocalFlagsTransferMask_ = ImGuiDockNodeFlags_LocalFlagsMask_ & ~ImGuiDockNodeFlags_DockSpace, // When splitting those flags are moved to the inheriting child, never duplicated - ImGuiDockNodeFlags_SavedFlagsMask_ = ImGuiDockNodeFlags_NoResize | ImGuiDockNodeFlags_DockSpace | ImGuiDockNodeFlags_CentralNode | ImGuiDockNodeFlags_NoTabBar | ImGuiDockNodeFlags_HiddenTabBar | ImGuiDockNodeFlags_NoWindowMenuButton | ImGuiDockNodeFlags_NoCloseButton + ImGuiDockNodeFlags_SavedFlagsMask_ = ImGuiDockNodeFlags_NoResize | ImGuiDockNodeFlags_DockSpace | ImGuiDockNodeFlags_CentralNode | ImGuiDockNodeFlags_NoTabBar | ImGuiDockNodeFlags_HiddenTabBar | ImGuiDockNodeFlags_NoWindowMenuButton | ImGuiDockNodeFlags_NoCloseButton | ImGuiDockNodeFlags_NoDocking }; // Store the source authority (dock node vs window) of a field From 05c1f2795a077681f836dd783f92442c39c79d52 Mon Sep 17 00:00:00 2001 From: "Ilya.Sevrikov" Date: Thu, 17 Oct 2019 16:53:43 +0300 Subject: [PATCH 553/828] Add multi-viewports for DX12. (#2851) (cherry picked from commit 899e48565d1ecefde06063f99c75e702adcef175) --- examples/example_win32_directx12/main.cpp | 4 +- examples/imgui_impl_dx12.cpp | 358 ++++++++++++++++------ examples/imgui_impl_dx12.h | 5 +- 3 files changed, 265 insertions(+), 102 deletions(-) diff --git a/examples/example_win32_directx12/main.cpp b/examples/example_win32_directx12/main.cpp index 26a16f0f0de4..0f519c1605e2 100644 --- a/examples/example_win32_directx12/main.cpp +++ b/examples/example_win32_directx12/main.cpp @@ -73,7 +73,7 @@ int main(int, char**) io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking - //io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows (FIXME: Currently broken in DX12 back-end, need some work!) + io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows //io.ConfigViewportsNoAutoMerge = true; //io.ConfigViewportsNoTaskBarIcon = true; @@ -92,7 +92,7 @@ int main(int, char**) // Setup Platform/Renderer bindings ImGui_ImplWin32_Init(hwnd); ImGui_ImplDX12_Init(g_pd3dDevice, NUM_FRAMES_IN_FLIGHT, - DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_R8G8B8A8_UNORM, g_pd3dSrvDescHeap, g_pd3dSrvDescHeap->GetCPUDescriptorHandleForHeapStart(), g_pd3dSrvDescHeap->GetGPUDescriptorHandleForHeapStart()); diff --git a/examples/imgui_impl_dx12.cpp b/examples/imgui_impl_dx12.cpp index d8a46221779a..7f645d3b016b 100644 --- a/examples/imgui_impl_dx12.cpp +++ b/examples/imgui_impl_dx12.cpp @@ -4,8 +4,8 @@ // Implemented features: // [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as ImTextureID. Read the FAQ about ImTextureID! // [X] Renderer: Support for large meshes (64k+ vertices) with 16-bits indices. +// [X] Renderer: Multi-viewport. // Missing features, issues: -// [ ] Renderer: Missing multi-viewport support. // [ ] 64-bit only for now! (Because sizeof(ImTextureId) == sizeof(void*)). See github.com/ocornut/imgui/pull/301 // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. @@ -46,6 +46,8 @@ static ID3D12Resource* g_pFontTextureResource = NULL; static D3D12_CPU_DESCRIPTOR_HANDLE g_hFontSrvCpuDescHandle = {}; static D3D12_GPU_DESCRIPTOR_HANDLE g_hFontSrvGpuDescHandle = {}; +static ID3D12DescriptorHeap* g_pd3dSrvDescHeap = NULL; + struct FrameResources { ID3D12Resource* IndexBuffer; @@ -53,9 +55,80 @@ struct FrameResources int IndexBufferSize; int VertexBufferSize; }; -static FrameResources* g_pFrameResources = NULL; + static UINT g_numFramesInFlight = 0; -static UINT g_frameIndex = UINT_MAX; + +struct FrameContext +{ + ID3D12CommandAllocator* CommandAllocator; + UINT64 FenceValue; + ID3D12Resource* RenderTarget; + D3D12_CPU_DESCRIPTOR_HANDLE RenderTargetCpuDescriptors; +}; + +struct ImGuiViewportDataDx12 +{ + ID3D12CommandQueue* CommandQueue; + ID3D12GraphicsCommandList* CommandList; + + ID3D12DescriptorHeap* RtvDescHeap; + IDXGISwapChain3* SwapChain; + + ID3D12Fence* Fence; + UINT64 FenceSignaledValue; + HANDLE FenceEvent; + + FrameContext* FrameCtx; + FrameResources* Resources; + + UINT FrameIndex; + + ImGuiViewportDataDx12() + { + CommandQueue = NULL; + CommandList = NULL; + + RtvDescHeap = NULL; + SwapChain = NULL; + FenceSignaledValue = 0; + Fence = NULL; + FenceEvent = NULL; + FrameIndex = UINT_MAX; + + FrameCtx = new FrameContext[g_numFramesInFlight]; + Resources = new FrameResources[g_numFramesInFlight]; + + for (UINT i = 0; i < g_numFramesInFlight; ++i) + { + FrameCtx[i].CommandAllocator = NULL; + FrameCtx[i].RenderTarget = NULL; + + // Create buffers with a default size (they will later be grown as needed) + Resources[i].IndexBuffer = NULL; + Resources[i].VertexBuffer = NULL; + Resources[i].VertexBufferSize = 5000; + Resources[i].IndexBufferSize = 10000; + } + + } + ~ImGuiViewportDataDx12() + { + IM_ASSERT(CommandQueue == NULL && CommandList == NULL); + IM_ASSERT(RtvDescHeap == NULL); + IM_ASSERT(SwapChain == NULL); + IM_ASSERT(Fence == NULL); + IM_ASSERT(FenceEvent == NULL); + + for (UINT i = 0; i < g_numFramesInFlight; ++i) + { + IM_ASSERT(FrameCtx[i].CommandAllocator == NULL && FrameCtx[i].RenderTarget == NULL); + IM_ASSERT(Resources[i].IndexBuffer == NULL && Resources[i].VertexBuffer == NULL); + } + + delete[] FrameCtx; FrameCtx = NULL; + delete[] Resources; Resources = NULL; + } +}; struct VERTEX_CONSTANT_BUFFER { @@ -129,10 +202,9 @@ void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandL if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f) return; - // FIXME: I'm assuming that this only gets called once per frame! - // If not, we can't just re-allocate the IB or VB, we'll have to do a proper allocator. - g_frameIndex = g_frameIndex + 1; - FrameResources* fr = &g_pFrameResources[g_frameIndex % g_numFramesInFlight]; + ImGuiViewportDataDx12* render_data = (ImGuiViewportDataDx12*)draw_data->OwnerViewport->RendererUserData; + render_data->FrameIndex++; + FrameResources* fr = &render_data->Resources[render_data->FrameIndex % g_numFramesInFlight]; // Create and grow vertex/index buffers if needed if (fr->VertexBuffer == NULL || fr->VertexBufferSize < draw_data->TotalVtxCount) @@ -599,15 +671,9 @@ void ImGui_ImplDX12_InvalidateDeviceObjects() if (g_pRootSignature) { g_pRootSignature->Release(); g_pRootSignature = NULL; } if (g_pPipelineState) { g_pPipelineState->Release(); g_pPipelineState = NULL; } if (g_pFontTextureResource) { g_pFontTextureResource->Release(); g_pFontTextureResource = NULL; io.Fonts->TexID = NULL; } // We copied g_pFontTextureView to io.Fonts->TexID so let's clear that as well. - for (UINT i = 0; i < g_numFramesInFlight; i++) - { - FrameResources* fr = &g_pFrameResources[i]; - if (fr->IndexBuffer) { fr->IndexBuffer->Release(); fr->IndexBuffer = NULL; } - if (fr->VertexBuffer) { fr->VertexBuffer->Release(); fr->VertexBuffer = NULL; } - } } -bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FORMAT rtv_format, +bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FORMAT rtv_format, ID3D12DescriptorHeap* cbv_srv_heap, D3D12_CPU_DESCRIPTOR_HANDLE font_srv_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE font_srv_gpu_desc_handle) { // Setup back-end capabilities flags @@ -620,20 +686,14 @@ bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FO g_RTVFormat = rtv_format; g_hFontSrvCpuDescHandle = font_srv_cpu_desc_handle; g_hFontSrvGpuDescHandle = font_srv_gpu_desc_handle; - g_pFrameResources = new FrameResources[num_frames_in_flight]; g_numFramesInFlight = num_frames_in_flight; - g_frameIndex = UINT_MAX; + g_pd3dSrvDescHeap = cbv_srv_heap; - // Create buffers with a default size (they will later be grown as needed) - for (int i = 0; i < num_frames_in_flight; i++) - { - FrameResources* fr = &g_pFrameResources[i]; - fr->IndexBuffer = NULL; - fr->VertexBuffer = NULL; - fr->IndexBufferSize = 10000; - fr->VertexBufferSize = 5000; - } + ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + main_viewport->RendererUserData = IM_NEW(ImGuiViewportDataDx12); + // Setup back-end capabilities flags + io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional) if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) ImGui_ImplDX12_InitPlatformInterface(); @@ -644,13 +704,17 @@ void ImGui_ImplDX12_Shutdown() { ImGui_ImplDX12_ShutdownPlatformInterface(); ImGui_ImplDX12_InvalidateDeviceObjects(); - delete[] g_pFrameResources; - g_pFrameResources = NULL; + g_pd3dDevice = NULL; g_hFontSrvCpuDescHandle.ptr = 0; g_hFontSrvGpuDescHandle.ptr = 0; g_numFramesInFlight = 0; - g_frameIndex = UINT_MAX; + g_pd3dSrvDescHeap = NULL; + + ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + if (ImGuiViewportDataDx12* data = (ImGuiViewportDataDx12*)main_viewport->RendererUserData) + IM_DELETE(data); + main_viewport->RendererUserData = NULL; } void ImGui_ImplDX12_NewFrame() @@ -665,54 +729,118 @@ void ImGui_ImplDX12_NewFrame() // If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first.. //-------------------------------------------------------------------------------------------------------- -struct ImGuiViewportDataDx12 -{ - IDXGISwapChain3* SwapChain; - - ImGuiViewportDataDx12() { SwapChain = NULL; } - ~ImGuiViewportDataDx12() { IM_ASSERT(SwapChain == NULL); } -}; - static void ImGui_ImplDX12_CreateWindow(ImGuiViewport* viewport) { ImGuiViewportDataDx12* data = IM_NEW(ImGuiViewportDataDx12)(); viewport->RendererUserData = data; - IM_ASSERT(0); - /* - // FIXME-PLATFORM // PlatformHandleRaw should always be a HWND, whereas PlatformHandle might be a higher-level handle (e.g. GLFWWindow*, SDL_Window*). // Some back-ends will leave PlatformHandleRaw NULL, in which case we assume PlatformHandle will contain the HWND. HWND hwnd = viewport->PlatformHandleRaw ? (HWND)viewport->PlatformHandleRaw : (HWND)viewport->PlatformHandle; IM_ASSERT(hwnd != 0); + data->FrameIndex = UINT_MAX; + + // Create command queue. + D3D12_COMMAND_QUEUE_DESC queue_desc = {}; + queue_desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; + queue_desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; + + HRESULT res = S_OK; + res = g_pd3dDevice->CreateCommandQueue(&queue_desc, IID_PPV_ARGS(&data->CommandQueue)); + IM_ASSERT(res == S_OK); + + // Create command allocator. + for (UINT i = 0; i < g_numFramesInFlight; ++i) + { + res = g_pd3dDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&data->FrameCtx[i].CommandAllocator)); + IM_ASSERT(res == S_OK); + } + + // Create command list. + res = g_pd3dDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, data->FrameCtx[0].CommandAllocator, NULL, IID_PPV_ARGS(&data->CommandList)); + IM_ASSERT(res == S_OK); + data->CommandList->Close(); + + // Create fence. + res = g_pd3dDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&data->Fence)); + IM_ASSERT(res == S_OK); + + data->FenceEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + IM_ASSERT(data->FenceEvent != NULL); + // Create swap chain - DXGI_SWAP_CHAIN_DESC sd; - ZeroMemory(&sd, sizeof(sd)); - sd.BufferDesc.Width = (UINT)viewport->Size.x; - sd.BufferDesc.Height = (UINT)viewport->Size.y; - sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - sd.SampleDesc.Count = 1; - sd.SampleDesc.Quality = 0; - sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; - sd.BufferCount = 1; - sd.OutputWindow = hwnd; - sd.Windowed = TRUE; - sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; - sd.Flags = 0; - - IM_ASSERT(data->SwapChain == NULL && data->RTView == NULL); - g_pFactory->CreateSwapChain(g_pd3dDevice, &sd, &data->SwapChain); - - // Create the render target + DXGI_SWAP_CHAIN_DESC1 sd1; + ZeroMemory(&sd1, sizeof(sd1)); + sd1.BufferCount = g_numFramesInFlight; + sd1.Width = (UINT)viewport->Size.x; + sd1.Height = (UINT)viewport->Size.y; + sd1.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + sd1.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + sd1.SampleDesc.Count = 1; + sd1.SampleDesc.Quality = 0; + sd1.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; + sd1.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED; + sd1.Scaling = DXGI_SCALING_STRETCH; + sd1.Stereo = FALSE; + + IM_ASSERT(data->SwapChain == NULL); + IM_ASSERT(data->FrameCtx[0].RenderTarget == NULL && data->FrameCtx[1].RenderTarget == NULL && data->FrameCtx[2].RenderTarget == NULL); + + IDXGIFactory4* dxgi_factory = nullptr; + res = CreateDXGIFactory1(IID_PPV_ARGS(&dxgi_factory)); + IM_ASSERT(res == S_OK); + + IDXGISwapChain1* swap_chain = nullptr; + res = dxgi_factory->CreateSwapChainForHwnd(data->CommandQueue, hwnd, &sd1, NULL, NULL, &swap_chain); + IM_ASSERT(res == S_OK); + + dxgi_factory->Release(); + + // Or swapChain.As(&mSwapChain) + swap_chain->QueryInterface(IID_PPV_ARGS(&data->SwapChain)); + + // Create the render targets if (data->SwapChain) { - ID3D11Texture2D* pBackBuffer; - data->SwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer)); - g_pd3dDevice->CreateRenderTargetView(pBackBuffer, NULL, &data->RTView); - pBackBuffer->Release(); + D3D12_DESCRIPTOR_HEAP_DESC desc = {}; + desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; + desc.NumDescriptors = g_numFramesInFlight; + desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; + desc.NodeMask = 1; + + IM_ASSERT(g_pd3dDevice->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&data->RtvDescHeap)) == S_OK); + + SIZE_T rtv_descriptor_size = g_pd3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV); + D3D12_CPU_DESCRIPTOR_HANDLE rtv_handle = data->RtvDescHeap->GetCPUDescriptorHandleForHeapStart(); + for (UINT i = 0; i < g_numFramesInFlight; i++) + { + data->FrameCtx[i].RenderTargetCpuDescriptors = rtv_handle; + rtv_handle.ptr += rtv_descriptor_size; + } + + ID3D12Resource* back_buffer; + for (UINT i = 0; i < g_numFramesInFlight; i++) + { + data->SwapChain->GetBuffer(i, IID_PPV_ARGS(&back_buffer)); + g_pd3dDevice->CreateRenderTargetView(back_buffer, NULL, data->FrameCtx[i].RenderTargetCpuDescriptors); + data->FrameCtx[i].RenderTarget = back_buffer; + } + } + + for (UINT i = 0; i < g_numFramesInFlight; i++) + { + if (data->Resources[i].IndexBuffer) { data->Resources[i].VertexBuffer->Release(); data->Resources[i].IndexBuffer = NULL; } + if (data->Resources[i].VertexBuffer) { data->Resources[i].VertexBuffer->Release(); data->Resources[i].VertexBuffer = NULL; } } - */ +} + +template +void SafeRelease(D12Resource*& res) +{ + if (res) + res->Release(); + res = NULL; } static void ImGui_ImplDX12_DestroyWindow(ImGuiViewport* viewport) @@ -720,16 +848,22 @@ static void ImGui_ImplDX12_DestroyWindow(ImGuiViewport* viewport) // The main viewport (owned by the application) will always have RendererUserData == NULL since we didn't create the data for it. if (ImGuiViewportDataDx12* data = (ImGuiViewportDataDx12*)viewport->RendererUserData) { - IM_ASSERT(0); - /* - if (data->SwapChain) - data->SwapChain->Release(); - data->SwapChain = NULL; - if (data->RTView) - data->RTView->Release(); - data->RTView = NULL; + SafeRelease(data->CommandQueue); + SafeRelease(data->CommandList); + SafeRelease(data->SwapChain); + SafeRelease(data->RtvDescHeap); + SafeRelease(data->Fence); + CloseHandle(data->FenceEvent); data->FenceEvent = NULL; + + for (UINT i = 0; i < g_numFramesInFlight; i++) + { + SafeRelease(data->FrameCtx[i].RenderTarget); + SafeRelease(data->FrameCtx[i].CommandAllocator); + SafeRelease(data->Resources[i].IndexBuffer); + SafeRelease(data->Resources[i].VertexBuffer); + } + IM_DELETE(data); - */ } viewport->RendererUserData = NULL; } @@ -737,51 +871,79 @@ static void ImGui_ImplDX12_DestroyWindow(ImGuiViewport* viewport) static void ImGui_ImplDX12_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) { ImGuiViewportDataDx12* data = (ImGuiViewportDataDx12*)viewport->RendererUserData; - IM_ASSERT(0); - (void)data; (void)size; - /* - if (data->RTView) + + for (UINT i = 0; i < g_numFramesInFlight; i++) { - data->RTView->Release(); - data->RTView = NULL; + SafeRelease(data->FrameCtx[i].RenderTarget); } + if (data->SwapChain) { - ID3D11Texture2D* pBackBuffer = NULL; + ID3D12Resource* back_buffer = NULL; data->SwapChain->ResizeBuffers(0, (UINT)size.x, (UINT)size.y, DXGI_FORMAT_UNKNOWN, 0); - data->SwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer)); - g_pd3dDevice->CreateRenderTargetView(pBackBuffer, NULL, &data->RTView); - pBackBuffer->Release(); + for (UINT i = 0; i < g_numFramesInFlight; i++) + { + data->SwapChain->GetBuffer(i, IID_PPV_ARGS(&back_buffer)); + g_pd3dDevice->CreateRenderTargetView(back_buffer, NULL, data->FrameCtx[i].RenderTargetCpuDescriptors); + data->FrameCtx[i].RenderTarget = back_buffer; + } } - */ } -// arg = ID3D12GraphicsCommandList* -static void ImGui_ImplDX12_RenderWindow(ImGuiViewport* viewport, void* renderer_arg) +static void ImGui_ImplDX12_RenderWindow(ImGuiViewport* viewport, void*) { ImGuiViewportDataDx12* data = (ImGuiViewportDataDx12*)viewport->RendererUserData; - IM_ASSERT(0); - (void)data; - - ID3D12GraphicsCommandList* command_list = (ID3D12GraphicsCommandList*)renderer_arg; - /* ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); - g_pd3dDeviceContext->OMSetRenderTargets(1, &data->RTView, NULL); + + //-- + FrameContext* frame_context = &data->FrameCtx[data->FrameIndex % g_numFramesInFlight]; + + UINT back_buffer_idx = data->SwapChain->GetCurrentBackBufferIndex(); + D3D12_RESOURCE_BARRIER barrier = {}; + barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + barrier.Transition.pResource = data->FrameCtx[back_buffer_idx].RenderTarget; + barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT; + barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET; + + //-- draw + ID3D12GraphicsCommandList* cmd_list = data->CommandList; + + frame_context->CommandAllocator->Reset(); + cmd_list->Reset(frame_context->CommandAllocator, NULL); + cmd_list->ResourceBarrier(1, &barrier); + cmd_list->OMSetRenderTargets(1, &data->FrameCtx[back_buffer_idx].RenderTargetCpuDescriptors, FALSE, NULL); if (!(viewport->Flags & ImGuiViewportFlags_NoRendererClear)) - g_pd3dDeviceContext->ClearRenderTargetView(data->RTView, (float*)&clear_color); - */ - ImGui_ImplDX12_RenderDrawData(viewport->DrawData, command_list); + cmd_list->ClearRenderTargetView(data->FrameCtx[back_buffer_idx].RenderTargetCpuDescriptors, (float*)&clear_color, 0, NULL); + cmd_list->SetDescriptorHeaps(1, &g_pd3dSrvDescHeap); + + ImGui_ImplDX12_RenderDrawData(viewport->DrawData, cmd_list); + + barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET; + barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT; + cmd_list->ResourceBarrier(1, &barrier); + cmd_list->Close(); + + //-- + data->CommandQueue->Wait(data->Fence, data->FenceSignaledValue); + //-- + data->CommandQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*)&cmd_list); + //-- + data->CommandQueue->Signal(data->Fence, ++data->FenceSignaledValue); } static void ImGui_ImplDX12_SwapBuffers(ImGuiViewport* viewport, void*) { ImGuiViewportDataDx12* data = (ImGuiViewportDataDx12*)viewport->RendererUserData; - IM_ASSERT(0); - (void)data; - /* - data->SwapChain->Present(0, 0); // Present without vsync - */ + + data->SwapChain->Present(0, 0); + + while (data->Fence->GetCompletedValue() < data->FenceSignaledValue) + { + SwitchToThread(); + } } void ImGui_ImplDX12_InitPlatformInterface() diff --git a/examples/imgui_impl_dx12.h b/examples/imgui_impl_dx12.h index 80c3bfa150ad..2d3b1fe64713 100644 --- a/examples/imgui_impl_dx12.h +++ b/examples/imgui_impl_dx12.h @@ -4,8 +4,8 @@ // Implemented features: // [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as ImTextureID. Read the FAQ about ImTextureID! // [X] Renderer: Support for large meshes (64k+ vertices) with 16-bits indices. +// [X] Renderer: Multi-viewport. // Missing features, issues: -// [ ] Renderer: Missing multi-viewport support. // [ ] 64-bit only for now! (Because sizeof(ImTextureId) == sizeof(void*)). See github.com/ocornut/imgui/pull/301 // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. @@ -16,6 +16,7 @@ enum DXGI_FORMAT; struct ID3D12Device; +struct ID3D12DescriptorHeap; struct ID3D12GraphicsCommandList; struct D3D12_CPU_DESCRIPTOR_HANDLE; struct D3D12_GPU_DESCRIPTOR_HANDLE; @@ -24,7 +25,7 @@ struct D3D12_GPU_DESCRIPTOR_HANDLE; // Before calling the render function, caller must prepare cmd_list by resetting it and setting the appropriate // render target and descriptor heap that contains font_srv_cpu_desc_handle/font_srv_gpu_desc_handle. // font_srv_cpu_desc_handle and font_srv_gpu_desc_handle are handles to a single SRV descriptor to use for the internal font texture. -IMGUI_IMPL_API bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FORMAT rtv_format, +IMGUI_IMPL_API bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FORMAT rtv_format, ID3D12DescriptorHeap* cbv_srv_heap, D3D12_CPU_DESCRIPTOR_HANDLE font_srv_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE font_srv_gpu_desc_handle); IMGUI_IMPL_API void ImGui_ImplDX12_Shutdown(); IMGUI_IMPL_API void ImGui_ImplDX12_NewFrame(); From 6faad0c34f05e9a4e51919197d35497a82fb8880 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 18 Oct 2019 18:03:56 +0200 Subject: [PATCH 554/828] Backend: DX12: Amend 899e485. Fix memory leaks. Remove unused variable. (#2851) (cherry picked from commit 39e2db6d94c295e7468c6a5fb39d247c641fb123) --- examples/imgui_impl_dx12.cpp | 82 +++++++++++++++--------------------- examples/imgui_impl_dx12.h | 2 +- 2 files changed, 36 insertions(+), 48 deletions(-) diff --git a/examples/imgui_impl_dx12.cpp b/examples/imgui_impl_dx12.cpp index 7f645d3b016b..91a902ea7160 100644 --- a/examples/imgui_impl_dx12.cpp +++ b/examples/imgui_impl_dx12.cpp @@ -3,8 +3,9 @@ // Implemented features: // [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as ImTextureID. Read the FAQ about ImTextureID! +// [X] Renderer: Multi-viewport support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. +// FIXME: The transition from removing a viewport and moving the window in an existing hosted viewport tends to flicker. // [X] Renderer: Support for large meshes (64k+ vertices) with 16-bits indices. -// [X] Renderer: Multi-viewport. // Missing features, issues: // [ ] 64-bit only for now! (Because sizeof(ImTextureId) == sizeof(void*)). See github.com/ocornut/imgui/pull/301 @@ -45,8 +46,8 @@ static DXGI_FORMAT g_RTVFormat = DXGI_FORMAT_UNKNOWN; static ID3D12Resource* g_pFontTextureResource = NULL; static D3D12_CPU_DESCRIPTOR_HANDLE g_hFontSrvCpuDescHandle = {}; static D3D12_GPU_DESCRIPTOR_HANDLE g_hFontSrvGpuDescHandle = {}; - static ID3D12DescriptorHeap* g_pd3dSrvDescHeap = NULL; +static UINT g_numFramesInFlight = 0; struct FrameResources { @@ -56,12 +57,9 @@ struct FrameResources int VertexBufferSize; }; -static UINT g_numFramesInFlight = 0; - struct FrameContext { ID3D12CommandAllocator* CommandAllocator; - UINT64 FenceValue; ID3D12Resource* RenderTarget; D3D12_CPU_DESCRIPTOR_HANDLE RenderTargetCpuDescriptors; }; @@ -70,7 +68,6 @@ struct ImGuiViewportDataDx12 { ID3D12CommandQueue* CommandQueue; ID3D12GraphicsCommandList* CommandList; - ID3D12DescriptorHeap* RtvDescHeap; IDXGISwapChain3* SwapChain; @@ -78,23 +75,21 @@ struct ImGuiViewportDataDx12 UINT64 FenceSignaledValue; HANDLE FenceEvent; + UINT FrameIndex; FrameContext* FrameCtx; FrameResources* Resources; - UINT FrameIndex; - ImGuiViewportDataDx12() { CommandQueue = NULL; CommandList = NULL; - RtvDescHeap = NULL; SwapChain = NULL; - FenceSignaledValue = 0; + Fence = NULL; + FenceSignaledValue = 0; FenceEvent = NULL; FrameIndex = UINT_MAX; - FrameCtx = new FrameContext[g_numFramesInFlight]; Resources = new FrameResources[g_numFramesInFlight]; @@ -109,7 +104,6 @@ struct ImGuiViewportDataDx12 Resources[i].VertexBufferSize = 5000; Resources[i].IndexBufferSize = 10000; } - } ~ImGuiViewportDataDx12() { @@ -130,6 +124,14 @@ struct ImGuiViewportDataDx12 } }; +template +static void SafeRelease(T*& res) +{ + if (res) + res->Release(); + res = NULL; +} + struct VERTEX_CONSTANT_BUFFER { float mvp[4][4]; @@ -209,7 +211,7 @@ void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandL // Create and grow vertex/index buffers if needed if (fr->VertexBuffer == NULL || fr->VertexBufferSize < draw_data->TotalVtxCount) { - if (fr->VertexBuffer != NULL) { fr->VertexBuffer->Release(); fr->VertexBuffer = NULL; } + SafeRelease(fr->VertexBuffer); fr->VertexBufferSize = draw_data->TotalVtxCount + 5000; D3D12_HEAP_PROPERTIES props; memset(&props, 0, sizeof(D3D12_HEAP_PROPERTIES)); @@ -232,7 +234,7 @@ void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandL } if (fr->IndexBuffer == NULL || fr->IndexBufferSize < draw_data->TotalIdxCount) { - if (fr->IndexBuffer != NULL) { fr->IndexBuffer->Release(); fr->IndexBuffer = NULL; } + SafeRelease(fr->IndexBuffer); fr->IndexBufferSize = draw_data->TotalIdxCount + 10000; D3D12_HEAP_PROPERTIES props; memset(&props, 0, sizeof(D3D12_HEAP_PROPERTIES)); @@ -452,8 +454,7 @@ static void ImGui_ImplDX12_CreateFontsTexture() srvDesc.Texture2D.MostDetailedMip = 0; srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; g_pd3dDevice->CreateShaderResourceView(pTexture, &srvDesc, g_hFontSrvCpuDescHandle); - if (g_pFontTextureResource != NULL) - g_pFontTextureResource->Release(); + SafeRelease(g_pFontTextureResource); g_pFontTextureResource = pTexture; } @@ -665,12 +666,14 @@ void ImGui_ImplDX12_InvalidateDeviceObjects() if (!g_pd3dDevice) return; + SafeRelease(g_pVertexShaderBlob); + SafeRelease(g_pPixelShaderBlob); + SafeRelease(g_pRootSignature); + SafeRelease(g_pPipelineState); + SafeRelease(g_pFontTextureResource); + ImGuiIO& io = ImGui::GetIO(); - if (g_pVertexShaderBlob) { g_pVertexShaderBlob->Release(); g_pVertexShaderBlob = NULL; } - if (g_pPixelShaderBlob) { g_pPixelShaderBlob->Release(); g_pPixelShaderBlob = NULL; } - if (g_pRootSignature) { g_pRootSignature->Release(); g_pRootSignature = NULL; } - if (g_pPipelineState) { g_pPipelineState->Release(); g_pPipelineState = NULL; } - if (g_pFontTextureResource) { g_pFontTextureResource->Release(); g_pFontTextureResource = NULL; io.Fonts->TexID = NULL; } // We copied g_pFontTextureView to io.Fonts->TexID so let's clear that as well. + io.Fonts->TexID = NULL; // We copied g_pFontTextureView to io.Fonts->TexID so let's clear that as well. } bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FORMAT rtv_format, ID3D12DescriptorHeap* cbv_srv_heap, @@ -690,7 +693,7 @@ bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FO g_pd3dSrvDescHeap = cbv_srv_heap; ImGuiViewport* main_viewport = ImGui::GetMainViewport(); - main_viewport->RendererUserData = IM_NEW(ImGuiViewportDataDx12); + main_viewport->RendererUserData = IM_NEW(ImGuiViewportDataDx12)(); // Setup back-end capabilities flags io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional) @@ -770,6 +773,7 @@ static void ImGui_ImplDX12_CreateWindow(ImGuiViewport* viewport) IM_ASSERT(data->FenceEvent != NULL); // Create swap chain + // FIXME-VIEWPORT: May want to copy/inherit swap chain settings from the user/application. DXGI_SWAP_CHAIN_DESC1 sd1; ZeroMemory(&sd1, sizeof(sd1)); sd1.BufferCount = g_numFramesInFlight; @@ -799,6 +803,7 @@ static void ImGui_ImplDX12_CreateWindow(ImGuiViewport* viewport) // Or swapChain.As(&mSwapChain) swap_chain->QueryInterface(IID_PPV_ARGS(&data->SwapChain)); + swap_chain->Release(); // Create the render targets if (data->SwapChain) @@ -830,19 +835,11 @@ static void ImGui_ImplDX12_CreateWindow(ImGuiViewport* viewport) for (UINT i = 0; i < g_numFramesInFlight; i++) { - if (data->Resources[i].IndexBuffer) { data->Resources[i].VertexBuffer->Release(); data->Resources[i].IndexBuffer = NULL; } - if (data->Resources[i].VertexBuffer) { data->Resources[i].VertexBuffer->Release(); data->Resources[i].VertexBuffer = NULL; } + SafeRelease(data->Resources[i].IndexBuffer); + SafeRelease(data->Resources[i].VertexBuffer); } } -template -void SafeRelease(D12Resource*& res) -{ - if (res) - res->Release(); - res = NULL; -} - static void ImGui_ImplDX12_DestroyWindow(ImGuiViewport* viewport) { // The main viewport (owned by the application) will always have RendererUserData == NULL since we didn't create the data for it. @@ -853,7 +850,8 @@ static void ImGui_ImplDX12_DestroyWindow(ImGuiViewport* viewport) SafeRelease(data->SwapChain); SafeRelease(data->RtvDescHeap); SafeRelease(data->Fence); - CloseHandle(data->FenceEvent); data->FenceEvent = NULL; + ::CloseHandle(data->FenceEvent); + data->FenceEvent = NULL; for (UINT i = 0; i < g_numFramesInFlight; i++) { @@ -873,9 +871,7 @@ static void ImGui_ImplDX12_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) ImGuiViewportDataDx12* data = (ImGuiViewportDataDx12*)viewport->RendererUserData; for (UINT i = 0; i < g_numFramesInFlight; i++) - { SafeRelease(data->FrameCtx[i].RenderTarget); - } if (data->SwapChain) { @@ -894,12 +890,10 @@ static void ImGui_ImplDX12_RenderWindow(ImGuiViewport* viewport, void*) { ImGuiViewportDataDx12* data = (ImGuiViewportDataDx12*)viewport->RendererUserData; - ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); - - //-- FrameContext* frame_context = &data->FrameCtx[data->FrameIndex % g_numFramesInFlight]; - UINT back_buffer_idx = data->SwapChain->GetCurrentBackBufferIndex(); + + const ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); D3D12_RESOURCE_BARRIER barrier = {}; barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; @@ -908,7 +902,7 @@ static void ImGui_ImplDX12_RenderWindow(ImGuiViewport* viewport, void*) barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT; barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET; - //-- draw + // Draw ID3D12GraphicsCommandList* cmd_list = data->CommandList; frame_context->CommandAllocator->Reset(); @@ -926,11 +920,8 @@ static void ImGui_ImplDX12_RenderWindow(ImGuiViewport* viewport, void*) cmd_list->ResourceBarrier(1, &barrier); cmd_list->Close(); - //-- data->CommandQueue->Wait(data->Fence, data->FenceSignaledValue); - //-- data->CommandQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*)&cmd_list); - //-- data->CommandQueue->Signal(data->Fence, ++data->FenceSignaledValue); } @@ -939,11 +930,8 @@ static void ImGui_ImplDX12_SwapBuffers(ImGuiViewport* viewport, void*) ImGuiViewportDataDx12* data = (ImGuiViewportDataDx12*)viewport->RendererUserData; data->SwapChain->Present(0, 0); - while (data->Fence->GetCompletedValue() < data->FenceSignaledValue) - { - SwitchToThread(); - } + ::SwitchToThread(); } void ImGui_ImplDX12_InitPlatformInterface() diff --git a/examples/imgui_impl_dx12.h b/examples/imgui_impl_dx12.h index 2d3b1fe64713..6cbd1ace5458 100644 --- a/examples/imgui_impl_dx12.h +++ b/examples/imgui_impl_dx12.h @@ -3,8 +3,8 @@ // Implemented features: // [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as ImTextureID. Read the FAQ about ImTextureID! +// [X] Renderer: Multi-viewport support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. // [X] Renderer: Support for large meshes (64k+ vertices) with 16-bits indices. -// [X] Renderer: Multi-viewport. // Missing features, issues: // [ ] 64-bit only for now! (Because sizeof(ImTextureId) == sizeof(void*)). See github.com/ocornut/imgui/pull/301 From 7b77cb3bb8712dff11c3420fc980abe2191dd395 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 21 Oct 2019 13:38:33 +0200 Subject: [PATCH 555/828] Backend: DX12: Fixed incorrect assert (#2851) --- examples/imgui_impl_dx12.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/examples/imgui_impl_dx12.cpp b/examples/imgui_impl_dx12.cpp index 91a902ea7160..b7ca61380fea 100644 --- a/examples/imgui_impl_dx12.cpp +++ b/examples/imgui_impl_dx12.cpp @@ -788,20 +788,18 @@ static void ImGui_ImplDX12_CreateWindow(ImGuiViewport* viewport) sd1.Scaling = DXGI_SCALING_STRETCH; sd1.Stereo = FALSE; - IM_ASSERT(data->SwapChain == NULL); - IM_ASSERT(data->FrameCtx[0].RenderTarget == NULL && data->FrameCtx[1].RenderTarget == NULL && data->FrameCtx[2].RenderTarget == NULL); - - IDXGIFactory4* dxgi_factory = nullptr; - res = CreateDXGIFactory1(IID_PPV_ARGS(&dxgi_factory)); + IDXGIFactory4* dxgi_factory = NULL; + res = ::CreateDXGIFactory1(IID_PPV_ARGS(&dxgi_factory)); IM_ASSERT(res == S_OK); - IDXGISwapChain1* swap_chain = nullptr; + IDXGISwapChain1* swap_chain = NULL; res = dxgi_factory->CreateSwapChainForHwnd(data->CommandQueue, hwnd, &sd1, NULL, NULL, &swap_chain); IM_ASSERT(res == S_OK); dxgi_factory->Release(); // Or swapChain.As(&mSwapChain) + IM_ASSERT(data->SwapChain == NULL); swap_chain->QueryInterface(IID_PPV_ARGS(&data->SwapChain)); swap_chain->Release(); @@ -827,6 +825,7 @@ static void ImGui_ImplDX12_CreateWindow(ImGuiViewport* viewport) ID3D12Resource* back_buffer; for (UINT i = 0; i < g_numFramesInFlight; i++) { + IM_ASSERT(data->FrameCtx[i].RenderTarget == NULL); data->SwapChain->GetBuffer(i, IID_PPV_ARGS(&back_buffer)); g_pd3dDevice->CreateRenderTargetView(back_buffer, NULL, data->FrameCtx[i].RenderTargetCpuDescriptors); data->FrameCtx[i].RenderTarget = back_buffer; @@ -860,7 +859,6 @@ static void ImGui_ImplDX12_DestroyWindow(ImGuiViewport* viewport) SafeRelease(data->Resources[i].IndexBuffer); SafeRelease(data->Resources[i].VertexBuffer); } - IM_DELETE(data); } viewport->RendererUserData = NULL; From 664fb38e399bd4b86f4adf75fb260b74fd8909ac Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 25 Oct 2019 11:13:51 +0200 Subject: [PATCH 556/828] Docking: child windows don't use style.ChildRounding. --- imgui.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 4883146171ae..731bb0bdda17 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6088,9 +6088,10 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->Pos = ImFloor(window->Pos); // Lock window rounding for the frame (so that altering them doesn't cause inconsistencies) - window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding; - if (window->ViewportOwned) + if (window->ViewportOwned || window->DockIsActive) window->WindowRounding = 0.0f; + else + window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding; // Apply window focus (new and reactivated windows are moved to front) bool want_focus = false; From 32380a0112b088ac97360c1d6029c93de60be157 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 29 Oct 2019 16:43:11 +0100 Subject: [PATCH 557/828] Viewport: Store current dpi scale in context. --- imgui.cpp | 5 +++-- imgui_internal.h | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 731bb0bdda17..7e07a2e1f9bc 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7391,8 +7391,7 @@ ImDrawList* ImGui::GetWindowDrawList() float ImGui::GetWindowDpiScale() { ImGuiContext& g = *GImGui; - IM_ASSERT(g.CurrentViewport != NULL); - return g.CurrentViewport->DpiScale; + return g.CurrentDpiScale; } ImGuiViewport* ImGui::GetWindowViewport() @@ -10253,6 +10252,7 @@ void ImGui::SetCurrentViewport(ImGuiWindow* current_window, ImGuiViewportP* view viewport->LastFrameActive = g.FrameCount; if (g.CurrentViewport == viewport) return; + g.CurrentDpiScale = viewport->DpiScale; g.CurrentViewport = viewport; //IMGUI_DEBUG_LOG_VIEWPORT("SetCurrentViewport changed '%s' 0x%08X\n", current_window ? current_window->Name : NULL, viewport ? viewport->ID : 0); @@ -10408,6 +10408,7 @@ static void ImGui::UpdateViewportsNewFrame() main_viewport_platform_pos = (main_viewport->Flags & ImGuiViewportFlags_Minimized) ? main_viewport->Pos : g.PlatformIO.Platform_GetWindowPos(main_viewport); AddUpdateViewport(NULL, IMGUI_VIEWPORT_DEFAULT_ID, main_viewport_platform_pos, main_viewport_platform_size, ImGuiViewportFlags_CanHostOtherWindows); + g.CurrentDpiScale = 0.0f; g.CurrentViewport = NULL; g.MouseViewport = NULL; for (int n = 0; n < g.Viewports.Size; n++) diff --git a/imgui_internal.h b/imgui_internal.h index 71d644696910..b198612f78cf 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1079,6 +1079,7 @@ struct ImGuiContext // Viewports ImVector Viewports; // Active viewports (always 1+, and generally 1 unless multi-viewports are enabled). Each viewports hold their copy of ImDrawData. + float CurrentDpiScale; // == CurrentViewport->DpiScale ImGuiViewportP* CurrentViewport; // We track changes of viewport (happening in Begin) so we can call Platform_OnChangedViewport() ImGuiViewportP* MouseViewport; ImGuiViewportP* MouseLastHoveredViewport; // Last known viewport that was hovered by mouse (even if we are not hovering any viewport any more) + honoring the _NoInputs flag. @@ -1271,6 +1272,7 @@ struct ImGuiContext LastActiveId = 0; LastActiveIdTimer = 0.0f; + CurrentDpiScale = 0.0f; CurrentViewport = NULL; MouseViewport = MouseLastHoveredViewport = NULL; PlatformLastFocusedViewport = 0; From 6024051a2fcfc75d640ba5ed1660bc607ad49937 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 29 Oct 2019 21:18:01 +0100 Subject: [PATCH 558/828] Viewport: Fixed 32380a0 (#2876) --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index de3ca78c6b97..48898ba8c88c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10252,7 +10252,7 @@ void ImGui::SetCurrentViewport(ImGuiWindow* current_window, ImGuiViewportP* view viewport->LastFrameActive = g.FrameCount; if (g.CurrentViewport == viewport) return; - g.CurrentDpiScale = viewport->DpiScale; + g.CurrentDpiScale = viewport ? viewport->DpiScale : 1.0f; g.CurrentViewport = viewport; //IMGUI_DEBUG_LOG_VIEWPORT("SetCurrentViewport changed '%s' 0x%08X\n", current_window ? current_window->Name : NULL, viewport ? viewport->ID : 0); From c3fd4ae473352ed1989738e927af55ad4fd4b1c4 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 12 Nov 2019 11:06:02 +0100 Subject: [PATCH 559/828] Docking: comments --- docs/TODO.txt | 57 ++++++++++++++++++++++++------------------------ imgui.cpp | 1 + imgui.h | 4 +++- imgui_demo.cpp | 3 ++- imgui_internal.h | 10 +++++---- 5 files changed, 41 insertions(+), 34 deletions(-) diff --git a/docs/TODO.txt b/docs/TODO.txt index bd3bca914258..828ef8adbe3e 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -153,34 +153,35 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - separator: width, thickness, centering (#1643) - splitter: formalize the splitter idiom into an official api (we want to handle n-way split) (#319) - - dock: merge docking branch (#2109) - - dock: B: ordering currently held in tab bar should be implicitly held by windows themselves (also see #2304) - - dock: B- tab bar: the order/focus restoring code could be part of TabBar and not DockNode? (#8) - - dock: B~ rework code to be able to lazily create tab bar instance in a single place. The _Unsorted tab flag could be replacing a trailing-counter in DockNode? - - dock: B~ fully track windows/settings reference in dock nodes. perhaps find a representation that allows facilitate use of dock builder functions. - - dock: B~ Unreal style document system (requires low-level controls of dockspace serialization fork/copy/delete). this is mostly working but the DockBuilderXXX api are not exposed/finished. - - dock: B: when docking outer, perform size locking on neighbors nodes the same way we do it with splitters, so other nodes are not resized. - - dock: B~ central node resizing behavior incorrect. - - dock: B: changing title font/style per-window is not supported as dock nodes are created in NewFrame. - - dock: B- dock node inside its own viewports creates 1 temporary viewport per window on startup before ditching them (doesn't affect the user nor request platform windows to be created, but unnecessary) - - dock: B- resize sibling locking behavior may be less desirable if we merged same-axis sibling in a same node level? - - dock: B- single visible node part of a hidden split hierarchy (OnlyNodeWithWindows != NULL) should show a normal title bar (not a tab bar) - - dock: B~ SetNextWindowDock() calls (with conditional) -> defer everything to DockContextUpdate (repro: Documents->[X]Windows->Dock 1 elsewhere->Click Redock All - - dock: B~ tidy up tab list popup buttons features (available with manual tab-bar, see ImGuiTabBarFlags_NoTabListPopupButton code, not used by docking nodes) - - dock: B- SetNextWindowDockId(0) with a second Begin() in the frame will asserts - - dock: B: resize grip drawn in host window typically appears under scrollbar. - - dock: B- SetNextWindowFocus() doesn't seem to apply if the window is hidden this frame, need repro (#4) - - dock: B- resizing a dock tree small currently has glitches (overlapping collapse and close button, etc.) - - dock: B- dpi: look at interaction with the hi-dpi and multi-dpi stuff. - - dock: B- tab bar: appearing on first frame with a dumb layout would do less harm that not appearing? (when behind dynamic branch) or store titles + render in EndTabBar() - - dock: B- tab bar: make selected tab always shows its full title? - - dock: B- nav: design interactions so nav controls can dock/undock - - dock: B- dockspace: flag to lock the dock tree and/or sizes (ImGuiDockNodeFlags_Locked?) - - dock: B- reintroduce collapsing a floating dock node. also collapsing a docked dock node! - - dock: B- allow dragging a non-floating dock node by clicking on the title-bar-looking section (not just the collapse/menu button) - - dock: B- option to remember undocked window size? (instead of keeping their docked size) (relate to #2104) - - dock: C- nav: CTRL+TAB highlighting tabs shows the mismatch between focus-stack and tab-order (not visible in VS because it doesn't highlight the tabs) - - dock: C- after a dock/undock, the Scrollbar Status update in Begin() should use an updated e.g. size_y_for_scrollbars to avoid a 1 frame scrollbar flicker. + - docking: merge docking branch (#2109) + - docking: B: ordering currently held in tab bar should be implicitly held by windows themselves (also see #2304) + - docking: B- tab bar: the order/focus restoring code could be part of TabBar and not DockNode? (#8) + - docking: B~ rework code to be able to lazily create tab bar instance in a single place. The _Unsorted tab flag could be replacing a trailing-counter in DockNode? + - docking: B~ fully track windows/settings reference in dock nodes. perhaps find a representation that allows facilitate use of dock builder functions. + - docking: B~ Unreal style document system (requires low-level controls of dockspace serialization fork/copy/delete). this is mostly working but the DockBuilderXXX api are not exposed/finished. + - docking: B: when docking outer, perform size locking on neighbors nodes the same way we do it with splitters, so other nodes are not resized. + - docking: B~ central node resizing behavior incorrect. + - docking: B: changing title font/style per-window is not supported as dock nodes are created in NewFrame. + - docking: B- dock node inside its own viewports creates 1 temporary viewport per window on startup before ditching them (doesn't affect the user nor request platform windows to be created, but unnecessary) + - docking: B- resize sibling locking behavior may be less desirable if we merged same-axis sibling in a same node level? + - docking: B- single visible node part of a hidden split hierarchy (OnlyNodeWithWindows != NULL) should show a normal title bar (not a tab bar) + - docking: B~ SetNextWindowDock() calls (with conditional) -> defer everything to DockContextUpdate (repro: Documents->[X]Windows->Dock 1 elsewhere->Click Redock All + - docking: B~ tidy up tab list popup buttons features (available with manual tab-bar, see ImGuiTabBarFlags_NoTabListPopupButton code, not used by docking nodes) + - docking: B- SetNextWindowDockId(0) with a second Begin() in the frame will asserts + - docking: B: resize grip drawn in host window typically appears under scrollbar. + - docking: B- SetNextWindowFocus() doesn't seem to apply if the window is hidden this frame, need repro (#4) + - docking: B- resizing a dock tree small currently has glitches (overlapping collapse and close button, etc.) + - docking: B- dpi: look at interaction with the hi-dpi and multi-dpi stuff. + - docking: B- tab bar: appearing on first frame with a dumb layout would do less harm that not appearing? (when behind dynamic branch) or store titles + render in EndTabBar() + - docking: B- tab bar: make selected tab always shows its full title? + - docking: B- hide close button on single tab bar? + - docking: B- nav: design interactions so nav controls can dock/undock + - docking: B- dockspace: flag to lock the dock tree and/or sizes (ImGuiDockNodeFlags_Locked?) + - docking: B- reintroduce collapsing a floating dock node. also collapsing a docked dock node! + - docking: B- allow dragging a non-floating dock node by clicking on the title-bar-looking section (not just the collapse/menu button) + - docking: B- option to remember undocked window size? (instead of keeping their docked size) (relate to #2104) + - docking: C- nav: CTRL+TAB highlighting tabs shows the mismatch between focus-stack and tab-order (not visible in VS because it doesn't highlight the tabs) + - docking: C- after a dock/undock, the Scrollbar Status update in Begin() should use an updated e.g. size_y_for_scrollbars to avoid a 1 frame scrollbar flicker. - tabs: make EndTabBar fail if users doesn't respect BeginTabBar return value, for consistency/future-proofing. - tabs: persistent order/focus in BeginTabBar() api (#261, #351) diff --git a/imgui.cpp b/imgui.cpp index 1fce28e581dd..5a8e987acc64 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -13481,6 +13481,7 @@ void ImGui::SetWindowDock(ImGuiWindow* window, ImGuiID dock_id, ImGuiCond cond) // Create an explicit dockspace node within an existing window. Also expose dock node flags and creates a CentralNode by default. // The Central Node is always displayed even when empty and shrink/extend according to the requested size of its neighbors. +// DockSpace() needs to be submitted _before_ any window they can host. If you use a dockspace, submit it early in your app. void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags flags, const ImGuiWindowClass* window_class) { ImGuiContext* ctx = GImGui; diff --git a/imgui.h b/imgui.h index 7322963a3efe..92abcf2511c8 100644 --- a/imgui.h +++ b/imgui.h @@ -611,10 +611,12 @@ namespace ImGui // Docking // [BETA API] Enable with io.ConfigFlags |= ImGuiConfigFlags_DockingEnable. - // Note: you DO NOT need to call DockSpace() to use most Docking facilities! + // Note: You can use most Docking facilities without calling any API. You DO NOT need to call DockSpace() to use Docking! // - To dock windows: if io.ConfigDockingWithShift == false (default) drag window from their title bar. // - To dock windows: if io.ConfigDockingWithShift == true: hold SHIFT anywhere while moving windows. + // About DockSpace: // - Use DockSpace() to create an explicit dock node _within_ an existing window. See Docking demo for details. + // - DockSpace() needs to be submitted _before_ any window they can host. If you use a dockspace, submit it early in your app. IMGUI_API void DockSpace(ImGuiID id, const ImVec2& size = ImVec2(0, 0), ImGuiDockNodeFlags flags = 0, const ImGuiWindowClass* window_class = NULL); IMGUI_API ImGuiID DockSpaceOverViewport(ImGuiViewport* viewport = NULL, ImGuiDockNodeFlags flags = 0, const ImGuiWindowClass* window_class = NULL); IMGUI_API void SetNextWindowDockID(ImGuiID dock_id, ImGuiCond cond = 0); // set next window dock id (FIXME-DOCK) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index e03396dde3f7..fddeb47e8f29 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -4652,7 +4652,8 @@ void ShowExampleAppDockSpace(bool* p_open) window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus; } - // When using ImGuiDockNodeFlags_PassthruCentralNode, DockSpace() will render our background and handle the pass-thru hole, so we ask Begin() to not render a background. + // When using ImGuiDockNodeFlags_PassthruCentralNode, DockSpace() will render our background + // and handle the pass-thru hole, so we ask Begin() to not render a background. if (dockspace_flags & ImGuiDockNodeFlags_PassthruCentralNode) window_flags |= ImGuiWindowFlags_NoBackground; diff --git a/imgui_internal.h b/imgui_internal.h index dc5621fe7f7e..cdd3e1c108ea 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1865,12 +1865,14 @@ namespace ImGui IMGUI_API void BeginAsDockableDragDropTarget(ImGuiWindow* window); IMGUI_API void SetWindowDock(ImGuiWindow* window, ImGuiID dock_id, ImGuiCond cond); - // Docking - Builder function needs to be generally called before the DockSpace() node is submitted. + // Docking - Builder function needs to be generally called before the node is used/submitted. // - The DockBuilderXXX functions are designed to _eventually_ become a public API, but it is too early to expose it and guarantee stability. - // - You can create dockspace _or_ floating nodes with this API. To create a dockspace node, make sure to set the ImGuiDockNodeFlags_DockSpace flag. - // - If you intend to split the node immediately after creation using DockBuilderSplitNode(), make sure to call DockBuilderSetNodeSize() beforehand. + // - Do not hold on ImGuiDockNode* pointers! They may be invalidated by any split/merge/remove operation and every frame. + // - To create a DockSpace() node, make sure to set the ImGuiDockNodeFlags_DockSpace flag when calling DockBuilderAddNode(). + // You can create dockspace nodes (attached to a window) _or_ floating nodes (carry its own window) with this API. + // - If you intend to split the node immediately after creation using DockBuilderSplitNode(), make sure + // to call DockBuilderSetNodeSize() beforehand. If you don't, the resulting split sizes may not be reliable. // - Call DockBuilderFinish() after you are done. - // - Important: do not hold on ImGuiDockNode* pointers! They may be invalidated by any split/merge/remove operation and every frame. IMGUI_API void DockBuilderDockWindow(const char* window_name, ImGuiID node_id); IMGUI_API ImGuiDockNode*DockBuilderGetNode(ImGuiID node_id); inline ImGuiDockNode* DockBuilderGetCentralNode(ImGuiID node_id) { ImGuiDockNode* node = DockBuilderGetNode(node_id); if (!node) return NULL; return DockNodeGetRootNode(node)->CentralNode; } From 106184bbeac4de9471f9acfd6af200ee5a1bd94c Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 22 Nov 2019 22:13:52 +0100 Subject: [PATCH 560/828] Docking: Fixed node->HasCloseButton not honoring ImGuiDockNodeFlags_NoCloseButton in a floating node, leading to empty space at the right of tab-bars with those flags. (#2109) --- imgui.cpp | 2 +- imgui.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index cf592d908f0c..7305baa6ad66 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -12329,7 +12329,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) { // [Automatic root or child nodes] node->EnableCloseButton = false; - node->HasCloseButton = (node->Windows.Size > 0) && (node_flags & ImGuiDockNodeFlags_NoWindowMenuButton) == 0; + node->HasCloseButton = (node->Windows.Size > 0) && (node_flags & ImGuiDockNodeFlags_NoCloseButton) == 0; node->HasWindowMenuButton = (node->Windows.Size > 0) && (node_flags & ImGuiDockNodeFlags_NoWindowMenuButton) == 0; for (int window_n = 0; window_n < node->Windows.Size; window_n++) { diff --git a/imgui.h b/imgui.h index 92abcf2511c8..6baf9161cc6a 100644 --- a/imgui.h +++ b/imgui.h @@ -1614,7 +1614,7 @@ struct ImGuiWindowClass ImGuiID ParentViewportId; // Hint for the platform back-end. If non-zero, the platform back-end can create a parent<>child relationship between the platform windows. Not conforming back-ends are free to e.g. parent every viewport to the main viewport or not. ImGuiViewportFlags ViewportFlagsOverrideSet; // Viewport flags to set when a window of this class owns a viewport. This allows you to enforce OS decoration or task bar icon, override the defaults on a per-window basis. ImGuiViewportFlags ViewportFlagsOverrideClear; // Viewport flags to clear when a window of this class owns a viewport. This allows you to enforce OS decoration or task bar icon, override the defaults on a per-window basis. - bool DockingAlwaysTabBar; // Set to true to enforce windows of this class always having their own tab (equivalent of setting the global io.ConfigDockingAlwaysTabBar) + bool DockingAlwaysTabBar; // Set to true to enforce single floating windows of this class always having their own tab bar (equivalent of setting the global io.ConfigDockingAlwaysTabBar) bool DockingAllowUnclassed; // Set to true to allow windows of this class to be docked/merged with an unclassed window. ImGuiWindowClass() { ClassId = 0; ParentViewportId = 0; ViewportFlagsOverrideSet = ViewportFlagsOverrideClear = 0x00; DockingAlwaysTabBar = false; DockingAllowUnclassed = true; } From 3a82994429544030337fbaae109e99ecb3685fb5 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 22 Nov 2019 22:35:04 +0100 Subject: [PATCH 561/828] Docking: Can undock from the small triangle button. (#2109,. #2645) --- imgui.cpp | 14 +++++++++----- imgui.h | 4 ++-- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 7305baa6ad66..250eb4614070 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5511,18 +5511,22 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border), style.FrameBorderSize); } - // Docking: Unhide tab bar (small triangle in the corner) - if (window->DockNode && window->DockNode->IsHiddenTabBar() && !window->DockNode->IsNoTabBar()) + // Docking: Unhide tab bar (small triangle in the corner), drag from small triangle to quickly undock + ImGuiDockNode* node = window->DockNode; + if (node && window->DockIsActive && node->IsHiddenTabBar() && !node->IsNoTabBar()) { float unhide_sz_draw = ImFloor(g.FontSize * 0.70f); float unhide_sz_hit = ImFloor(g.FontSize * 0.55f); - ImVec2 p = window->DockNode->Pos; + ImVec2 p = node->Pos; ImRect r(p, p + ImVec2(unhide_sz_hit, unhide_sz_hit)); bool hovered, held; if (ButtonBehavior(r, window->GetID("#UNHIDE"), &hovered, &held, ImGuiButtonFlags_FlattenChildren)) - window->DockNode->WantHiddenTabBarToggle = true; + node->WantHiddenTabBarToggle = true; + else if (held && IsMouseDragging(0)) + StartMouseDragFromTitleBar(window, node, true); + // FIXME-DOCK: Ideally we'd use ImGuiCol_TitleBgActive/ImGuiCol_TitleBg here, but neither is guaranteed to be visible enough at this sort of size.. - ImU32 col = GetColorU32(((held && hovered) || (window->DockNode->IsFocused && !hovered)) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); + ImU32 col = GetColorU32(((held && hovered) || (node->IsFocused && !hovered)) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); window->DrawList->AddTriangleFilled(p, p + ImVec2(unhide_sz_draw, 0.0f), p + ImVec2(0.0f, unhide_sz_draw), col); } diff --git a/imgui.h b/imgui.h index 6baf9161cc6a..1b4ce621e2a5 100644 --- a/imgui.h +++ b/imgui.h @@ -1610,11 +1610,11 @@ struct ImGuiSizeCallbackData // Provide hints to the platform back-end via altered viewport flags (enable/disable OS decoration, OS task bar icons, etc.) and OS level parent/child relationships. struct ImGuiWindowClass { - ImGuiID ClassId; // User data. 0 = Default class (unclassed) + ImGuiID ClassId; // User data. 0 = Default class (unclassed). Windows of different classes cannot be docked with each others. ImGuiID ParentViewportId; // Hint for the platform back-end. If non-zero, the platform back-end can create a parent<>child relationship between the platform windows. Not conforming back-ends are free to e.g. parent every viewport to the main viewport or not. ImGuiViewportFlags ViewportFlagsOverrideSet; // Viewport flags to set when a window of this class owns a viewport. This allows you to enforce OS decoration or task bar icon, override the defaults on a per-window basis. ImGuiViewportFlags ViewportFlagsOverrideClear; // Viewport flags to clear when a window of this class owns a viewport. This allows you to enforce OS decoration or task bar icon, override the defaults on a per-window basis. - bool DockingAlwaysTabBar; // Set to true to enforce single floating windows of this class always having their own tab bar (equivalent of setting the global io.ConfigDockingAlwaysTabBar) + bool DockingAlwaysTabBar; // Set to true to enforce single floating windows of this class always having their own docking node (equivalent of setting the global io.ConfigDockingAlwaysTabBar) bool DockingAllowUnclassed; // Set to true to allow windows of this class to be docked/merged with an unclassed window. ImGuiWindowClass() { ClassId = 0; ParentViewportId = 0; ViewportFlagsOverrideSet = ViewportFlagsOverrideClear = 0x00; DockingAlwaysTabBar = false; DockingAllowUnclassed = true; } From 1c3a9c8e7452ae491143c5210dba70ef9b311460 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 26 Nov 2019 20:25:54 +0100 Subject: [PATCH 562/828] Docking: Remove Size > 0.0f asserts added in 718e15c7 and 7c183dc6. (#2690, #2109, #2906) In #2906 the zero input came from a minimized viewport, but even without it we cannot prevent DockNode size from eventually reaching zero as padding are taken from the starting size. In a separate commit we'll however shortcut some of the existing codepath on zero-sized viewport to reduce the likehood of lossy side-effects (just like we don't call ClampWindowRect in Begin) --- imgui.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 1aae6351282a..e04267e85b3e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -13278,7 +13278,6 @@ void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 si { // During the regular dock node update we write to all nodes. // 'only_write_to_marked_nodes' is only set when turning a node visible mid-frame and we need its size right-away. - IM_ASSERT(size.x > 0.0f && size.y > 0.0f); const bool write_to_node = (only_write_to_marked_nodes == false) || (node->MarkedForPosSizeWrite); if (write_to_node) { @@ -14223,9 +14222,10 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) return; } + // We can have zero-sized nodes (e.g. children of a small-size dockspace) IM_ASSERT(node->HostWindow); IM_ASSERT(node->IsLeafNode()); - IM_ASSERT(node->Size.x > 0.0f && node->Size.y > 0.0f); + IM_ASSERT(node->Size.x >= 0.0f && node->Size.y >= 0.0f); node->State = ImGuiDockNodeState_HostWindowVisible; // Undock if we are submitted earlier than the host window From 3096e7a9cd25946a2bd5f526e15d482cc98c9124 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 26 Nov 2019 21:02:28 +0100 Subject: [PATCH 563/828] Viewports: Preserve last known size for minimized main viewport to be consistent with secondary viewports. Amend 606175b9, d8ab2c1a. However becomes inconsistent with viewport-enabled setup. Should report some of that logic in master, need back-end rework. --- imgui.cpp | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index e04267e85b3e..374f5a90311c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10476,7 +10476,8 @@ static void ImGui::UpdateViewportsNewFrame() IM_ASSERT(g.PlatformIO.Viewports.Size <= g.Viewports.Size); // Update Minimized status (we need it first in order to decide if we'll apply Pos/Size of the main viewport) - if ((g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable)) + const bool viewports_enabled = (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable) != 0; + if (viewports_enabled) { for (int n = 0; n < g.Viewports.Size; n++) { @@ -10493,15 +10494,19 @@ static void ImGui::UpdateViewportsNewFrame() } } - // Create/update main viewport with current platform position and size + // Create/update main viewport with current platform position. + // FIXME-VIEWPORT: Size is driven by back-end/user code for backward-compatibility but we should aim to make this more consistent. ImGuiViewportP* main_viewport = g.Viewports[0]; IM_ASSERT(main_viewport->ID == IMGUI_VIEWPORT_DEFAULT_ID); IM_ASSERT(main_viewport->Window == NULL); - ImVec2 main_viewport_platform_pos = ImVec2(0.0f, 0.0f); - ImVec2 main_viewport_platform_size = g.IO.DisplaySize; - if (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable) - main_viewport_platform_pos = (main_viewport->Flags & ImGuiViewportFlags_Minimized) ? main_viewport->Pos : g.PlatformIO.Platform_GetWindowPos(main_viewport); - AddUpdateViewport(NULL, IMGUI_VIEWPORT_DEFAULT_ID, main_viewport_platform_pos, main_viewport_platform_size, ImGuiViewportFlags_CanHostOtherWindows); + ImVec2 main_viewport_pos = viewports_enabled ? g.PlatformIO.Platform_GetWindowPos(main_viewport) : ImVec2(0.0f, 0.0f); + ImVec2 main_viewport_size = g.IO.DisplaySize; + if (viewports_enabled && (main_viewport->Flags & ImGuiViewportFlags_Minimized)) + { + main_viewport_pos = main_viewport->Pos; // Preserve last pos/size when minimized (FIXME: We don't do the same for Size outside of the viewport path) + main_viewport_size = main_viewport->Size; + } + AddUpdateViewport(NULL, IMGUI_VIEWPORT_DEFAULT_ID, main_viewport_pos, main_viewport_size, ImGuiViewportFlags_CanHostOtherWindows); g.CurrentDpiScale = 0.0f; g.CurrentViewport = NULL; @@ -10535,7 +10540,7 @@ static void ImGui::UpdateViewportsNewFrame() } const bool platform_funcs_available = viewport->PlatformWindowCreated; - if ((g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable)) + if (viewports_enabled) { // Update Position and Size (from Platform Window to ImGui) if requested. // We do it early in the frame instead of waiting for UpdatePlatformWindows() to avoid a frame of lag when moving/resizing using OS facilities. @@ -10585,7 +10590,7 @@ static void ImGui::UpdateViewportsNewFrame() viewport->DpiScale = new_dpi_scale; } - if (!(g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable)) + if (!viewports_enabled) { g.MouseViewport = main_viewport; return; From 8d1b82d5961a03021b1cc3c6ce8bedeb1a846be4 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 27 Nov 2019 15:33:31 +0100 Subject: [PATCH 564/828] Docking: Internals: Rename StartMouseDragFromTitleBar() -> StartMouseMovingWindowOrNode(), clarify. --- imgui.cpp | 45 ++++++++++++++++++++++++++------------------- imgui_internal.h | 2 +- imgui_widgets.cpp | 2 +- 3 files changed, 28 insertions(+), 21 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 374f5a90311c..42e10aefbff5 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3296,26 +3296,32 @@ void ImGui::StartMouseMovingWindow(ImGuiWindow* window) g.MovingWindow = window; } -void ImGui::StartMouseDragFromTitleBar(ImGuiWindow* window, ImGuiDockNode* node, bool from_collapse_button) +// We use 'undock_floating_node == false' when dragging from title bar to allow moving groups of floating nodes without undocking them. +// - undock_floating_node == true: when dragging from a floating node within a hierarchy, always undock the node. +// - undock_floating_node == false: when dragging from a floating node within a hierarchy, move root window. +void ImGui::StartMouseMovingWindowOrNode(ImGuiWindow* window, ImGuiDockNode* node, bool undock_floating_node) { ImGuiContext& g = *GImGui; - bool can_extract_dock_node = false; + bool can_undock_node = false; if (node != NULL && node->VisibleWindow && (node->VisibleWindow->Flags & ImGuiWindowFlags_NoMove) == 0) { + // Can undock if: + // - part of a floating node hierarchy with more than one visible node (if only one is visible, we'll just move the whole hierarchy) + // - part of a dockspace node hierarchy (trivia: undocking from a fixed/central node will create a new node and copy windows) ImGuiDockNode* root_node = DockNodeGetRootNode(node); - if (root_node->OnlyNodeWithWindows != node || (root_node->CentralNode != NULL)) - if (from_collapse_button || root_node->IsDockSpace()) - can_extract_dock_node = true; + if (root_node->OnlyNodeWithWindows != node || root_node->CentralNode != NULL) + if (undock_floating_node || root_node->IsDockSpace()) + can_undock_node = true; } const bool clicked = IsMouseClicked(0); const bool dragging = IsMouseDragging(0, g.IO.MouseDragThreshold * 1.70f); - if (can_extract_dock_node && dragging) + if (can_undock_node && dragging) { DockContextQueueUndockNode(&g, node); g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - node->Pos; } - else if (!can_extract_dock_node && (clicked || dragging) && g.MovingWindow != window) + else if (!can_undock_node && (clicked || dragging) && g.MovingWindow != window) { StartMouseMovingWindow(window); g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - window->RootWindow->Pos; @@ -5541,7 +5547,7 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar if (ButtonBehavior(r, window->GetID("#UNHIDE"), &hovered, &held, ImGuiButtonFlags_FlattenChildren)) node->WantHiddenTabBarToggle = true; else if (held && IsMouseDragging(0)) - StartMouseDragFromTitleBar(window, node, true); + StartMouseMovingWindowOrNode(window, node, true); // FIXME-DOCK: Ideally we'd use ImGuiCol_TitleBgActive/ImGuiCol_TitleBg here, but neither is guaranteed to be visible enough at this sort of size.. ImU32 col = GetColorU32(((held && hovered) || (node->IsFocused && !hovered)) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); @@ -12188,18 +12194,19 @@ static void ImGui::DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* nod remove |= node_was_active && (window->LastFrameActive + 1 < g.FrameCount); remove |= node_was_active && (node->WantCloseAll || node->WantCloseTabID == window->ID) && window->HasCloseButton && !(window->Flags & ImGuiWindowFlags_UnsavedDocument); // Submit all _expected_ closure from last frame remove |= (window->DockTabWantClose); - if (!remove) - continue; - window->DockTabWantClose = false; - if (node->Windows.Size == 1 && !node->IsCentralNode()) + if (remove) { - DockNodeHideHostWindow(node); - node->State = ImGuiDockNodeState_HostWindowHiddenBecauseSingleWindow; - DockNodeRemoveWindow(node, window, node->ID); // Will delete the node so it'll be invalid on return - return; + window->DockTabWantClose = false; + if (node->Windows.Size == 1 && !node->IsCentralNode()) + { + DockNodeHideHostWindow(node); + node->State = ImGuiDockNodeState_HostWindowHiddenBecauseSingleWindow; + DockNodeRemoveWindow(node, window, node->ID); // Will delete the node so it'll be invalid on return + return; + } + DockNodeRemoveWindow(node, window, node->ID); + window_n--; } - DockNodeRemoveWindow(node, window, node->ID); - window_n--; } // Auto-hide tab bar option @@ -12798,7 +12805,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w // Forward moving request to selected window if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_bar->SelectedTabId)) - StartMouseDragFromTitleBar(tab->Window, node, false); + StartMouseMovingWindowOrNode(tab->Window, node, false); } } diff --git a/imgui_internal.h b/imgui_internal.h index ba03351b0f00..40db120964d0 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1805,7 +1805,7 @@ namespace ImGui // NewFrame IMGUI_API void UpdateHoveredWindowAndCaptureFlags(); IMGUI_API void StartMouseMovingWindow(ImGuiWindow* window); - IMGUI_API void StartMouseDragFromTitleBar(ImGuiWindow* window, ImGuiDockNode* node, bool from_collapse_button); + IMGUI_API void StartMouseMovingWindowOrNode(ImGuiWindow* window, ImGuiDockNode* node, bool undock_floating_node); IMGUI_API void UpdateMouseMovingWindowNewFrame(); IMGUI_API void UpdateMouseMovingWindowEndFrame(); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index b938e09c495b..088639bf5637 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -776,7 +776,7 @@ bool ImGui::CollapseButton(ImGuiID id, const ImVec2& pos, ImGuiDockNode* dock_no // Switch to moving the window after mouse is moved beyond the initial drag threshold if (IsItemActive() && IsMouseDragging(0)) - StartMouseDragFromTitleBar(window, dock_node, true); + StartMouseMovingWindowOrNode(window, dock_node, true); return pressed; } From 71a58261f6d14fdda4eb8c907b6d3aa0c1abcf10 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 27 Nov 2019 18:21:04 +0100 Subject: [PATCH 565/828] Docking: Internals: Removed redundancy in code path leading to the BeginAsDockableDragDropSource(), clarified UpdateMouseMovingWindowEndFrame() Note that the ConfigWindowsMoveFromTitleBarOnly path for UpdateMouseMovingWindowEndFrame() would previously test the window->RootWindow title bar instead of window->RootWindowDockStop. This didn't have any side effect afaik because we wouldn't enter that function anyway as clicking on any tab bar would trigger the move before UpdateMouseMovingWindowEndFrame() does it. However for consistency made the UpdateMouseMovingWindowEndFrame()code more correct. + minor renaming --- docs/TODO.txt | 1 + imgui.cpp | 49 +++++++++++++++++++++++++----------------------- imgui_internal.h | 4 ++-- 3 files changed, 29 insertions(+), 25 deletions(-) diff --git a/docs/TODO.txt b/docs/TODO.txt index e78447b32ca7..cb5a06d64874 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -169,6 +169,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - docking: B~ tidy up tab list popup buttons features (available with manual tab-bar, see ImGuiTabBarFlags_NoTabListPopupButton code, not used by docking nodes) - docking: B- SetNextWindowDockId(0) with a second Begin() in the frame will asserts - docking: B: resize grip drawn in host window typically appears under scrollbar. + - docking: B: resize grip auto-resize on multiple node hierarchy doesn't make much sense or should be improved? - docking: B- SetNextWindowFocus() doesn't seem to apply if the window is hidden this frame, need repro (#4) - docking: B- resizing a dock tree small currently has glitches (overlapping collapse and close button, etc.) - docking: B- dpi: look at interaction with the hi-dpi and multi-dpi stuff. diff --git a/imgui.cpp b/imgui.cpp index 42e10aefbff5..7f57bba8e7d2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3382,10 +3382,10 @@ void ImGui::UpdateMouseMovingWindowNewFrame() } } -// Initiate moving window, handle left-click and right-click focus +// Initiate moving window when clicking on empty space or title bar. +// Handle left-click and right-click focus. void ImGui::UpdateMouseMovingWindowEndFrame() { - // Initiate moving window ImGuiContext& g = *GImGui; if (g.ActiveId != 0 || g.HoveredId != 0) return; @@ -3394,15 +3394,18 @@ void ImGui::UpdateMouseMovingWindowEndFrame() if (g.NavWindow && g.NavWindow->Appearing) return; - // Click to focus window and start moving (after we're done with all our widgets) + // Click on void to focus window and start moving + // (after we're done with all our widgets, so e.g. clicking on docking tab-bar which have set HoveredId already and not get us here!) if (g.IO.MouseClicked[0]) { - if (g.HoveredRootWindow != NULL) + ImGuiWindow* root_window = g.HoveredWindow->RootWindowDockStop; + if (root_window != NULL) { StartMouseMovingWindow(g.HoveredWindow); - if (g.IO.ConfigWindowsMoveFromTitleBarOnly && (!(g.HoveredRootWindow->Flags & ImGuiWindowFlags_NoTitleBar) || g.HoveredWindow->RootWindowDockStop->DockIsActive)) - if (!g.HoveredRootWindow->TitleBarRect().Contains(g.IO.MouseClickedPos[0])) - g.MovingWindow = NULL; + if (g.IO.ConfigWindowsMoveFromTitleBarOnly) + if (!(root_window->Flags & ImGuiWindowFlags_NoTitleBar) || root_window->DockIsActive) + if (!root_window->TitleBarRect().Contains(g.IO.MouseClickedPos[0])) + g.MovingWindow = NULL; } else if (g.NavWindow != NULL && GetTopMostPopupModal() == NULL) { @@ -6406,17 +6409,16 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if (g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable) { // Docking: Dragging a dockable window (or any of its child) turns it into a drag and drop source. - // We need to do this _before_ we overwrite window->DC.LastItemId below because BeginAsDockableDragDropSource() also overwrites it. - if ((g.ActiveId == window->MoveId) && (g.IO.ConfigDockingWithShift == g.IO.KeyShift)) - if ((window->Flags & ImGuiWindowFlags_NoMove) == 0) - if ((window->RootWindow->Flags & (ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDocking)) == 0) - BeginAsDockableDragDropSource(window); + // We need to do this _before_ we overwrite window->DC.LastItemId below because BeginDockableDragDropSource() also overwrites it. + if ((g.MovingWindow == window) && (g.IO.ConfigDockingWithShift == g.IO.KeyShift)) + if ((window->RootWindow->Flags & ImGuiWindowFlags_NoDocking) == 0) + BeginDockableDragDropSource(window); - // Docking: Any dockable window can act as a target. For dock node hosts we call BeginAsDockableDragDropTarget() in DockNodeUpdate() instead. + // Docking: Any dockable window can act as a target. For dock node hosts we call BeginDockableDragDropTarget() in DockNodeUpdate() instead. if (g.DragDropActive && !(flags & ImGuiWindowFlags_NoDocking)) if (g.MovingWindow == NULL || g.MovingWindow->RootWindow != window) if ((window == window->RootWindow) && !(window->Flags & ImGuiWindowFlags_DockNodeHost)) - BeginAsDockableDragDropTarget(window); + BeginDockableDragDropTarget(window); } // We fill last item data based on Title Bar/Tab, in order for IsItemHovered() and IsItemActive() to be usable after Begin(). @@ -11817,8 +11819,7 @@ void ImGui::DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node) DockSettingsRenameNodeReferences(node->ID, new_node->ID); for (int n = 0; n < new_node->Windows.Size; n++) UpdateWindowParentAndRootLinks(new_node->Windows[n], new_node->Windows[n]->Flags, NULL); - new_node->AuthorityForPos = new_node->AuthorityForSize = ImGuiDataAuthority_Window; - new_node->WantMouseMove = true; + node = new_node; } else { @@ -11829,9 +11830,9 @@ void ImGui::DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node) DockNodeTreeMerge(ctx, node->ParentNode, node->ParentNode->ChildNodes[index_in_parent ^ 1]); node->ParentNode->AuthorityForViewport = ImGuiDataAuthority_Window; // The node that stays in place keeps the viewport, so our newly dragged out node will create a new viewport node->ParentNode = NULL; - node->AuthorityForPos = node->AuthorityForSize = ImGuiDataAuthority_Window; - node->WantMouseMove = true; } + node->AuthorityForPos = node->AuthorityForSize = ImGuiDataAuthority_Window; + node->WantMouseMove = true; MarkIniSettingsDirty(); } @@ -12525,7 +12526,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) // Draw payload drop target if (host_window && node->IsVisible) if (node->IsRootNode() && (g.MovingWindow == NULL || g.MovingWindow->RootWindow != host_window)) - BeginAsDockableDragDropTarget(host_window); + BeginDockableDragDropTarget(host_window); // We update this after DockNodeUpdateTabBar() node->LastFrameActive = g.FrameCount; @@ -14099,9 +14100,10 @@ void ImGui::DockBuilderFinish(ImGuiID root_id) // Docking: Begin/End Support Functions (called from Begin/End) //----------------------------------------------------------------------------- // - GetWindowAlwaysWantOwnTabBar() +// - DockContextBindNodeToWindow() // - BeginDocked() -// - BeginAsDockableDragDropSource() -// - BeginAsDockableDragDropTarget() +// - BeginDockableDragDropSource() +// - BeginDockableDragDropTarget() //----------------------------------------------------------------------------- bool ImGui::GetWindowAlwaysWantOwnTabBar(ImGuiWindow* window) @@ -14281,10 +14283,11 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) window->ChildId = parent_window->GetID(window->Name); } -void ImGui::BeginAsDockableDragDropSource(ImGuiWindow* window) +void ImGui::BeginDockableDragDropSource(ImGuiWindow* window) { ImGuiContext& g = *GImGui; IM_ASSERT(g.ActiveId == window->MoveId); + IM_ASSERT(g.MovingWindow == window); window->DC.LastItemId = window->MoveId; window = window->RootWindow; @@ -14297,7 +14300,7 @@ void ImGui::BeginAsDockableDragDropSource(ImGuiWindow* window) } } -void ImGui::BeginAsDockableDragDropTarget(ImGuiWindow* window) +void ImGui::BeginDockableDragDropTarget(ImGuiWindow* window) { ImGuiContext* ctx = GImGui; ImGuiContext& g = *ctx; diff --git a/imgui_internal.h b/imgui_internal.h index 40db120964d0..408001934887 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1914,8 +1914,8 @@ namespace ImGui inline ImGuiDockNode* DockNodeGetRootNode(ImGuiDockNode* node) { while (node->ParentNode) node = node->ParentNode; return node; } IMGUI_API bool GetWindowAlwaysWantOwnTabBar(ImGuiWindow* window); IMGUI_API void BeginDocked(ImGuiWindow* window, bool* p_open); - IMGUI_API void BeginAsDockableDragDropSource(ImGuiWindow* window); - IMGUI_API void BeginAsDockableDragDropTarget(ImGuiWindow* window); + IMGUI_API void BeginDockableDragDropSource(ImGuiWindow* window); + IMGUI_API void BeginDockableDragDropTarget(ImGuiWindow* window); IMGUI_API void SetWindowDock(ImGuiWindow* window, ImGuiID dock_id, ImGuiCond cond); // Docking - Builder function needs to be generally called before the node is used/submitted. From 4dff49b2f1408e48418f4a67461036f4900a3eca Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 27 Nov 2019 19:13:15 +0100 Subject: [PATCH 566/828] Docking, Viewports: Moving code. Moved NewFrame() sanity checks in NewFrameSanityChecks(). Moved some of DockNodeUpdate() into DockNodeUpdateForRootNode(). --- imgui.cpp | 98 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 51 insertions(+), 47 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 7f57bba8e7d2..ab1c4abaf36e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3693,22 +3693,8 @@ static void NewFrameSanityChecks() // Perform simple check: the beta io.ConfigWindowsResizeFromEdges option requires back-end to honor mouse cursor changes and set the ImGuiBackendFlags_HasMouseCursors flag accordingly. if (g.IO.ConfigWindowsResizeFromEdges && !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseCursors)) g.IO.ConfigWindowsResizeFromEdges = false; -} - -void ImGui::NewFrame() -{ - IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?"); - ImGuiContext& g = *GImGui; - -#ifdef IMGUI_ENABLE_TEST_ENGINE - ImGuiTestEngineHook_PreNewFrame(&g); -#endif - - // Check and assert for various common IO and Configuration mistakes - NewFrameSanityChecks(); // Perform simple check: error if Docking or Viewport are enabled _exactly_ on frame 1 (instead of frame 0 or later), which is a common error leading to loss of .ini data. - g.ConfigFlagsLastFrame = g.ConfigFlagsCurrFrame; if (g.FrameCount == 1 && (g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable) && (g.ConfigFlagsLastFrame & ImGuiConfigFlags_DockingEnable) == 0) IM_ASSERT(0 && "Please set DockingEnable before the first call to NewFrame()! Otherwise you will lose your .ini settings!"); if (g.FrameCount == 1 && (g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) && (g.ConfigFlagsLastFrame & ImGuiConfigFlags_ViewportsEnable) == 0) @@ -3720,12 +3706,12 @@ void ImGui::NewFrame() if ((g.IO.BackendFlags & ImGuiBackendFlags_PlatformHasViewports) && (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasViewports)) { IM_ASSERT((g.FrameCount == 0 || g.FrameCount == g.FrameCountPlatformEnded) && "Forgot to call UpdatePlatformWindows() in main loop after EndFrame()? Check examples/ applications for reference."); - IM_ASSERT(g.PlatformIO.Platform_CreateWindow != NULL && "Platform init didn't install handlers?"); + IM_ASSERT(g.PlatformIO.Platform_CreateWindow != NULL && "Platform init didn't install handlers?"); IM_ASSERT(g.PlatformIO.Platform_DestroyWindow != NULL && "Platform init didn't install handlers?"); - IM_ASSERT(g.PlatformIO.Platform_GetWindowPos != NULL && "Platform init didn't install handlers?"); - IM_ASSERT(g.PlatformIO.Platform_SetWindowPos != NULL && "Platform init didn't install handlers?"); - IM_ASSERT(g.PlatformIO.Platform_GetWindowSize != NULL && "Platform init didn't install handlers?"); - IM_ASSERT(g.PlatformIO.Platform_SetWindowSize != NULL && "Platform init didn't install handlers?"); + IM_ASSERT(g.PlatformIO.Platform_GetWindowPos != NULL && "Platform init didn't install handlers?"); + IM_ASSERT(g.PlatformIO.Platform_SetWindowPos != NULL && "Platform init didn't install handlers?"); + IM_ASSERT(g.PlatformIO.Platform_GetWindowSize != NULL && "Platform init didn't install handlers?"); + IM_ASSERT(g.PlatformIO.Platform_SetWindowSize != NULL && "Platform init didn't install handlers?"); IM_ASSERT(g.PlatformIO.Monitors.Size > 0 && "Platform init didn't setup Monitors list?"); IM_ASSERT((g.Viewports[0]->PlatformUserData != NULL || g.Viewports[0]->PlatformHandle != NULL) && "Platform init didn't setup main viewport."); if (g.IO.ConfigDockingTransparentPayload && (g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable)) @@ -3750,6 +3736,20 @@ void ImGui::NewFrame() IM_ASSERT(mon.DpiScale != 0.0f); } } +} + +void ImGui::NewFrame() +{ + IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?"); + ImGuiContext& g = *GImGui; + +#ifdef IMGUI_ENABLE_TEST_ENGINE + ImGuiTestEngineHook_PreNewFrame(&g); +#endif + + // Check and assert for various common IO and Configuration mistakes + g.ConfigFlagsLastFrame = g.ConfigFlagsCurrFrame; + NewFrameSanityChecks(); g.ConfigFlagsCurrFrame = g.IO.ConfigFlags; // Load settings on first frame (if not explicitly loaded manually before) @@ -11232,6 +11232,7 @@ namespace ImGui static void DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window, ImGuiID save_dock_id); static void DockNodeHideHostWindow(ImGuiDockNode* node); static void DockNodeUpdate(ImGuiDockNode* node); + static void DockNodeUpdateForRootNode(ImGuiDockNode* node); static void DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* node); static void DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_window); static void DockNodeAddTabBar(ImGuiDockNode* node); @@ -12168,7 +12169,6 @@ static ImGuiWindow* ImGui::DockNodeFindWindowByID(ImGuiDockNode* node, ImGuiID i static void ImGui::DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* node) { ImGuiContext& g = *GImGui; - IM_ASSERT(node->ParentNode == NULL || node->ParentNode->ChildNodes[0] == node || node->ParentNode->ChildNodes[1] == node); // Inherit most flags @@ -12247,6 +12247,36 @@ static void ImGui::DockNodeStartMouseMovingWindow(ImGuiDockNode* node, ImGuiWind g.ActiveIdClickOffset = backup_active_click_offset; } +// Update CentralNode, OnlyNodeWithWindows, LastFocusedNodeID. Copy window class. +static void ImGui::DockNodeUpdateForRootNode(ImGuiDockNode* node) +{ + DockNodeUpdateVisibleFlagAndInactiveChilds(node); + + // FIXME-DOCK: Merge this scan into the one above. + // - Setup central node pointers + // - Find if there's only a single visible window in the hierarchy (in which case we need to display a regular title bar -> FIXME-DOCK: that last part is not done yet!) + ImGuiDockNodeFindInfoResults results; + DockNodeFindInfo(node, &results); + node->CentralNode = results.CentralNode; + node->OnlyNodeWithWindows = (results.CountNodesWithWindows == 1) ? results.FirstNodeWithWindows : NULL; + if (node->LastFocusedNodeID == 0 && results.FirstNodeWithWindows != NULL) + node->LastFocusedNodeID = results.FirstNodeWithWindows->ID; + + // Copy the window class from of our first window so it can be used for proper dock filtering. + // When node has mixed windows, prioritize the class with the most constraint (DockingAllowUnclassed = false) as the reference to copy. + // FIXME-DOCK: We don't recurse properly, this code could be reworked to work from DockNodeUpdateScanRec. + if (ImGuiDockNode* first_node_with_windows = results.FirstNodeWithWindows) + { + node->WindowClass = first_node_with_windows->Windows[0]->WindowClass; + for (int n = 1; n < first_node_with_windows->Windows.Size; n++) + if (first_node_with_windows->Windows[n]->WindowClass.DockingAllowUnclassed == false) + { + node->WindowClass = first_node_with_windows->Windows[n]->WindowClass; + break; + } + } +} + static void ImGui::DockNodeUpdate(ImGuiDockNode* node) { ImGuiContext& g = *GImGui; @@ -12256,33 +12286,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) node->CentralNode = node->OnlyNodeWithWindows = NULL; if (node->IsRootNode()) - { - DockNodeUpdateVisibleFlagAndInactiveChilds(node); - - // FIXME-DOCK: Merge this scan into the one above. - // - Setup central node pointers - // - Find if there's only a single visible window in the hierarchy (in which case we need to display a regular title bar -> FIXME-DOCK: that last part is not done yet!) - ImGuiDockNodeFindInfoResults results; - DockNodeFindInfo(node, &results); - node->CentralNode = results.CentralNode; - node->OnlyNodeWithWindows = (results.CountNodesWithWindows == 1) ? results.FirstNodeWithWindows : NULL; - if (node->LastFocusedNodeID == 0 && results.FirstNodeWithWindows != NULL) - node->LastFocusedNodeID = results.FirstNodeWithWindows->ID; - - // Copy the window class from of our first window so it can be used for proper dock filtering. - // When node has mixed windows, prioritize the class with the most constraint (DockingAllowUnclassed = false) as the reference to copy. - // FIXME-DOCK: We don't recurse properly, this code could be reworked to work from DockNodeUpdateScanRec. - if (ImGuiDockNode* first_node_with_windows = results.FirstNodeWithWindows) - { - node->WindowClass = first_node_with_windows->Windows[0]->WindowClass; - for (int n = 1; n < first_node_with_windows->Windows.Size; n++) - if (first_node_with_windows->Windows[n]->WindowClass.DockingAllowUnclassed == false) - { - node->WindowClass = first_node_with_windows->Windows[n]->WindowClass; - break; - } - } - } + DockNodeUpdateForRootNode(node); // Remove tab bar if not needed if (node->TabBar && node->IsNoTabBar()) From 28dd8d7efdb6c66bddb18d1f6f48ddf2b5249d28 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 27 Nov 2019 19:27:44 +0100 Subject: [PATCH 567/828] Docking: Fixed various conflicts not properly resolved on 813e0c1 New git client confusion. --- docs/CHANGELOG.txt | 4 ---- examples/imgui_impl_dx9.h | 4 ---- examples/imgui_impl_metal.mm | 6 +----- examples/imgui_impl_vulkan.cpp | 6 +----- 4 files changed, 2 insertions(+), 18 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index c765a54d167e..4deca1ec296f 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -30,7 +30,6 @@ HOW TO UPDATE? ----------------------------------------------------------------------- -<<<<<<< HEAD DOCKING BRANCH (In Progress) ----------------------------------------------------------------------- @@ -100,10 +99,7 @@ Other changes: ----------------------------------------------------------------------- - VERSION 1.74 WIP (In Progress) -======= VERSION 1.74 (Released 2019-11-25) ->>>>>>> master ----------------------------------------------------------------------- Breaking Changes: diff --git a/examples/imgui_impl_dx9.h b/examples/imgui_impl_dx9.h index ccbd0eeb1f14..339645e3b860 100644 --- a/examples/imgui_impl_dx9.h +++ b/examples/imgui_impl_dx9.h @@ -3,12 +3,8 @@ // Implemented features: // [X] Renderer: User texture binding. Use 'LPDIRECT3DTEXTURE9' as ImTextureID. Read the FAQ about ImTextureID! -<<<<<<< HEAD // [X] Renderer: Multi-viewport support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. -// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bits indices. -======= // [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. ->>>>>>> master // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp. diff --git a/examples/imgui_impl_metal.mm b/examples/imgui_impl_metal.mm index 5bf60beab850..10814b460575 100644 --- a/examples/imgui_impl_metal.mm +++ b/examples/imgui_impl_metal.mm @@ -3,13 +3,9 @@ // Implemented features: // [X] Renderer: User texture binding. Use 'MTLTexture' as ImTextureID. Read the FAQ about ImTextureID! -<<<<<<< HEAD -// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bits indices. +// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. // Missing features: // [ ] Renderer: Multi-viewport / platform windows. -======= -// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. ->>>>>>> master // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp. diff --git a/examples/imgui_impl_vulkan.cpp b/examples/imgui_impl_vulkan.cpp index 3e726bb7196a..a503703e73fb 100644 --- a/examples/imgui_impl_vulkan.cpp +++ b/examples/imgui_impl_vulkan.cpp @@ -2,12 +2,8 @@ // This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..) // Implemented features: -<<<<<<< HEAD -// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bits indices. -// [x] Platform: Multi-viewport / platform windows. With issues (flickering when creating a new viewport). -======= // [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. ->>>>>>> master +// [x] Platform: Multi-viewport / platform windows. With issues (flickering when creating a new viewport). // Missing features: // [ ] Renderer: User texture binding. Changes of ImTextureID aren't supported by this binding! See https://github.com/ocornut/imgui/pull/914 From a1e4af62d35bdd271b3e0546258f8533b7f7ea5e Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 28 Nov 2019 17:34:26 +0100 Subject: [PATCH 568/828] Docking: Fix bug added in 71a58261 + Misc docking omments --- imgui.cpp | 8 +++++--- imgui.h | 9 +++++++-- imgui_internal.h | 1 + 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index ab1c4abaf36e..92af81d0f90c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3398,7 +3398,7 @@ void ImGui::UpdateMouseMovingWindowEndFrame() // (after we're done with all our widgets, so e.g. clicking on docking tab-bar which have set HoveredId already and not get us here!) if (g.IO.MouseClicked[0]) { - ImGuiWindow* root_window = g.HoveredWindow->RootWindowDockStop; + ImGuiWindow* root_window = g.HoveredWindow ? g.HoveredWindow->RootWindowDockStop : NULL; if (root_window != NULL) { StartMouseMovingWindow(g.HoveredWindow); @@ -13006,6 +13006,7 @@ bool ImGui::DockNodeCalcDropRectsAndTestMousePos(const ImRect& parent, ImGuiDir } // host_node may be NULL if the window doesn't have a DockNode already. +// FIXME-DOCK: This is misnamed since it's also doing the filtering. static void ImGui::DockNodePreviewDockCalc(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* root_payload, ImGuiDockPreviewData* data, bool is_explicit_target, bool is_outer_docking) { ImGuiContext& g = *GImGui; @@ -14253,7 +14254,7 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) return; } - // Position window + // Position/Size window SetNextWindowPos(node->Pos); SetNextWindowSize(node->Size); g.NextWindowData.PosUndock = false; // Cancel implicit undocking of SetNextWindowPos() @@ -14346,7 +14347,8 @@ void ImGui::BeginDockableDragDropTarget(ImGuiWindow* window) const bool do_preview = payload->IsPreview() || payload->IsDelivery(); if (do_preview && (node != NULL || allow_null_target_node)) { - ImGuiDockPreviewData split_inner, split_outer; + ImGuiDockPreviewData split_inner; + ImGuiDockPreviewData split_outer; ImGuiDockPreviewData* split_data = &split_inner; if (node && (node->ParentNode || node->IsCentralNode())) if (ImGuiDockNode* root_node = DockNodeGetRootNode(node)) diff --git a/imgui.h b/imgui.h index a1f50e075b67..3766ce5360fa 100644 --- a/imgui.h +++ b/imgui.h @@ -1609,8 +1609,13 @@ struct ImGuiSizeCallbackData ImVec2 DesiredSize; // Read-write. Desired size, based on user's mouse position. Write to this field to restrain resizing. }; -// [BETA] Rarely used / very advanced uses only. Use with SetNextWindowClass() and DockSpace() functions. -// Provide hints to the platform back-end via altered viewport flags (enable/disable OS decoration, OS task bar icons, etc.) and OS level parent/child relationships. +// [ALPHA] Rarely used / very advanced uses only. Use with SetNextWindowClass() and DockSpace() functions. +// Important: the content of this class is still highly WIP and likely to change and be refactored +// before we stabilize Docking features. Please be mindful if using this. +// Provide hints: +// - To the platform back-end via altered viewport flags (enable/disable OS decoration, OS task bar icons, etc.) +// - To the platform back-end for OS level parent/child relationships of viewport. +// - To the docking system for various options and filtering. struct ImGuiWindowClass { ImGuiID ClassId; // User data. 0 = Default class (unclassed). Windows of different classes cannot be docked with each others. diff --git a/imgui_internal.h b/imgui_internal.h index 408001934887..eeaefb7b2be6 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1912,6 +1912,7 @@ namespace ImGui IMGUI_API void DockContextQueueUndockNode(ImGuiContext* ctx, ImGuiDockNode* node); IMGUI_API bool DockContextCalcDropPosForDocking(ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, bool split_outer, ImVec2* out_pos); inline ImGuiDockNode* DockNodeGetRootNode(ImGuiDockNode* node) { while (node->ParentNode) node = node->ParentNode; return node; } + inline ImGuiDockNode* GetWindowDockNode() { ImGuiContext& g = *GImGui; return g.CurrentWindow->DockNode; } IMGUI_API bool GetWindowAlwaysWantOwnTabBar(ImGuiWindow* window); IMGUI_API void BeginDocked(ImGuiWindow* window, bool* p_open); IMGUI_API void BeginDockableDragDropSource(ImGuiWindow* window); From 927580d4a86acd1e131d20627d3cb42a13a969c3 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 28 Nov 2019 22:44:29 +0100 Subject: [PATCH 569/828] Docking: Cleanup, rename DockNodePreviewDockCalc() -> DockNodePreviewDockSetup() --- imgui.cpp | 42 ++++++++++++++++++++++-------------------- imgui_internal.h | 2 +- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 92af81d0f90c..eb6f9359ba4d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11241,7 +11241,7 @@ namespace ImGui static void DockNodeUpdateVisibleFlag(ImGuiDockNode* node); static void DockNodeStartMouseMovingWindow(ImGuiDockNode* node, ImGuiWindow* window); static bool DockNodeIsDropAllowed(ImGuiWindow* host_window, ImGuiWindow* payload_window); - static void DockNodePreviewDockCalc(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* payload_window, ImGuiDockPreviewData* preview_data, bool is_explicit_target, bool is_outer_docking); + static void DockNodePreviewDockSetup(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* payload_window, ImGuiDockPreviewData* preview_data, bool is_explicit_target, bool is_outer_docking); static void DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* payload_window, const ImGuiDockPreviewData* preview_data); static void DockNodeCalcTabBarLayout(const ImGuiDockNode* node, ImRect* out_title_rect, ImRect* out_tab_bar_rect, ImVec2* out_window_menu_button_pos); static void DockNodeCalcSplitRects(ImVec2& pos_old, ImVec2& size_old, ImVec2& pos_new, ImVec2& size_new, ImGuiDir dir, ImVec2 size_new_desired); @@ -11740,7 +11740,7 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) // We can dock a split payload into a node that already has windows _only_ if our payload is a node tree with a single visible node. // In this situation, we move the windows of the target node into the currently visible node of the payload. // This allows us to preserve some of the underlying dock tree settings nicely. - IM_ASSERT(payload_node->OnlyNodeWithWindows != NULL); // The docking should have been blocked by DockNodePreviewDockCalc() early on and never submitted. + IM_ASSERT(payload_node->OnlyNodeWithWindows != NULL); // The docking should have been blocked by DockNodePreviewDockSetup() early on and never submitted. ImGuiDockNode* visible_node = payload_node->OnlyNodeWithWindows; if (visible_node->TabBar) IM_ASSERT(visible_node->TabBar->Tabs.Size > 0); @@ -11847,7 +11847,7 @@ bool ImGui::DockContextCalcDropPosForDocking(ImGuiWindow* target, ImGuiDockNode* else { ImGuiDockPreviewData split_data; - DockNodePreviewDockCalc(target, target_node, payload, &split_data, false, split_outer); + DockNodePreviewDockSetup(target, target_node, payload, &split_data, false, split_outer); if (split_data.DropRectsDraw[split_dir+1].IsInverted()) return false; *out_pos = split_data.DropRectsDraw[split_dir+1].GetCenter(); @@ -11882,7 +11882,7 @@ bool ImGui::DockContextCalcDropPosForDocking(ImGuiWindow* target, ImGuiDockNode* // - DockNodeCalcTabBarLayout() // - DockNodeCalcSplitRects() // - DockNodeCalcDropRectsAndTestMousePos() -// - DockNodePreviewDockCalc() +// - DockNodePreviewDockSetup() // - DockNodePreviewDockRender() //----------------------------------------------------------------------------- @@ -13007,40 +13007,42 @@ bool ImGui::DockNodeCalcDropRectsAndTestMousePos(const ImRect& parent, ImGuiDir // host_node may be NULL if the window doesn't have a DockNode already. // FIXME-DOCK: This is misnamed since it's also doing the filtering. -static void ImGui::DockNodePreviewDockCalc(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* root_payload, ImGuiDockPreviewData* data, bool is_explicit_target, bool is_outer_docking) +static void ImGui::DockNodePreviewDockSetup(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* root_payload, ImGuiDockPreviewData* data, bool is_explicit_target, bool is_outer_docking) { ImGuiContext& g = *GImGui; // There is an edge case when docking into a dockspace which only has inactive nodes. // In this case DockNodeTreeFindNodeByPos() will have selected a leaf node which is inactive. // Because the inactive leaf node doesn't have proper pos/size yet, we'll use the root node as reference. + ImGuiDockNode* root_payload_as_host = root_payload->DockNodeAsHost; ImGuiDockNode* ref_node_for_rect = (host_node && !host_node->IsVisible) ? DockNodeGetRootNode(host_node) : host_node; if (ref_node_for_rect) IM_ASSERT(ref_node_for_rect->IsVisible); - // Build a tentative future node (reuse same structure because it is practical) - data->FutureNode.HasCloseButton = (host_node ? host_node->HasCloseButton : host_window->HasCloseButton) || (root_payload->HasCloseButton); - data->FutureNode.HasWindowMenuButton = host_node ? true : ((host_window->Flags & ImGuiWindowFlags_NoCollapse) == 0); - data->FutureNode.Pos = host_node ? ref_node_for_rect->Pos : host_window->Pos; - data->FutureNode.Size = host_node ? ref_node_for_rect->Size : host_window->Size; - - // Figure out here we are allowed to dock + // Filter, figure out where we are allowed to dock ImGuiDockNodeFlags host_node_flags = host_node ? host_node->GetMergedFlags() : 0; - const bool src_is_visibly_splitted = root_payload->DockNodeAsHost && root_payload->DockNodeAsHost->IsSplitNode() && (root_payload->DockNodeAsHost->OnlyNodeWithWindows == NULL); - data->IsCenterAvailable = !is_outer_docking; - if (src_is_visibly_splitted && (!host_node || !host_node->IsEmpty())) + data->IsCenterAvailable = true; + if (is_outer_docking) + data->IsCenterAvailable = false; + else if (host_node && (host_node_flags & ImGuiDockNodeFlags_NoDocking)) data->IsCenterAvailable = false; - if (host_node && (host_node_flags & ImGuiDockNodeFlags_NoDocking)) + else if (host_node && (host_node_flags & ImGuiDockNodeFlags_NoDockingInCentralNode) && host_node->IsCentralNode()) data->IsCenterAvailable = false; - if (host_node && (host_node_flags & ImGuiDockNodeFlags_NoDockingInCentralNode) && host_node->IsCentralNode()) + else if ((!host_node || !host_node->IsEmpty()) && root_payload_as_host && root_payload_as_host->IsSplitNode() && (root_payload_as_host->OnlyNodeWithWindows == NULL)) // Is _visibly_ split? data->IsCenterAvailable = false; data->IsSidesAvailable = true; if ((host_node && (host_node_flags & ImGuiDockNodeFlags_NoSplit)) || g.IO.ConfigDockingNoSplit) data->IsSidesAvailable = false; - if (!is_outer_docking && host_node && host_node->ParentNode == NULL && host_node->IsCentralNode()) + else if (!is_outer_docking && host_node && host_node->ParentNode == NULL && host_node->IsCentralNode()) data->IsSidesAvailable = false; + // Build a tentative future node (reuse same structure because it is practical. Shape will be readjusted when previewing a split) + data->FutureNode.HasCloseButton = (host_node ? host_node->HasCloseButton : host_window->HasCloseButton) || (root_payload->HasCloseButton); + data->FutureNode.HasWindowMenuButton = host_node ? true : ((host_window->Flags & ImGuiWindowFlags_NoCollapse) == 0); + data->FutureNode.Pos = host_node ? ref_node_for_rect->Pos : host_window->Pos; + data->FutureNode.Size = host_node ? ref_node_for_rect->Size : host_window->Size; + // Calculate drop shapes geometry for allowed splitting directions IM_ASSERT(ImGuiDir_None == -1); data->SplitNode = host_node; @@ -14353,11 +14355,11 @@ void ImGui::BeginDockableDragDropTarget(ImGuiWindow* window) if (node && (node->ParentNode || node->IsCentralNode())) if (ImGuiDockNode* root_node = DockNodeGetRootNode(node)) { - DockNodePreviewDockCalc(window, root_node, payload_window, &split_outer, is_explicit_target, true); + DockNodePreviewDockSetup(window, root_node, payload_window, &split_outer, is_explicit_target, true); if (split_outer.IsSplitDirExplicit) split_data = &split_outer; } - DockNodePreviewDockCalc(window, node, payload_window, &split_inner, is_explicit_target, false); + DockNodePreviewDockSetup(window, node, payload_window, &split_inner, is_explicit_target, false); if (split_data == &split_outer) split_inner.IsDropAllowed = false; diff --git a/imgui_internal.h b/imgui_internal.h index eeaefb7b2be6..a7e8ed728b37 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1053,7 +1053,7 @@ struct ImGuiDockNode ImVec2 Size; // Current size ImVec2 SizeRef; // [Split node only] Last explicitly written-to size (overridden when using a splitter affecting the node), used to calculate Size. int SplitAxis; // [Split node only] Split axis (X or Y) - ImGuiWindowClass WindowClass; + ImGuiWindowClass WindowClass; // [Root node only] ImGuiDockNodeState State; ImGuiWindow* HostWindow; From 4c108d22f08f236f4b11d0f55944148f88f1df74 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 19 Dec 2019 15:02:55 +0100 Subject: [PATCH 570/828] Minor bits, placeholder aimed at facilitating merging of Tables branch into Docking # Conflicts: # imgui.cpp --- imgui.cpp | 81 ++++++++++++++++++++++++++++++++++++++------------ imgui_draw.cpp | 2 +- 2 files changed, 63 insertions(+), 20 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 7d3c050add86..eec03a8666d4 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4106,6 +4106,21 @@ void ImGui::Initialize(ImGuiContext* context) ini_handler.WriteAllFn = WindowSettingsHandler_WriteAll; g.SettingsHandlers.push_back(ini_handler); } + +#if 0 // FIXME-WIP: This is a placeholder to facilitate merging of Tables branch into multiple branches. + + // Add .ini handle for ImGuiTable type + { + ImGuiSettingsHandler ini_handler; + ini_handler.TypeName = "Table"; + ini_handler.TypeHash = ImHashStr("Table"); + ini_handler.ReadOpenFn = TableSettingsHandler_ReadOpen; + ini_handler.ReadLineFn = TableSettingsHandler_ReadLine; + ini_handler.WriteAllFn = TableSettingsHandler_WriteAll; + g.SettingsHandlers.push_back(ini_handler); + } + +#endif // Create default viewport ImGuiViewportP* viewport = IM_NEW(ImGuiViewportP)(); @@ -14909,12 +14924,18 @@ void ImGui::ShowMetricsWindow(bool* p_open) return; } - // State + // Debugging enums enum { WRT_OuterRect, WRT_OuterRectClipped, WRT_InnerRect, WRT_InnerClipRect, WRT_WorkRect, WRT_Content, WRT_ContentRegionRect, WRT_Count }; // Windows Rect Type const char* wrt_rects_names[WRT_Count] = { "OuterRect", "OuterRectClipped", "InnerRect", "InnerClipRect", "WorkRect", "Content", "ContentRegionRect" }; + enum { TRT_OuterRect, TRT_WorkRect, TRT_HostClipRect, TRT_InnerClipRect, TRT_BackgroundClipRect, TRT_ColumnsRect, TRT_ColumnsClipRect, TRT_ColumnsContentHeadersUsed, TRT_ColumnsContentHeadersDesired, TRT_ColumnsContentRowsFrozen, TRT_ColumnsContentRowsUnfrozen, TRT_Count }; // Tables Rect Type + const char* trt_rects_names[TRT_Count] = { "OuterRect", "WorkRect", "HostClipRect", "InnerClipRect", "BackgroundClipRect", "ColumnsRect", "ColumnsClipRect", "ColumnsContentHeadersUsed", "ColumnsContentHeadersDesired", "ColumnsContentRowsFrozen", "ColumnsContentRowsUnfrozen" }; + + // State static bool show_windows_rects = false; static int show_windows_rect_type = WRT_WorkRect; static bool show_windows_begin_order = false; + static bool show_tables_rects = false; + static int show_tables_rect_type = TRT_WorkRect; static bool show_drawcmd_details = true; static bool show_docking_nodes = false; @@ -14929,13 +14950,14 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::Separator(); // Helper functions to display common structures: - // - NodeDrawList - // - NodeColumns - // - NodeWindow - // - NodeWindows - // - NodeViewport - // - NodeDockNode - // - NodeTabBar + // - NodeDrawList() + // - NodeColumns() + // - NodeWindow() + // - NodeWindows() + // - NodeViewport() + // - NodeDockNode() + // - NodeTabBar() + // - NodeStorage() struct Funcs { static ImRect GetWindowRect(ImGuiWindow* window, int rect_type) @@ -15255,6 +15277,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::TreePop(); } + // Details for Popups if (ImGui::TreeNode("Popups", "Popups (%d)", g.OpenPopupStack.Size)) { for (int i = 0; i < g.OpenPopupStack.Size; i++) @@ -15265,6 +15288,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::TreePop(); } + // Details for TabBars if (ImGui::TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.GetSize())) { for (int n = 0; n < g.TabBars.GetSize(); n++) @@ -15272,6 +15296,18 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::TreePop(); } + // Details for Tables + IM_UNUSED(trt_rects_names); + IM_UNUSED(show_tables_rects); + IM_UNUSED(show_tables_rect_type); +#if 0 + if (ImGui::TreeNode("Tables", "Tables (%d)", g.Tables.GetSize())) + { + ImGui::TreePop(); + } +#endif + + // Details for Docking if (ImGui::TreeNode("Docking")) { ImGuiDockContext* dc = g.DockContext; @@ -15318,17 +15354,10 @@ void ImGui::ShowMetricsWindow(bool* p_open) } ImGui::TreePop(); } - - ImGui::TreePop(); - } - -#if 0 - if (ImGui::TreeNode("Tables", "Tables (%d)", g.Tables.GetSize())) - { ImGui::TreePop(); } -#endif - + + // Misc Details if (ImGui::TreeNode("Internal state")) { const char* input_source_names[] = { "None", "Mouse", "Nav", "NavKeyboard", "NavGamepad" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT); @@ -15351,6 +15380,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::TreePop(); } + // Tools if (ImGui::TreeNode("Tools")) { // The Item Picker tool is super useful to visually select an item and break into the call-stack of where it was submitted. @@ -15363,7 +15393,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::Checkbox("Show windows rectangles", &show_windows_rects); ImGui::SameLine(); ImGui::SetNextItemWidth(ImGui::GetFontSize() * 12); - show_windows_rects |= ImGui::Combo("##show_windows_rect_type", &show_windows_rect_type, wrt_rects_names, WRT_Count); + show_windows_rects |= ImGui::Combo("##show_windows_rect_type", &show_windows_rect_type, wrt_rects_names, WRT_Count, WRT_Count); if (show_windows_rects && g.NavWindow) { ImGui::BulletText("'%s':", g.NavWindow->Name); @@ -15379,7 +15409,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::TreePop(); } - // Tool: Display windows Rectangles and Begin Order + // Overlay: Display windows Rectangles and Begin Order if (show_windows_rects || show_windows_begin_order) { for (int n = 0; n < g.Windows.Size; n++) @@ -15404,6 +15434,19 @@ void ImGui::ShowMetricsWindow(bool* p_open) } } + // FIXME-WIP: This is a placeholder to facilitate merging of Tables branch into multiple branches. +#if 0 + // Overlay: Display Tables Rectangles + if (show_tables_rects) + { + for (int table_n = 0; table_n < g.Tables.GetSize(); table_n++) + { + ImGuiTable* table = g.Tables.GetByIndex(table_n); + } + } +#endif + + // Overlay: Display Docking info if (show_docking_nodes && g.IO.KeyCtrl) { for (int n = 0; n < g.DockContext->Nodes.Data.Size; n++) diff --git a/imgui_draw.cpp b/imgui_draw.cpp index aa956ce70b90..e2d08774af00 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1321,7 +1321,7 @@ void ImDrawListSplitter::Merge(ImDrawList* draw_list) // Merge previous channel last draw command with current channel first draw command if matching. last_cmd->ElemCount += ch._CmdBuffer[0].ElemCount; idx_offset += ch._CmdBuffer[0].ElemCount; - ch._CmdBuffer.erase(ch._CmdBuffer.Data); + ch._CmdBuffer.erase(ch._CmdBuffer.Data); // FIXME-OPT: Improve for multiple merges. } if (ch._CmdBuffer.Size > 0) last_cmd = &ch._CmdBuffer.back(); From c163b856d718f3c6648b7f248c65da5befb30ef1 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 2 Jan 2020 21:54:23 +0100 Subject: [PATCH 571/828] Docking: Fix for IMGUI_DEBUG_INI_SETTINGS=1, comments --- imgui.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index b4b9ba4ce1b7..84b9ed6b2064 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6761,7 +6761,7 @@ void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWind if (window != ignore_window && window->WasActive && !(window->Flags & ImGuiWindowFlags_ChildWindow)) if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) { - // FIXME-DOCKING: This is failing (lagging by one frame) for docked windows. + // FIXME-DOCK: This is failing (lagging by one frame) for docked windows. // If A and B are docked into window and B disappear, at the NewFrame() call site window->NavLastChildNavWindow will still point to B. // We might leverage the tab order implicitly stored in window->DockNodeAsHost->TabBar (essentially the 'most_recently_selected_tab' code in tab bar will do that but on next update) // to tell which is the "previous" window. Or we may leverage 'LastFrameFocused/LastFrameJustFocused' and have this function handle child window itself? @@ -11534,7 +11534,7 @@ static ImGuiDockNode* ImGui::DockContextFindNodeByID(ImGuiContext* ctx, ImGuiID ImGuiID ImGui::DockContextGenNodeID(ImGuiContext* ctx) { // Generate an ID for new node (the exact ID value doesn't matter as long as it is not already used) - // FIXME-OPT FIXME-DOCKING: This is suboptimal, even if the node count is small enough not to be a worry. We should poke in ctx->Nodes to find a suitable ID faster. + // FIXME-OPT FIXME-DOCK: This is suboptimal, even if the node count is small enough not to be a worry. We should poke in ctx->Nodes to find a suitable ID faster. ImGuiID id = 0x0001; while (DockContextFindNodeByID(ctx, id) != NULL) id++; @@ -11862,7 +11862,7 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) if (node->IsCentralNode()) { // Central node property needs to be moved to a leaf node, pick the last focused one. - // FIXME-DOCKING: If we had to transfer other flags here, what would the policy be? + // FIXME-DOCK: If we had to transfer other flags here, what would the policy be? ImGuiDockNode* last_focused_node = DockContextFindNodeByID(ctx, payload_node->LastFocusedNodeID); IM_ASSERT(last_focused_node != NULL); ImGuiDockNode* last_focused_root_node = DockNodeGetRootNode(last_focused_node); @@ -12775,7 +12775,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w const bool has_close_button = (node_flags & ImGuiDockNodeFlags_NoCloseButton) == 0; // In a dock node, the Collapse Button turns into the Window Menu button. - // FIXME-DOCK FIXME-OPT: Could we recycle popups id accross multiple dock nodes? + // FIXME-DOCK FIXME-OPT: Could we recycle popups id across multiple dock nodes? if (has_window_menu_button && IsPopupOpen("#WindowMenu")) { if (ImGuiID tab_id = DockNodeUpdateWindowMenu(node, tab_bar)) @@ -12818,7 +12818,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w int tabs_unsorted_start = tab_bar->Tabs.Size; for (int tab_n = tab_bar->Tabs.Size - 1; tab_n >= 0 && (tab_bar->Tabs[tab_n].Flags & ImGuiTabItemFlags_Unsorted); tab_n--) { - // FIXME-DOCKING: Consider only clearing the flag after the tab has been alive for a few consecutive frames, allowing late comers to not break sorting? + // FIXME-DOCK: Consider only clearing the flag after the tab has been alive for a few consecutive frames, allowing late comers to not break sorting? tab_bar->Tabs[tab_n].Flags &= ~ImGuiTabItemFlags_Unsorted; tabs_unsorted_start = tab_n; } @@ -13747,7 +13747,7 @@ void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags fla SetNextWindowSize(node->Size); g.NextWindowData.PosUndock = false; - // FIXME-DOCKING: Why do we need a child window to host a dockspace, could we host it in the existing window? + // FIXME-DOCK Why do we need a child window to host a dockspace, could we host it in the existing window? ImGuiWindowFlags window_flags = ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_DockNodeHost; window_flags |= ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar; window_flags |= ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse; @@ -14674,13 +14674,14 @@ static void ImGui::DockSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettings buf->appendf("%*s", ImMax(2, (line_start_pos + 92) - buf->size()), ""); // Align everything if (node->IsDockSpace() && node->HostWindow && node->HostWindow->ParentWindow) buf->appendf(" ; in '%s'", node->HostWindow->ParentWindow->Name); + // Iterate settings so we can give info about windows that didn't exist during the session. int contains_window = 0; - for (int window_n = 0; window_n < ctx->SettingsWindows.Size; window_n++) // Iterate settings so we can give info about windows that didn't exist during the session. - if (ctx->SettingsWindows[window_n].DockId == node_settings->ID) + for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings)) + if (settings->DockId == node_settings->ID) { if (contains_window++ == 0) buf->appendf(" ; contains "); - buf->appendf("'%s' ", ctx->SettingsWindows[window_n].Name); + buf->appendf("'%s' ", settings->GetName()); } } #endif From 3b1b5266e54fa6daba32cdf0da95b8d709312d1b Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 2 Jan 2020 22:50:56 +0100 Subject: [PATCH 572/828] Docking: Fixed a bug where the tab bar of a hidden dockspace would keep requesting focus. (#2960) --- imgui.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/imgui.cpp b/imgui.cpp index 84b9ed6b2064..748630741d9d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -13703,6 +13703,12 @@ void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags fla if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable)) return; + // Early out if parent window is hidden/collapsed + // This is faster but also DockNodeUpdateTabBar() relies on TabBarLayout() running (which won't if SkipItems=true) to set NextSelectedTabId = 0). See #2960. + // If for whichever reason this is causing problem we would need to ensure that DockNodeUpdateTabBar() ends up clearing NextSelectedTabId even if SkipItems=true. + if (window->SkipItems) + flags |= ImGuiDockNodeFlags_KeepAliveOnly; + IM_ASSERT((flags & ImGuiDockNodeFlags_DockSpace) == 0); ImGuiDockNode* node = DockContextFindNodeByID(ctx, id); if (!node) From f6951bb67d5a463e9deddac0ca0db2a132901924 Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Mon, 6 Jan 2020 09:49:36 +0200 Subject: [PATCH 573/828] Viewports: SDL2: Honor NoTaskBarIcon flag under non Win32 OS. (#2117) --- examples/imgui_impl_sdl.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/examples/imgui_impl_sdl.cpp b/examples/imgui_impl_sdl.cpp index d78db88d1072..12b8ebfd81e9 100644 --- a/examples/imgui_impl_sdl.cpp +++ b/examples/imgui_impl_sdl.cpp @@ -467,6 +467,10 @@ static void ImGui_ImplSDL2_CreateWindow(ImGuiViewport* viewport) sdl_flags |= SDL_WINDOW_HIDDEN; sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? SDL_WINDOW_BORDERLESS : 0; sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? 0 : SDL_WINDOW_RESIZABLE; +#if !defined(_WIN32) + // See SDL hack in ImGui_ImplSDL2_ShowWindow(). + sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoTaskBarIcon) ? SDL_WINDOW_SKIP_TASKBAR : 0; +#endif #if SDL_HAS_ALWAYS_ON_TOP sdl_flags |= (viewport->Flags & ImGuiViewportFlags_TopMost) ? SDL_WINDOW_ALWAYS_ON_TOP : 0; #endif From 7e068da2bdf15cc3af63d49d1422091b5a4c3932 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 20 Jan 2020 19:29:25 +0100 Subject: [PATCH 574/828] Docking: Internals: Renamed members from XxxxID to XxxxxId to be more consistent with rest of the codebase (still some inconsistency left that are harder to fix) --- imgui.cpp | 124 +++++++++++++++++++++++------------------------ imgui_internal.h | 6 +-- 2 files changed, 65 insertions(+), 65 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 80a3a162badc..a58b6fafc35b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11310,16 +11310,16 @@ struct ImGuiDockPreviewData struct ImGuiDockNodeSettings { ImGuiID ID; - ImGuiID ParentNodeID; - ImGuiID ParentWindowID; - ImGuiID SelectedWindowID; + ImGuiID ParentNodeId; + ImGuiID ParentWindowId; + ImGuiID SelectedWindowId; signed char SplitAxis; char Depth; ImGuiDockNodeFlags Flags; // NB: We save individual flags one by one in ascii format (ImGuiDockNodeFlags_SavedFlagsMask_) ImVec2ih Pos; ImVec2ih Size; ImVec2ih SizeRef; - ImGuiDockNodeSettings() { ID = ParentNodeID = ParentWindowID = SelectedWindowID = 0; SplitAxis = ImGuiAxis_None; Depth = 0; Flags = ImGuiDockNodeFlags_None; } + ImGuiDockNodeSettings() { ID = ParentNodeId = ParentWindowId = SelectedWindowId = 0; SplitAxis = ImGuiAxis_None; Depth = 0; Flags = ImGuiDockNodeFlags_None; } }; struct ImGuiDockContext @@ -11615,8 +11615,8 @@ static int IMGUI_CDECL DockNodeComparerDepthMostFirst(const void* lhs, const voi struct ImGuiDockContextPruneNodeData { int CountWindows, CountChildWindows, CountChildNodes; - ImGuiID RootID; - ImGuiDockContextPruneNodeData() { CountWindows = CountChildWindows = CountChildNodes = 0; RootID = 0; } + ImGuiID RootId; + ImGuiDockContextPruneNodeData() { CountWindows = CountChildWindows = CountChildNodes = 0; RootId = 0; } }; // Garbage collect unused nodes (run once at init time) @@ -11633,10 +11633,10 @@ static void ImGui::DockContextPruneUnusedSettingsNodes(ImGuiContext* ctx) for (int settings_n = 0; settings_n < dc->SettingsNodes.Size; settings_n++) { ImGuiDockNodeSettings* settings = &dc->SettingsNodes[settings_n]; - ImGuiDockContextPruneNodeData* parent_data = settings->ParentNodeID ? pool.GetByKey(settings->ParentNodeID) : 0; - pool.GetOrAddByKey(settings->ID)->RootID = parent_data ? parent_data->RootID : settings->ID; - if (settings->ParentNodeID) - pool.GetOrAddByKey(settings->ParentNodeID)->CountChildNodes++; + ImGuiDockContextPruneNodeData* parent_data = settings->ParentNodeId ? pool.GetByKey(settings->ParentNodeId) : 0; + pool.GetOrAddByKey(settings->ID)->RootId = parent_data ? parent_data->RootId : settings->ID; + if (settings->ParentNodeId) + pool.GetOrAddByKey(settings->ParentNodeId)->CountChildNodes++; } // Count reference to dock ids from dockspaces @@ -11644,8 +11644,8 @@ static void ImGui::DockContextPruneUnusedSettingsNodes(ImGuiContext* ctx) for (int settings_n = 0; settings_n < dc->SettingsNodes.Size; settings_n++) { ImGuiDockNodeSettings* settings = &dc->SettingsNodes[settings_n]; - if (settings->ParentWindowID != 0) - if (ImGuiWindowSettings* window_settings = FindWindowSettings(settings->ParentWindowID)) + if (settings->ParentWindowId != 0) + if (ImGuiWindowSettings* window_settings = FindWindowSettings(settings->ParentWindowId)) if (window_settings->DockId) if (ImGuiDockContextPruneNodeData* data = pool.GetByKey(window_settings->DockId)) data->CountChildNodes++; @@ -11658,7 +11658,7 @@ static void ImGui::DockContextPruneUnusedSettingsNodes(ImGuiContext* ctx) if (ImGuiDockContextPruneNodeData* data = pool.GetByKey(dock_id)) { data->CountWindows++; - if (ImGuiDockContextPruneNodeData* data_root = (data->RootID == dock_id) ? data : pool.GetByKey(data->RootID)) + if (ImGuiDockContextPruneNodeData* data_root = (data->RootId == dock_id) ? data : pool.GetByKey(data->RootId)) data_root->CountChildWindows++; } @@ -11669,11 +11669,11 @@ static void ImGui::DockContextPruneUnusedSettingsNodes(ImGuiContext* ctx) ImGuiDockContextPruneNodeData* data = pool.GetByKey(settings->ID); if (data->CountWindows > 1) continue; - ImGuiDockContextPruneNodeData* data_root = (data->RootID == settings->ID) ? data : pool.GetByKey(data->RootID); + ImGuiDockContextPruneNodeData* data_root = (data->RootId == settings->ID) ? data : pool.GetByKey(data->RootId); bool remove = false; - remove |= (data->CountWindows == 1 && settings->ParentNodeID == 0 && data->CountChildNodes == 0 && !(settings->Flags & ImGuiDockNodeFlags_CentralNode)); // Floating root node with only 1 window - remove |= (data->CountWindows == 0 && settings->ParentNodeID == 0 && data->CountChildNodes == 0); // Leaf nodes with 0 window + remove |= (data->CountWindows == 1 && settings->ParentNodeId == 0 && data->CountChildNodes == 0 && !(settings->Flags & ImGuiDockNodeFlags_CentralNode)); // Floating root node with only 1 window + remove |= (data->CountWindows == 0 && settings->ParentNodeId == 0 && data->CountChildNodes == 0); // Leaf nodes with 0 window remove |= (data_root->CountChildWindows == 0); if (remove) { @@ -11693,7 +11693,7 @@ static void ImGui::DockContextBuildNodesFromSettings(ImGuiContext* ctx, ImGuiDoc if (settings->ID == 0) continue; ImGuiDockNode* node = DockContextAddNode(ctx, settings->ID); - node->ParentNode = settings->ParentNodeID ? DockContextFindNodeByID(ctx, settings->ParentNodeID) : NULL; + node->ParentNode = settings->ParentNodeId ? DockContextFindNodeByID(ctx, settings->ParentNodeId) : NULL; node->Pos = ImVec2(settings->Pos.x, settings->Pos.y); node->Size = ImVec2(settings->Size.x, settings->Size.y); node->SizeRef = ImVec2(settings->SizeRef.x, settings->SizeRef.y); @@ -11702,7 +11702,7 @@ static void ImGui::DockContextBuildNodesFromSettings(ImGuiContext* ctx, ImGuiDoc node->ParentNode->ChildNodes[0] = node; else if (node->ParentNode && node->ParentNode->ChildNodes[1] == NULL) node->ParentNode->ChildNodes[1] = node; - node->SelectedTabID = settings->SelectedWindowID; + node->SelectedTabId = settings->SelectedWindowId; node->SplitAxis = settings->SplitAxis; node->LocalFlags |= (settings->Flags & ImGuiDockNodeFlags_SavedFlagsMask_); @@ -11880,7 +11880,7 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) { // Central node property needs to be moved to a leaf node, pick the last focused one. // FIXME-DOCK: If we had to transfer other flags here, what would the policy be? - ImGuiDockNode* last_focused_node = DockContextFindNodeByID(ctx, payload_node->LastFocusedNodeID); + ImGuiDockNode* last_focused_node = DockContextFindNodeByID(ctx, payload_node->LastFocusedNodeId); IM_ASSERT(last_focused_node != NULL); ImGuiDockNode* last_focused_root_node = DockNodeGetRootNode(last_focused_node); IM_ASSERT(last_focused_root_node == DockNodeGetRootNode(payload_node)); @@ -12026,9 +12026,9 @@ ImGuiDockNode::ImGuiDockNode(ImGuiID id) HostWindow = VisibleWindow = NULL; CentralNode = OnlyNodeWithWindows = NULL; LastFrameAlive = LastFrameActive = LastFrameFocused = -1; - LastFocusedNodeID = 0; - SelectedTabID = 0; - WantCloseTabID = 0; + LastFocusedNodeId = 0; + SelectedTabId = 0; + WantCloseTabId = 0; AuthorityForPos = AuthorityForSize = ImGuiDataAuthority_DockNode; AuthorityForViewport = ImGuiDataAuthority_Auto; IsVisible = true; @@ -12098,7 +12098,7 @@ static void ImGui::DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window, b if (node->TabBar == NULL) { DockNodeAddTabBar(node); - node->TabBar->SelectedTabId = node->TabBar->NextSelectedTabId = node->SelectedTabID; + node->TabBar->SelectedTabId = node->TabBar->NextSelectedTabId = node->SelectedTabId; // Add existing windows for (int n = 0; n < node->Windows.Size - 1; n++) @@ -12321,7 +12321,7 @@ static void ImGui::DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* nod bool node_was_active = (node->LastFrameActive + 1 == g.FrameCount); bool remove = false; remove |= node_was_active && (window->LastFrameActive + 1 < g.FrameCount); - remove |= node_was_active && (node->WantCloseAll || node->WantCloseTabID == window->ID) && window->HasCloseButton && !(window->Flags & ImGuiWindowFlags_UnsavedDocument); // Submit all _expected_ closure from last frame + remove |= node_was_active && (node->WantCloseAll || node->WantCloseTabId == window->ID) && window->HasCloseButton && !(window->Flags & ImGuiWindowFlags_UnsavedDocument); // Submit all _expected_ closure from last frame remove |= (window->DockTabWantClose); if (remove) { @@ -12387,8 +12387,8 @@ static void ImGui::DockNodeUpdateForRootNode(ImGuiDockNode* node) DockNodeFindInfo(node, &results); node->CentralNode = results.CentralNode; node->OnlyNodeWithWindows = (results.CountNodesWithWindows == 1) ? results.FirstNodeWithWindows : NULL; - if (node->LastFocusedNodeID == 0 && results.FirstNodeWithWindows != NULL) - node->LastFocusedNodeID = results.FirstNodeWithWindows->ID; + if (node->LastFocusedNodeId == 0 && results.FirstNodeWithWindows != NULL) + node->LastFocusedNodeId = results.FirstNodeWithWindows->ID; // Copy the window class from of our first window so it can be used for proper dock filtering. // When node has mixed windows, prioritize the class with the most constraint (DockingAllowUnclassed = false) as the reference to copy. @@ -12453,7 +12453,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) DockNodeHideHostWindow(node); node->State = ImGuiDockNodeState_HostWindowHiddenBecauseSingleWindow; node->WantCloseAll = false; - node->WantCloseTabID = 0; + node->WantCloseTabId = 0; node->HasCloseButton = node->HasWindowMenuButton = node->EnableCloseButton = false; node->LastFrameActive = g.FrameCount; @@ -12476,8 +12476,8 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) { IM_ASSERT(node->Windows.Size > 0); ImGuiWindow* ref_window = NULL; - if (node->SelectedTabID != 0) // Note that we prune single-window-node settings on .ini loading, so this is generally 0 for them! - ref_window = DockNodeFindWindowByID(node, node->SelectedTabID); + if (node->SelectedTabId != 0) // Note that we prune single-window-node settings on .ini loading, so this is generally 0 for them! + ref_window = DockNodeFindWindowByID(node, node->SelectedTabId); if (ref_window == NULL) ref_window = node->Windows[0]; if (ref_window->AutoFitFramesX > 0 || ref_window->AutoFitFramesY > 0) @@ -12585,7 +12585,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) IM_ASSERT(node->TabBar == NULL); if (node->IsRootNode()) if (g.NavWindow && g.NavWindow->RootWindowDockStop->DockNode && g.NavWindow->RootWindowDockStop->ParentWindow == host_window) - node->LastFocusedNodeID = g.NavWindow->RootWindowDockStop->DockNode->ID; + node->LastFocusedNodeId = g.NavWindow->RootWindowDockStop->DockNode->ID; // We need to draw a background at the root level if requested by ImGuiDockNodeFlags_PassthruCentralNode, but we will only know the correct pos/size after // processing the resizing splitters. So we are using the DrawList channel splitting facility to submit drawing primitives out of order! @@ -12647,13 +12647,13 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) else { node->WantCloseAll = false; - node->WantCloseTabID = 0; + node->WantCloseTabId = 0; node->IsFocused = false; } if (node->TabBar && node->TabBar->SelectedTabId) - node->SelectedTabID = node->TabBar->SelectedTabId; + node->SelectedTabId = node->TabBar->SelectedTabId; else if (node->Windows.Size > 0) - node->SelectedTabID = node->Windows[0]->ID; + node->SelectedTabId = node->Windows[0]->ID; // Draw payload drop target if (host_window && node->IsVisible) @@ -12733,16 +12733,16 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w const bool node_was_active = (node->LastFrameActive + 1 == g.FrameCount); const bool closed_all = node->WantCloseAll && node_was_active; - const ImGuiID closed_one = node->WantCloseTabID && node_was_active; + const ImGuiID closed_one = node->WantCloseTabId && node_was_active; node->WantCloseAll = false; - node->WantCloseTabID = 0; + node->WantCloseTabId = 0; // Decide if we should use a focused title bar color bool is_focused = false; ImGuiDockNode* root_node = DockNodeGetRootNode(node); if (g.NavWindowingTarget) is_focused = (g.NavWindowingTarget->DockNode == node); - else if (g.NavWindow && g.NavWindow->RootWindowForTitleBarHighlight == host_window->RootWindow && root_node->LastFocusedNodeID == node->ID) + else if (g.NavWindow && g.NavWindow->RootWindowForTitleBarHighlight == host_window->RootWindow && root_node->LastFocusedNodeId == node->ID) is_focused = true; // Hidden tab bar will show a triangle on the upper-left (in Begin) @@ -12849,8 +12849,8 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w } // Selected newly added tabs, or persistent tab ID if the tab bar was just recreated - if (tab_bar_is_recreated && TabBarFindTabByID(tab_bar, node->SelectedTabID) != NULL) - tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = node->SelectedTabID; + if (tab_bar_is_recreated && TabBarFindTabByID(tab_bar, node->SelectedTabId) != NULL) + tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = node->SelectedTabId; else if (tab_bar->Tabs.Size > tabs_count_old) tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = tab_bar->Tabs.back().Window->ID; @@ -12880,7 +12880,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w bool tab_open = true; TabItemEx(tab_bar, window->Name, window->HasCloseButton ? &tab_open : NULL, tab_item_flags, window); if (!tab_open) - node->WantCloseTabID = window->ID; + node->WantCloseTabId = window->ID; if (tab_bar->VisibleTabId == window->ID) node->VisibleWindow = window; @@ -12912,7 +12912,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w if (CloseButton(host_window->GetID("#CLOSE"), title_bar_rect.GetTR() + ImVec2(-style.FramePadding.x * 2.0f - button_sz, 0.0f))) if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_bar->VisibleTabId)) { - node->WantCloseTabID = tab->ID; + node->WantCloseTabId = tab->ID; TabBarCloseTab(tab_bar, tab); } //if (IsItemActive()) @@ -13697,7 +13697,7 @@ void ImGui::SetWindowDock(ImGuiWindow* window, ImGuiID dock_id, ImGuiCond cond) } else { - dock_id = new_node->LastFocusedNodeID; + dock_id = new_node->LastFocusedNodeId; } } @@ -14416,7 +14416,7 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) if (node->TabBar && node->TabBar->CurrFrameVisible != -1) window->DockOrder = (short)DockNodeGetTabOrder(window); - if ((node->WantCloseAll || node->WantCloseTabID == window->ID) && p_open != NULL) + if ((node->WantCloseAll || node->WantCloseTabId == window->ID) && p_open != NULL) *p_open = false; // Update ChildId to allow returning from Child to Parent with Escape @@ -14588,9 +14588,9 @@ static void ImGui::DockSettingsHandler_ReadLine(ImGuiContext* ctx, ImGuiSettings else if (strncmp(line, "DockSpace", 9) == 0) { line = ImStrSkipBlank(line + strlen("DockSpace")); node.Flags |= ImGuiDockNodeFlags_DockSpace; } else return; if (sscanf(line, "ID=0x%08X%n", &node.ID, &r) == 1) { line += r; } else return; - if (sscanf(line, " Parent=0x%08X%n", &node.ParentNodeID, &r) == 1) { line += r; if (node.ParentNodeID == 0) return; } - if (sscanf(line, " Window=0x%08X%n", &node.ParentWindowID, &r) ==1) { line += r; if (node.ParentWindowID == 0) return; } - if (node.ParentNodeID == 0) + if (sscanf(line, " Parent=0x%08X%n", &node.ParentNodeId, &r) == 1) { line += r; if (node.ParentNodeId == 0) return; } + if (sscanf(line, " Window=0x%08X%n", &node.ParentWindowId, &r) ==1) { line += r; if (node.ParentWindowId == 0) return; } + if (node.ParentNodeId == 0) { if (sscanf(line, " Pos=%i,%i%n", &x, &y, &r) == 2) { line += r; node.Pos = ImVec2ih((short)x, (short)y); } else return; if (sscanf(line, " Size=%i,%i%n", &x, &y, &r) == 2) { line += r; node.Size = ImVec2ih((short)x, (short)y); } else return; @@ -14606,10 +14606,10 @@ static void ImGui::DockSettingsHandler_ReadLine(ImGuiContext* ctx, ImGuiSettings if (sscanf(line, " HiddenTabBar=%d%n", &x, &r) == 1) { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_HiddenTabBar; } if (sscanf(line, " NoWindowMenuButton=%d%n", &x, &r) == 1) { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoWindowMenuButton; } if (sscanf(line, " NoCloseButton=%d%n", &x, &r) == 1) { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoCloseButton; } - if (sscanf(line, " Selected=0x%08X%n", &node.SelectedWindowID,&r) == 1) { line += r; } + if (sscanf(line, " Selected=0x%08X%n", &node.SelectedWindowId,&r) == 1) { line += r; } ImGuiDockContext* dc = ctx->DockContext; - if (node.ParentNodeID != 0) - if (ImGuiDockNodeSettings* parent_settings = DockSettingsFindNodeSettings(ctx, node.ParentNodeID)) + if (node.ParentNodeId != 0) + if (ImGuiDockNodeSettings* parent_settings = DockSettingsFindNodeSettings(ctx, node.ParentNodeId)) node.Depth = parent_settings->Depth + 1; dc->SettingsNodes.push_back(node); } @@ -14619,9 +14619,9 @@ static void DockSettingsHandler_DockNodeToSettings(ImGuiDockContext* dc, ImGuiDo ImGuiDockNodeSettings node_settings; IM_ASSERT(depth < (1 << (sizeof(node_settings.Depth) << 3))); node_settings.ID = node->ID; - node_settings.ParentNodeID = node->ParentNode ? node->ParentNode->ID : 0; - node_settings.ParentWindowID = (node->IsDockSpace() && node->HostWindow && node->HostWindow->ParentWindow) ? node->HostWindow->ParentWindow->ID : 0; - node_settings.SelectedWindowID = node->SelectedTabID; + node_settings.ParentNodeId = node->ParentNode ? node->ParentNode->ID : 0; + node_settings.ParentWindowId = (node->IsDockSpace() && node->HostWindow && node->HostWindow->ParentWindow) ? node->HostWindow->ParentWindow->ID : 0; + node_settings.SelectedWindowId = node->SelectedTabId; node_settings.SplitAxis = node->IsSplitNode() ? (char)node->SplitAxis : ImGuiAxis_None; node_settings.Depth = (char)depth; node_settings.Flags = (node->LocalFlags & ImGuiDockNodeFlags_SavedFlagsMask_); @@ -14663,14 +14663,14 @@ static void ImGui::DockSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettings const ImGuiDockNodeSettings* node_settings = &dc->SettingsNodes[node_n]; buf->appendf("%*s%s%*s", node_settings->Depth * 2, "", (node_settings->Flags & ImGuiDockNodeFlags_DockSpace) ? "DockSpace" : "DockNode ", (max_depth - node_settings->Depth) * 2, ""); // Text align nodes to facilitate looking at .ini file buf->appendf(" ID=0x%08X", node_settings->ID); - if (node_settings->ParentNodeID) + if (node_settings->ParentNodeId) { - buf->appendf(" Parent=0x%08X SizeRef=%d,%d", node_settings->ParentNodeID, node_settings->SizeRef.x, node_settings->SizeRef.y); + buf->appendf(" Parent=0x%08X SizeRef=%d,%d", node_settings->ParentNodeId, node_settings->SizeRef.x, node_settings->SizeRef.y); } else { - if (node_settings->ParentWindowID) - buf->appendf(" Window=0x%08X", node_settings->ParentWindowID); + if (node_settings->ParentWindowId) + buf->appendf(" Window=0x%08X", node_settings->ParentWindowId); buf->appendf(" Pos=%d,%d Size=%d,%d", node_settings->Pos.x, node_settings->Pos.y, node_settings->Size.x, node_settings->Size.y); } if (node_settings->SplitAxis != ImGuiAxis_None) @@ -14687,8 +14687,8 @@ static void ImGui::DockSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettings buf->appendf(" NoWindowMenuButton=1"); if (node_settings->Flags & ImGuiDockNodeFlags_NoCloseButton) buf->appendf(" NoCloseButton=1"); - if (node_settings->SelectedWindowID) - buf->appendf(" Selected=0x%08X", node_settings->SelectedWindowID); + if (node_settings->SelectedWindowId) + buf->appendf(" Selected=0x%08X", node_settings->SelectedWindowId); #if IMGUI_DEBUG_INI_SETTINGS // [DEBUG] Include comments in the .ini file to ease debugging @@ -15211,7 +15211,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) node->Pos.x, node->Pos.y, node->Size.x, node->Size.y, node->SizeRef.x, node->SizeRef.y); NodeWindow(node->HostWindow, "HostWindow"); NodeWindow(node->VisibleWindow, "VisibleWindow"); - ImGui::BulletText("SelectedTabID: 0x%08X, LastFocusedNodeID: 0x%08X", node->SelectedTabID, node->LastFocusedNodeID); + ImGui::BulletText("SelectedTabID: 0x%08X, LastFocusedNodeID: 0x%08X", node->SelectedTabId, node->LastFocusedNodeId); ImGui::BulletText("Misc:%s%s%s%s", node->IsDockSpace() ? " IsDockSpace" : "", node->IsCentralNode() ? " IsCentralNode" : "", (g.FrameCount - node->LastFrameAlive < 2) ? " IsAlive" : "", (g.FrameCount - node->LastFrameActive < 2) ? " IsActive" : ""); if (ImGui::TreeNode("flags", "LocalFlags: 0x%04X SharedFlags: 0x%04X", node->LocalFlags, node->SharedFlags)) { @@ -15372,14 +15372,14 @@ void ImGui::ShowMetricsWindow(bool* p_open) { ImGuiDockNodeSettings* settings = &dc->SettingsNodes[n]; const char* selected_tab_name = NULL; - if (settings->SelectedWindowID) + if (settings->SelectedWindowId) { - if (ImGuiWindow* window = FindWindowByID(settings->SelectedWindowID)) + if (ImGuiWindow* window = FindWindowByID(settings->SelectedWindowId)) selected_tab_name = window->Name; - else if (ImGuiWindowSettings* window_settings = FindWindowSettings(settings->SelectedWindowID)) + else if (ImGuiWindowSettings* window_settings = FindWindowSettings(settings->SelectedWindowId)) selected_tab_name = window_settings->GetName(); } - ImGui::BulletText("Node %08X, Parent %08X, SelectedTab %08X ('%s')", settings->ID, settings->ParentNodeID, settings->SelectedWindowID, selected_tab_name ? selected_tab_name : settings->SelectedWindowID ? "N/A" : ""); + ImGui::BulletText("Node %08X, Parent %08X, SelectedTab %08X ('%s')", settings->ID, settings->ParentNodeId, settings->SelectedWindowId, selected_tab_name ? selected_tab_name : settings->SelectedWindowId ? "N/A" : ""); } ImGui::TreePop(); } diff --git a/imgui_internal.h b/imgui_internal.h index 7a316e8975d9..e4a4cab96b5a 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1072,9 +1072,9 @@ struct ImGuiDockNode int LastFrameAlive; // Last frame number the node was updated or kept alive explicitly with DockSpace() + ImGuiDockNodeFlags_KeepAliveOnly int LastFrameActive; // Last frame number the node was updated. int LastFrameFocused; // Last frame number the node was focused. - ImGuiID LastFocusedNodeID; // [Root node only] Which of our child docking node (any ancestor in the hierarchy) was last focused. - ImGuiID SelectedTabID; // [Leaf node only] Which of our tab/window is selected. - ImGuiID WantCloseTabID; // [Leaf node only] Set when closing a specific tab/window. + ImGuiID LastFocusedNodeId; // [Root node only] Which of our child docking node (any ancestor in the hierarchy) was last focused. + ImGuiID SelectedTabId; // [Leaf node only] Which of our tab/window is selected. + ImGuiID WantCloseTabId; // [Leaf node only] Set when closing a specific tab/window. ImGuiDataAuthority AuthorityForPos :3; ImGuiDataAuthority AuthorityForSize :3; ImGuiDataAuthority AuthorityForViewport :3; From f1b5c742ff33a44f9a70dfec8f4b6072288fb685 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 20 Jan 2020 19:33:46 +0100 Subject: [PATCH 575/828] Viewports: Add various comments --- examples/imgui_impl_dx10.cpp | 1 + examples/imgui_impl_dx11.cpp | 1 + examples/imgui_impl_dx12.cpp | 1 + examples/imgui_impl_dx9.cpp | 1 + examples/imgui_impl_glfw.cpp | 8 +++++--- examples/imgui_impl_sdl.cpp | 2 ++ examples/imgui_impl_vulkan.cpp | 3 ++- examples/imgui_impl_win32.cpp | 2 ++ imgui.cpp | 2 +- imgui.h | 6 +++--- 10 files changed, 19 insertions(+), 8 deletions(-) diff --git a/examples/imgui_impl_dx10.cpp b/examples/imgui_impl_dx10.cpp index 021be31d779d..95dc6fd28e6d 100644 --- a/examples/imgui_impl_dx10.cpp +++ b/examples/imgui_impl_dx10.cpp @@ -544,6 +544,7 @@ void ImGui_ImplDX10_NewFrame() // If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first.. //-------------------------------------------------------------------------------------------------------- +// Helper structure we store in the void* RenderUserData field of each ImGuiViewport to easily retrieve our backend data. struct ImGuiViewportDataDx10 { IDXGISwapChain* SwapChain; diff --git a/examples/imgui_impl_dx11.cpp b/examples/imgui_impl_dx11.cpp index f03177f2ee31..ffd05625b3e8 100644 --- a/examples/imgui_impl_dx11.cpp +++ b/examples/imgui_impl_dx11.cpp @@ -560,6 +560,7 @@ void ImGui_ImplDX11_NewFrame() // If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first.. //-------------------------------------------------------------------------------------------------------- +// Helper structure we store in the void* RenderUserData field of each ImGuiViewport to easily retrieve our backend data. struct ImGuiViewportDataDx11 { IDXGISwapChain* SwapChain; diff --git a/examples/imgui_impl_dx12.cpp b/examples/imgui_impl_dx12.cpp index 5beadf0dfa31..3ead29c5affd 100644 --- a/examples/imgui_impl_dx12.cpp +++ b/examples/imgui_impl_dx12.cpp @@ -66,6 +66,7 @@ struct FrameContext D3D12_CPU_DESCRIPTOR_HANDLE RenderTargetCpuDescriptors; }; +// Helper structure we store in the void* RenderUserData field of each ImGuiViewport to easily retrieve our backend data. struct ImGuiViewportDataDx12 { ID3D12CommandQueue* CommandQueue; diff --git a/examples/imgui_impl_dx9.cpp b/examples/imgui_impl_dx9.cpp index 2dd0ee8c90ad..ab1b6578877a 100644 --- a/examples/imgui_impl_dx9.cpp +++ b/examples/imgui_impl_dx9.cpp @@ -310,6 +310,7 @@ void ImGui_ImplDX9_NewFrame() // If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first.. //-------------------------------------------------------------------------------------------------------- +// Helper structure we store in the void* RenderUserData field of each ImGuiViewport to easily retrieve our backend data. struct ImGuiViewportDataDx9 { IDirect3DSwapChain9* SwapChain; diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index ed03aece61e6..388942c61eff 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -428,6 +428,7 @@ void ImGui_ImplGlfw_NewFrame() // If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first.. //-------------------------------------------------------------------------------------------------------- +// Helper structure we store in the void* RenderUserData field of each ImGuiViewport to easily retrieve our backend data. struct ImGuiViewportDataGlfw { GLFWwindow* Window; @@ -496,7 +497,7 @@ static void ImGui_ImplGlfw_CreateWindow(ImGuiViewport* viewport) #endif glfwSetWindowPos(data->Window, (int)viewport->Pos.x, (int)viewport->Pos.y); - // Install callbacks for secondary viewports + // Install GLFW callbacks for secondary viewports glfwSetMouseButtonCallback(data->Window, ImGui_ImplGlfw_MouseButtonCallback); glfwSetScrollCallback(data->Window, ImGui_ImplGlfw_ScrollCallback); glfwSetKeyCallback(data->Window, ImGui_ImplGlfw_KeyCallback); @@ -529,7 +530,8 @@ static void ImGui_ImplGlfw_DestroyWindow(ImGuiViewport* viewport) viewport->PlatformUserData = viewport->PlatformHandle = NULL; } -// FIXME-VIEWPORT: Implement same work-around for Linux/OSX in the meanwhile. +// We have submitted https://github.com/glfw/glfw/pull/1568 to allow GLFW to support "transparent inputs". +// In the meanwhile we implement custom per-platform workarounds here (FIXME-VIEWPORT: Implement same work-around for Linux/OSX!) #if defined(_WIN32) && GLFW_HAS_GLFW_HOVERED static WNDPROC g_GlfwWndProc = NULL; static LRESULT CALLBACK WndProcNoInputs(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) @@ -733,7 +735,6 @@ static int ImGui_ImplGlfw_CreateVkSurface(ImGuiViewport* viewport, ImU64 vk_inst } #endif // GLFW_HAS_VULKAN -// FIXME-PLATFORM: GLFW doesn't export monitor work area (see https://github.com/glfw/glfw/pull/989) static void ImGui_ImplGlfw_UpdateMonitors() { ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); @@ -805,6 +806,7 @@ static void ImGui_ImplGlfw_InitPlatformInterface() glfwSetMonitorCallback(ImGui_ImplGlfw_MonitorCallback); // Register main window handle (which is owned by the main application, not by us) + // This is mostly for simplicity and consistency, so that our code (e.g. mouse handling etc.) can use same logic for main and secondary viewports. ImGuiViewport* main_viewport = ImGui::GetMainViewport(); ImGuiViewportDataGlfw* data = IM_NEW(ImGuiViewportDataGlfw)(); data->Window = g_Window; diff --git a/examples/imgui_impl_sdl.cpp b/examples/imgui_impl_sdl.cpp index 63021d3995d5..ebb1ff15e809 100644 --- a/examples/imgui_impl_sdl.cpp +++ b/examples/imgui_impl_sdl.cpp @@ -432,6 +432,7 @@ void ImGui_ImplSDL2_NewFrame(SDL_Window* window) // If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first.. //-------------------------------------------------------------------------------------------------------- +// Helper structure we store in the void* RenderUserData field of each ImGuiViewport to easily retrieve our backend data. struct ImGuiViewportDataSDL2 { SDL_Window* Window; @@ -685,6 +686,7 @@ static void ImGui_ImplSDL2_InitPlatformInterface(SDL_Window* window, void* sdl_g ImGui_ImplSDL2_UpdateMonitors(); // Register main window handle (which is owned by the main application, not by us) + // This is mostly for simplicity and consistency, so that our code (e.g. mouse handling etc.) can use same logic for main and secondary viewports. ImGuiViewport* main_viewport = ImGui::GetMainViewport(); ImGuiViewportDataSDL2* data = IM_NEW(ImGuiViewportDataSDL2)(); data->Window = window; diff --git a/examples/imgui_impl_vulkan.cpp b/examples/imgui_impl_vulkan.cpp index a503703e73fb..3d0ad9a1e0b2 100644 --- a/examples/imgui_impl_vulkan.cpp +++ b/examples/imgui_impl_vulkan.cpp @@ -70,7 +70,8 @@ struct ImGui_ImplVulkanH_WindowRenderBuffers ImGui_ImplVulkanH_FrameRenderBuffers* FrameRenderBuffers; }; -// For multi-viewport support +// For multi-viewport support: +// Helper structure we store in the void* RenderUserData field of each ImGuiViewport to easily retrieve our backend data. struct ImGuiViewportDataVulkan { bool WindowOwned; diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index 61750145877c..cc190093811a 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -511,6 +511,7 @@ static void ImGui_ImplWin32_SetImeInputPos(ImGuiViewport* viewport, ImVec2 pos) // If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first.. //-------------------------------------------------------------------------------------------------------- +// Helper structure we store in the void* RenderUserData field of each ImGuiViewport to easily retrieve our backend data. struct ImGuiViewportDataWin32 { HWND Hwnd; @@ -831,6 +832,7 @@ static void ImGui_ImplWin32_InitPlatformInterface() #endif // Register main window handle (which is owned by the main application, not by us) + // This is mostly for simplicity and consistency, so that our code (e.g. mouse handling etc.) can use same logic for main and secondary viewports. ImGuiViewport* main_viewport = ImGui::GetMainViewport(); ImGuiViewportDataWin32* data = IM_NEW(ImGuiViewportDataWin32)(); data->Hwnd = g_hWnd; diff --git a/imgui.cpp b/imgui.cpp index a58b6fafc35b..82d8eee07181 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3326,7 +3326,7 @@ static ImDrawList* GetViewportDrawList(ImGuiViewportP* viewport, size_t drawlist { // Create the draw list on demand, because they are not frequently used for all viewports ImGuiContext& g = *GImGui; - IM_ASSERT(drawlist_no >= 0 && drawlist_no < IM_ARRAYSIZE(viewport->DrawLists)); + IM_ASSERT(drawlist_no < IM_ARRAYSIZE(viewport->DrawLists)); ImDrawList* draw_list = viewport->DrawLists[drawlist_no]; if (draw_list == NULL) { diff --git a/imgui.h b/imgui.h index c74fc7caf7cb..cee80e3580c0 100644 --- a/imgui.h +++ b/imgui.h @@ -1159,7 +1159,7 @@ enum ImGuiCol_ ImGuiCol_TabActive, ImGuiCol_TabUnfocused, ImGuiCol_TabUnfocusedActive, - ImGuiCol_DockingPreview, + ImGuiCol_DockingPreview, // Preview overlay color when about to docking something ImGuiCol_DockingEmptyBg, // Background color for empty node (e.g. CentralNode with no window docked into it) ImGuiCol_PlotLines, ImGuiCol_PlotLinesHovered, @@ -2463,8 +2463,8 @@ struct ImGuiViewport ImDrawData* DrawData; // The ImDrawData corresponding to this viewport. Valid after Render() and until the next call to NewFrame(). ImGuiID ParentViewportId; // (Advanced) 0: no parent. Instruct the platform back-end to setup a parent/child relationship between platform windows. - void* RendererUserData; // void* to hold custom data structure for the renderer (e.g. swap chain, frame-buffers etc.) - void* PlatformUserData; // void* to hold custom data structure for the OS / platform (e.g. windowing info, render context) + void* RendererUserData; // void* to hold custom data structure for the renderer (e.g. swap chain, frame-buffers etc.). If somehow everything you need can fit in the void* PlatformHandle field you may ignore this. + void* PlatformUserData; // void* to hold custom data structure for the OS / platform (e.g. windowing info, render context). If somehow everything you need can fit in the void* PlatformHandle field you may ignore this. void* PlatformHandle; // void* for FindViewportByPlatformHandle(). (e.g. suggested to use natural platform handle such as HWND, GLFWWindow*, SDL_Window*) void* PlatformHandleRaw; // void* to hold low-level, platform-native window handle (e.g. the HWND) when using an abstraction layer like GLFW or SDL (where PlatformHandle would be a SDL_Window*) bool PlatformRequestClose; // Platform window requested closure (e.g. window was moved by the OS / host window manager, e.g. pressing ALT-F4) From f68075b333b017520e2d98a5c9901f04b3c2745c Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 28 Nov 2019 22:42:13 +0100 Subject: [PATCH 576/828] Docking: Added DockNodeFlagsOverrideSet/DockNodeFlagsOverrideClear (experimental) --- imgui.cpp | 10 ++++++++++ imgui.h | 4 +++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 82d8eee07181..642ebed97739 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -12313,6 +12313,7 @@ static void ImGui::DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* nod DockNodeUpdateVisibleFlagAndInactiveChilds(node->ChildNodes[1]); // Remove inactive windows + // Merge node flags overrides stored in windows for (int window_n = 0; window_n < node->Windows.Size; window_n++) { ImGuiWindow* window = node->Windows[window_n]; @@ -12336,6 +12337,11 @@ static void ImGui::DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* nod DockNodeRemoveWindow(node, window, node->ID); window_n--; } + else + { + node->LocalFlags &= ~window->WindowClass.DockNodeFlagsOverrideClear; + node->LocalFlags |= window->WindowClass.DockNodeFlagsOverrideSet; + } } // Auto-hide tab bar option @@ -12344,6 +12350,10 @@ static void ImGui::DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* nod node->WantHiddenTabBarToggle = true; node->WantHiddenTabBarUpdate = false; + // Cancel toggling if we know our tab bar is enforced to be hidden at all times + if (node->WantHiddenTabBarToggle && node->VisibleWindow && (node->VisibleWindow->WindowClass.DockNodeFlagsOverrideSet & ImGuiDockNodeFlags_HiddenTabBar)) + node->WantHiddenTabBarToggle = false; + // Apply toggles at a single point of the frame (here!) if (node->Windows.Size > 1) node->LocalFlags &= ~ImGuiDockNodeFlags_HiddenTabBar; diff --git a/imgui.h b/imgui.h index cee80e3580c0..7d9c4750f791 100644 --- a/imgui.h +++ b/imgui.h @@ -1646,10 +1646,12 @@ struct ImGuiWindowClass ImGuiID ParentViewportId; // Hint for the platform back-end. If non-zero, the platform back-end can create a parent<>child relationship between the platform windows. Not conforming back-ends are free to e.g. parent every viewport to the main viewport or not. ImGuiViewportFlags ViewportFlagsOverrideSet; // Viewport flags to set when a window of this class owns a viewport. This allows you to enforce OS decoration or task bar icon, override the defaults on a per-window basis. ImGuiViewportFlags ViewportFlagsOverrideClear; // Viewport flags to clear when a window of this class owns a viewport. This allows you to enforce OS decoration or task bar icon, override the defaults on a per-window basis. + ImGuiDockNodeFlags DockNodeFlagsOverrideSet; // [EXPERIMENTAL] Dock node flags to set when a window of this class is hosted by a dock node (it doesn't have to be selected!) + ImGuiDockNodeFlags DockNodeFlagsOverrideClear; // [EXPERIMENTAL] bool DockingAlwaysTabBar; // Set to true to enforce single floating windows of this class always having their own docking node (equivalent of setting the global io.ConfigDockingAlwaysTabBar) bool DockingAllowUnclassed; // Set to true to allow windows of this class to be docked/merged with an unclassed window. - ImGuiWindowClass() { ClassId = 0; ParentViewportId = 0; ViewportFlagsOverrideSet = ViewportFlagsOverrideClear = 0x00; DockingAlwaysTabBar = false; DockingAllowUnclassed = true; } + ImGuiWindowClass() { ClassId = 0; ParentViewportId = 0; ViewportFlagsOverrideSet = ViewportFlagsOverrideClear = 0x00; DockNodeFlagsOverrideSet = DockNodeFlagsOverrideClear = 0x00; DockingAlwaysTabBar = false; DockingAllowUnclassed = true; } }; // Data payload for Drag and Drop operations: AcceptDragDropPayload(), GetDragDropPayload() From 377f7300546e2a43e225d38c724a7f28da0ed6f2 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 27 Jan 2020 17:57:07 +0100 Subject: [PATCH 577/828] Fix resizing viewport-owning windows when mouse pos is outside the InnerClipRect (can happen with OS decoration on). (#1542) --- imgui.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/imgui.cpp b/imgui.cpp index a9615907a30d..b0f350bc8349 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5403,9 +5403,12 @@ static bool ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au ImRect resize_rect(corner - grip.InnerDir * grip_hover_outer_size, corner + grip.InnerDir * grip_hover_inner_size); if (resize_rect.Min.x > resize_rect.Max.x) ImSwap(resize_rect.Min.x, resize_rect.Max.x); if (resize_rect.Min.y > resize_rect.Max.y) ImSwap(resize_rect.Min.y, resize_rect.Max.y); + ImVec2 resize_rect_ref_min = resize_rect.Min; resize_rect.ClipWith(clip_viewport_rect); bool hovered, held; ButtonBehavior(resize_rect, window->GetID(resize_grip_n), &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus); + if (held && g.ActiveIdIsJustActivated) + g.ActiveIdClickOffset = g.IO.MousePos - resize_rect_ref_min; // Override our reference click offset as viewport clipping may be moved it. //GetForegroundDrawList(window)->AddRect(resize_rect.Min, resize_rect.Max, IM_COL32(255, 255, 0, 255)); if (hovered || held) g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE; @@ -5431,8 +5434,11 @@ static bool ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au { bool hovered, held; ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_inner_size, WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); + ImVec2 border_rect_ref_min = border_rect.Min; border_rect.ClipWith(clip_viewport_rect); ButtonBehavior(border_rect, window->GetID(border_n + 4), &hovered, &held, ImGuiButtonFlags_FlattenChildren); + if (held && g.ActiveIdIsJustActivated) + g.ActiveIdClickOffset = g.IO.MousePos - border_rect_ref_min; // Override our reference click offset as viewport clipping may be moved it. //GetForegroundDrawLists(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255)); if ((hovered && g.HoveredIdTimer > WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER) || held) { From bbe946fb6c9ba015a5b1a6a938ff7a4b5af03c32 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 28 Jan 2020 20:08:04 +0100 Subject: [PATCH 578/828] Minor fixes/tweaks (some to reduce drift wiih master branch) --- imgui.cpp | 15 ++++++--------- imgui.h | 2 +- imgui_demo.cpp | 2 +- imgui_draw.cpp | 8 ++++---- imgui_internal.h | 10 +++++++--- imgui_widgets.cpp | 4 ---- 6 files changed, 19 insertions(+), 22 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index b0f350bc8349..9cdc2f338648 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -388,7 +388,6 @@ CODE - IMGUI_DISABLE_TEST_WINDOWS -> use IMGUI_DISABLE_DEMO_WINDOWS - 2019/12/08 (1.75) - obsoleted calling ImDrawList::PrimReserve() with a negative count (which was the vaguely documented and rarely if ever used). Instead we added an explicit PrimUnreserve() API. - 2019/12/06 (1.75) - removed implicit default parameter to IsMouseDragging(int button = 0) to be consistent with other mouse functions (none of the other functions have it). ->>>>>>> refs/heads/master - 2019/11/21 (1.74) - ImFontAtlas::AddCustomRectRegular() now requires an ID larger than 0x110000 (instead of 0x10000) to conform with supporting Unicode planes 1-16 in a future update. ID below 0x110000 will now assert. - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS to IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS for consistency. - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_MATH_FUNCTIONS to IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS for consistency. @@ -3848,7 +3847,7 @@ void ImGui::NewFrame() ImRect virtual_space(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX); for (int n = 0; n < g.Viewports.Size; n++) virtual_space.Add(g.Viewports[n]->GetRect()); - g.DrawListSharedData.ClipRectFullscreen = ImVec4(virtual_space.Min.x, virtual_space.Min.y, virtual_space.Max.x, virtual_space.Max.y); + g.DrawListSharedData.ClipRectFullscreen = ImVec4(virtual_space.Min, virtual_space.Max); g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol; g.DrawListSharedData.SetCircleSegmentMaxError(g.Style.CircleSegmentMaxError); g.DrawListSharedData.InitialFlags = ImDrawListFlags_None; @@ -4500,7 +4499,7 @@ void ImGui::Render() AddRootWindowToDrawData(window); } for (int n = 0; n < IM_ARRAYSIZE(windows_to_render_top_most); n++) - if (windows_to_render_top_most[n] && IsWindowActiveAndVisible(windows_to_render_top_most[n])) // NavWindowingTarget is always temporarily displayed as the tp-most window + if (windows_to_render_top_most[n] && IsWindowActiveAndVisible(windows_to_render_top_most[n])) // NavWindowingTarget is always temporarily displayed as the top-most window AddRootWindowToDrawData(windows_to_render_top_most[n]); // Draw software mouse cursor if requested @@ -5459,7 +5458,7 @@ static bool ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au } PopID(); - // Resize nav layer + // Restore nav layer window->DC.NavLayerCurrent = ImGuiNavLayer_Main; window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Main); @@ -6341,13 +6340,12 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha * 0.25f), g.Style.WindowRounding); } - const bool is_undocked_or_docked_visible = !window->DockIsActive || window->DockTabIsVisible; - // Since 1.71, child window can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call. // When using overlapping child windows, this will break the assumption that child z-order is mapped to submission order. // We disable this when the parent window has zero vertices, which is a common pattern leading to laying out multiple overlapping child. // We also disabled this when we have dimming overlay behind this specific one child. // FIXME: More code may rely on explicit sorting of overlapping child window and would need to disable this somehow. Please get in contact if you are affected. + const bool is_undocked_or_docked_visible = !window->DockIsActive || window->DockTabIsVisible; if (is_undocked_or_docked_visible) { bool render_decorations_in_parent = false; @@ -6464,7 +6462,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) NavInitWindow(window, false); } - // Close from platform window + // Close requested by platform window if (p_open != NULL && window->Viewport->PlatformRequestClose && window->Viewport != GetMainViewport()) { if (!window->DockIsActive || window->DockTabIsVisible) @@ -15280,8 +15278,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) char buf[256]; char* p = buf; const char* buf_end = buf + IM_ARRAYSIZE(buf); - p += ImFormatString(p, buf_end - p, "TabBar (%d tabs)%s", - tab_bar->Tabs.Size, (tab_bar->PrevFrameVisible < ImGui::GetFrameCount() - 2) ? " *Inactive*" : ""); + p += ImFormatString(p, buf_end - p, "TabBar (%d tabs)%s", tab_bar->Tabs.Size, (tab_bar->PrevFrameVisible < ImGui::GetFrameCount() - 2) ? " *Inactive*" : ""); if (tab_bar->Flags & ImGuiTabBarFlags_DockNode) { p += ImFormatString(p, buf_end - p, " { "); diff --git a/imgui.h b/imgui.h index 8cd0263ad165..11b73b876058 100644 --- a/imgui.h +++ b/imgui.h @@ -1454,7 +1454,7 @@ struct ImGuiIO ImGuiConfigFlags ConfigFlags; // = 0 // See ImGuiConfigFlags_ enum. Set by user/application. Gamepad/keyboard navigation options, etc. ImGuiBackendFlags BackendFlags; // = 0 // See ImGuiBackendFlags_ enum. Set by back-end (imgui_impl_xxx files or custom back-end) to communicate features supported by the back-end. - ImVec2 DisplaySize; // // Main display size, in pixels. This is for the default viewport. Use BeginViewport() for other viewports. + ImVec2 DisplaySize; // // Main display size, in pixels. This is for the default viewport. float DeltaTime; // = 1.0f/60.0f // Time elapsed since last frame, in seconds. float IniSavingRate; // = 5.0f // Minimum time between saving positions/sizes to .ini file, in seconds. const char* IniFilename; // = "imgui.ini" // Path to .ini file. Set NULL to disable automatic .ini loading/saving, if e.g. you want to manually load/save from memory. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 9c321b8520f4..3e2fbef5d82c 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -4867,7 +4867,7 @@ void ShowExampleAppDocuments(bool* p_open) Target_DockSpaceAndWindow // Create documents as regular windows, and create an embedded dockspace }; static Target opt_target = Target_Tab; - static bool opt_reorderable = true; + static bool opt_reorderable = true; static ImGuiTabBarFlags opt_fitting_flags = ImGuiTabBarFlags_FittingPolicyDefault_; // When (opt_target == Target_DockSpaceAndWindow) there is the possibily that one of our child Document window (e.g. "Eggplant") diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 627e3e25b412..228df4108ecb 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -3153,10 +3153,10 @@ void ImGui::RenderMouseCursor(ImDrawList* draw_list, ImVec2 pos, float scale, Im pos -= offset; const ImTextureID tex_id = font_atlas->TexID; draw_list->PushTextureID(tex_id); - draw_list->AddImage(tex_id, pos + ImVec2(1, 0)*scale, pos + ImVec2(1, 0)*scale + size*scale, uv[2], uv[3], col_shadow); - draw_list->AddImage(tex_id, pos + ImVec2(2, 0)*scale, pos + ImVec2(2, 0)*scale + size*scale, uv[2], uv[3], col_shadow); - draw_list->AddImage(tex_id, pos, pos + size*scale, uv[2], uv[3], col_border); - draw_list->AddImage(tex_id, pos, pos + size*scale, uv[0], uv[1], col_fill); + draw_list->AddImage(tex_id, pos + ImVec2(1,0)*scale, pos + ImVec2(1,0)*scale + size*scale, uv[2], uv[3], col_shadow); + draw_list->AddImage(tex_id, pos + ImVec2(2,0)*scale, pos + ImVec2(2,0)*scale + size*scale, uv[2], uv[3], col_shadow); + draw_list->AddImage(tex_id, pos, pos + size*scale, uv[2], uv[3], col_border); + draw_list->AddImage(tex_id, pos, pos + size*scale, uv[0], uv[1], col_fill); draw_list->PopTextureID(); } } diff --git a/imgui_internal.h b/imgui_internal.h index f17a48d8c20c..65f322171238 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1004,7 +1004,7 @@ struct ImGuiNextItemData }; //----------------------------------------------------------------------------- -// Docking, Tabs +// Tabs //----------------------------------------------------------------------------- struct ImGuiShrinkWidthItem @@ -1022,6 +1022,10 @@ struct ImGuiPtrOrIndex ImGuiPtrOrIndex(int index) { Ptr = NULL; Index = index; } }; +//----------------------------------------------------------------------------- +// Docking +//----------------------------------------------------------------------------- + // Extend ImGuiDockNodeFlags_ enum ImGuiDockNodeFlagsPrivate_ { @@ -1113,7 +1117,7 @@ struct ImGuiDockNode }; //----------------------------------------------------------------------------- -// Main imgui context +// Main Dear ImGui context //----------------------------------------------------------------------------- struct ImGuiContext @@ -1610,7 +1614,7 @@ struct IMGUI_API ImGuiWindow bool SkipItems; // Set when items can safely be all clipped (e.g. window not visible or collapsed) bool Appearing; // Set during the frame where the window is appearing (or re-appearing) bool Hidden; // Do not display (== (HiddenFrames*** > 0)) - bool IsFallbackWindow; + bool IsFallbackWindow; // Set on the "Debug##Default" window. bool HasCloseButton; // Set when the window has a close button (p_open != NULL) signed char ResizeBorderHeld; // Current border being held for resize (-1: none, otherwise 0-3) short BeginCount; // Number of Begin() during the current frame (generally 0 or 1, 1+ if appending via multiple Begin/End pairs) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index b033ca2d0dbc..fb5eb516e8b8 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6366,8 +6366,6 @@ bool ImGui::MenuItem(const char* label, const char* shortcut, bool* p_selected, //------------------------------------------------------------------------- // [SECTION] Widgets: BeginTabBar, EndTabBar, etc. //------------------------------------------------------------------------- -// [BETA API] API may evolve! -//------------------------------------------------------------------------- // - BeginTabBar() // - BeginTabBarEx() [Internal] // - EndTabBar() @@ -6926,8 +6924,6 @@ static ImGuiTabItem* ImGui::TabBarTabListPopupButton(ImGuiTabBar* tab_bar) //------------------------------------------------------------------------- // [SECTION] Widgets: BeginTabItem, EndTabItem, etc. //------------------------------------------------------------------------- -// [BETA API] API may evolve! -//------------------------------------------------------------------------- // - BeginTabItem() // - EndTabItem() // - TabItemEx() [Internal] From 3b828e6f96ea8a2fdbf5beda942bcd43a4b1f613 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 28 Jan 2020 20:29:07 +0100 Subject: [PATCH 579/828] Fix following bbe946f (ImRect <> ImVec4) --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 9cdc2f338648..af9e76f317ec 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3847,7 +3847,7 @@ void ImGui::NewFrame() ImRect virtual_space(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX); for (int n = 0; n < g.Viewports.Size; n++) virtual_space.Add(g.Viewports[n]->GetRect()); - g.DrawListSharedData.ClipRectFullscreen = ImVec4(virtual_space.Min, virtual_space.Max); + g.DrawListSharedData.ClipRectFullscreen = ImVec4(virtual_space.Min.x, virtual_space.Min.y, virtual_space.Max.x, virtual_space.Max.y); g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol; g.DrawListSharedData.SetCircleSegmentMaxError(g.Style.CircleSegmentMaxError); g.DrawListSharedData.InitialFlags = ImDrawListFlags_None; From 7794b104c5c6aa0f41680cf527b528f451e7352e Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Wed, 29 Jan 2020 11:40:49 +0200 Subject: [PATCH 580/828] Backends: SDL/Viewports: Fix crash when SDL backend is used with DirectX graphics API and system does not support Vulkan. We must pass this flag only when we intend to render using vulkan backend. --- examples/imgui_impl_sdl.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/imgui_impl_sdl.cpp b/examples/imgui_impl_sdl.cpp index 32ac402274b3..8d52a2125830 100644 --- a/examples/imgui_impl_sdl.cpp +++ b/examples/imgui_impl_sdl.cpp @@ -74,6 +74,7 @@ static bool g_MousePressed[3] = { false, false, false }; static SDL_Cursor* g_MouseCursors[ImGuiMouseCursor_COUNT] = {}; static char* g_ClipboardTextData = NULL; static bool g_MouseCanUseGlobalState = true; +static bool g_UseVulkan = false; // Forward Declarations static void ImGui_ImplSDL2_InitPlatformInterface(SDL_Window* window, void* sdl_gl_context); @@ -240,6 +241,7 @@ bool ImGui_ImplSDL2_InitForVulkan(SDL_Window* window) #if !SDL_HAS_VULKAN IM_ASSERT(0 && "Unsupported"); #endif + g_UseVulkan = true; return ImGui_ImplSDL2_Init(window, NULL); } @@ -467,7 +469,7 @@ static void ImGui_ImplSDL2_CreateWindow(ImGuiViewport* viewport) } Uint32 sdl_flags = 0; - sdl_flags |= use_opengl ? SDL_WINDOW_OPENGL : SDL_WINDOW_VULKAN; + sdl_flags |= use_opengl ? SDL_WINDOW_OPENGL : (g_UseVulkan ? SDL_WINDOW_VULKAN : 0); sdl_flags |= SDL_GetWindowFlags(g_Window) & SDL_WINDOW_ALLOW_HIGHDPI; sdl_flags |= SDL_WINDOW_HIDDEN; sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? SDL_WINDOW_BORDERLESS : 0; From 4b4be11fd2dabc7a72d749c22a5eeeb3647078bc Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 31 Jan 2020 14:57:47 +0100 Subject: [PATCH 581/828] Docking: Clarifying that DockNode!=NULL when DockIsActive. Comments. Cleanup. --- imgui.cpp | 6 ++++-- imgui_internal.h | 8 ++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 6a55cff7d3e3..ca718d741759 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5626,7 +5626,7 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar // Docking: Unhide tab bar (small triangle in the corner), drag from small triangle to quickly undock ImGuiDockNode* node = window->DockNode; - if (node && window->DockIsActive && node->IsHiddenTabBar() && !node->IsNoTabBar()) + if (window->DockIsActive && node->IsHiddenTabBar() && !node->IsNoTabBar()) { float unhide_sz_draw = ImFloor(g.FontSize * 0.70f); float unhide_sz_hit = ImFloor(g.FontSize * 0.55f); @@ -5850,6 +5850,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) { BeginDocked(window, p_open); flags = window->Flags; + if (window->DockIsActive) + IM_ASSERT(window->DockNode != NULL); // Docking currently override constraints g.NextWindowData.Flags &= ~ImGuiNextWindowDataFlags_HasSizeConstraint; @@ -13135,7 +13137,7 @@ bool ImGui::DockNodeCalcDropRectsAndTestMousePos(const ImRect& parent, ImGuiDir //off = ImVec2(ImFloor(parent.GetWidth() * 0.5f - GetFrameHeightWithSpacing() * 1.4f - hs_h), ImFloor(parent.GetHeight() * 0.5f - GetFrameHeightWithSpacing() * 1.4f - hs_h)); hs_w = ImFloor(hs_for_central_nodes * 1.50f); hs_h = ImFloor(hs_for_central_nodes * 0.80f); - off = ImVec2(ImFloor(parent.GetWidth() * 0.5f - GetFrameHeightWithSpacing() * 0.0f - hs_h), ImFloor(parent.GetHeight() * 0.5f - GetFrameHeightWithSpacing() * 0.0f - hs_h)); + off = ImVec2(ImFloor(parent.GetWidth() * 0.5f - hs_h), ImFloor(parent.GetHeight() * 0.5f - hs_h)); } else { diff --git a/imgui_internal.h b/imgui_internal.h index b17ebcc86c4e..4f21d6463943 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1691,14 +1691,14 @@ struct IMGUI_API ImGuiWindow int MemoryDrawListVtxCapacity; // Docking - ImGuiDockNode* DockNode; // Which node are we docked into + ImGuiDockNode* DockNode; // Which node are we docked into. Important: Prefer testing DockIsActive in many cases as this will still be set when the dock node is hidden. ImGuiDockNode* DockNodeAsHost; // Which node are we owning (for parent windows) - ImGuiID DockId; // Backup of last valid DockNode->Id, so single value remember their dock node id + ImGuiID DockId; // Backup of last valid DockNode->ID, so single window remember their dock node id even when they are not bound any more ImGuiItemStatusFlags DockTabItemStatusFlags; ImRect DockTabItemRect; short DockOrder; // Order of the last time the window was visible within its DockNode. This is used to reorder windows that are reappearing on the same frame. Same value between windows that were active and windows that were none are possible. - bool DockIsActive :1; // =~ (DockNode != NULL) && (DockNode->Windows.Size > 1) - bool DockTabIsVisible :1; // Is the window visible this frame? =~ is the corresponding tab selected? + bool DockIsActive :1; // When docking artifacts are actually visible. When this is set, DockNode is guaranteed to be != NULL. ~~ (DockNode != NULL) && (DockNode->Windows.Size > 1). + bool DockTabIsVisible :1; // Is our window visible this frame? ~~ is the corresponding tab selected? bool DockTabWantClose :1; public: From d4fc525614eada62c61b6ba965b08021647d674b Mon Sep 17 00:00:00 2001 From: omar Date: Sun, 2 Feb 2020 21:24:53 +0100 Subject: [PATCH 582/828] Nav, Focus: Fixed messed up Ctrl+Tab order with Docked windows. Amend d9bca0d8531b8c6fca08cf883781fc0776418222, fc41839cabb173b07897726abb9187061e3a44fa, fc42528f134a3aec1f6c34daec6f26390e7b29b5 for docking. --- imgui.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index f24d040db276..519ec41e44c2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6717,7 +6717,7 @@ void ImGui::FocusWindow(ImGuiWindow* window) // Move the root window to the top of the pile IM_ASSERT(window->RootWindow != NULL); - ImGuiWindow* focus_front_window = window->RootWindow; // NB: In docking branch this is window->RootWindowDockStop + ImGuiWindow* focus_front_window = window->RootWindowDockStop; ImGuiWindow* display_front_window = window->RootWindow; // Steal focus on active widgets @@ -6727,7 +6727,7 @@ void ImGui::FocusWindow(ImGuiWindow* window) // Bring to front BringWindowToFocusFront(focus_front_window); - if (((window->Flags | display_front_window->Flags) & ImGuiWindowFlags_NoBringToFrontOnFocus) == 0) + if (((window->Flags | focus_front_window->Flags | display_front_window->Flags) & ImGuiWindowFlags_NoBringToFrontOnFocus) == 0) BringWindowToDisplayFront(display_front_window); } @@ -9532,7 +9532,7 @@ static void ImGui::NavUpdateWindowing() if (start_windowing_with_gamepad || start_windowing_with_keyboard) if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1)) { - g.NavWindowingTarget = g.NavWindowingTargetAnim = window->RootWindow; // FIXME-DOCK: Will need to use RootWindowDockStop + g.NavWindowingTarget = g.NavWindowingTargetAnim = window->RootWindowDockStop; g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f; g.NavWindowingToggleLayer = start_windowing_with_keyboard ? false : true; g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_NavKeyboard : ImGuiInputSource_NavGamepad; From 365b6399815acf66c65a5afa5d76f7fcea9588d9 Mon Sep 17 00:00:00 2001 From: omar Date: Sun, 2 Feb 2020 22:17:04 +0100 Subject: [PATCH 583/828] Nav, Docking: Fixed failing to restore NavId when refocusing a child within a docked window. --- imgui.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 519ec41e44c2..6de7c77115b6 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -8928,11 +8928,11 @@ void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags mov // This way we could find the last focused window among our children. It would be much less confusing this way? static void ImGui::NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window) { - ImGuiWindow* parent_window = nav_window; - while (parent_window && (parent_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (parent_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0) - parent_window = parent_window->ParentWindow; - if (parent_window && parent_window != nav_window) - parent_window->NavLastChildNavWindow = nav_window; + ImGuiWindow* parent = nav_window; + while (parent && parent->RootWindowDockStop != parent && (parent->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (parent->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0) + parent = parent->ParentWindow; + if (parent && parent != nav_window) + parent->NavLastChildNavWindow = nav_window; } // Restore the last focused child. From 3bde375078db5fe15959f4ba6480fcf4013189d7 Mon Sep 17 00:00:00 2001 From: omar Date: Sun, 2 Feb 2020 22:40:56 +0100 Subject: [PATCH 584/828] Nav, Docking: Fixed failing to restore NavId when refocusing due to missing nav window. --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 6de7c77115b6..e0c882b52a55 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6746,7 +6746,7 @@ void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWind { // We may later decide to test for different NoXXXInputs based on the active navigation input (mouse vs nav) but that may feel more confusing to the user. ImGuiWindow* window = g.WindowsFocusOrder[i]; - if (window != ignore_window && window->WasActive && !(window->Flags & ImGuiWindowFlags_ChildWindow)) + if (window != ignore_window && window->WasActive && window->RootWindowDockStop == window) if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) { // FIXME-DOCK: This is failing (lagging by one frame) for docked windows. From 7e2d172ae5c8b53184bbf2982ceb1fefbb99ceba Mon Sep 17 00:00:00 2001 From: omar Date: Sun, 9 Feb 2020 17:31:18 +0100 Subject: [PATCH 585/828] Backends: GLFW, SDL: Platform monitors declared properly even if multi-viewport is not enabled. --- examples/imgui_impl_glfw.cpp | 91 ++++++++++++++++++----------------- examples/imgui_impl_glfw.h | 2 + examples/imgui_impl_sdl.cpp | 62 ++++++++++++------------ examples/imgui_impl_win32.cpp | 54 ++++++++++----------- imgui.cpp | 6 ++- 5 files changed, 112 insertions(+), 103 deletions(-) diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index 2d3431e94185..bcbee43655d7 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -83,11 +83,12 @@ static GLFWmousebuttonfun g_PrevUserCallbackMousebutton = NULL; static GLFWscrollfun g_PrevUserCallbackScroll = NULL; static GLFWkeyfun g_PrevUserCallbackKey = NULL; static GLFWcharfun g_PrevUserCallbackChar = NULL; +static GLFWmonitorfun g_PrevUserCallbackMonitor = NULL; // Forward Declarations +static void ImGui_ImplGlfw_UpdateMonitors(); static void ImGui_ImplGlfw_InitPlatformInterface(); static void ImGui_ImplGlfw_ShutdownPlatformInterface(); -static void ImGui_ImplGlfw_UpdateMonitors(); static const char* ImGui_ImplGlfw_GetClipboardText(void* user_data) { @@ -149,6 +150,11 @@ void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c) io.AddInputCharacter(c); } +void ImGui_ImplGlfw_MonitorCallback(GLFWmonitor*, int) +{ + g_WantUpdateMonitors = true; +} + static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, GlfwClientApi client_api) { g_Window = window; @@ -220,6 +226,7 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw g_PrevUserCallbackScroll = NULL; g_PrevUserCallbackKey = NULL; g_PrevUserCallbackChar = NULL; + g_PrevUserCallbackMonitor = NULL; if (install_callbacks) { g_InstalledCallbacks = true; @@ -227,8 +234,13 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw g_PrevUserCallbackScroll = glfwSetScrollCallback(window, ImGui_ImplGlfw_ScrollCallback); g_PrevUserCallbackKey = glfwSetKeyCallback(window, ImGui_ImplGlfw_KeyCallback); g_PrevUserCallbackChar = glfwSetCharCallback(window, ImGui_ImplGlfw_CharCallback); + g_PrevUserCallbackMonitor = glfwSetMonitorCallback(ImGui_ImplGlfw_MonitorCallback); } + // Update monitors the first time (note: monitor callback are broken in GLFW 3.2 and earlier, see github.com/glfw/glfw/issues/784) + ImGui_ImplGlfw_UpdateMonitors(); + glfwSetMonitorCallback(ImGui_ImplGlfw_MonitorCallback); + // Our mouse update function expect PlatformHandle to be filled for the main viewport ImGuiViewport* main_viewport = ImGui::GetMainViewport(); main_viewport->PlatformHandle = (void*)g_Window; @@ -405,6 +417,40 @@ static void ImGui_ImplGlfw_UpdateGamepads() io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; } +static void ImGui_ImplGlfw_UpdateMonitors() +{ + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + int monitors_count = 0; + GLFWmonitor** glfw_monitors = glfwGetMonitors(&monitors_count); + platform_io.Monitors.resize(0); + for (int n = 0; n < monitors_count; n++) + { + ImGuiPlatformMonitor monitor; + int x, y; + glfwGetMonitorPos(glfw_monitors[n], &x, &y); + const GLFWvidmode* vid_mode = glfwGetVideoMode(glfw_monitors[n]); +#if GLFW_HAS_MONITOR_WORK_AREA + monitor.MainPos = ImVec2((float)x, (float)y); + monitor.MainSize = ImVec2((float)vid_mode->width, (float)vid_mode->height); + int w, h; + glfwGetMonitorWorkarea(glfw_monitors[n], &x, &y, &w, &h); + monitor.WorkPos = ImVec2((float)x, (float)y);; + monitor.WorkSize = ImVec2((float)w, (float)h); +#else + monitor.MainPos = monitor.WorkPos = ImVec2((float)x, (float)y); + monitor.MainSize = monitor.WorkSize = ImVec2((float)vid_mode->width, (float)vid_mode->height); +#endif +#if GLFW_HAS_PER_MONITOR_DPI + // Warning: the validity of monitor DPI information on Windows depends on the application DPI awareness settings, which generally needs to be set in the manifest or at runtime. + float x_scale, y_scale; + glfwGetMonitorContentScale(glfw_monitors[n], &x_scale, &y_scale); + monitor.DpiScale = x_scale; +#endif + platform_io.Monitors.push_back(monitor); + } + g_WantUpdateMonitors = false; +} + void ImGui_ImplGlfw_NewFrame() { ImGuiIO& io = ImGui::GetIO(); @@ -746,45 +792,6 @@ static int ImGui_ImplGlfw_CreateVkSurface(ImGuiViewport* viewport, ImU64 vk_inst } #endif // GLFW_HAS_VULKAN -static void ImGui_ImplGlfw_UpdateMonitors() -{ - ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); - int monitors_count = 0; - GLFWmonitor** glfw_monitors = glfwGetMonitors(&monitors_count); - platform_io.Monitors.resize(0); - for (int n = 0; n < monitors_count; n++) - { - ImGuiPlatformMonitor monitor; - int x, y; - glfwGetMonitorPos(glfw_monitors[n], &x, &y); - const GLFWvidmode* vid_mode = glfwGetVideoMode(glfw_monitors[n]); -#if GLFW_HAS_MONITOR_WORK_AREA - monitor.MainPos = ImVec2((float)x, (float)y); - monitor.MainSize = ImVec2((float)vid_mode->width, (float)vid_mode->height); - int w, h; - glfwGetMonitorWorkarea(glfw_monitors[n], &x, &y, &w, &h); - monitor.WorkPos = ImVec2((float)x, (float)y);; - monitor.WorkSize = ImVec2((float)w, (float)h); -#else - monitor.MainPos = monitor.WorkPos = ImVec2((float)x, (float)y); - monitor.MainSize = monitor.WorkSize = ImVec2((float)vid_mode->width, (float)vid_mode->height); -#endif -#if GLFW_HAS_PER_MONITOR_DPI - // Warning: the validity of monitor DPI information on Windows depends on the application DPI awareness settings, which generally needs to be set in the manifest or at runtime. - float x_scale, y_scale; - glfwGetMonitorContentScale(glfw_monitors[n], &x_scale, &y_scale); - monitor.DpiScale = x_scale; -#endif - platform_io.Monitors.push_back(monitor); - } - g_WantUpdateMonitors = false; -} - -static void ImGui_ImplGlfw_MonitorCallback(GLFWmonitor*, int) -{ - g_WantUpdateMonitors = true; -} - static void ImGui_ImplGlfw_InitPlatformInterface() { // Register platform interface (will be coupled with a renderer interface) @@ -812,10 +819,6 @@ static void ImGui_ImplGlfw_InitPlatformInterface() platform_io.Platform_SetImeInputPos = ImGui_ImplWin32_SetImeInputPos; #endif - // Note: monitor callback are broken GLFW 3.2 and earlier (see github.com/glfw/glfw/issues/784) - ImGui_ImplGlfw_UpdateMonitors(); - glfwSetMonitorCallback(ImGui_ImplGlfw_MonitorCallback); - // Register main window handle (which is owned by the main application, not by us) // This is mostly for simplicity and consistency, so that our code (e.g. mouse handling etc.) can use same logic for main and secondary viewports. ImGuiViewport* main_viewport = ImGui::GetMainViewport(); diff --git a/examples/imgui_impl_glfw.h b/examples/imgui_impl_glfw.h index dcab351c1fe7..07a3456865de 100644 --- a/examples/imgui_impl_glfw.h +++ b/examples/imgui_impl_glfw.h @@ -20,6 +20,7 @@ #pragma once struct GLFWwindow; +struct GLFWmonitor; IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow* window, bool install_callbacks); IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForVulkan(GLFWwindow* window, bool install_callbacks); @@ -32,3 +33,4 @@ IMGUI_IMPL_API void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, i IMGUI_IMPL_API void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset); IMGUI_IMPL_API void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods); IMGUI_IMPL_API void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c); +IMGUI_IMPL_API void ImGui_ImplGlfw_MonitorCallback(GLFWmonitor* monitor, int event); diff --git a/examples/imgui_impl_sdl.cpp b/examples/imgui_impl_sdl.cpp index 8d52a2125830..ab2ca5ac4c3b 100644 --- a/examples/imgui_impl_sdl.cpp +++ b/examples/imgui_impl_sdl.cpp @@ -77,6 +77,7 @@ static bool g_MouseCanUseGlobalState = true; static bool g_UseVulkan = false; // Forward Declarations +static void ImGui_ImplSDL2_UpdateMonitors(); static void ImGui_ImplSDL2_InitPlatformInterface(SDL_Window* window, void* sdl_gl_context); static void ImGui_ImplSDL2_ShutdownPlatformInterface(); @@ -222,6 +223,9 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window, void* sdl_gl_context) main_viewport->PlatformHandleRaw = info.info.win.window; #endif + // Update monitors + ImGui_ImplSDL2_UpdateMonitors(); + // We need SDL_CaptureMouse(), SDL_GetGlobalMouseState() from SDL 2.0.4+ to support multiple viewports. // We left the call to ImGui_ImplSDL2_InitPlatformInterface() outside of #ifdef to avoid unused-function warnings. if ((io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) && (io.BackendFlags & ImGuiBackendFlags_PlatformHasViewports)) @@ -405,6 +409,34 @@ static void ImGui_ImplSDL2_UpdateGamepads() #undef MAP_ANALOG } +// FIXME-PLATFORM: SDL doesn't have an event to notify the application of display/monitor changes +static void ImGui_ImplSDL2_UpdateMonitors() +{ + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.Monitors.resize(0); + int display_count = SDL_GetNumVideoDisplays(); + for (int n = 0; n < display_count; n++) + { + // Warning: the validity of monitor DPI information on Windows depends on the application DPI awareness settings, which generally needs to be set in the manifest or at runtime. + ImGuiPlatformMonitor monitor; + SDL_Rect r; + SDL_GetDisplayBounds(n, &r); + monitor.MainPos = monitor.WorkPos = ImVec2((float)r.x, (float)r.y); + monitor.MainSize = monitor.WorkSize = ImVec2((float)r.w, (float)r.h); +#if SDL_HAS_USABLE_DISPLAY_BOUNDS + SDL_GetDisplayUsableBounds(n, &r); + monitor.WorkPos = ImVec2((float)r.x, (float)r.y); + monitor.WorkSize = ImVec2((float)r.w, (float)r.h); +#endif +#if SDL_HAS_PER_MONITOR_DPI + float dpi = 0.0f; + if (!SDL_GetDisplayDPI(n, &dpi, NULL, NULL)) + monitor.DpiScale = dpi / 96.0f; +#endif + platform_io.Monitors.push_back(monitor); + } +} + void ImGui_ImplSDL2_NewFrame(SDL_Window* window) { ImGuiIO& io = ImGui::GetIO(); @@ -632,34 +664,6 @@ static int ImGui_ImplSDL2_CreateVkSurface(ImGuiViewport* viewport, ImU64 vk_inst } #endif // SDL_HAS_VULKAN -// FIXME-PLATFORM: SDL doesn't have an event to notify the application of display/monitor changes -static void ImGui_ImplSDL2_UpdateMonitors() -{ - ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); - platform_io.Monitors.resize(0); - int display_count = SDL_GetNumVideoDisplays(); - for (int n = 0; n < display_count; n++) - { - // Warning: the validity of monitor DPI information on Windows depends on the application DPI awareness settings, which generally needs to be set in the manifest or at runtime. - ImGuiPlatformMonitor monitor; - SDL_Rect r; - SDL_GetDisplayBounds(n, &r); - monitor.MainPos = monitor.WorkPos = ImVec2((float)r.x, (float)r.y); - monitor.MainSize = monitor.WorkSize = ImVec2((float)r.w, (float)r.h); -#if SDL_HAS_USABLE_DISPLAY_BOUNDS - SDL_GetDisplayUsableBounds(n, &r); - monitor.WorkPos = ImVec2((float)r.x, (float)r.y); - monitor.WorkSize = ImVec2((float)r.w, (float)r.h); -#endif -#if SDL_HAS_PER_MONITOR_DPI - float dpi = 0.0f; - if (!SDL_GetDisplayDPI(n, &dpi, NULL, NULL)) - monitor.DpiScale = dpi / 96.0f; -#endif - platform_io.Monitors.push_back(monitor); - } -} - static void ImGui_ImplSDL2_InitPlatformInterface(SDL_Window* window, void* sdl_gl_context) { // Register platform interface (will be coupled with a renderer interface) @@ -689,8 +693,6 @@ static void ImGui_ImplSDL2_InitPlatformInterface(SDL_Window* window, void* sdl_g SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1"); #endif - ImGui_ImplSDL2_UpdateMonitors(); - // Register main window handle (which is owned by the main application, not by us) // This is mostly for simplicity and consistency, so that our code (e.g. mouse handling etc.) can use same logic for main and secondary viewports. ImGuiViewport* main_viewport = ImGui::GetMainViewport(); diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index cc190093811a..7a6077997c9f 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -263,6 +263,33 @@ static void ImGui_ImplWin32_UpdateGamepads() #endif // #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD } +static BOOL CALLBACK ImGui_ImplWin32_UpdateMonitors_EnumFunc(HMONITOR monitor, HDC, LPRECT, LPARAM) +{ + MONITORINFO info = { 0 }; + info.cbSize = sizeof(MONITORINFO); + if (!::GetMonitorInfo(monitor, &info)) + return TRUE; + ImGuiPlatformMonitor imgui_monitor; + imgui_monitor.MainPos = ImVec2((float)info.rcMonitor.left, (float)info.rcMonitor.top); + imgui_monitor.MainSize = ImVec2((float)(info.rcMonitor.right - info.rcMonitor.left), (float)(info.rcMonitor.bottom - info.rcMonitor.top)); + imgui_monitor.WorkPos = ImVec2((float)info.rcWork.left, (float)info.rcWork.top); + imgui_monitor.WorkSize = ImVec2((float)(info.rcWork.right - info.rcWork.left), (float)(info.rcWork.bottom - info.rcWork.top)); + imgui_monitor.DpiScale = ImGui_ImplWin32_GetDpiScaleForMonitor(monitor); + ImGuiPlatformIO& io = ImGui::GetPlatformIO(); + if (info.dwFlags & MONITORINFOF_PRIMARY) + io.Monitors.push_front(imgui_monitor); + else + io.Monitors.push_back(imgui_monitor); + return TRUE; +} + +static void ImGui_ImplWin32_UpdateMonitors() +{ + ImGui::GetPlatformIO().Monitors.resize(0); + ::EnumDisplayMonitors(NULL, NULL, ImGui_ImplWin32_UpdateMonitors_EnumFunc, NULL); + g_WantUpdateMonitors = false; +} + void ImGui_ImplWin32_NewFrame() { ImGuiIO& io = ImGui::GetIO(); @@ -764,33 +791,6 @@ static LRESULT CALLBACK ImGui_ImplWin32_WndProcHandler_PlatformWindow(HWND hWnd, return DefWindowProc(hWnd, msg, wParam, lParam); } -static BOOL CALLBACK ImGui_ImplWin32_UpdateMonitors_EnumFunc(HMONITOR monitor, HDC, LPRECT, LPARAM) -{ - MONITORINFO info = { 0 }; - info.cbSize = sizeof(MONITORINFO); - if (!::GetMonitorInfo(monitor, &info)) - return TRUE; - ImGuiPlatformMonitor imgui_monitor; - imgui_monitor.MainPos = ImVec2((float)info.rcMonitor.left, (float)info.rcMonitor.top); - imgui_monitor.MainSize = ImVec2((float)(info.rcMonitor.right - info.rcMonitor.left), (float)(info.rcMonitor.bottom - info.rcMonitor.top)); - imgui_monitor.WorkPos = ImVec2((float)info.rcWork.left, (float)info.rcWork.top); - imgui_monitor.WorkSize = ImVec2((float)(info.rcWork.right - info.rcWork.left), (float)(info.rcWork.bottom - info.rcWork.top)); - imgui_monitor.DpiScale = ImGui_ImplWin32_GetDpiScaleForMonitor(monitor); - ImGuiPlatformIO& io = ImGui::GetPlatformIO(); - if (info.dwFlags & MONITORINFOF_PRIMARY) - io.Monitors.push_front(imgui_monitor); - else - io.Monitors.push_back(imgui_monitor); - return TRUE; -} - -static void ImGui_ImplWin32_UpdateMonitors() -{ - ImGui::GetPlatformIO().Monitors.resize(0); - ::EnumDisplayMonitors(NULL, NULL, ImGui_ImplWin32_UpdateMonitors_EnumFunc, NULL); - g_WantUpdateMonitors = false; -} - static void ImGui_ImplWin32_InitPlatformInterface() { WNDCLASSEX wcex; diff --git a/imgui.cpp b/imgui.cpp index e0c882b52a55..2191d378f06e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -15329,9 +15329,11 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::Indent(ImGui::GetTreeNodeToLabelSpacing()); ImGui::ShowViewportThumbnails(); ImGui::Unindent(ImGui::GetTreeNodeToLabelSpacing()); - if (g.PlatformIO.Monitors.Size > 0 && ImGui::TreeNode("Monitors", "Monitors (%d)", g.PlatformIO.Monitors.Size)) + bool open = ImGui::TreeNode("Monitors", "Monitors (%d)", g.PlatformIO.Monitors.Size); + ImGui::SameLine(); + MetricsHelpMarker("Dear ImGui uses monitor data:\n- to query DPI settings on a per monitor basis\n- to position popup/tooltips so they don't straddle monitors."); + if (open) { - ImGui::TextWrapped("(When viewports are enabled, imgui needs uses monitor data to position popup/tooltips so they don't straddle monitors.)"); for (int i = 0; i < g.PlatformIO.Monitors.Size; i++) { const ImGuiPlatformMonitor& mon = g.PlatformIO.Monitors[i]; From 3c80d57dc7f7fd663e572c4de5388c427e537d5a Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 19 Feb 2020 23:40:08 +0100 Subject: [PATCH 586/828] Viewports, Window: Avoid manually clipping resize grips and borders, which messes up with automation ability to locate the items. Also simpler and more standard. Amend d8f9f6ba2abb6cfc00f704b887264ac9472cf9f7 and 377f7300546e2a43e225d38c724a7f28da0ed6f2. --- imgui.cpp | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 706d03d4a2be..54fe7dc64eca 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5383,14 +5383,14 @@ static bool ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au ImVec2 pos_target(FLT_MAX, FLT_MAX); ImVec2 size_target(FLT_MAX, FLT_MAX); - // Clip mouse interaction rectangles within the viewport (in practice the narrowing is going to happen most of the time). + // Clip mouse interaction rectangles within the viewport rectangle (in practice the narrowing is going to happen most of the time). // - Not narrowing would mostly benefit the situation where OS windows _without_ decoration have a threshold for hovering when outside their limits. // This is however not the case with current back-ends under Win32, but a custom borderless window implementation would benefit from it. // - When decoration are enabled we typically benefit from that distance, but then our resize elements would be conflicting with OS resize elements, so we also narrow. // - Note that we are unable to tell if the platform setup allows hovering with a distance threshold (on Win32, decorated window have such threshold). - ImRect clip_viewport_rect(-FLT_MAX, -FLT_MAX, +FLT_MAX, +FLT_MAX); - if (!(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport) || (g.IO.MouseHoveredViewport != window->ViewportId) || !(window->Viewport->Flags & ImGuiViewportFlags_NoDecoration)) - clip_viewport_rect = window->Viewport->GetRect(); + const bool clip_with_viewport_rect = !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport) || (g.IO.MouseHoveredViewport != window->ViewportId) || !(window->Viewport->Flags & ImGuiViewportFlags_NoDecoration); + if (clip_with_viewport_rect) + PushClipRect(window->Viewport->Pos, window->Viewport->Pos + window->Viewport->Size, true); // Won't incur a draw command as we are not drawing here. // Resize grips and borders are on layer 1 window->DC.NavLayerCurrent = ImGuiNavLayer_Menu; @@ -5407,12 +5407,8 @@ static bool ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au ImRect resize_rect(corner - grip.InnerDir * grip_hover_outer_size, corner + grip.InnerDir * grip_hover_inner_size); if (resize_rect.Min.x > resize_rect.Max.x) ImSwap(resize_rect.Min.x, resize_rect.Max.x); if (resize_rect.Min.y > resize_rect.Max.y) ImSwap(resize_rect.Min.y, resize_rect.Max.y); - ImVec2 resize_rect_ref_min = resize_rect.Min; - resize_rect.ClipWith(clip_viewport_rect); bool hovered, held; ButtonBehavior(resize_rect, window->GetID(resize_grip_n), &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus); - if (held && g.ActiveIdIsJustActivated) - g.ActiveIdClickOffset = g.IO.MousePos - resize_rect_ref_min; // Override our reference click offset as viewport clipping may be moved it. //GetForegroundDrawList(window)->AddRect(resize_rect.Min, resize_rect.Max, IM_COL32(255, 255, 0, 255)); if (hovered || held) g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE; @@ -5438,11 +5434,7 @@ static bool ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au { bool hovered, held; ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_inner_size, WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); - ImVec2 border_rect_ref_min = border_rect.Min; - border_rect.ClipWith(clip_viewport_rect); ButtonBehavior(border_rect, window->GetID(border_n + 4), &hovered, &held, ImGuiButtonFlags_FlattenChildren); - if (held && g.ActiveIdIsJustActivated) - g.ActiveIdClickOffset = g.IO.MousePos - border_rect_ref_min; // Override our reference click offset as viewport clipping may be moved it. //GetForegroundDrawLists(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255)); if ((hovered && g.HoveredIdTimer > WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER) || held) { @@ -5462,6 +5454,8 @@ static bool ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au } } PopID(); + if (clip_with_viewport_rect) + PopClipRect(); // Restore nav layer window->DC.NavLayerCurrent = ImGuiNavLayer_Main; From 7d80a8f4f502557685ba1f4ed0e4603e86745d40 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 20 Feb 2020 12:21:11 +0100 Subject: [PATCH 587/828] Backends: Fix ImGui_ImplSDL2_InitForMetal() in docking branch. --- examples/imgui_impl_sdl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/imgui_impl_sdl.cpp b/examples/imgui_impl_sdl.cpp index 138a1d3a6c55..08cf99246edb 100644 --- a/examples/imgui_impl_sdl.cpp +++ b/examples/imgui_impl_sdl.cpp @@ -259,7 +259,7 @@ bool ImGui_ImplSDL2_InitForD3D(SDL_Window* window) bool ImGui_ImplSDL2_InitForMetal(SDL_Window* window) { - return ImGui_ImplSDL2_Init(window); + return ImGui_ImplSDL2_Init(window, NULL); } void ImGui_ImplSDL2_Shutdown() From 3ff13edad91b58a45ebdf73e56461903a15631d6 Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Mon, 24 Feb 2020 15:14:04 +0200 Subject: [PATCH 588/828] Viewports: Improve menu positioning in multi-monitor setups. This change uses mouse position for initial positioning of popup menus. It ensures that menu appears in expected location on monitor mouse is currently hovering. This change fixes incorrect menu positioning (permanent or on the first frame) when menu item spans multiple monitors. --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 54fe7dc64eca..3d98a6055540 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10986,7 +10986,7 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) ImVec2 mouse_ref = (flags & ImGuiWindowFlags_Tooltip) ? g.IO.MousePos : g.BeginPopupStack.back().OpenMousePos; bool use_mouse_ref = (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow); bool mouse_valid = IsMousePosValid(&mouse_ref); - if ((window->Appearing || (flags & ImGuiWindowFlags_Tooltip)) && (!use_mouse_ref || mouse_valid)) + if ((window->Appearing || (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_ChildMenu))) && (!use_mouse_ref || mouse_valid)) window->ViewportAllowPlatformMonitorExtend = FindPlatformMonitorForPos((use_mouse_ref && mouse_valid) ? mouse_ref : NavCalcPreferredRefPos()); else window->ViewportAllowPlatformMonitorExtend = window->Viewport->PlatformMonitor; From 871727dd2f4446bf1951a8555dc6e0001c35c8ae Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 25 Feb 2020 15:51:57 +0100 Subject: [PATCH 589/828] Viewports: Software mouse cursor is also scaled by current DpiScale. (amend #939) --- imgui.cpp | 19 ++++++++++--------- imgui.h | 2 +- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 3d98a6055540..1f2729796ebd 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4507,14 +4507,9 @@ void ImGui::Render() if (windows_to_render_top_most[n] && IsWindowActiveAndVisible(windows_to_render_top_most[n])) // NavWindowingTarget is always temporarily displayed as the top-most window AddRootWindowToDrawData(windows_to_render_top_most[n]); - // Draw software mouse cursor if requested - ImRect mouse_cursor_rect; + ImVec2 mouse_cursor_offset, mouse_cursor_size, mouse_cursor_uv[4]; if (g.IO.MouseDrawCursor && g.MouseCursor != ImGuiMouseCursor_None) - { - ImVec2 offset, size, uv[4]; - if (g.IO.Fonts->GetMouseCursorTexData(g.MouseCursor, &offset, &size, &uv[0], &uv[2])) - mouse_cursor_rect = ImRect(g.IO.MousePos, g.IO.MousePos + ImVec2(size.x + 2, size.y + 2) * g.Style.MouseCursorScale); - } + g.IO.Fonts->GetMouseCursorTexData(g.MouseCursor, &mouse_cursor_offset, &mouse_cursor_size, &mouse_cursor_uv[0], &mouse_cursor_uv[2]); // Setup ImDrawData structures for end-user g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = 0; @@ -4523,8 +4518,14 @@ void ImGui::Render() ImGuiViewportP* viewport = g.Viewports[n]; viewport->DrawDataBuilder.FlattenIntoSingleLayer(); - if (viewport->GetRect().Overlaps(mouse_cursor_rect)) - RenderMouseCursor(GetForegroundDrawList(viewport), g.IO.MousePos, g.Style.MouseCursorScale, g.MouseCursor, IM_COL32_WHITE, IM_COL32_BLACK, IM_COL32(0, 0, 0, 48)); + // Draw software mouse cursor if requested by io.MouseDrawCursor flag + // (note we scale cursor by current viewport/monitor, however Windows 10 for its own hardware cursor seems to be using a different scale factor) + if (mouse_cursor_size.x > 0.0f && mouse_cursor_size.y > 0.0f) + { + float scale = g.Style.MouseCursorScale * viewport->DpiScale; + if (viewport->GetRect().Overlaps(ImRect(g.IO.MousePos, g.IO.MousePos + ImVec2(mouse_cursor_size.x + 2, mouse_cursor_size.y + 2) * scale))) + RenderMouseCursor(GetForegroundDrawList(viewport), g.IO.MousePos, scale, g.MouseCursor, IM_COL32_WHITE, IM_COL32_BLACK, IM_COL32(0, 0, 0, 48)); + } // Add foreground ImDrawList (for each active viewport) if (viewport->DrawLists[1] != NULL) diff --git a/imgui.h b/imgui.h index 50d1a561af9a..8e790dc2cb27 100644 --- a/imgui.h +++ b/imgui.h @@ -1432,7 +1432,7 @@ struct ImGuiStyle ImVec2 SelectableTextAlign; // Alignment of selectable text when selectable is larger than text. Defaults to (0.0f, 0.0f) (top-left aligned). ImVec2 DisplayWindowPadding; // Window position are clamped to be visible within the display area or monitors by at least this amount. Only applies to regular windows. ImVec2 DisplaySafeAreaPadding; // If you cannot see the edges of your screen (e.g. on a TV) increase the safe area padding. Apply to popups/tooltips as well regular windows. NB: Prefer configuring your TV sets correctly! - float MouseCursorScale; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later. + float MouseCursorScale; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). We apply per-monitor DPI scaling over this scale. May be removed later. bool AntiAliasedLines; // Enable anti-aliasing on lines/borders. Disable if you are really tight on CPU/GPU. bool AntiAliasedFill; // Enable anti-aliasing on filled shapes (rounded rectangles, circles, etc.) float CurveTessellationTol; // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality. From f032ad6b1fe58d531d2336c0c8d03e6d6ea45669 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 26 Feb 2020 20:18:06 +0100 Subject: [PATCH 590/828] Viewports: Renamed GetRect() to GetMainRect() in prevision for work rect. Comments --- imgui.cpp | 28 ++++++++++++++-------------- imgui.h | 14 ++++++++------ imgui_internal.h | 9 ++++----- 3 files changed, 26 insertions(+), 25 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 1f2729796ebd..a3d5c8f139f3 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3851,7 +3851,7 @@ void ImGui::NewFrame() IM_ASSERT(g.Font->IsLoaded()); ImRect virtual_space(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX); for (int n = 0; n < g.Viewports.Size; n++) - virtual_space.Add(g.Viewports[n]->GetRect()); + virtual_space.Add(g.Viewports[n]->GetMainRect()); g.DrawListSharedData.ClipRectFullscreen = ImVec4(virtual_space.Min.x, virtual_space.Min.y, virtual_space.Max.x, virtual_space.Max.y); g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol; g.DrawListSharedData.SetCircleSegmentMaxError(g.Style.CircleSegmentMaxError); @@ -4376,7 +4376,7 @@ static void ImGui::EndFrameDrawDimmedBackgrounds() float rounding = ImMax(window->WindowRounding, g.Style.WindowRounding); ImRect bb = window->Rect(); bb.Expand(g.FontSize); - if (bb.Contains(window->Viewport->GetRect())) // If a window fits the entire viewport, adjust its highlight inward + if (bb.Contains(window->Viewport->GetMainRect())) // If a window fits the entire viewport, adjust its highlight inward { bb.Expand(-g.FontSize - 1.0f); rounding = window->WindowRounding; @@ -4523,7 +4523,7 @@ void ImGui::Render() if (mouse_cursor_size.x > 0.0f && mouse_cursor_size.y > 0.0f) { float scale = g.Style.MouseCursorScale * viewport->DpiScale; - if (viewport->GetRect().Overlaps(ImRect(g.IO.MousePos, g.IO.MousePos + ImVec2(mouse_cursor_size.x + 2, mouse_cursor_size.y + 2) * scale))) + if (viewport->GetMainRect().Overlaps(ImRect(g.IO.MousePos, g.IO.MousePos + ImVec2(mouse_cursor_size.x + 2, mouse_cursor_size.y + 2) * scale))) RenderMouseCursor(GetForegroundDrawList(viewport), g.IO.MousePos, scale, g.MouseCursor, IM_COL32_WHITE, IM_COL32_BLACK, IM_COL32(0, 0, 0, 48)); } @@ -4648,7 +4648,7 @@ bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool c const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding); if (!rect_for_touch.Contains(g.IO.MousePos)) return false; - if (!g.MouseViewport->GetRect().Overlaps(rect_clipped)) + if (!g.MouseViewport->GetMainRect().Overlaps(rect_clipped)) return false; return true; } @@ -6100,7 +6100,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Late create viewport if we don't fit within our current host viewport. if (window->ViewportAllowPlatformMonitorExtend >= 0 && !window->ViewportOwned && !(window->Viewport->Flags & ImGuiViewportFlags_Minimized)) - if (!window->Viewport->GetRect().Contains(window->Rect())) + if (!window->Viewport->GetMainRect().Contains(window->Rect())) { // This is based on the assumption that the DPI will be known ahead (same as the DPI of the selection done in UpdateSelectWindowViewport) //ImGuiViewport* old_viewport = window->Viewport; @@ -6182,7 +6182,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Clamp position/size so window stays visible within its viewport or monitor // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing. - ImRect viewport_rect = window->Viewport->GetRect(); + ImRect viewport_rect = window->Viewport->GetMainRect(); if (!window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) { ImVec2 clamp_padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding); @@ -6242,7 +6242,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->Viewport->Pos = window->Pos; if (!window->Viewport->PlatformRequestResize) window->Viewport->Size = window->Size; - viewport_rect = window->Viewport->GetRect(); + viewport_rect = window->Viewport->GetMainRect(); } // Save last known viewport position within the window itself (so it can be saved in .ini file and restored) @@ -8374,7 +8374,7 @@ bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags fla if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) == 0) { ImGuiViewportP* viewport = window->WasActive ? window->Viewport : (ImGuiViewportP*)GetMainViewport(); // FIXME-VIEWPORT: What may be our reference viewport? - SetNextWindowPos(viewport->GetCenter(), ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); + SetNextWindowPos(viewport->GetMainRect().GetCenter(), ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); } flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoDocking; @@ -9009,7 +9009,7 @@ static ImVec2 ImGui::NavCalcPreferredRefPos() // When navigation is active and mouse is disabled, decide on an arbitrary position around the bottom left of the currently navigated item. const ImRect& rect_rel = g.NavWindow->NavRectRel[g.NavLayer]; ImVec2 pos = g.NavWindow->Pos + ImVec2(rect_rel.Min.x + ImMin(g.Style.FramePadding.x * 4, rect_rel.GetWidth()), rect_rel.Max.y - ImMin(g.Style.FramePadding.y, rect_rel.GetHeight())); - ImRect visible_rect = g.NavWindow->Viewport->GetRect(); + ImRect visible_rect = g.NavWindow->Viewport->GetMainRect(); return ImFloor(ImClamp(pos, visible_rect.Min, visible_rect.Max)); // ImFloor() is important because non-integer mouse position application in back-end might be lossy and result in undesirable non-zero delta. } } @@ -10557,7 +10557,7 @@ static bool ImGui::UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImG ImGuiContext& g = *GImGui; if (!(viewport->Flags & (ImGuiViewportFlags_CanHostOtherWindows | ImGuiViewportFlags_Minimized)) || window->Viewport == viewport) return false; - if (!viewport->GetRect().Contains(window->Rect())) + if (!viewport->GetMainRect().Contains(window->Rect())) return false; if (GetWindowAlwaysWantOwnViewport(window)) return false; @@ -10568,7 +10568,7 @@ static bool ImGui::UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImG if (window_behind == window) break; if (window_behind->WasActive && window_behind->ViewportOwned && !(window_behind->Flags & ImGuiWindowFlags_ChildWindow)) - if (window_behind->Viewport->GetRect().Overlaps(window->Rect())) + if (window_behind->Viewport->GetMainRect().Overlaps(window->Rect())) return false; } @@ -10636,7 +10636,7 @@ static ImGuiViewportP* FindHoveredViewportFromPlatformWindowStack(const ImVec2 m for (int n = 0; n < g.Viewports.Size; n++) { ImGuiViewportP* viewport = g.Viewports[n]; - if (!(viewport->Flags & (ImGuiViewportFlags_NoInputs | ImGuiViewportFlags_Minimized)) && viewport->GetRect().Contains(mouse_platform_pos)) + if (!(viewport->Flags & (ImGuiViewportFlags_NoInputs | ImGuiViewportFlags_Minimized)) && viewport->GetMainRect().Contains(mouse_platform_pos)) if (best_candidate == NULL || best_candidate->LastFrontMostStampCount < viewport->LastFrontMostStampCount) best_candidate = viewport; } @@ -11231,7 +11231,7 @@ static int ImGui::FindPlatformMonitorForRect(const ImRect& rect) // Update monitor from viewport rectangle (we'll use this info to clamp windows and save windows lost in a removed monitor) static void ImGui::UpdateViewportPlatformMonitor(ImGuiViewportP* viewport) { - viewport->PlatformMonitor = (short)FindPlatformMonitorForRect(viewport->GetRect()); + viewport->PlatformMonitor = (short)FindPlatformMonitorForRect(viewport->GetMainRect()); } void ImGui::DestroyPlatformWindow(ImGuiViewportP* viewport) @@ -14957,7 +14957,7 @@ void ImGui::ShowViewportThumbnails() //for (int n = 0; n < g.PlatformIO.Monitors.Size; n++) // bb_full.Add(GetPlatformMonitorMainRect(g.PlatformIO.Monitors[n])); for (int n = 0; n < g.Viewports.Size; n++) - bb_full.Add(g.Viewports[n]->GetRect()); + bb_full.Add(g.Viewports[n]->GetMainRect()); ImVec2 p = window->DC.CursorPos; ImVec2 off = p - bb_full.Min * SCALE; //for (int n = 0; n < g.PlatformIO.Monitors.Size; n++) diff --git a/imgui.h b/imgui.h index 8e790dc2cb27..59291d1108ca 100644 --- a/imgui.h +++ b/imgui.h @@ -716,7 +716,7 @@ namespace ImGui IMGUI_API void ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b); // Inputs Utilities: Keyboard - // - For 'int user_key_index' you can use your own indices/enums according to how your backend/engine stored them in io.KeysDown[]. + // - For 'int user_key_index' you can use your own indices/enums according to how your back-end/engine stored them in io.KeysDown[]. // - We don't know the meaning of those value. You can use GetKeyIndex() to map a ImGuiKey_ value into the user index. IMGUI_API int GetKeyIndex(ImGuiKey imgui_key); // map ImGuiKey_* values into user's key index. == io.KeyMap[key] IMGUI_API bool IsKeyDown(int user_key_index); // is key being held. == io.KeysDown[user_key_index]. @@ -2479,15 +2479,17 @@ struct ImGuiViewport ImDrawData* DrawData; // The ImDrawData corresponding to this viewport. Valid after Render() and until the next call to NewFrame(). ImGuiID ParentViewportId; // (Advanced) 0: no parent. Instruct the platform back-end to setup a parent/child relationship between platform windows. - void* RendererUserData; // void* to hold custom data structure for the renderer (e.g. swap chain, frame-buffers etc.). If somehow everything you need can fit in the void* PlatformHandle field you may ignore this. - void* PlatformUserData; // void* to hold custom data structure for the OS / platform (e.g. windowing info, render context). If somehow everything you need can fit in the void* PlatformHandle field you may ignore this. + // Our design separate the Renderer and Platform back-ends to facilitate combining default back-ends with each others. + // When our create your own back-end for a custom engine, it is possible that both Renderer and Platform will be handled by the same system and you may not need to use all the UserData/Handle fields. + void* RendererUserData; // void* to hold custom data structure for the renderer (e.g. swap chain, framebuffers etc.). + void* PlatformUserData; // void* to hold custom data structure for the OS / platform (e.g. windowing info, render context). void* PlatformHandle; // void* for FindViewportByPlatformHandle(). (e.g. suggested to use natural platform handle such as HWND, GLFWWindow*, SDL_Window*) - void* PlatformHandleRaw; // void* to hold low-level, platform-native window handle (e.g. the HWND) when using an abstraction layer like GLFW or SDL (where PlatformHandle would be a SDL_Window*) - bool PlatformRequestClose; // Platform window requested closure (e.g. window was moved by the OS / host window manager, e.g. pressing ALT-F4) + void* PlatformHandleRaw; // void* to hold lower-level, platform-native window handle (e.g. the HWND) when using an abstraction layer like GLFW or SDL (where PlatformHandle would be a SDL_Window*) bool PlatformRequestMove; // Platform window requested move (e.g. window was moved by the OS / host window manager, authoritative position will be OS window position) bool PlatformRequestResize; // Platform window requested resize (e.g. window was resized by the OS / host window manager, authoritative size will be OS window size) + bool PlatformRequestClose; // Platform window requested closure (e.g. window was moved by the OS / host window manager, e.g. pressing ALT-F4) - ImGuiViewport() { ID = 0; Flags = 0; DpiScale = 0.0f; DrawData = NULL; ParentViewportId = 0; RendererUserData = PlatformUserData = PlatformHandle = PlatformHandleRaw = NULL; PlatformRequestClose = PlatformRequestMove = PlatformRequestResize = false; } + ImGuiViewport() { ID = 0; Flags = 0; DpiScale = 0.0f; DrawData = NULL; ParentViewportId = 0; RendererUserData = PlatformUserData = PlatformHandle = PlatformHandleRaw = NULL; PlatformRequestMove = PlatformRequestResize = PlatformRequestClose = false; } ~ImGuiViewport() { IM_ASSERT(PlatformUserData == NULL && RendererUserData == NULL); } }; diff --git a/imgui_internal.h b/imgui_internal.h index 3ffa83eb177c..56d6cf9d6f4a 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -935,11 +935,10 @@ struct ImGuiViewportP : public ImGuiViewport ImVec2 LastPlatformSize; ImVec2 LastRendererSize; - ImGuiViewportP() { Idx = -1; LastFrameActive = LastFrameDrawLists[0] = LastFrameDrawLists[1] = LastFrontMostStampCount = -1; LastNameHash = 0; Alpha = LastAlpha = 1.0f; PlatformMonitor = -1; PlatformWindowCreated = false; Window = NULL; DrawLists[0] = DrawLists[1] = NULL; LastPlatformPos = LastPlatformSize = LastRendererSize = ImVec2(FLT_MAX, FLT_MAX); } - ~ImGuiViewportP() { if (DrawLists[0]) IM_DELETE(DrawLists[0]); if (DrawLists[1]) IM_DELETE(DrawLists[1]); } - ImRect GetRect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } - ImVec2 GetCenter() const { return ImVec2(Pos.x + Size.x * 0.5f, Pos.y + Size.y * 0.5f); } - void ClearRequestFlags() { PlatformRequestClose = PlatformRequestMove = PlatformRequestResize = false; } + ImGuiViewportP() { Idx = -1; LastFrameActive = LastFrameDrawLists[0] = LastFrameDrawLists[1] = LastFrontMostStampCount = -1; LastNameHash = 0; Alpha = LastAlpha = 1.0f; PlatformMonitor = -1; PlatformWindowCreated = false; Window = NULL; DrawLists[0] = DrawLists[1] = NULL; LastPlatformPos = LastPlatformSize = LastRendererSize = ImVec2(FLT_MAX, FLT_MAX); } + ~ImGuiViewportP() { if (DrawLists[0]) IM_DELETE(DrawLists[0]); if (DrawLists[1]) IM_DELETE(DrawLists[1]); } + ImRect GetMainRect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } + void ClearRequestFlags() { PlatformRequestClose = PlatformRequestMove = PlatformRequestResize = false; } }; struct ImGuiNavMoveResult From 75de34e281141f851f7e5f9f6a923573167bc0f0 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 26 Feb 2020 23:58:45 +0100 Subject: [PATCH 591/828] Viewports, Docking: Added per-viewport work area system for e.g. menu-bars. Fixed DocksapceOverViewport() and demo code (overlay etc) (#3035, #2889, #2474, #1542, #2109) Clarified that BeginMenuMainBar() had an incorrect knowledge of its height (which was previously harmless). Designed to easily allow for status bars although we don't have/use them yet, but custom code could use them. --- imgui.cpp | 28 +++++++++++++++++++++------- imgui.h | 16 ++++++++++++---- imgui_demo.cpp | 16 +++++++++------- imgui_internal.h | 5 ++++- imgui_widgets.cpp | 24 ++++++++++++++++++------ 5 files changed, 64 insertions(+), 25 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index a3d5c8f139f3..cc4c08f2f736 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5273,6 +5273,7 @@ static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_cont if (is_popup || is_menu) // Popups and menus bypass style.WindowMinSize by default, but we give then a non-zero minimum size to facilitate understanding problematic cases (e.g. empty popups) size_min = ImMin(size_min, ImVec2(4.0f, 4.0f)); + // FIXME-VIEWPORT-WORKAREA: May want to use GetWorkSize() instead of Size depending on the type of windows? ImVec2 avail_size = window->Viewport->Size; if (window->ViewportOwned) avail_size = ImVec2(FLT_MAX, FLT_MAX); @@ -6002,6 +6003,10 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) else window->WindowPadding = style.WindowPadding; + // Lock menu offset so size calculation can use it as menu-bar windows need a minimum size. + window->DC.MenuBarOffset.x = ImMax(ImMax(window->WindowPadding.x, style.ItemSpacing.x), g.NextWindowData.MenuBarOffsetMinVal.x); + window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y; + // Collapse window by double-clicking on title bar // At this point we don't have a clipping rectangle setup yet, so we can use the title bar area for hit detection and drawing if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse) && !window->DockIsActive) @@ -6182,13 +6187,14 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Clamp position/size so window stays visible within its viewport or monitor // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing. + // FIXME: Similar to code in GetWindowAllowedExtentRect() ImRect viewport_rect = window->Viewport->GetMainRect(); if (!window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) { ImVec2 clamp_padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding); if (!window->ViewportOwned && viewport_rect.GetWidth() > 0 && viewport_rect.GetHeight() > 0.0f) { - ClampWindowRect(window, viewport_rect, clamp_padding); + ClampWindowRect(window, window->Viewport->GetWorkRect(), clamp_padding); } else if (window->ViewportOwned && g.PlatformIO.Monitors.Size > 0) { @@ -6437,8 +6443,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DC.NavHasScroll = (window->ScrollMax.y > 0.0f); window->DC.MenuBarAppending = false; - window->DC.MenuBarOffset.x = ImMax(ImMax(window->WindowPadding.x, style.ItemSpacing.x), g.NextWindowData.MenuBarOffsetMinVal.x); - window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y; window->DC.MenuColumns.Update(3, style.ItemSpacing.x, window_just_activated_by_user); window->DC.TreeDepth = 0; window->DC.TreeJumpToParentOnPopMask = 0x00; @@ -8514,6 +8518,7 @@ ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& s return pos; } +// Note that this is used for popups, which can overlap the non work-area of individual viewports. ImRect ImGui::GetWindowAllowedExtentRect(ImGuiWindow* window) { ImGuiContext& g = *GImGui; @@ -8527,6 +8532,7 @@ ImRect ImGui::GetWindowAllowedExtentRect(ImGuiWindow* window) } else { + // Use the full viewport area (not work area) for popups r_screen.Min = window->Viewport->Pos; r_screen.Max = window->Viewport->Pos + window->Viewport->Size; } @@ -10731,6 +10737,11 @@ static void ImGui::UpdateViewportsNewFrame() // Update/copy monitor info UpdateViewportPlatformMonitor(viewport); + // Lock down space taken by menu bars and status bars, reset the offset for fucntions like BeginMainMenuBar() to alter them again. + viewport->WorkOffsetMin = viewport->CurrWorkOffsetMin; + viewport->WorkOffsetMax = viewport->CurrWorkOffsetMax; + viewport->CurrWorkOffsetMin = viewport->CurrWorkOffsetMax = ImVec2(0.0f, 0.0f); + // Reset alpha every frame. Users of transparency (docking) needs to request a lower alpha back. viewport->Alpha = 1.0f; @@ -13852,14 +13863,14 @@ void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags fla // Tips: Use with ImGuiDockNodeFlags_PassthruCentralNode! // The limitation with this call is that your window won't have a menu bar. // Even though we could pass window flags, it would also require the user to be able to call BeginMenuBar() somehow meaning we can't Begin/End in a single function. -// So if you want a menu bar you need to repeat this code manually ourselves. As with advanced other Docking API, we may change this function signature. +// But you can also use BeginMainMenuBar(). If you really want a menu bar inside the same window as the one hosting the dockspace, you will need to copy this code somewhere and tweak it. ImGuiID ImGui::DockSpaceOverViewport(ImGuiViewport* viewport, ImGuiDockNodeFlags dockspace_flags, const ImGuiWindowClass* window_class) { if (viewport == NULL) viewport = GetMainViewport(); - SetNextWindowPos(viewport->Pos); - SetNextWindowSize(viewport->Size); + SetNextWindowPos(viewport->GetWorkPos()); + SetNextWindowSize(viewport->GetWorkSize()); SetNextWindowViewport(viewport->ID); ImGuiWindowFlags host_window_flags = 0; @@ -15230,7 +15241,10 @@ void ImGui::ShowMetricsWindow(bool* p_open) if (ImGui::TreeNode((void*)(intptr_t)viewport->ID, "Viewport #%d, ID: 0x%08X, Parent: 0x%08X, Window: \"%s\"", viewport->Idx, viewport->ID, viewport->ParentViewportId, viewport->Window ? viewport->Window->Name : "N/A")) { ImGuiWindowFlags flags = viewport->Flags; - ImGui::BulletText("Pos: (%.0f,%.0f), Size: (%.0f,%.0f), Monitor: %d, DpiScale: %.0f%%", viewport->Pos.x, viewport->Pos.y, viewport->Size.x, viewport->Size.y, viewport->PlatformMonitor, viewport->DpiScale * 100.0f); + ImGui::BulletText("Main Pos: (%.0f,%.0f), Size: (%.0f,%.0f)\nWorkArea Offset Left: %.0f Top: %.0f, Right: %.0f, Bottom: %.0f\nMonitor: %d, DpiScale: %.0f%%", + viewport->Pos.x, viewport->Pos.y, viewport->Size.x, viewport->Size.y, + viewport->WorkOffsetMin.x, viewport->WorkOffsetMin.y, viewport->WorkOffsetMax.x, viewport->WorkOffsetMax.y, + viewport->PlatformMonitor, viewport->DpiScale * 100.0f); if (viewport->Idx > 0) { ImGui::SameLine(); if (ImGui::SmallButton("Reset Pos")) { viewport->Pos = ImVec2(200,200); if (viewport->Window) viewport->Window->Pos = ImVec2(200,200); } } ImGui::BulletText("Flags: 0x%04X =%s%s%s%s%s%s%s", viewport->Flags, (flags & ImGuiViewportFlags_CanHostOtherWindows) ? " CanHostOtherWindows" : "", (flags & ImGuiViewportFlags_NoDecoration) ? " NoDecoration" : "", diff --git a/imgui.h b/imgui.h index 59291d1108ca..3439311e37c7 100644 --- a/imgui.h +++ b/imgui.h @@ -2468,14 +2468,18 @@ enum ImGuiViewportFlags_ ImGuiViewportFlags_CanHostOtherWindows = 1 << 9 // Main viewport: can host multiple imgui windows (secondary viewports are associated to a single window). }; -// The viewports created and managed by imgui. The role of the platform back-end is to create the platform/OS windows corresponding to each viewport. +// The viewports created and managed by Dear ImGui. The role of the platform back-end is to create the platform/OS windows corresponding to each viewport. +// - Main Area = entire viewport. +// - Work Area = entire viewport minus sections optionally used by menu bars, status bars. Some positioning code will prefer to use this. Window are also trying to stay within this area. struct ImGuiViewport { ImGuiID ID; // Unique identifier for the viewport ImGuiViewportFlags Flags; // See ImGuiViewportFlags_ - ImVec2 Pos; // Position of viewport both in imgui space and in OS desktop/native space - ImVec2 Size; // Size of viewport in pixel - float DpiScale; // 1.0f = 96 DPI = No extra scale + ImVec2 Pos; // Main Area: Position of the viewport (the imgui coordinates are the same as OS desktop/native coordinates) + ImVec2 Size; // Main Area: Size of the viewport. + ImVec2 WorkOffsetMin; // Work Area: Offset from Pos to top-left corner of Work Area. Generally (0,0) or (0,+main_menu_bar_height). Work Area is Full Area but without menu-bars/status-bars (so WorkArea always fit inside Pos/Size!) + ImVec2 WorkOffsetMax; // Work Area: Offset from Pos+Size to bottom-right corner of Work Area. Generally (0,0) or (0,-status_bar_height). + float DpiScale; // 1.0f = 96 DPI = No extra scale. ImDrawData* DrawData; // The ImDrawData corresponding to this viewport. Valid after Render() and until the next call to NewFrame(). ImGuiID ParentViewportId; // (Advanced) 0: no parent. Instruct the platform back-end to setup a parent/child relationship between platform windows. @@ -2491,6 +2495,10 @@ struct ImGuiViewport ImGuiViewport() { ID = 0; Flags = 0; DpiScale = 0.0f; DrawData = NULL; ParentViewportId = 0; RendererUserData = PlatformUserData = PlatformHandle = PlatformHandleRaw = NULL; PlatformRequestMove = PlatformRequestResize = PlatformRequestClose = false; } ~ImGuiViewport() { IM_ASSERT(PlatformUserData == NULL && RendererUserData == NULL); } + + // Access work-area rectangle + ImVec2 GetWorkPos() { return ImVec2(Pos.x + WorkOffsetMin.x, Pos.y + WorkOffsetMin.y); } + ImVec2 GetWorkSize() { return ImVec2(Size.x - WorkOffsetMin.x + WorkOffsetMax.x, Size.y - WorkOffsetMin.y + WorkOffsetMax.y); } // This not clamped }; #if defined(__clang__) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 3c42606dcc7a..4ae8ae583f23 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -222,9 +222,9 @@ void ImGui::ShowDemoWindow(bool* p_open) IM_ASSERT(ImGui::GetCurrentContext() != NULL && "Missing dear imgui context. Refer to examples app!"); // Exceptionally add an extra assert here for people confused with initial dear imgui setup // Examples Apps (accessible from the "Examples" menu) + static bool show_app_main_menu_bar = false; static bool show_app_dockspace = false; static bool show_app_documents = false; - static bool show_app_main_menu_bar = false; static bool show_app_console = false; static bool show_app_log = false; static bool show_app_layout = false; @@ -236,9 +236,9 @@ void ImGui::ShowDemoWindow(bool* p_open) static bool show_app_window_titles = false; static bool show_app_custom_rendering = false; + if (show_app_main_menu_bar) ShowExampleAppMainMenuBar(); if (show_app_dockspace) ShowExampleAppDockSpace(&show_app_dockspace); // Process the Docking app first, as explicit DockSpace() nodes needs to be submitted early (read comments near the DockSpace function) if (show_app_documents) ShowExampleAppDocuments(&show_app_documents); // Process the Document app next, as it may also use a DockSpace() - if (show_app_main_menu_bar) ShowExampleAppMainMenuBar(); if (show_app_console) ShowExampleAppConsole(&show_app_console); if (show_app_log) ShowExampleAppLog(&show_app_log); if (show_app_layout) ShowExampleAppLayout(&show_app_layout); @@ -286,8 +286,8 @@ void ImGui::ShowDemoWindow(bool* p_open) if (no_close) p_open = NULL; // Don't pass our bool* to Begin // We specify a default position/size in case there's no data in the .ini file. Typically this isn't required! We only do it to make the Demo applications a little more welcoming. - ImVec2 main_viewport_pos = ImGui::GetMainViewport()->Pos; - ImGui::SetNextWindowPos(ImVec2(main_viewport_pos.x + 650, main_viewport_pos.y + 20), ImGuiCond_FirstUseEver); + ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + ImGui::SetNextWindowPos(ImVec2(main_viewport->GetWorkPos().x + 650, main_viewport->GetWorkPos().y + 20), ImGuiCond_FirstUseEver); ImGui::SetNextWindowSize(ImVec2(550, 680), ImGuiCond_FirstUseEver); // Main body of the Demo window starts here. @@ -4458,7 +4458,9 @@ static void ShowExampleAppSimpleOverlay(bool* p_open) if (corner != -1) { ImGuiViewport* viewport = ImGui::GetMainViewport(); - ImVec2 window_pos = ImVec2((corner & 1) ? (viewport->Pos.x + viewport->Size.x - DISTANCE) : (viewport->Pos.x + DISTANCE), (corner & 2) ? (viewport->Pos.y + viewport->Size.y - DISTANCE) : (viewport->Pos.y + DISTANCE)); + ImVec2 work_area_pos = viewport->GetWorkPos(); // Instead of using viewport->Pos we use GetWorkPos() to avoid menu bars, if any! + ImVec2 work_area_size = viewport->GetWorkSize(); + ImVec2 window_pos = ImVec2((corner & 1) ? (work_area_pos.x + work_area_size.x - DISTANCE) : (work_area_pos.x + DISTANCE), (corner & 2) ? (work_area_pos.y + work_area_size.y - DISTANCE) : (work_area_pos.y + DISTANCE)); ImVec2 window_pos_pivot = ImVec2((corner & 1) ? 1.0f : 0.0f, (corner & 2) ? 1.0f : 0.0f); ImGui::SetNextWindowPos(window_pos, ImGuiCond_Always, window_pos_pivot); ImGui::SetNextWindowViewport(viewport->ID); @@ -4714,8 +4716,8 @@ void ShowExampleAppDockSpace(bool* p_open) if (opt_fullscreen) { ImGuiViewport* viewport = ImGui::GetMainViewport(); - ImGui::SetNextWindowPos(viewport->Pos); - ImGui::SetNextWindowSize(viewport->Size); + ImGui::SetNextWindowPos(viewport->GetWorkPos()); + ImGui::SetNextWindowSize(viewport->GetWorkSize()); ImGui::SetNextWindowViewport(viewport->ID); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); diff --git a/imgui_internal.h b/imgui_internal.h index 56d6cf9d6f4a..c3ab97f36bd3 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -934,10 +934,13 @@ struct ImGuiViewportP : public ImGuiViewport ImVec2 LastPlatformPos; ImVec2 LastPlatformSize; ImVec2 LastRendererSize; + ImVec2 CurrWorkOffsetMin; // Work area top-left offset being increased during the frame + ImVec2 CurrWorkOffsetMax; // Work area bottom-right offset being decreased during the frame ImGuiViewportP() { Idx = -1; LastFrameActive = LastFrameDrawLists[0] = LastFrameDrawLists[1] = LastFrontMostStampCount = -1; LastNameHash = 0; Alpha = LastAlpha = 1.0f; PlatformMonitor = -1; PlatformWindowCreated = false; Window = NULL; DrawLists[0] = DrawLists[1] = NULL; LastPlatformPos = LastPlatformSize = LastRendererSize = ImVec2(FLT_MAX, FLT_MAX); } ~ImGuiViewportP() { if (DrawLists[0]) IM_DELETE(DrawLists[0]); if (DrawLists[1]) IM_DELETE(DrawLists[1]); } ImRect GetMainRect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } + ImRect GetWorkRect() const { return ImRect(Pos.x + WorkOffsetMin.x, Pos.y + WorkOffsetMin.y, Pos.x + Size.x + WorkOffsetMax.x, Pos.y + Size.y + WorkOffsetMax.y); } void ClearRequestFlags() { PlatformRequestClose = PlatformRequestMove = PlatformRequestResize = false; } }; @@ -991,7 +994,7 @@ struct ImGuiNextWindowData ImGuiID ViewportId; ImGuiID DockId; ImGuiWindowClass WindowClass; - ImVec2 MenuBarOffsetMinVal; // *Always on* This is not exposed publicly, so we don't clear it. + ImVec2 MenuBarOffsetMinVal; // (Always on) This is not exposed publicly, so we don't clear it and it doesn't have a corresponding flag (could we? for consistency?) ImGuiNextWindowData() { memset(this, 0, sizeof(*this)); } inline void ClearFlags() { Flags = ImGuiNextWindowDataFlags_None; } diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index f0f29d266838..800432ea4a07 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6182,20 +6182,32 @@ void ImGui::EndMenuBar() window->DC.MenuBarAppending = false; } -// For the main menu bar, which cannot be moved, we honor g.Style.DisplaySafeAreaPadding to ensure text can be visible on a TV set. bool ImGui::BeginMainMenuBar() { ImGuiContext& g = *GImGui; - ImGuiViewport* viewport = g.Viewports[0]; + ImGuiViewportP* viewport = g.Viewports[0]; + + // For the main menu bar, which cannot be moved, we honor g.Style.DisplaySafeAreaPadding to ensure text can be visible on a TV set. g.NextWindowData.MenuBarOffsetMinVal = ImVec2(g.Style.DisplaySafeAreaPadding.x, ImMax(g.Style.DisplaySafeAreaPadding.y - g.Style.FramePadding.y, 0.0f)); - SetNextWindowPos(viewport->Pos); - SetNextWindowSize(ImVec2(viewport->Size.x, g.NextWindowData.MenuBarOffsetMinVal.y + g.FontBaseSize + g.Style.FramePadding.y)); - SetNextWindowViewport(viewport->ID); // Enforce viewport so we don't create our onw viewport when ImGuiConfigFlags_ViewportsNoMerge is set. + + // Get our rectangle in the work area, and report the size we need for next frame. + // We don't attempt to calculate our height ahead, as it depends on the per-viewport font size. However menu-bar will affect the minimum window size so we'll get the right height. + ImVec2 menu_bar_pos = viewport->Pos + viewport->CurrWorkOffsetMin; + ImVec2 menu_bar_size = ImVec2(viewport->Size.x - viewport->CurrWorkOffsetMin.x + viewport->CurrWorkOffsetMax.x, 1.0f); + + // Create window + SetNextWindowPos(menu_bar_pos); + SetNextWindowSize(menu_bar_size); + SetNextWindowViewport(viewport->ID); // Enforce viewport so we don't create our own viewport when ImGuiConfigFlags_ViewportsNoMerge is set. PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); - PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0, 0)); + PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0, 0)); // Lift normal size constraint, however the presence of a menu-bar will give us the minimum height we want. ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_MenuBar; bool is_open = Begin("##MainMenuBar", NULL, window_flags) && BeginMenuBar(); PopStyleVar(2); + + // Feed back into work area using actual window size + viewport->CurrWorkOffsetMin.y += GetCurrentWindow()->Size.y; + g.NextWindowData.MenuBarOffsetMinVal = ImVec2(0.0f, 0.0f); if (!is_open) { From 103c5edaaa832ccded2523ea328dc4398ba7b456 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 27 Feb 2020 16:10:36 +0100 Subject: [PATCH 592/828] Internals: debug log macros. --- imgui.cpp | 7 ++++--- imgui_internal.h | 9 +++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index cc4c08f2f736..f4c53193bf63 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -8204,7 +8204,7 @@ void ImGui::OpenPopupEx(ImGuiID id) popup_ref.OpenPopupPos = NavCalcPreferredRefPos(); popup_ref.OpenMousePos = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : popup_ref.OpenPopupPos; - //IMGUI_DEBUG_LOG("OpenPopupEx(0x%08X)\n", g.FrameCount, id); + IMGUI_DEBUG_LOG_POPUP("OpenPopupEx(0x%08X)\n", id); if (g.OpenPopupStack.Size < current_stack_size + 1) { g.OpenPopupStack.push_back(popup_ref); @@ -8265,7 +8265,7 @@ void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to } if (popup_count_to_keep < g.OpenPopupStack.Size) // This test is not required but it allows to set a convenient breakpoint on the statement below { - //IMGUI_DEBUG_LOG("ClosePopupsOverWindow(%s) -> ClosePopupToLevel(%d)\n", ref_window->Name, popup_count_to_keep); + IMGUI_DEBUG_LOG_POPUP("ClosePopupsOverWindow(\"%s\") -> ClosePopupToLevel(%d)\n", ref_window->Name, popup_count_to_keep); ClosePopupToLevel(popup_count_to_keep, restore_focus_to_window_under_popup); } } @@ -8273,6 +8273,7 @@ void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to void ImGui::ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup) { ImGuiContext& g = *GImGui; + IMGUI_DEBUG_LOG_POPUP("ClosePopupToLevel(%d), restore_focus_to_window_under_popup=%d\n", remaining, restore_focus_to_window_under_popup); IM_ASSERT(remaining >= 0 && remaining < g.OpenPopupStack.Size); ImGuiWindow* focus_window = g.OpenPopupStack[remaining].SourceWindow; ImGuiWindow* popup_window = g.OpenPopupStack[remaining].Window; @@ -8315,7 +8316,7 @@ void ImGui::CloseCurrentPopup() break; popup_idx--; } - //IMGUI_DEBUG_LOG("CloseCurrentPopup %d -> %d\n", g.BeginPopupStack.Size - 1, popup_idx); + IMGUI_DEBUG_LOG_POPUP("CloseCurrentPopup %d -> %d\n", g.BeginPopupStack.Size - 1, popup_idx); ClosePopupToLevel(popup_idx, true); // A common pattern is to close a popup when selecting a menu item/selectable that will open another window. diff --git a/imgui_internal.h b/imgui_internal.h index c3ab97f36bd3..a664610621f3 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -156,10 +156,11 @@ extern IMGUI_API ImGuiContext* GImGui; // Current implicit context pointer #ifndef IMGUI_DEBUG_LOG #define IMGUI_DEBUG_LOG(_FMT,...) printf("[%05d] " _FMT, GImGui->FrameCount, __VA_ARGS__) #endif -#define IMGUI_DEBUG_LOG_VIEWPORT(...) ((void)0) // Disable log -#define IMGUI_DEBUG_LOG_DOCKING(...) ((void)0) // Disable log -//#define IMGUI_DEBUG_LOG_VIEWPORT IMGUI_DEBUG_LOG // Enable log -//#define IMGUI_DEBUG_LOG_DOCKING IMGUI_DEBUG_LOG // Enable log + +// Debug Logging for selected systems. Remove the '((void)0) //' to enable. +#define IMGUI_DEBUG_LOG_POPUP(...) ((void)0) // IMGUI_DEBUG_LOG(__VA_ARGS__) +#define IMGUI_DEBUG_LOG_VIEWPORT(...) ((void)0) // IMGUI_DEBUG_LOG(__VA_ARGS__) +#define IMGUI_DEBUG_LOG_DOCKING(...) ((void)0) // IMGUI_DEBUG_LOG(__VA_ARGS__) // Static Asserts #if (__cplusplus >= 201100) From 1ecc1db226592ea61923fd19ec86ed8bc2109ee9 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 5 Mar 2020 12:35:01 +0100 Subject: [PATCH 593/828] Fix GCC warning --- imgui.cpp | 4 ++-- imgui_internal.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index ef3c3eeb715c..533216e06507 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11849,7 +11849,7 @@ static void ImGui::DockContextBuildNodesFromSettings(ImGuiContext* ctx, ImGuiDoc else if (node->ParentNode && node->ParentNode->ChildNodes[1] == NULL) node->ParentNode->ChildNodes[1] = node; node->SelectedTabId = settings->SelectedWindowId; - node->SplitAxis = settings->SplitAxis; + node->SplitAxis = (ImGuiAxis)settings->SplitAxis; node->LocalFlags |= (settings->Flags & ImGuiDockNodeFlags_SavedFlagsMask_); // Bind host window immediately if it already exist (in case of a rebuild) @@ -14778,7 +14778,7 @@ static void DockSettingsHandler_DockNodeToSettings(ImGuiDockContext* dc, ImGuiDo node_settings.ParentNodeId = node->ParentNode ? node->ParentNode->ID : 0; node_settings.ParentWindowId = (node->IsDockSpace() && node->HostWindow && node->HostWindow->ParentWindow) ? node->HostWindow->ParentWindow->ID : 0; node_settings.SelectedWindowId = node->SelectedTabId; - node_settings.SplitAxis = node->IsSplitNode() ? (char)node->SplitAxis : ImGuiAxis_None; + node_settings.SplitAxis = (signed char)(node->IsSplitNode() ? node->SplitAxis : ImGuiAxis_None); node_settings.Depth = (char)depth; node_settings.Flags = (node->LocalFlags & ImGuiDockNodeFlags_SavedFlagsMask_); node_settings.Pos = ImVec2ih(node->Pos); diff --git a/imgui_internal.h b/imgui_internal.h index a3c31b1a5281..2e82ed413465 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1094,7 +1094,7 @@ struct ImGuiDockNode ImVec2 Pos; // Current position ImVec2 Size; // Current size ImVec2 SizeRef; // [Split node only] Last explicitly written-to size (overridden when using a splitter affecting the node), used to calculate Size. - int SplitAxis; // [Split node only] Split axis (X or Y) + ImGuiAxis SplitAxis; // [Split node only] Split axis (X or Y) ImGuiWindowClass WindowClass; // [Root node only] ImGuiDockNodeState State; From 7e00cde8a9ebf41948f629f86ce8c4cf0f68334f Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Thu, 5 Mar 2020 16:22:16 +0200 Subject: [PATCH 594/828] Disable false-positive warning that papeared in pvs-studio v7.06.37052.34. --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 533216e06507..1331aef9295a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3476,7 +3476,7 @@ void ImGui::StartMouseMovingWindowOrNode(ImGuiWindow* window, ImGuiDockNode* nod // - part of a floating node hierarchy with more than one visible node (if only one is visible, we'll just move the whole hierarchy) // - part of a dockspace node hierarchy (trivia: undocking from a fixed/central node will create a new node and copy windows) ImGuiDockNode* root_node = DockNodeGetRootNode(node); - if (root_node->OnlyNodeWithWindows != node || root_node->CentralNode != NULL) + if (root_node->OnlyNodeWithWindows != node || root_node->CentralNode != NULL) // -V1051 PVS-Studio thinks node should be root_node and is wrong about that. if (undock_floating_node || root_node->IsDockSpace()) can_undock_node = true; } From 1b579a110dfd6919d6716dc97d2603cd23b395d5 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 5 Mar 2020 18:26:18 +0100 Subject: [PATCH 595/828] Viewports: Lots of comments about setting up multi-viewports. (#1542) --- imgui.h | 168 ++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 109 insertions(+), 59 deletions(-) diff --git a/imgui.h b/imgui.h index e9d08a2b53f8..b13f35355d7b 100644 --- a/imgui.h +++ b/imgui.h @@ -32,7 +32,7 @@ Index of this file: // Helpers (ImGuiOnceUponAFrame, ImGuiTextFilter, ImGuiTextBuffer, ImGuiStorage, ImGuiListClipper, ImColor) // Draw List API (ImDrawCallback, ImDrawCmd, ImDrawIdx, ImDrawVert, ImDrawChannel, ImDrawListSplitter, ImDrawListFlags, ImDrawList, ImDrawData) // Font API (ImFontConfig, ImFontGlyph, ImFontGlyphRangesBuilder, ImFontAtlasFlags, ImFontAtlas, ImFont) -// Platform interface for multi-viewport support (ImGuiPlatformMonitor, ImGuiPlatformIO, ImGuiViewport) +// Platform interface for multi-viewport support (ImGuiPlatformIO, ImGuiPlatformMonitor, ImGuiViewportFlags, ImGuiViewport) */ @@ -775,11 +775,12 @@ namespace ImGui IMGUI_API void MemFree(void* ptr); // (Optional) Platform/OS interface for multi-viewport support + // Read comments around the ImGuiPlatformIO structure for more details. // Note: You may use GetWindowViewport() to get the current viewport of the current window. IMGUI_API ImGuiPlatformIO& GetPlatformIO(); // platform/renderer functions, for back-end to setup + viewports list. IMGUI_API ImGuiViewport* GetMainViewport(); // main viewport. same as GetPlatformIO().MainViewport == GetPlatformIO().Viewports[0]. IMGUI_API void UpdatePlatformWindows(); // call in main loop. will call CreateWindow/ResizeWindow/etc. platform functions for each secondary viewport, and DestroyWindow for each inactive viewport. - IMGUI_API void RenderPlatformWindowsDefault(void* platform_arg = NULL, void* renderer_arg = NULL); // call in main loop. will call RenderWindow/SwapBuffers platform functions for each secondary viewport which doesn't have the ImGuiViewportFlags_Minimized flag set. May be reimplemented by user for custom rendering needs. + IMGUI_API void RenderPlatformWindowsDefault(void* platform_render_arg = NULL, void* renderer_render_arg = NULL); // call in main loop. will call RenderWindow/SwapBuffers platform functions for each secondary viewport which doesn't have the ImGuiViewportFlags_Minimized flag set. May be reimplemented by user for custom rendering needs. IMGUI_API void DestroyPlatformWindows(); // call DestroyWindow platform functions for all viewports. call from back-end Shutdown() if you need to close platform windows before imgui shutdown. otherwise will be called by DestroyContext(). IMGUI_API ImGuiViewport* FindViewportByID(ImGuiID id); // this is a helper for back-ends. IMGUI_API ImGuiViewport* FindViewportByPlatformHandle(void* platform_handle); // this is a helper for back-ends. the type platform_handle is decided by the back-end (e.g. HWND, MyWindow*, GLFWwindow* etc.) @@ -2392,32 +2393,54 @@ struct ImFont //----------------------------------------------------------------------------- // [BETA] Platform interface for multi-viewport support -// - completely optional, for advanced users! -// - this is used for back-ends aiming to support the seamless creation of multiple viewport (= multiple Platform/OS windows) -// dear imgui manages the viewports, and the back-end create one Platform/OS windows for each secondary viewport. -// - if you are new to dear imgui and trying to integrate it into your engine, you should probably ignore this for now. +//----------------------------------------------------------------------------- +// (Optional) This is completely optional, for advanced users! +// If you are new to Dear ImGui and trying to integrate it into your engine, you can probably ignore this for now. +// +// This feature allows you to seamlessly drag Dear ImGui windows outside of your application viewport. +// This is achieved by creating new Platform/OS windows on the fly, and rendering into them. +// Dear ImGui manages the viewport structures, and the back-end create and maintain one Platform/OS window for each of those viewports. +// +// See Glossary https://github.com/ocornut/imgui/wiki/Glossary for details about some of the terminology. +// See Thread https://github.com/ocornut/imgui/issues/1542 for gifs, news and questions about this evolving feature. +// +// About the coordinates system: +// - When multi-viewports are enabled, all Dear ImGui coordinates become absolute coordinates (same as OS coordinates!) +// - So e.g. ImGui::SetNextWindowPos(ImVec2(0,0)) will position a window relative to your primary monitor! +// - If you want to position windows relative to your main application viewport, use ImGui::GetMainViewport()->Pos as a base position. +// +// Steps to use multi-viewports in your application, when using a default back-end from the examples/ folder: +// - Application: Enable feature with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. +// - Back-end: The back-end initialization will setup all necessary ImGuiPlatformIO's functions and update monitors info every frame. +// - Application: In your main loop, call ImGui::UpdatePlatformWindows(), ImGui::RenderPlatformWindowsDefault() after EndFrame() or Render(). +// - Application: Fix absolute coordinates used in ImGui::SetWindowPos() or ImGui::SetNextWindowPos() calls. +// +// Steps to use multi-viewports in your application, when using a custom back-end: +// - Important: THIS IS NOT EASY TO DO and comes with many subtleties not described here! +// It's also an experimental feature, so some of the requirements may evolve. +// Consider using default back-ends if you can. Either way, carefully follow and refer to examples/ back-ends for details. +// - Application: Enable feature with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. +// - Back-end: Hook ImGuiPlatformIO's Platform_* and Renderer_* callbacks (see below). +// Set 'io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports' and 'io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports'. +// Update ImGuiPlatformIO's Monitors list every frame. +// Update MousePos every frame, in absolute coordinates. +// - Application: In your main loop, call ImGui::UpdatePlatformWindows(), ImGui::RenderPlatformWindowsDefault() after EndFrame() or Render(). +// You may skip calling RenderPlatformWindowsDefault() if its API is not convenient for your needs. Read comments below. +// - Application: Fix absolute coordinates used in ImGui::SetWindowPos() or ImGui::SetNextWindowPos() calls. +// +// About ImGui::RenderPlatformWindowsDefault(): +// - This function is a mostly a _helper_ for the common-most cases, and to facilitate using default back-ends. +// - You can check its simple source code to understand what it does. +// It basically iterates secondary viewports and call 4 functions that are setup in ImGuiPlatformIO, if available: +// Platform_RenderWindow(), Renderer_RenderWindow(), Platform_SwapBuffers(), Renderer_SwapBuffers() +// Those functions pointers exists only for the benefit of RenderPlatformWindowsDefault(). +// - If you have very specific rendering needs (e.g. flipping multiple swap-chain simultaneously, unusual sync/threading issues, etc.), +// you may be tempted to ignore RenderPlatformWindowsDefault() and write customized code to perform your renderingg. +// You may decide to setup the platform_io's *RenderWindow and *SwapBuffers pointers and call your functions through those pointers, +// or you may decide to never setup those pointers and call your code directly. They are a convenience, not an obligatory interface. //----------------------------------------------------------------------------- -// (Optional) This is required when enabling multi-viewport. Represent the bounds of each connected monitor/display and their DPI. -// We use this information for multiple DPI support + clamping the position of popups and tooltips so they don't straddle multiple monitors. -struct ImGuiPlatformMonitor -{ - ImVec2 MainPos, MainSize; // Coordinates of the area displayed on this monitor (Min = upper left, Max = bottom right) - ImVec2 WorkPos, WorkSize; // (Optional) Coordinates without task bars / side bars / menu bars. imgui uses this to avoid positioning popups/tooltips inside this region. - float DpiScale; // 1.0f = 96 DPI - ImGuiPlatformMonitor() { MainPos = MainSize = WorkPos = WorkSize = ImVec2(0,0); DpiScale = 1.0f; } -}; - -// (Optional) Setup required only if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) is enabled. -// Access via ImGui::GetPlatformIO(). This is designed so we can mix and match two imgui_impl_xxxx files, -// one for the Platform (~window handling), one for Renderer. Custom engine back-ends will often provide -// both Platform and Renderer interfaces and so may not need to use all functions. -// Platform functions are typically called before their Renderer counterpart, -// apart from Destroy which are called the other way. -// RenderPlatformWindowsDefault() is that helper that iterate secondary viewports and call, in this order: -// Platform_RenderWindow(), Renderer_RenderWindow(), Platform_SwapBuffers(), Renderer_SwapBuffers() -// You may skip using RenderPlatformWindowsDefault() and call your draw/swap functions yourself if you need -// specific behavior for your multi-window rendering. +// (Optional) Access via ImGui::GetPlatformIO() struct ImGuiPlatformIO { //------------------------------------------------------------------ @@ -2425,47 +2448,72 @@ struct ImGuiPlatformIO //------------------------------------------------------------------ // (Optional) Platform functions (e.g. Win32, GLFW, SDL2) - // Most of them are called by ImGui::UpdatePlatformWindows() and ImGui::RenderPlatformWindowsDefault(). - void (*Platform_CreateWindow)(ImGuiViewport* vp); // Create a new platform window for the given viewport - void (*Platform_DestroyWindow)(ImGuiViewport* vp); - void (*Platform_ShowWindow)(ImGuiViewport* vp); // Newly created windows are initially hidden so SetWindowPos/Size/Title can be called on them first - void (*Platform_SetWindowPos)(ImGuiViewport* vp, ImVec2 pos); - ImVec2 (*Platform_GetWindowPos)(ImGuiViewport* vp); - void (*Platform_SetWindowSize)(ImGuiViewport* vp, ImVec2 size); - ImVec2 (*Platform_GetWindowSize)(ImGuiViewport* vp); - void (*Platform_SetWindowFocus)(ImGuiViewport* vp); // Move window to front and set input focus - bool (*Platform_GetWindowFocus)(ImGuiViewport* vp); - bool (*Platform_GetWindowMinimized)(ImGuiViewport* vp); - void (*Platform_SetWindowTitle)(ImGuiViewport* vp, const char* title); - void (*Platform_SetWindowAlpha)(ImGuiViewport* vp, float alpha); // (Optional) Setup window transparency - void (*Platform_UpdateWindow)(ImGuiViewport* vp); // (Optional) Called in UpdatePlatforms(). Optional hook to allow the platform back-end from doing general book-keeping every frame. - void (*Platform_RenderWindow)(ImGuiViewport* vp, void* render_arg); // (Optional) Setup for render - void (*Platform_SwapBuffers)(ImGuiViewport* vp, void* render_arg); // (Optional) Call Present/SwapBuffers (platform side) - float (*Platform_GetWindowDpiScale)(ImGuiViewport* vp); // (Optional) [BETA] (FIXME-DPI) DPI handling: Return DPI scale for this viewport. 1.0f = 96 DPI. - void (*Platform_OnChangedViewport)(ImGuiViewport* vp); // (Optional) [BETA] (FIXME-DPI) DPI handling: Called during Begin() every time the viewport we are outputting into changes, so back-end has a chance to swap fonts to adjust style. - void (*Platform_SetImeInputPos)(ImGuiViewport* vp, ImVec2 pos); // (Optional) Set IME (Input Method Editor, e.g. for Asian languages) input position, so text preview appears over the imgui input box. - int (*Platform_CreateVkSurface)(ImGuiViewport* vp, ImU64 vk_inst, const void* vk_allocators, ImU64* out_vk_surface); // (Optional) For Renderer to call into Platform code - - // (Optional) Renderer functions (e.g. DirectX, OpenGL3, Vulkan) - void (*Renderer_CreateWindow)(ImGuiViewport* vp); // Create swap chains, frame buffers etc. - void (*Renderer_DestroyWindow)(ImGuiViewport* vp); - void (*Renderer_SetWindowSize)(ImGuiViewport* vp, ImVec2 size); // Resize swap chain, frame buffers etc. - void (*Renderer_RenderWindow)(ImGuiViewport* vp, void* render_arg); // (Optional) Clear targets, Render viewport->DrawData - void (*Renderer_SwapBuffers)(ImGuiViewport* vp, void* render_arg); // (Optional) Call Present/SwapBuffers (renderer side) - - // (Optional) List of monitors (updated by: app/back-end, used by: imgui to clamp popups/tooltips within same monitor and not have them straddle monitors) + // For reference, the second column shows which function are generally calling the Platform Functions: + // N = ImGui::NewFrame() ~ beginning of the dear imgui frame: read info from platform/OS windows (latest size/position) + // F = ImGui::Begin(), ImGui::EndFrame() ~ during the dear imgui frame + // U = ImGui::UpdatePlatformWindows() ~ after the dear imgui frame: create and update all platform/OS windows + // R = ImGui::RenderPlatformWindowsDefault() ~ render + // D = ImGui::DestroyPlatformWindows() ~ shutdown + // The general idea is that NewFrame() we will read the current Platform/OS state, and UpdatePlatformWindows() will write to it. + // + // The functions are designed so we can mix and match 2 imgui_impl_xxxx files, one for the Platform (~window/input handling), one for Renderer. + // Custom engine back-ends will often provide both Platform and Renderer interfaces and so may not need to use all functions. + // Platform functions are typically called before their Renderer counterpart, apart from Destroy which are called the other way. + + // Platform function --------------------------------------------------- Called by ----- + void (*Platform_CreateWindow)(ImGuiViewport* vp); // . . U . . // Create a new platform window for the given viewport + void (*Platform_DestroyWindow)(ImGuiViewport* vp); // N . U . D // + void (*Platform_ShowWindow)(ImGuiViewport* vp); // . . U . . // Newly created windows are initially hidden so SetWindowPos/Size/Title can be called on them before showing the window + void (*Platform_SetWindowPos)(ImGuiViewport* vp, ImVec2 pos); // . . U . . // Set platform window position (given the upper-left corner of client area) + ImVec2 (*Platform_GetWindowPos)(ImGuiViewport* vp); // N . . . . // + void (*Platform_SetWindowSize)(ImGuiViewport* vp, ImVec2 size); // . . U . . // Set platform window client area size (ignoring OS decorations such as OS title bar etc.) + ImVec2 (*Platform_GetWindowSize)(ImGuiViewport* vp); // N . . . . // Get platform window client area size + void (*Platform_SetWindowFocus)(ImGuiViewport* vp); // N . . . . // Move window to front and set input focus + bool (*Platform_GetWindowFocus)(ImGuiViewport* vp); // . . U . . // + bool (*Platform_GetWindowMinimized)(ImGuiViewport* vp); // N . . . . // Get platform window minimized state. When minimized, we generally won't attempt to get/set size and contents will be culled more easily + void (*Platform_SetWindowTitle)(ImGuiViewport* vp, const char* str); // . . U . . // Set platform window title (given an UTF-8 string) + void (*Platform_SetWindowAlpha)(ImGuiViewport* vp, float alpha); // . . U . . // (Optional) Setup window transparency + void (*Platform_UpdateWindow)(ImGuiViewport* vp); // . . U . . // (Optional) Called by UpdatePlatformWindows(). Optional hook to allow the platform back-end from doing general book-keeping every frame. + void (*Platform_RenderWindow)(ImGuiViewport* vp, void* render_arg); // . . . R . // (Optional) Main rendering (platform side! This is often unused, or just setting a "current" context for OpenGL bindings). 'render_arg' is the value passed to RenderPlatformWindowsDefault(). + void (*Platform_SwapBuffers)(ImGuiViewport* vp, void* render_arg); // . . . R . // (Optional) Call Present/SwapBuffers (platform side! This is often unused!). 'render_arg' is the value passed to RenderPlatformWindowsDefault(). + float (*Platform_GetWindowDpiScale)(ImGuiViewport* vp); // N . . . . // (Optional) [BETA] FIXME-DPI: DPI handling: Return DPI scale for this viewport. 1.0f = 96 DPI. + void (*Platform_OnChangedViewport)(ImGuiViewport* vp); // . F . . . // (Optional) [BETA] FIXME-DPI: DPI handling: Called during Begin() every time the viewport we are outputting into changes, so back-end has a chance to swap fonts to adjust style. + void (*Platform_SetImeInputPos)(ImGuiViewport* vp, ImVec2 pos); // . F . . . // (Optional) Set IME (Input Method Editor, e.g. for Asian languages) input position, so text preview appears over the imgui input box. FIXME: The call timing of this is inconsistent because we want to support without multi-viewports. + int (*Platform_CreateVkSurface)(ImGuiViewport* vp, ImU64 vk_inst, const void* vk_allocators, ImU64* out_vk_surface); // (Optional) For a Vulkan Renderer to call into Platform code (since the surface creation needs to tie them both). + + // (Optional) Renderer functions (e.g. DirectX, OpenGL, Vulkan) + void (*Renderer_CreateWindow)(ImGuiViewport* vp); // . . U . . // Create swap chain, frame buffers etc. (called after Platform_CreateWindow) + void (*Renderer_DestroyWindow)(ImGuiViewport* vp); // N . U . D // Destroy swap chain, frame buffers etc. (called before Platform_DestroyWindow) + void (*Renderer_SetWindowSize)(ImGuiViewport* vp, ImVec2 size); // . . U . . // Resize swap chain, frame buffers etc. (called after Platform_SetWindowSize) + void (*Renderer_RenderWindow)(ImGuiViewport* vp, void* render_arg); // . . . R . // (Optional) Clear framebuffer, setup render target, then render the viewport->DrawData. 'render_arg' is the value passed to RenderPlatformWindowsDefault(). + void (*Renderer_SwapBuffers)(ImGuiViewport* vp, void* render_arg); // . . . R . // (Optional) Call Present/SwapBuffers. 'render_arg' is the value passed to RenderPlatformWindowsDefault(). + + // (Optional) Monitor list + // - Updated by: app/back-end. Update every frame to dynamically support changing monitor or DPI configuration. + // - Used by: dear imgui to query DPI info, clamp popups/tooltips within same monitor and not have them straddle monitors. ImVector Monitors; //------------------------------------------------------------------ // Output - List of viewports to render into platform windows //------------------------------------------------------------------ - // List of viewports (the list is updated by calling ImGui::EndFrame or ImGui::Render) + // Viewports list (the list is updated by calling ImGui::EndFrame or ImGui::Render) + // (in the future we will attempt to organize this feature to remove the need for a "main viewport") ImGuiViewport* MainViewport; // Guaranteed to be == Viewports[0] ImVector Viewports; // Main viewports, followed by all secondary viewports. ImGuiPlatformIO() { memset(this, 0, sizeof(*this)); } // Zero clear }; +// (Optional) This is required when enabling multi-viewport. Represent the bounds of each connected monitor/display and their DPI. +// We use this information for multiple DPI support + clamping the position of popups and tooltips so they don't straddle multiple monitors. +struct ImGuiPlatformMonitor +{ + ImVec2 MainPos, MainSize; // Coordinates of the area displayed on this monitor (Min = upper left, Max = bottom right) + ImVec2 WorkPos, WorkSize; // (Optional) Coordinates without task bars / side bars / menu bars. imgui uses this to avoid positioning popups/tooltips inside this region. + float DpiScale; // 1.0f = 96 DPI + ImGuiPlatformMonitor() { MainPos = MainSize = WorkPos = WorkSize = ImVec2(0, 0); DpiScale = 1.0f; } +}; + // Flags stored in ImGuiViewport::Flags, giving indications to the platform back-ends. enum ImGuiViewportFlags_ { @@ -2498,9 +2546,11 @@ struct ImGuiViewport ImGuiID ParentViewportId; // (Advanced) 0: no parent. Instruct the platform back-end to setup a parent/child relationship between platform windows. // Our design separate the Renderer and Platform back-ends to facilitate combining default back-ends with each others. - // When our create your own back-end for a custom engine, it is possible that both Renderer and Platform will be handled by the same system and you may not need to use all the UserData/Handle fields. - void* RendererUserData; // void* to hold custom data structure for the renderer (e.g. swap chain, framebuffers etc.). - void* PlatformUserData; // void* to hold custom data structure for the OS / platform (e.g. windowing info, render context). + // When our create your own back-end for a custom engine, it is possible that both Renderer and Platform will be handled + // by the same system and you may not need to use all the UserData/Handle fields. + // The library never uses those fields, they are merely storage to facilitate back-end implementation. + void* RendererUserData; // void* to hold custom data structure for the renderer (e.g. swap chain, framebuffers etc.). generally set by your Renderer_CreateWindow function. + void* PlatformUserData; // void* to hold custom data structure for the OS / platform (e.g. windowing info, render context). generally set by your Platform_CreateWindow function. void* PlatformHandle; // void* for FindViewportByPlatformHandle(). (e.g. suggested to use natural platform handle such as HWND, GLFWWindow*, SDL_Window*) void* PlatformHandleRaw; // void* to hold lower-level, platform-native window handle (e.g. the HWND) when using an abstraction layer like GLFW or SDL (where PlatformHandle would be a SDL_Window*) bool PlatformRequestMove; // Platform window requested move (e.g. window was moved by the OS / host window manager, authoritative position will be OS window position) From f2c7f3b200d82f3965bedbe4f52d6082fb74d1be Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 6 Mar 2020 17:28:32 +0100 Subject: [PATCH 596/828] Viewports: Renamed UpdatePlatformWindows > PlatformWindowsUpdate, RenderPlatformWindowsDefault -> PlatformWindowsRender, DestroyPlatformWindows > PlatformWindowsDestroy (#1542) --- docs/CHANGELOG.txt | 2 +- examples/example_glfw_opengl2/main.cpp | 4 +-- examples/example_glfw_opengl3/main.cpp | 4 +-- examples/example_glfw_vulkan/main.cpp | 4 +-- examples/example_sdl_directx11/main.cpp | 4 +-- examples/example_sdl_opengl2/main.cpp | 4 +-- examples/example_sdl_opengl3/main.cpp | 4 +-- examples/example_sdl_vulkan/main.cpp | 4 +-- examples/example_win32_directx10/main.cpp | 4 +-- examples/example_win32_directx11/main.cpp | 4 +-- examples/example_win32_directx12/main.cpp | 4 +-- examples/example_win32_directx9/main.cpp | 4 +-- examples/imgui_impl_dx10.cpp | 2 +- examples/imgui_impl_dx11.cpp | 2 +- examples/imgui_impl_dx12.cpp | 2 +- examples/imgui_impl_dx9.cpp | 2 +- examples/imgui_impl_glfw.cpp | 2 +- examples/imgui_impl_opengl2.cpp | 2 +- examples/imgui_impl_opengl3.cpp | 2 +- examples/imgui_impl_vulkan.cpp | 2 +- examples/imgui_impl_win32.cpp | 2 +- imgui.cpp | 36 ++++++++++---------- imgui.h | 40 +++++++++++------------ imgui_internal.h | 2 +- 24 files changed, 71 insertions(+), 71 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 0289741a95ab..c1810abaac95 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -82,7 +82,7 @@ Other changes: - Added GetWindowDpiScale(). - Added GetOverlayDrawList(ImGuiViewport* viewport). The no-parameter version of GetOverlayDrawList() return the overlay for the current window's viewport. -- Added UpdatePlatformWindows(), RenderPlatformWindows(), DestroyPlatformWindows() for usage for application core. +- Added PlatformWindowsUpdate(), PlatformWindowsRender(), PlatformWindowsDestroy() for usage for application core. - Added FindViewportByID(), FindViewportByPlatformHandle() for usage by back-ends. - Added ImGuiConfigFlags_ViewportsEnable configuration flag and other viewport options. - Added io.ConfigViewportsNoAutoMerge, io.ConfigViewportsNoTaskBarIcon, io.ConfigViewportsNoDecoration, io.ConfigViewportsNoDefaultParent options. diff --git a/examples/example_glfw_opengl2/main.cpp b/examples/example_glfw_opengl2/main.cpp index 051d4281b9d4..585db73fe69d 100644 --- a/examples/example_glfw_opengl2/main.cpp +++ b/examples/example_glfw_opengl2/main.cpp @@ -160,8 +160,8 @@ int main(int, char**) if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { GLFWwindow* backup_current_context = glfwGetCurrentContext(); - ImGui::UpdatePlatformWindows(); - ImGui::RenderPlatformWindowsDefault(); + ImGui::PlatformWindowsUpdate(); + ImGui::PlatformWindowsRender(); glfwMakeContextCurrent(backup_current_context); } diff --git a/examples/example_glfw_opengl3/main.cpp b/examples/example_glfw_opengl3/main.cpp index 0f29f849f4e6..b8de5cbe0b91 100644 --- a/examples/example_glfw_opengl3/main.cpp +++ b/examples/example_glfw_opengl3/main.cpp @@ -205,8 +205,8 @@ int main(int, char**) if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { GLFWwindow* backup_current_context = glfwGetCurrentContext(); - ImGui::UpdatePlatformWindows(); - ImGui::RenderPlatformWindowsDefault(); + ImGui::PlatformWindowsUpdate(); + ImGui::PlatformWindowsRender(); glfwMakeContextCurrent(backup_current_context); } diff --git a/examples/example_glfw_vulkan/main.cpp b/examples/example_glfw_vulkan/main.cpp index da23d24708a8..f30219f677eb 100644 --- a/examples/example_glfw_vulkan/main.cpp +++ b/examples/example_glfw_vulkan/main.cpp @@ -525,8 +525,8 @@ int main(int, char**) // Update and Render additional Platform Windows if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { - ImGui::UpdatePlatformWindows(); - ImGui::RenderPlatformWindowsDefault(); + ImGui::PlatformWindowsUpdate(); + ImGui::PlatformWindowsRender(); } FramePresent(wd); diff --git a/examples/example_sdl_directx11/main.cpp b/examples/example_sdl_directx11/main.cpp index 60c06245aed9..ea2e85a5cc5c 100644 --- a/examples/example_sdl_directx11/main.cpp +++ b/examples/example_sdl_directx11/main.cpp @@ -166,8 +166,8 @@ int main(int, char**) // Update and Render additional Platform Windows if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { - ImGui::UpdatePlatformWindows(); - ImGui::RenderPlatformWindowsDefault(); + ImGui::PlatformWindowsUpdate(); + ImGui::PlatformWindowsRender(); } g_pSwapChain->Present(1, 0); // Present with vsync diff --git a/examples/example_sdl_opengl2/main.cpp b/examples/example_sdl_opengl2/main.cpp index aa76563464ce..78b0b220c754 100644 --- a/examples/example_sdl_opengl2/main.cpp +++ b/examples/example_sdl_opengl2/main.cpp @@ -158,8 +158,8 @@ int main(int, char**) { SDL_Window* backup_current_window = SDL_GL_GetCurrentWindow(); SDL_GLContext backup_current_context = SDL_GL_GetCurrentContext(); - ImGui::UpdatePlatformWindows(); - ImGui::RenderPlatformWindowsDefault(); + ImGui::PlatformWindowsUpdate(); + ImGui::PlatformWindowsRender(); SDL_GL_MakeCurrent(backup_current_window, backup_current_context); } diff --git a/examples/example_sdl_opengl3/main.cpp b/examples/example_sdl_opengl3/main.cpp index af0892870444..e8f7441b5b39 100644 --- a/examples/example_sdl_opengl3/main.cpp +++ b/examples/example_sdl_opengl3/main.cpp @@ -207,8 +207,8 @@ int main(int, char**) { SDL_Window* backup_current_window = SDL_GL_GetCurrentWindow(); SDL_GLContext backup_current_context = SDL_GL_GetCurrentContext(); - ImGui::UpdatePlatformWindows(); - ImGui::RenderPlatformWindowsDefault(); + ImGui::PlatformWindowsUpdate(); + ImGui::PlatformWindowsRender(); SDL_GL_MakeCurrent(backup_current_window, backup_current_context); } diff --git a/examples/example_sdl_vulkan/main.cpp b/examples/example_sdl_vulkan/main.cpp index 7d84f34af63b..ec99439bdcc6 100644 --- a/examples/example_sdl_vulkan/main.cpp +++ b/examples/example_sdl_vulkan/main.cpp @@ -522,8 +522,8 @@ int main(int, char**) // Update and Render additional Platform Windows if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { - ImGui::UpdatePlatformWindows(); - ImGui::RenderPlatformWindowsDefault(); + ImGui::PlatformWindowsUpdate(); + ImGui::PlatformWindowsRender(); } FramePresent(wd); diff --git a/examples/example_win32_directx10/main.cpp b/examples/example_win32_directx10/main.cpp index 284bf787456f..f30b26822076 100644 --- a/examples/example_win32_directx10/main.cpp +++ b/examples/example_win32_directx10/main.cpp @@ -159,8 +159,8 @@ int main(int, char**) // Update and Render additional Platform Windows if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { - ImGui::UpdatePlatformWindows(); - ImGui::RenderPlatformWindowsDefault(); + ImGui::PlatformWindowsUpdate(); + ImGui::PlatformWindowsRender(); } g_pSwapChain->Present(1, 0); // Present with vsync diff --git a/examples/example_win32_directx11/main.cpp b/examples/example_win32_directx11/main.cpp index d3828ff942b8..07cf519ddbbe 100644 --- a/examples/example_win32_directx11/main.cpp +++ b/examples/example_win32_directx11/main.cpp @@ -166,8 +166,8 @@ int main(int, char**) // Update and Render additional Platform Windows if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { - ImGui::UpdatePlatformWindows(); - ImGui::RenderPlatformWindowsDefault(); + ImGui::PlatformWindowsUpdate(); + ImGui::PlatformWindowsRender(); } g_pSwapChain->Present(1, 0); // Present with vsync diff --git a/examples/example_win32_directx12/main.cpp b/examples/example_win32_directx12/main.cpp index 259c30269a55..6c1e3deb9cbe 100644 --- a/examples/example_win32_directx12/main.cpp +++ b/examples/example_win32_directx12/main.cpp @@ -210,8 +210,8 @@ int main(int, char**) // Update and Render additional Platform Windows if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { - ImGui::UpdatePlatformWindows(); - ImGui::RenderPlatformWindowsDefault(NULL, (void*)g_pd3dCommandList); + ImGui::PlatformWindowsUpdate(); + ImGui::PlatformWindowsRender(NULL, (void*)g_pd3dCommandList); } g_pSwapChain->Present(1, 0); // Present with vsync diff --git a/examples/example_win32_directx9/main.cpp b/examples/example_win32_directx9/main.cpp index d96e5c481323..3be45fa07dae 100644 --- a/examples/example_win32_directx9/main.cpp +++ b/examples/example_win32_directx9/main.cpp @@ -165,8 +165,8 @@ int main(int, char**) // Update and Render additional Platform Windows if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { - ImGui::UpdatePlatformWindows(); - ImGui::RenderPlatformWindowsDefault(); + ImGui::PlatformWindowsUpdate(); + ImGui::PlatformWindowsRender(); } HRESULT result = g_pd3dDevice->Present(NULL, NULL, NULL, NULL); diff --git a/examples/imgui_impl_dx10.cpp b/examples/imgui_impl_dx10.cpp index 95dc6fd28e6d..852a908efffd 100644 --- a/examples/imgui_impl_dx10.cpp +++ b/examples/imgui_impl_dx10.cpp @@ -655,6 +655,6 @@ void ImGui_ImplDX10_InitPlatformInterface() void ImGui_ImplDX10_ShutdownPlatformInterface() { - ImGui::DestroyPlatformWindows(); + ImGui::PlatformWindowsDestroy(); } diff --git a/examples/imgui_impl_dx11.cpp b/examples/imgui_impl_dx11.cpp index ffd05625b3e8..2a40cc5831a0 100644 --- a/examples/imgui_impl_dx11.cpp +++ b/examples/imgui_impl_dx11.cpp @@ -671,5 +671,5 @@ static void ImGui_ImplDX11_InitPlatformInterface() static void ImGui_ImplDX11_ShutdownPlatformInterface() { - ImGui::DestroyPlatformWindows(); + ImGui::PlatformWindowsDestroy(); } diff --git a/examples/imgui_impl_dx12.cpp b/examples/imgui_impl_dx12.cpp index 3ead29c5affd..57ac0afe166a 100644 --- a/examples/imgui_impl_dx12.cpp +++ b/examples/imgui_impl_dx12.cpp @@ -947,5 +947,5 @@ void ImGui_ImplDX12_InitPlatformInterface() void ImGui_ImplDX12_ShutdownPlatformInterface() { - ImGui::DestroyPlatformWindows(); + ImGui::PlatformWindowsDestroy(); } diff --git a/examples/imgui_impl_dx9.cpp b/examples/imgui_impl_dx9.cpp index 27435cbc38f7..0a22f9228358 100644 --- a/examples/imgui_impl_dx9.cpp +++ b/examples/imgui_impl_dx9.cpp @@ -423,7 +423,7 @@ static void ImGui_ImplDX9_InitPlatformInterface() static void ImGui_ImplDX9_ShutdownPlatformInterface() { - ImGui::DestroyPlatformWindows(); + ImGui::PlatformWindowsDestroy(); } static void ImGui_ImplDX9_CreateDeviceObjectsForPlatformWindows() diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index bcbee43655d7..462c35fef2a7 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -598,7 +598,7 @@ static LRESULT CALLBACK WndProcNoInputs(HWND hWnd, UINT msg, WPARAM wParam, LPAR // Let mouse pass-through the window. This will allow the back-end to set io.MouseHoveredViewport properly (which is OPTIONAL). // The ImGuiViewportFlags_NoInputs flag is set while dragging a viewport, as want to detect the window behind the one we are dragging. // If you cannot easily access those viewport flags from your windowing/event code: you may manually synchronize its state e.g. in - // your main loop after calling UpdatePlatformWindows(). Iterate all viewports/platform windows and pass the flag to your windowing system. + // your main loop after calling PlatformWindowsUpdate(). Iterate all viewports/platform windows and pass the flag to your windowing system. ImGuiViewport* viewport = (ImGuiViewport*)::GetPropA(hWnd, "IMGUI_VIEWPORT"); if (viewport->Flags & ImGuiViewportFlags_NoInputs) return HTTRANSPARENT; diff --git a/examples/imgui_impl_opengl2.cpp b/examples/imgui_impl_opengl2.cpp index ba440957fcb1..d3a264a3efd6 100644 --- a/examples/imgui_impl_opengl2.cpp +++ b/examples/imgui_impl_opengl2.cpp @@ -283,5 +283,5 @@ static void ImGui_ImplOpenGL2_InitPlatformInterface() static void ImGui_ImplOpenGL2_ShutdownPlatformInterface() { - ImGui::DestroyPlatformWindows(); + ImGui::PlatformWindowsDestroy(); } diff --git a/examples/imgui_impl_opengl3.cpp b/examples/imgui_impl_opengl3.cpp index 6eb38c7ca5c4..624c30ffea8f 100644 --- a/examples/imgui_impl_opengl3.cpp +++ b/examples/imgui_impl_opengl3.cpp @@ -712,5 +712,5 @@ static void ImGui_ImplOpenGL3_InitPlatformInterface() static void ImGui_ImplOpenGL3_ShutdownPlatformInterface() { - ImGui::DestroyPlatformWindows(); + ImGui::PlatformWindowsDestroy(); } diff --git a/examples/imgui_impl_vulkan.cpp b/examples/imgui_impl_vulkan.cpp index 3d0ad9a1e0b2..6482ffd88e44 100644 --- a/examples/imgui_impl_vulkan.cpp +++ b/examples/imgui_impl_vulkan.cpp @@ -1445,5 +1445,5 @@ void ImGui_ImplVulkan_InitPlatformInterface() void ImGui_ImplVulkan_ShutdownPlatformInterface() { - ImGui::DestroyPlatformWindows(); + ImGui::PlatformWindowsDestroy(); } diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index 716709a3714e..b5b272d32e2a 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -798,7 +798,7 @@ static LRESULT CALLBACK ImGui_ImplWin32_WndProcHandler_PlatformWindow(HWND hWnd, // Let mouse pass-through the window. This will allow the back-end to set io.MouseHoveredViewport properly (which is OPTIONAL). // The ImGuiViewportFlags_NoInputs flag is set while dragging a viewport, as want to detect the window behind the one we are dragging. // If you cannot easily access those viewport flags from your windowing/event code: you may manually synchronize its state e.g. in - // your main loop after calling UpdatePlatformWindows(). Iterate all viewports/platform windows and pass the flag to your windowing system. + // your main loop after calling PlatformWindowsUpdate(). Iterate all viewports/platform windows and pass the flag to your windowing system. if (viewport->Flags & ImGuiViewportFlags_NoInputs) return HTTRANSPARENT; break; diff --git a/imgui.cpp b/imgui.cpp index 1331aef9295a..9d3688d66be0 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3912,7 +3912,7 @@ static void NewFrameSanityChecks() { if ((g.IO.BackendFlags & ImGuiBackendFlags_PlatformHasViewports) && (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasViewports)) { - IM_ASSERT((g.FrameCount == 0 || g.FrameCount == g.FrameCountPlatformEnded) && "Forgot to call UpdatePlatformWindows() in main loop after EndFrame()? Check examples/ applications for reference."); + IM_ASSERT((g.FrameCount == 0 || g.FrameCount == g.FrameCountPlatformEnded) && "Forgot to call PlatformWindowsUpdate() in main loop after EndFrame()? Check examples/ applications for reference."); IM_ASSERT(g.PlatformIO.Platform_CreateWindow != NULL && "Platform init didn't install handlers?"); IM_ASSERT(g.PlatformIO.Platform_DestroyWindow != NULL && "Platform init didn't install handlers?"); IM_ASSERT(g.PlatformIO.Platform_GetWindowPos != NULL && "Platform init didn't install handlers?"); @@ -4233,7 +4233,7 @@ void ImGui::Shutdown(ImGuiContext* context) // Destroy platform windows ImGuiContext* backup_context = ImGui::GetCurrentContext(); SetCurrentContext(context); - DestroyPlatformWindows(); + PlatformWindowsDestroy(); SetCurrentContext(backup_context); // Shutdown extensions @@ -10582,13 +10582,13 @@ static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandl // - UpdateViewportsEndFrame() [Internal] // - AddUpdateViewport() [Internal] // - UpdateSelectWindowViewport() [Internal] -// - UpdatePlatformWindows() -// - RenderPlatformWindowsDefault() +// - PlatformWindowsUpdate() +// - PlatformWindowsRender() // - FindPlatformMonitorForPos() [Internal] // - FindPlatformMonitorForRect() [Internal] // - UpdateViewportPlatformMonitor() [Internal] -// - DestroyPlatformWindow() [Internal] -// - DestroyPlatformWindows() +// - PlatformWindowsDestroyOne() [Internal] +// - PlatformWindowsDestroy() //----------------------------------------------------------------------------- ImGuiViewport* ImGui::GetMainViewport() @@ -10809,7 +10809,7 @@ static void ImGui::UpdateViewportsNewFrame() // Destroy IMGUI_DEBUG_LOG_VIEWPORT("Delete Viewport %08X (%s)\n", viewport->ID, viewport->Window ? viewport->Window->Name : "n/a"); - DestroyPlatformWindow(viewport); // In most circumstances the platform window will already be destroyed here. + PlatformWindowsDestroyOne(viewport); // In most circumstances the platform window will already be destroyed here. IM_ASSERT(g.PlatformIO.Viewports.contains(viewport) == false); IM_DELETE(viewport); n--; @@ -10820,7 +10820,7 @@ static void ImGui::UpdateViewportsNewFrame() if (viewports_enabled) { // Update Position and Size (from Platform Window to ImGui) if requested. - // We do it early in the frame instead of waiting for UpdatePlatformWindows() to avoid a frame of lag when moving/resizing using OS facilities. + // We do it early in the frame instead of waiting for PlatformWindowsUpdate() to avoid a frame of lag when moving/resizing using OS facilities. if (!(viewport->Flags & ImGuiViewportFlags_Minimized) && platform_funcs_available) { if (viewport->PlatformRequestMove) @@ -10943,7 +10943,7 @@ static void ImGui::UpdateViewportsEndFrame() IM_ASSERT(viewport->Window != NULL); g.PlatformIO.Viewports.push_back(viewport); } - g.Viewports[0]->ClearRequestFlags(); // Clear main viewport flags because UpdatePlatformWindows() won't do it and may not even be called + g.Viewports[0]->ClearRequestFlags(); // Clear main viewport flags because PlatformWindowsUpdate() won't do it and may not even be called } // FIXME: We should ideally refactor the system to call this every frame (we currently don't) @@ -11135,11 +11135,11 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) } // Called by user at the end of the main loop, after EndFrame() -// This will handle the creation/update of all OS windows via function defined in the ImGuiPlatformIO api. -void ImGui::UpdatePlatformWindows() +// This will handle the creation/update of all OS windows via function defined in the ImGuiPlatformIO API. +void ImGui::PlatformWindowsUpdate() { ImGuiContext& g = *GImGui; - IM_ASSERT(g.FrameCountEnded == g.FrameCount && "Forgot to call Render() or EndFrame() before UpdatePlatformWindows()?"); + IM_ASSERT(g.FrameCountEnded == g.FrameCount && "Forgot to call Render() or EndFrame() before PlatformWindowsUpdate()?"); IM_ASSERT(g.FrameCountPlatformEnded < g.FrameCount); g.FrameCountPlatformEnded = g.FrameCount; if (!(g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable)) @@ -11152,13 +11152,13 @@ void ImGui::UpdatePlatformWindows() ImGuiViewportP* viewport = g.Viewports[i]; // Destroy platform window if the viewport hasn't been submitted or if it is hosting a hidden window - // (the implicit/fallback Debug##Default window will be registering its viewport then be disabled, causing a dummy DestroyPlatformWindow to be made each frame) + // (the implicit/fallback Debug##Default window will be registering its viewport then be disabled, causing a dummy PlatformWindowsDestroyOne to be made each frame) bool destroy_platform_window = false; destroy_platform_window |= (viewport->LastFrameActive < g.FrameCount - 1); destroy_platform_window |= (viewport->Window && !IsWindowActiveAndVisible(viewport->Window)); if (destroy_platform_window) { - DestroyPlatformWindow(viewport); + PlatformWindowsDestroyOne(viewport); continue; } @@ -11267,7 +11267,7 @@ void ImGui::UpdatePlatformWindows() // if ((platform_io.Viewports[i]->Flags & ImGuiViewportFlags_Minimized) == 0) // MySwapBufferFunction(platform_io.Viewports[i], my_args); // -void ImGui::RenderPlatformWindowsDefault(void* platform_render_arg, void* renderer_render_arg) +void ImGui::PlatformWindowsRender(void* platform_render_arg, void* renderer_render_arg) { // Skip the main viewport (index 0), which is always fully handled by the application! ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); @@ -11341,7 +11341,7 @@ static void ImGui::UpdateViewportPlatformMonitor(ImGuiViewportP* viewport) viewport->PlatformMonitor = (short)FindPlatformMonitorForRect(viewport->GetMainRect()); } -void ImGui::DestroyPlatformWindow(ImGuiViewportP* viewport) +void ImGui::PlatformWindowsDestroyOne(ImGuiViewportP* viewport) { ImGuiContext& g = *GImGui; if (viewport->PlatformWindowCreated) @@ -11365,7 +11365,7 @@ void ImGui::DestroyPlatformWindow(ImGuiViewportP* viewport) viewport->ClearRequestFlags(); } -void ImGui::DestroyPlatformWindows() +void ImGui::PlatformWindowsDestroy() { // We call the destroy window on every viewport (including the main viewport, index 0) to give a chance to the back-end // to clear any data they may have stored in e.g. PlatformUserData, RendererUserData. @@ -11375,7 +11375,7 @@ void ImGui::DestroyPlatformWindows() // crashing if it doesn't have data stored. ImGuiContext& g = *GImGui; for (int i = 0; i < g.Viewports.Size; i++) - DestroyPlatformWindow(g.Viewports[i]); + PlatformWindowsDestroyOne(g.Viewports[i]); } diff --git a/imgui.h b/imgui.h index b13f35355d7b..f277e8ff0652 100644 --- a/imgui.h +++ b/imgui.h @@ -779,11 +779,11 @@ namespace ImGui // Note: You may use GetWindowViewport() to get the current viewport of the current window. IMGUI_API ImGuiPlatformIO& GetPlatformIO(); // platform/renderer functions, for back-end to setup + viewports list. IMGUI_API ImGuiViewport* GetMainViewport(); // main viewport. same as GetPlatformIO().MainViewport == GetPlatformIO().Viewports[0]. - IMGUI_API void UpdatePlatformWindows(); // call in main loop. will call CreateWindow/ResizeWindow/etc. platform functions for each secondary viewport, and DestroyWindow for each inactive viewport. - IMGUI_API void RenderPlatformWindowsDefault(void* platform_render_arg = NULL, void* renderer_render_arg = NULL); // call in main loop. will call RenderWindow/SwapBuffers platform functions for each secondary viewport which doesn't have the ImGuiViewportFlags_Minimized flag set. May be reimplemented by user for custom rendering needs. - IMGUI_API void DestroyPlatformWindows(); // call DestroyWindow platform functions for all viewports. call from back-end Shutdown() if you need to close platform windows before imgui shutdown. otherwise will be called by DestroyContext(). - IMGUI_API ImGuiViewport* FindViewportByID(ImGuiID id); // this is a helper for back-ends. - IMGUI_API ImGuiViewport* FindViewportByPlatformHandle(void* platform_handle); // this is a helper for back-ends. the type platform_handle is decided by the back-end (e.g. HWND, MyWindow*, GLFWwindow* etc.) + IMGUI_API void PlatformWindowsUpdate(); // call in main loop. will call CreateWindow/ResizeWindow/etc. platform functions for each secondary viewport, and DestroyWindow for each inactive viewport. + IMGUI_API void PlatformWindowsRender(void* platform_render_arg = NULL, void* renderer_render_arg = NULL); // call in main loop. will call RenderWindow/SwapBuffers platform functions for each secondary viewport which doesn't have the ImGuiViewportFlags_Minimized flag set. May be reimplemented by user for custom rendering needs. + IMGUI_API void PlatformWindowsDestroy(); // call DestroyWindow platform functions for all viewports. call from back-end Shutdown() if you need to close platform windows before imgui shutdown. otherwise will be called by DestroyContext(). + IMGUI_API ImGuiViewport* FindViewportByID(ImGuiID id); // helper for back-ends to find a viewport. + IMGUI_API ImGuiViewport* FindViewportByPlatformHandle(void* platform_handle); // helper for back-ends to find a viewport. the type platform_handle is decided by the back-end (e.g. HWND, MyWindow*, GLFWwindow* etc.) } // namespace ImGui @@ -2412,7 +2412,7 @@ struct ImFont // Steps to use multi-viewports in your application, when using a default back-end from the examples/ folder: // - Application: Enable feature with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. // - Back-end: The back-end initialization will setup all necessary ImGuiPlatformIO's functions and update monitors info every frame. -// - Application: In your main loop, call ImGui::UpdatePlatformWindows(), ImGui::RenderPlatformWindowsDefault() after EndFrame() or Render(). +// - Application: In your main loop, call ImGui::PlatformWindowsUpdate(), ImGui::PlatformWindowsRender() after EndFrame() or Render(). // - Application: Fix absolute coordinates used in ImGui::SetWindowPos() or ImGui::SetNextWindowPos() calls. // // Steps to use multi-viewports in your application, when using a custom back-end: @@ -2424,18 +2424,18 @@ struct ImFont // Set 'io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports' and 'io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports'. // Update ImGuiPlatformIO's Monitors list every frame. // Update MousePos every frame, in absolute coordinates. -// - Application: In your main loop, call ImGui::UpdatePlatformWindows(), ImGui::RenderPlatformWindowsDefault() after EndFrame() or Render(). -// You may skip calling RenderPlatformWindowsDefault() if its API is not convenient for your needs. Read comments below. +// - Application: In your main loop, call ImGui::PlatformWindowsUpdate(), ImGui::PlatformWindowsRender() after EndFrame() or Render(). +// You may skip calling PlatformWindowsRender() if its API is not convenient for your needs. Read comments below. // - Application: Fix absolute coordinates used in ImGui::SetWindowPos() or ImGui::SetNextWindowPos() calls. // -// About ImGui::RenderPlatformWindowsDefault(): +// About ImGui::PlatformWindowsRender(): // - This function is a mostly a _helper_ for the common-most cases, and to facilitate using default back-ends. // - You can check its simple source code to understand what it does. // It basically iterates secondary viewports and call 4 functions that are setup in ImGuiPlatformIO, if available: // Platform_RenderWindow(), Renderer_RenderWindow(), Platform_SwapBuffers(), Renderer_SwapBuffers() -// Those functions pointers exists only for the benefit of RenderPlatformWindowsDefault(). +// Those functions pointers exists only for the benefit of PlatformWindowsRender(). // - If you have very specific rendering needs (e.g. flipping multiple swap-chain simultaneously, unusual sync/threading issues, etc.), -// you may be tempted to ignore RenderPlatformWindowsDefault() and write customized code to perform your renderingg. +// you may be tempted to ignore PlatformWindowsRender() and write customized code to perform your renderingg. // You may decide to setup the platform_io's *RenderWindow and *SwapBuffers pointers and call your functions through those pointers, // or you may decide to never setup those pointers and call your code directly. They are a convenience, not an obligatory interface. //----------------------------------------------------------------------------- @@ -2451,10 +2451,10 @@ struct ImGuiPlatformIO // For reference, the second column shows which function are generally calling the Platform Functions: // N = ImGui::NewFrame() ~ beginning of the dear imgui frame: read info from platform/OS windows (latest size/position) // F = ImGui::Begin(), ImGui::EndFrame() ~ during the dear imgui frame - // U = ImGui::UpdatePlatformWindows() ~ after the dear imgui frame: create and update all platform/OS windows - // R = ImGui::RenderPlatformWindowsDefault() ~ render - // D = ImGui::DestroyPlatformWindows() ~ shutdown - // The general idea is that NewFrame() we will read the current Platform/OS state, and UpdatePlatformWindows() will write to it. + // U = ImGui::PlatformWindowsUpdate() ~ after the dear imgui frame: create and update all platform/OS windows + // R = ImGui::PlatformWindowsRender() ~ render (read comment above about this function!) + // D = ImGui::PlatformWindowsDestroy() ~ shutdown + // The general idea is that NewFrame() we will read the current Platform/OS state, and PlatformWindowsUpdate() will write to it. // // The functions are designed so we can mix and match 2 imgui_impl_xxxx files, one for the Platform (~window/input handling), one for Renderer. // Custom engine back-ends will often provide both Platform and Renderer interfaces and so may not need to use all functions. @@ -2473,9 +2473,9 @@ struct ImGuiPlatformIO bool (*Platform_GetWindowMinimized)(ImGuiViewport* vp); // N . . . . // Get platform window minimized state. When minimized, we generally won't attempt to get/set size and contents will be culled more easily void (*Platform_SetWindowTitle)(ImGuiViewport* vp, const char* str); // . . U . . // Set platform window title (given an UTF-8 string) void (*Platform_SetWindowAlpha)(ImGuiViewport* vp, float alpha); // . . U . . // (Optional) Setup window transparency - void (*Platform_UpdateWindow)(ImGuiViewport* vp); // . . U . . // (Optional) Called by UpdatePlatformWindows(). Optional hook to allow the platform back-end from doing general book-keeping every frame. - void (*Platform_RenderWindow)(ImGuiViewport* vp, void* render_arg); // . . . R . // (Optional) Main rendering (platform side! This is often unused, or just setting a "current" context for OpenGL bindings). 'render_arg' is the value passed to RenderPlatformWindowsDefault(). - void (*Platform_SwapBuffers)(ImGuiViewport* vp, void* render_arg); // . . . R . // (Optional) Call Present/SwapBuffers (platform side! This is often unused!). 'render_arg' is the value passed to RenderPlatformWindowsDefault(). + void (*Platform_UpdateWindow)(ImGuiViewport* vp); // . . U . . // (Optional) Called by PlatformWindowsUpdate(). Optional hook to allow the platform back-end from doing general book-keeping every frame. + void (*Platform_RenderWindow)(ImGuiViewport* vp, void* render_arg); // . . . R . // (Optional) Main rendering (platform side! This is often unused, or just setting a "current" context for OpenGL bindings). 'render_arg' is the value passed to PlatformWindowsRender(). + void (*Platform_SwapBuffers)(ImGuiViewport* vp, void* render_arg); // . . . R . // (Optional) Call Present/SwapBuffers (platform side! This is often unused!). 'render_arg' is the value passed to PlatformWindowsRender(). float (*Platform_GetWindowDpiScale)(ImGuiViewport* vp); // N . . . . // (Optional) [BETA] FIXME-DPI: DPI handling: Return DPI scale for this viewport. 1.0f = 96 DPI. void (*Platform_OnChangedViewport)(ImGuiViewport* vp); // . F . . . // (Optional) [BETA] FIXME-DPI: DPI handling: Called during Begin() every time the viewport we are outputting into changes, so back-end has a chance to swap fonts to adjust style. void (*Platform_SetImeInputPos)(ImGuiViewport* vp, ImVec2 pos); // . F . . . // (Optional) Set IME (Input Method Editor, e.g. for Asian languages) input position, so text preview appears over the imgui input box. FIXME: The call timing of this is inconsistent because we want to support without multi-viewports. @@ -2485,8 +2485,8 @@ struct ImGuiPlatformIO void (*Renderer_CreateWindow)(ImGuiViewport* vp); // . . U . . // Create swap chain, frame buffers etc. (called after Platform_CreateWindow) void (*Renderer_DestroyWindow)(ImGuiViewport* vp); // N . U . D // Destroy swap chain, frame buffers etc. (called before Platform_DestroyWindow) void (*Renderer_SetWindowSize)(ImGuiViewport* vp, ImVec2 size); // . . U . . // Resize swap chain, frame buffers etc. (called after Platform_SetWindowSize) - void (*Renderer_RenderWindow)(ImGuiViewport* vp, void* render_arg); // . . . R . // (Optional) Clear framebuffer, setup render target, then render the viewport->DrawData. 'render_arg' is the value passed to RenderPlatformWindowsDefault(). - void (*Renderer_SwapBuffers)(ImGuiViewport* vp, void* render_arg); // . . . R . // (Optional) Call Present/SwapBuffers. 'render_arg' is the value passed to RenderPlatformWindowsDefault(). + void (*Renderer_RenderWindow)(ImGuiViewport* vp, void* render_arg); // . . . R . // (Optional) Clear framebuffer, setup render target, then render the viewport->DrawData. 'render_arg' is the value passed to PlatformWindowsRender(). + void (*Renderer_SwapBuffers)(ImGuiViewport* vp, void* render_arg); // . . . R . // (Optional) Call Present/SwapBuffers. 'render_arg' is the value passed to PlatformWindowsRender(). // (Optional) Monitor list // - Updated by: app/back-end. Update every frame to dynamically support changing monitor or DPI configuration. diff --git a/imgui_internal.h b/imgui_internal.h index 2e82ed413465..9b71f80606d3 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1869,7 +1869,7 @@ namespace ImGui // Viewports IMGUI_API void TranslateWindowsInViewport(ImGuiViewportP* viewport, const ImVec2& old_pos, const ImVec2& new_pos); IMGUI_API void ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale); - IMGUI_API void DestroyPlatformWindow(ImGuiViewportP* viewport); + IMGUI_API void PlatformWindowsDestroyOne(ImGuiViewportP* viewport); IMGUI_API void ShowViewportThumbnails(); // Settings From fbaf65b8f6b1fe47a70d85ab19e601eb1398bc94 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 6 Mar 2020 17:53:09 +0100 Subject: [PATCH 597/828] Revert "Viewports: Renamed UpdatePlatformWindows > PlatformWindowsUpdate, RenderPlatformWindowsDefault -> PlatformWindowsRender, DestroyPlatformWindows > PlatformWindowsDestroy (#1542)" This reverts commit f2c7f3b200d82f3965bedbe4f52d6082fb74d1be. --- docs/CHANGELOG.txt | 2 +- examples/example_glfw_opengl2/main.cpp | 4 +-- examples/example_glfw_opengl3/main.cpp | 4 +-- examples/example_glfw_vulkan/main.cpp | 4 +-- examples/example_sdl_directx11/main.cpp | 4 +-- examples/example_sdl_opengl2/main.cpp | 4 +-- examples/example_sdl_opengl3/main.cpp | 4 +-- examples/example_sdl_vulkan/main.cpp | 4 +-- examples/example_win32_directx10/main.cpp | 4 +-- examples/example_win32_directx11/main.cpp | 4 +-- examples/example_win32_directx12/main.cpp | 4 +-- examples/example_win32_directx9/main.cpp | 4 +-- examples/imgui_impl_dx10.cpp | 2 +- examples/imgui_impl_dx11.cpp | 2 +- examples/imgui_impl_dx12.cpp | 2 +- examples/imgui_impl_dx9.cpp | 2 +- examples/imgui_impl_glfw.cpp | 2 +- examples/imgui_impl_opengl2.cpp | 2 +- examples/imgui_impl_opengl3.cpp | 2 +- examples/imgui_impl_vulkan.cpp | 2 +- examples/imgui_impl_win32.cpp | 2 +- imgui.cpp | 36 ++++++++++---------- imgui.h | 40 +++++++++++------------ imgui_internal.h | 2 +- 24 files changed, 71 insertions(+), 71 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index c1810abaac95..0289741a95ab 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -82,7 +82,7 @@ Other changes: - Added GetWindowDpiScale(). - Added GetOverlayDrawList(ImGuiViewport* viewport). The no-parameter version of GetOverlayDrawList() return the overlay for the current window's viewport. -- Added PlatformWindowsUpdate(), PlatformWindowsRender(), PlatformWindowsDestroy() for usage for application core. +- Added UpdatePlatformWindows(), RenderPlatformWindows(), DestroyPlatformWindows() for usage for application core. - Added FindViewportByID(), FindViewportByPlatformHandle() for usage by back-ends. - Added ImGuiConfigFlags_ViewportsEnable configuration flag and other viewport options. - Added io.ConfigViewportsNoAutoMerge, io.ConfigViewportsNoTaskBarIcon, io.ConfigViewportsNoDecoration, io.ConfigViewportsNoDefaultParent options. diff --git a/examples/example_glfw_opengl2/main.cpp b/examples/example_glfw_opengl2/main.cpp index 585db73fe69d..051d4281b9d4 100644 --- a/examples/example_glfw_opengl2/main.cpp +++ b/examples/example_glfw_opengl2/main.cpp @@ -160,8 +160,8 @@ int main(int, char**) if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { GLFWwindow* backup_current_context = glfwGetCurrentContext(); - ImGui::PlatformWindowsUpdate(); - ImGui::PlatformWindowsRender(); + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); glfwMakeContextCurrent(backup_current_context); } diff --git a/examples/example_glfw_opengl3/main.cpp b/examples/example_glfw_opengl3/main.cpp index b8de5cbe0b91..0f29f849f4e6 100644 --- a/examples/example_glfw_opengl3/main.cpp +++ b/examples/example_glfw_opengl3/main.cpp @@ -205,8 +205,8 @@ int main(int, char**) if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { GLFWwindow* backup_current_context = glfwGetCurrentContext(); - ImGui::PlatformWindowsUpdate(); - ImGui::PlatformWindowsRender(); + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); glfwMakeContextCurrent(backup_current_context); } diff --git a/examples/example_glfw_vulkan/main.cpp b/examples/example_glfw_vulkan/main.cpp index f30219f677eb..da23d24708a8 100644 --- a/examples/example_glfw_vulkan/main.cpp +++ b/examples/example_glfw_vulkan/main.cpp @@ -525,8 +525,8 @@ int main(int, char**) // Update and Render additional Platform Windows if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { - ImGui::PlatformWindowsUpdate(); - ImGui::PlatformWindowsRender(); + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); } FramePresent(wd); diff --git a/examples/example_sdl_directx11/main.cpp b/examples/example_sdl_directx11/main.cpp index ea2e85a5cc5c..60c06245aed9 100644 --- a/examples/example_sdl_directx11/main.cpp +++ b/examples/example_sdl_directx11/main.cpp @@ -166,8 +166,8 @@ int main(int, char**) // Update and Render additional Platform Windows if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { - ImGui::PlatformWindowsUpdate(); - ImGui::PlatformWindowsRender(); + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); } g_pSwapChain->Present(1, 0); // Present with vsync diff --git a/examples/example_sdl_opengl2/main.cpp b/examples/example_sdl_opengl2/main.cpp index 78b0b220c754..aa76563464ce 100644 --- a/examples/example_sdl_opengl2/main.cpp +++ b/examples/example_sdl_opengl2/main.cpp @@ -158,8 +158,8 @@ int main(int, char**) { SDL_Window* backup_current_window = SDL_GL_GetCurrentWindow(); SDL_GLContext backup_current_context = SDL_GL_GetCurrentContext(); - ImGui::PlatformWindowsUpdate(); - ImGui::PlatformWindowsRender(); + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); SDL_GL_MakeCurrent(backup_current_window, backup_current_context); } diff --git a/examples/example_sdl_opengl3/main.cpp b/examples/example_sdl_opengl3/main.cpp index e8f7441b5b39..af0892870444 100644 --- a/examples/example_sdl_opengl3/main.cpp +++ b/examples/example_sdl_opengl3/main.cpp @@ -207,8 +207,8 @@ int main(int, char**) { SDL_Window* backup_current_window = SDL_GL_GetCurrentWindow(); SDL_GLContext backup_current_context = SDL_GL_GetCurrentContext(); - ImGui::PlatformWindowsUpdate(); - ImGui::PlatformWindowsRender(); + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); SDL_GL_MakeCurrent(backup_current_window, backup_current_context); } diff --git a/examples/example_sdl_vulkan/main.cpp b/examples/example_sdl_vulkan/main.cpp index ec99439bdcc6..7d84f34af63b 100644 --- a/examples/example_sdl_vulkan/main.cpp +++ b/examples/example_sdl_vulkan/main.cpp @@ -522,8 +522,8 @@ int main(int, char**) // Update and Render additional Platform Windows if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { - ImGui::PlatformWindowsUpdate(); - ImGui::PlatformWindowsRender(); + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); } FramePresent(wd); diff --git a/examples/example_win32_directx10/main.cpp b/examples/example_win32_directx10/main.cpp index f30b26822076..284bf787456f 100644 --- a/examples/example_win32_directx10/main.cpp +++ b/examples/example_win32_directx10/main.cpp @@ -159,8 +159,8 @@ int main(int, char**) // Update and Render additional Platform Windows if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { - ImGui::PlatformWindowsUpdate(); - ImGui::PlatformWindowsRender(); + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); } g_pSwapChain->Present(1, 0); // Present with vsync diff --git a/examples/example_win32_directx11/main.cpp b/examples/example_win32_directx11/main.cpp index 07cf519ddbbe..d3828ff942b8 100644 --- a/examples/example_win32_directx11/main.cpp +++ b/examples/example_win32_directx11/main.cpp @@ -166,8 +166,8 @@ int main(int, char**) // Update and Render additional Platform Windows if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { - ImGui::PlatformWindowsUpdate(); - ImGui::PlatformWindowsRender(); + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); } g_pSwapChain->Present(1, 0); // Present with vsync diff --git a/examples/example_win32_directx12/main.cpp b/examples/example_win32_directx12/main.cpp index 6c1e3deb9cbe..259c30269a55 100644 --- a/examples/example_win32_directx12/main.cpp +++ b/examples/example_win32_directx12/main.cpp @@ -210,8 +210,8 @@ int main(int, char**) // Update and Render additional Platform Windows if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { - ImGui::PlatformWindowsUpdate(); - ImGui::PlatformWindowsRender(NULL, (void*)g_pd3dCommandList); + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(NULL, (void*)g_pd3dCommandList); } g_pSwapChain->Present(1, 0); // Present with vsync diff --git a/examples/example_win32_directx9/main.cpp b/examples/example_win32_directx9/main.cpp index 3be45fa07dae..d96e5c481323 100644 --- a/examples/example_win32_directx9/main.cpp +++ b/examples/example_win32_directx9/main.cpp @@ -165,8 +165,8 @@ int main(int, char**) // Update and Render additional Platform Windows if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { - ImGui::PlatformWindowsUpdate(); - ImGui::PlatformWindowsRender(); + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); } HRESULT result = g_pd3dDevice->Present(NULL, NULL, NULL, NULL); diff --git a/examples/imgui_impl_dx10.cpp b/examples/imgui_impl_dx10.cpp index 852a908efffd..95dc6fd28e6d 100644 --- a/examples/imgui_impl_dx10.cpp +++ b/examples/imgui_impl_dx10.cpp @@ -655,6 +655,6 @@ void ImGui_ImplDX10_InitPlatformInterface() void ImGui_ImplDX10_ShutdownPlatformInterface() { - ImGui::PlatformWindowsDestroy(); + ImGui::DestroyPlatformWindows(); } diff --git a/examples/imgui_impl_dx11.cpp b/examples/imgui_impl_dx11.cpp index 2a40cc5831a0..ffd05625b3e8 100644 --- a/examples/imgui_impl_dx11.cpp +++ b/examples/imgui_impl_dx11.cpp @@ -671,5 +671,5 @@ static void ImGui_ImplDX11_InitPlatformInterface() static void ImGui_ImplDX11_ShutdownPlatformInterface() { - ImGui::PlatformWindowsDestroy(); + ImGui::DestroyPlatformWindows(); } diff --git a/examples/imgui_impl_dx12.cpp b/examples/imgui_impl_dx12.cpp index 57ac0afe166a..3ead29c5affd 100644 --- a/examples/imgui_impl_dx12.cpp +++ b/examples/imgui_impl_dx12.cpp @@ -947,5 +947,5 @@ void ImGui_ImplDX12_InitPlatformInterface() void ImGui_ImplDX12_ShutdownPlatformInterface() { - ImGui::PlatformWindowsDestroy(); + ImGui::DestroyPlatformWindows(); } diff --git a/examples/imgui_impl_dx9.cpp b/examples/imgui_impl_dx9.cpp index 0a22f9228358..27435cbc38f7 100644 --- a/examples/imgui_impl_dx9.cpp +++ b/examples/imgui_impl_dx9.cpp @@ -423,7 +423,7 @@ static void ImGui_ImplDX9_InitPlatformInterface() static void ImGui_ImplDX9_ShutdownPlatformInterface() { - ImGui::PlatformWindowsDestroy(); + ImGui::DestroyPlatformWindows(); } static void ImGui_ImplDX9_CreateDeviceObjectsForPlatformWindows() diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index 462c35fef2a7..bcbee43655d7 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -598,7 +598,7 @@ static LRESULT CALLBACK WndProcNoInputs(HWND hWnd, UINT msg, WPARAM wParam, LPAR // Let mouse pass-through the window. This will allow the back-end to set io.MouseHoveredViewport properly (which is OPTIONAL). // The ImGuiViewportFlags_NoInputs flag is set while dragging a viewport, as want to detect the window behind the one we are dragging. // If you cannot easily access those viewport flags from your windowing/event code: you may manually synchronize its state e.g. in - // your main loop after calling PlatformWindowsUpdate(). Iterate all viewports/platform windows and pass the flag to your windowing system. + // your main loop after calling UpdatePlatformWindows(). Iterate all viewports/platform windows and pass the flag to your windowing system. ImGuiViewport* viewport = (ImGuiViewport*)::GetPropA(hWnd, "IMGUI_VIEWPORT"); if (viewport->Flags & ImGuiViewportFlags_NoInputs) return HTTRANSPARENT; diff --git a/examples/imgui_impl_opengl2.cpp b/examples/imgui_impl_opengl2.cpp index d3a264a3efd6..ba440957fcb1 100644 --- a/examples/imgui_impl_opengl2.cpp +++ b/examples/imgui_impl_opengl2.cpp @@ -283,5 +283,5 @@ static void ImGui_ImplOpenGL2_InitPlatformInterface() static void ImGui_ImplOpenGL2_ShutdownPlatformInterface() { - ImGui::PlatformWindowsDestroy(); + ImGui::DestroyPlatformWindows(); } diff --git a/examples/imgui_impl_opengl3.cpp b/examples/imgui_impl_opengl3.cpp index 624c30ffea8f..6eb38c7ca5c4 100644 --- a/examples/imgui_impl_opengl3.cpp +++ b/examples/imgui_impl_opengl3.cpp @@ -712,5 +712,5 @@ static void ImGui_ImplOpenGL3_InitPlatformInterface() static void ImGui_ImplOpenGL3_ShutdownPlatformInterface() { - ImGui::PlatformWindowsDestroy(); + ImGui::DestroyPlatformWindows(); } diff --git a/examples/imgui_impl_vulkan.cpp b/examples/imgui_impl_vulkan.cpp index 6482ffd88e44..3d0ad9a1e0b2 100644 --- a/examples/imgui_impl_vulkan.cpp +++ b/examples/imgui_impl_vulkan.cpp @@ -1445,5 +1445,5 @@ void ImGui_ImplVulkan_InitPlatformInterface() void ImGui_ImplVulkan_ShutdownPlatformInterface() { - ImGui::PlatformWindowsDestroy(); + ImGui::DestroyPlatformWindows(); } diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index b5b272d32e2a..716709a3714e 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -798,7 +798,7 @@ static LRESULT CALLBACK ImGui_ImplWin32_WndProcHandler_PlatformWindow(HWND hWnd, // Let mouse pass-through the window. This will allow the back-end to set io.MouseHoveredViewport properly (which is OPTIONAL). // The ImGuiViewportFlags_NoInputs flag is set while dragging a viewport, as want to detect the window behind the one we are dragging. // If you cannot easily access those viewport flags from your windowing/event code: you may manually synchronize its state e.g. in - // your main loop after calling PlatformWindowsUpdate(). Iterate all viewports/platform windows and pass the flag to your windowing system. + // your main loop after calling UpdatePlatformWindows(). Iterate all viewports/platform windows and pass the flag to your windowing system. if (viewport->Flags & ImGuiViewportFlags_NoInputs) return HTTRANSPARENT; break; diff --git a/imgui.cpp b/imgui.cpp index 9d3688d66be0..1331aef9295a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3912,7 +3912,7 @@ static void NewFrameSanityChecks() { if ((g.IO.BackendFlags & ImGuiBackendFlags_PlatformHasViewports) && (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasViewports)) { - IM_ASSERT((g.FrameCount == 0 || g.FrameCount == g.FrameCountPlatformEnded) && "Forgot to call PlatformWindowsUpdate() in main loop after EndFrame()? Check examples/ applications for reference."); + IM_ASSERT((g.FrameCount == 0 || g.FrameCount == g.FrameCountPlatformEnded) && "Forgot to call UpdatePlatformWindows() in main loop after EndFrame()? Check examples/ applications for reference."); IM_ASSERT(g.PlatformIO.Platform_CreateWindow != NULL && "Platform init didn't install handlers?"); IM_ASSERT(g.PlatformIO.Platform_DestroyWindow != NULL && "Platform init didn't install handlers?"); IM_ASSERT(g.PlatformIO.Platform_GetWindowPos != NULL && "Platform init didn't install handlers?"); @@ -4233,7 +4233,7 @@ void ImGui::Shutdown(ImGuiContext* context) // Destroy platform windows ImGuiContext* backup_context = ImGui::GetCurrentContext(); SetCurrentContext(context); - PlatformWindowsDestroy(); + DestroyPlatformWindows(); SetCurrentContext(backup_context); // Shutdown extensions @@ -10582,13 +10582,13 @@ static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandl // - UpdateViewportsEndFrame() [Internal] // - AddUpdateViewport() [Internal] // - UpdateSelectWindowViewport() [Internal] -// - PlatformWindowsUpdate() -// - PlatformWindowsRender() +// - UpdatePlatformWindows() +// - RenderPlatformWindowsDefault() // - FindPlatformMonitorForPos() [Internal] // - FindPlatformMonitorForRect() [Internal] // - UpdateViewportPlatformMonitor() [Internal] -// - PlatformWindowsDestroyOne() [Internal] -// - PlatformWindowsDestroy() +// - DestroyPlatformWindow() [Internal] +// - DestroyPlatformWindows() //----------------------------------------------------------------------------- ImGuiViewport* ImGui::GetMainViewport() @@ -10809,7 +10809,7 @@ static void ImGui::UpdateViewportsNewFrame() // Destroy IMGUI_DEBUG_LOG_VIEWPORT("Delete Viewport %08X (%s)\n", viewport->ID, viewport->Window ? viewport->Window->Name : "n/a"); - PlatformWindowsDestroyOne(viewport); // In most circumstances the platform window will already be destroyed here. + DestroyPlatformWindow(viewport); // In most circumstances the platform window will already be destroyed here. IM_ASSERT(g.PlatformIO.Viewports.contains(viewport) == false); IM_DELETE(viewport); n--; @@ -10820,7 +10820,7 @@ static void ImGui::UpdateViewportsNewFrame() if (viewports_enabled) { // Update Position and Size (from Platform Window to ImGui) if requested. - // We do it early in the frame instead of waiting for PlatformWindowsUpdate() to avoid a frame of lag when moving/resizing using OS facilities. + // We do it early in the frame instead of waiting for UpdatePlatformWindows() to avoid a frame of lag when moving/resizing using OS facilities. if (!(viewport->Flags & ImGuiViewportFlags_Minimized) && platform_funcs_available) { if (viewport->PlatformRequestMove) @@ -10943,7 +10943,7 @@ static void ImGui::UpdateViewportsEndFrame() IM_ASSERT(viewport->Window != NULL); g.PlatformIO.Viewports.push_back(viewport); } - g.Viewports[0]->ClearRequestFlags(); // Clear main viewport flags because PlatformWindowsUpdate() won't do it and may not even be called + g.Viewports[0]->ClearRequestFlags(); // Clear main viewport flags because UpdatePlatformWindows() won't do it and may not even be called } // FIXME: We should ideally refactor the system to call this every frame (we currently don't) @@ -11135,11 +11135,11 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) } // Called by user at the end of the main loop, after EndFrame() -// This will handle the creation/update of all OS windows via function defined in the ImGuiPlatformIO API. -void ImGui::PlatformWindowsUpdate() +// This will handle the creation/update of all OS windows via function defined in the ImGuiPlatformIO api. +void ImGui::UpdatePlatformWindows() { ImGuiContext& g = *GImGui; - IM_ASSERT(g.FrameCountEnded == g.FrameCount && "Forgot to call Render() or EndFrame() before PlatformWindowsUpdate()?"); + IM_ASSERT(g.FrameCountEnded == g.FrameCount && "Forgot to call Render() or EndFrame() before UpdatePlatformWindows()?"); IM_ASSERT(g.FrameCountPlatformEnded < g.FrameCount); g.FrameCountPlatformEnded = g.FrameCount; if (!(g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable)) @@ -11152,13 +11152,13 @@ void ImGui::PlatformWindowsUpdate() ImGuiViewportP* viewport = g.Viewports[i]; // Destroy platform window if the viewport hasn't been submitted or if it is hosting a hidden window - // (the implicit/fallback Debug##Default window will be registering its viewport then be disabled, causing a dummy PlatformWindowsDestroyOne to be made each frame) + // (the implicit/fallback Debug##Default window will be registering its viewport then be disabled, causing a dummy DestroyPlatformWindow to be made each frame) bool destroy_platform_window = false; destroy_platform_window |= (viewport->LastFrameActive < g.FrameCount - 1); destroy_platform_window |= (viewport->Window && !IsWindowActiveAndVisible(viewport->Window)); if (destroy_platform_window) { - PlatformWindowsDestroyOne(viewport); + DestroyPlatformWindow(viewport); continue; } @@ -11267,7 +11267,7 @@ void ImGui::PlatformWindowsUpdate() // if ((platform_io.Viewports[i]->Flags & ImGuiViewportFlags_Minimized) == 0) // MySwapBufferFunction(platform_io.Viewports[i], my_args); // -void ImGui::PlatformWindowsRender(void* platform_render_arg, void* renderer_render_arg) +void ImGui::RenderPlatformWindowsDefault(void* platform_render_arg, void* renderer_render_arg) { // Skip the main viewport (index 0), which is always fully handled by the application! ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); @@ -11341,7 +11341,7 @@ static void ImGui::UpdateViewportPlatformMonitor(ImGuiViewportP* viewport) viewport->PlatformMonitor = (short)FindPlatformMonitorForRect(viewport->GetMainRect()); } -void ImGui::PlatformWindowsDestroyOne(ImGuiViewportP* viewport) +void ImGui::DestroyPlatformWindow(ImGuiViewportP* viewport) { ImGuiContext& g = *GImGui; if (viewport->PlatformWindowCreated) @@ -11365,7 +11365,7 @@ void ImGui::PlatformWindowsDestroyOne(ImGuiViewportP* viewport) viewport->ClearRequestFlags(); } -void ImGui::PlatformWindowsDestroy() +void ImGui::DestroyPlatformWindows() { // We call the destroy window on every viewport (including the main viewport, index 0) to give a chance to the back-end // to clear any data they may have stored in e.g. PlatformUserData, RendererUserData. @@ -11375,7 +11375,7 @@ void ImGui::PlatformWindowsDestroy() // crashing if it doesn't have data stored. ImGuiContext& g = *GImGui; for (int i = 0; i < g.Viewports.Size; i++) - PlatformWindowsDestroyOne(g.Viewports[i]); + DestroyPlatformWindow(g.Viewports[i]); } diff --git a/imgui.h b/imgui.h index f277e8ff0652..b13f35355d7b 100644 --- a/imgui.h +++ b/imgui.h @@ -779,11 +779,11 @@ namespace ImGui // Note: You may use GetWindowViewport() to get the current viewport of the current window. IMGUI_API ImGuiPlatformIO& GetPlatformIO(); // platform/renderer functions, for back-end to setup + viewports list. IMGUI_API ImGuiViewport* GetMainViewport(); // main viewport. same as GetPlatformIO().MainViewport == GetPlatformIO().Viewports[0]. - IMGUI_API void PlatformWindowsUpdate(); // call in main loop. will call CreateWindow/ResizeWindow/etc. platform functions for each secondary viewport, and DestroyWindow for each inactive viewport. - IMGUI_API void PlatformWindowsRender(void* platform_render_arg = NULL, void* renderer_render_arg = NULL); // call in main loop. will call RenderWindow/SwapBuffers platform functions for each secondary viewport which doesn't have the ImGuiViewportFlags_Minimized flag set. May be reimplemented by user for custom rendering needs. - IMGUI_API void PlatformWindowsDestroy(); // call DestroyWindow platform functions for all viewports. call from back-end Shutdown() if you need to close platform windows before imgui shutdown. otherwise will be called by DestroyContext(). - IMGUI_API ImGuiViewport* FindViewportByID(ImGuiID id); // helper for back-ends to find a viewport. - IMGUI_API ImGuiViewport* FindViewportByPlatformHandle(void* platform_handle); // helper for back-ends to find a viewport. the type platform_handle is decided by the back-end (e.g. HWND, MyWindow*, GLFWwindow* etc.) + IMGUI_API void UpdatePlatformWindows(); // call in main loop. will call CreateWindow/ResizeWindow/etc. platform functions for each secondary viewport, and DestroyWindow for each inactive viewport. + IMGUI_API void RenderPlatformWindowsDefault(void* platform_render_arg = NULL, void* renderer_render_arg = NULL); // call in main loop. will call RenderWindow/SwapBuffers platform functions for each secondary viewport which doesn't have the ImGuiViewportFlags_Minimized flag set. May be reimplemented by user for custom rendering needs. + IMGUI_API void DestroyPlatformWindows(); // call DestroyWindow platform functions for all viewports. call from back-end Shutdown() if you need to close platform windows before imgui shutdown. otherwise will be called by DestroyContext(). + IMGUI_API ImGuiViewport* FindViewportByID(ImGuiID id); // this is a helper for back-ends. + IMGUI_API ImGuiViewport* FindViewportByPlatformHandle(void* platform_handle); // this is a helper for back-ends. the type platform_handle is decided by the back-end (e.g. HWND, MyWindow*, GLFWwindow* etc.) } // namespace ImGui @@ -2412,7 +2412,7 @@ struct ImFont // Steps to use multi-viewports in your application, when using a default back-end from the examples/ folder: // - Application: Enable feature with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. // - Back-end: The back-end initialization will setup all necessary ImGuiPlatformIO's functions and update monitors info every frame. -// - Application: In your main loop, call ImGui::PlatformWindowsUpdate(), ImGui::PlatformWindowsRender() after EndFrame() or Render(). +// - Application: In your main loop, call ImGui::UpdatePlatformWindows(), ImGui::RenderPlatformWindowsDefault() after EndFrame() or Render(). // - Application: Fix absolute coordinates used in ImGui::SetWindowPos() or ImGui::SetNextWindowPos() calls. // // Steps to use multi-viewports in your application, when using a custom back-end: @@ -2424,18 +2424,18 @@ struct ImFont // Set 'io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports' and 'io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports'. // Update ImGuiPlatformIO's Monitors list every frame. // Update MousePos every frame, in absolute coordinates. -// - Application: In your main loop, call ImGui::PlatformWindowsUpdate(), ImGui::PlatformWindowsRender() after EndFrame() or Render(). -// You may skip calling PlatformWindowsRender() if its API is not convenient for your needs. Read comments below. +// - Application: In your main loop, call ImGui::UpdatePlatformWindows(), ImGui::RenderPlatformWindowsDefault() after EndFrame() or Render(). +// You may skip calling RenderPlatformWindowsDefault() if its API is not convenient for your needs. Read comments below. // - Application: Fix absolute coordinates used in ImGui::SetWindowPos() or ImGui::SetNextWindowPos() calls. // -// About ImGui::PlatformWindowsRender(): +// About ImGui::RenderPlatformWindowsDefault(): // - This function is a mostly a _helper_ for the common-most cases, and to facilitate using default back-ends. // - You can check its simple source code to understand what it does. // It basically iterates secondary viewports and call 4 functions that are setup in ImGuiPlatformIO, if available: // Platform_RenderWindow(), Renderer_RenderWindow(), Platform_SwapBuffers(), Renderer_SwapBuffers() -// Those functions pointers exists only for the benefit of PlatformWindowsRender(). +// Those functions pointers exists only for the benefit of RenderPlatformWindowsDefault(). // - If you have very specific rendering needs (e.g. flipping multiple swap-chain simultaneously, unusual sync/threading issues, etc.), -// you may be tempted to ignore PlatformWindowsRender() and write customized code to perform your renderingg. +// you may be tempted to ignore RenderPlatformWindowsDefault() and write customized code to perform your renderingg. // You may decide to setup the platform_io's *RenderWindow and *SwapBuffers pointers and call your functions through those pointers, // or you may decide to never setup those pointers and call your code directly. They are a convenience, not an obligatory interface. //----------------------------------------------------------------------------- @@ -2451,10 +2451,10 @@ struct ImGuiPlatformIO // For reference, the second column shows which function are generally calling the Platform Functions: // N = ImGui::NewFrame() ~ beginning of the dear imgui frame: read info from platform/OS windows (latest size/position) // F = ImGui::Begin(), ImGui::EndFrame() ~ during the dear imgui frame - // U = ImGui::PlatformWindowsUpdate() ~ after the dear imgui frame: create and update all platform/OS windows - // R = ImGui::PlatformWindowsRender() ~ render (read comment above about this function!) - // D = ImGui::PlatformWindowsDestroy() ~ shutdown - // The general idea is that NewFrame() we will read the current Platform/OS state, and PlatformWindowsUpdate() will write to it. + // U = ImGui::UpdatePlatformWindows() ~ after the dear imgui frame: create and update all platform/OS windows + // R = ImGui::RenderPlatformWindowsDefault() ~ render + // D = ImGui::DestroyPlatformWindows() ~ shutdown + // The general idea is that NewFrame() we will read the current Platform/OS state, and UpdatePlatformWindows() will write to it. // // The functions are designed so we can mix and match 2 imgui_impl_xxxx files, one for the Platform (~window/input handling), one for Renderer. // Custom engine back-ends will often provide both Platform and Renderer interfaces and so may not need to use all functions. @@ -2473,9 +2473,9 @@ struct ImGuiPlatformIO bool (*Platform_GetWindowMinimized)(ImGuiViewport* vp); // N . . . . // Get platform window minimized state. When minimized, we generally won't attempt to get/set size and contents will be culled more easily void (*Platform_SetWindowTitle)(ImGuiViewport* vp, const char* str); // . . U . . // Set platform window title (given an UTF-8 string) void (*Platform_SetWindowAlpha)(ImGuiViewport* vp, float alpha); // . . U . . // (Optional) Setup window transparency - void (*Platform_UpdateWindow)(ImGuiViewport* vp); // . . U . . // (Optional) Called by PlatformWindowsUpdate(). Optional hook to allow the platform back-end from doing general book-keeping every frame. - void (*Platform_RenderWindow)(ImGuiViewport* vp, void* render_arg); // . . . R . // (Optional) Main rendering (platform side! This is often unused, or just setting a "current" context for OpenGL bindings). 'render_arg' is the value passed to PlatformWindowsRender(). - void (*Platform_SwapBuffers)(ImGuiViewport* vp, void* render_arg); // . . . R . // (Optional) Call Present/SwapBuffers (platform side! This is often unused!). 'render_arg' is the value passed to PlatformWindowsRender(). + void (*Platform_UpdateWindow)(ImGuiViewport* vp); // . . U . . // (Optional) Called by UpdatePlatformWindows(). Optional hook to allow the platform back-end from doing general book-keeping every frame. + void (*Platform_RenderWindow)(ImGuiViewport* vp, void* render_arg); // . . . R . // (Optional) Main rendering (platform side! This is often unused, or just setting a "current" context for OpenGL bindings). 'render_arg' is the value passed to RenderPlatformWindowsDefault(). + void (*Platform_SwapBuffers)(ImGuiViewport* vp, void* render_arg); // . . . R . // (Optional) Call Present/SwapBuffers (platform side! This is often unused!). 'render_arg' is the value passed to RenderPlatformWindowsDefault(). float (*Platform_GetWindowDpiScale)(ImGuiViewport* vp); // N . . . . // (Optional) [BETA] FIXME-DPI: DPI handling: Return DPI scale for this viewport. 1.0f = 96 DPI. void (*Platform_OnChangedViewport)(ImGuiViewport* vp); // . F . . . // (Optional) [BETA] FIXME-DPI: DPI handling: Called during Begin() every time the viewport we are outputting into changes, so back-end has a chance to swap fonts to adjust style. void (*Platform_SetImeInputPos)(ImGuiViewport* vp, ImVec2 pos); // . F . . . // (Optional) Set IME (Input Method Editor, e.g. for Asian languages) input position, so text preview appears over the imgui input box. FIXME: The call timing of this is inconsistent because we want to support without multi-viewports. @@ -2485,8 +2485,8 @@ struct ImGuiPlatformIO void (*Renderer_CreateWindow)(ImGuiViewport* vp); // . . U . . // Create swap chain, frame buffers etc. (called after Platform_CreateWindow) void (*Renderer_DestroyWindow)(ImGuiViewport* vp); // N . U . D // Destroy swap chain, frame buffers etc. (called before Platform_DestroyWindow) void (*Renderer_SetWindowSize)(ImGuiViewport* vp, ImVec2 size); // . . U . . // Resize swap chain, frame buffers etc. (called after Platform_SetWindowSize) - void (*Renderer_RenderWindow)(ImGuiViewport* vp, void* render_arg); // . . . R . // (Optional) Clear framebuffer, setup render target, then render the viewport->DrawData. 'render_arg' is the value passed to PlatformWindowsRender(). - void (*Renderer_SwapBuffers)(ImGuiViewport* vp, void* render_arg); // . . . R . // (Optional) Call Present/SwapBuffers. 'render_arg' is the value passed to PlatformWindowsRender(). + void (*Renderer_RenderWindow)(ImGuiViewport* vp, void* render_arg); // . . . R . // (Optional) Clear framebuffer, setup render target, then render the viewport->DrawData. 'render_arg' is the value passed to RenderPlatformWindowsDefault(). + void (*Renderer_SwapBuffers)(ImGuiViewport* vp, void* render_arg); // . . . R . // (Optional) Call Present/SwapBuffers. 'render_arg' is the value passed to RenderPlatformWindowsDefault(). // (Optional) Monitor list // - Updated by: app/back-end. Update every frame to dynamically support changing monitor or DPI configuration. diff --git a/imgui_internal.h b/imgui_internal.h index 9b71f80606d3..2e82ed413465 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1869,7 +1869,7 @@ namespace ImGui // Viewports IMGUI_API void TranslateWindowsInViewport(ImGuiViewportP* viewport, const ImVec2& old_pos, const ImVec2& new_pos); IMGUI_API void ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale); - IMGUI_API void PlatformWindowsDestroyOne(ImGuiViewportP* viewport); + IMGUI_API void DestroyPlatformWindow(ImGuiViewportP* viewport); IMGUI_API void ShowViewportThumbnails(); // Settings From 1abb02fb67e44af8610cd2af94eb8344a30e056f Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 9 Mar 2020 14:20:59 +0100 Subject: [PATCH 598/828] Viewports: Fix for UWP on IME code moved back-end side (#2895, #2892) + Docking: Comments. --- examples/imgui_impl_win32.cpp | 4 ++++ imgui.h | 2 +- imgui_internal.h | 7 ++++--- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index 716709a3714e..a63fa464877a 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -529,6 +529,10 @@ float ImGui_ImplWin32_GetDpiScaleForHwnd(void* hwnd) // IME (Input Method Editor) basic support for e.g. Asian language users //-------------------------------------------------------------------------------------------------------- +#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) // UWP doesn't have Win32 functions +#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS +#endif + #if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS) && !defined(__GNUC__) #define HAS_WIN32_IME 1 #include diff --git a/imgui.h b/imgui.h index b13f35355d7b..881c91f75722 100644 --- a/imgui.h +++ b/imgui.h @@ -2526,7 +2526,7 @@ enum ImGuiViewportFlags_ ImGuiViewportFlags_NoRendererClear = 1 << 5, // Platform Window: Renderer doesn't need to clear the framebuffer ahead (because we will fill it entirely). ImGuiViewportFlags_TopMost = 1 << 6, // Platform Window: Display on top (for tooltips only). ImGuiViewportFlags_Minimized = 1 << 7, // Platform Window: Window is minimized, can skip render. When minimized we tend to avoid using the viewport pos/size for clipping window or testing if they are contained in the viewport. - ImGuiViewportFlags_NoAutoMerge = 1 << 8, // Platform Window: Avoid merging this widow into another host window. This can only be set via ImGuiWindowClass viewport flags override (because we need to now ahead if we are going to create a viewport in the first place!). + ImGuiViewportFlags_NoAutoMerge = 1 << 8, // Platform Window: Avoid merging this window into another host window. This can only be set via ImGuiWindowClass viewport flags override (because we need to now ahead if we are going to create a viewport in the first place!). ImGuiViewportFlags_CanHostOtherWindows = 1 << 9 // Main viewport: can host multiple imgui windows (secondary viewports are associated to a single window). }; diff --git a/imgui_internal.h b/imgui_internal.h index 2e82ed413465..fbce3e645bdf 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1053,7 +1053,7 @@ enum ImGuiDockNodeFlagsPrivate_ { // [Internal] ImGuiDockNodeFlags_DockSpace = 1 << 10, // Local, Saved // A dockspace is a node that occupy space within an existing user window. Otherwise the node is floating and create its own window. - ImGuiDockNodeFlags_CentralNode = 1 << 11, // Local, Saved // + ImGuiDockNodeFlags_CentralNode = 1 << 11, // Local, Saved // The central node has 2 main properties: stay visible when empty, only use "remaining" spaces from its neighbor. ImGuiDockNodeFlags_NoTabBar = 1 << 12, // Local, Saved // Tab bar is completely unavailable. No triangle in the corner to enable it back. ImGuiDockNodeFlags_HiddenTabBar = 1 << 13, // Local, Saved // Tab bar is hidden, with a triangle in the corner to show it again (NB: actual tab-bar instance may be destroyed as this is only used for single-window tab bar) ImGuiDockNodeFlags_NoWindowMenuButton = 1 << 14, // Local, Saved // Disable window/docking menu (that one that appears instead of the collapse button) @@ -1987,7 +1987,8 @@ namespace ImGui // - The DockBuilderXXX functions are designed to _eventually_ become a public API, but it is too early to expose it and guarantee stability. // - Do not hold on ImGuiDockNode* pointers! They may be invalidated by any split/merge/remove operation and every frame. // - To create a DockSpace() node, make sure to set the ImGuiDockNodeFlags_DockSpace flag when calling DockBuilderAddNode(). - // You can create dockspace nodes (attached to a window) _or_ floating nodes (carry its own window) with this API. + // You can create dockspace nodes (attached to a window) _or_ floating nodes (carry its own window) with this API. + // - DockBuilderSplitNode() create 2 child nodes within 1 node. The initial node becomes a parent node. // - If you intend to split the node immediately after creation using DockBuilderSplitNode(), make sure // to call DockBuilderSetNodeSize() beforehand. If you don't, the resulting split sizes may not be reliable. // - Call DockBuilderFinish() after you are done. @@ -2000,7 +2001,7 @@ namespace ImGui IMGUI_API void DockBuilderRemoveNodeChildNodes(ImGuiID node_id); // Remove all split/hierarchy. All remaining docked windows will be re-docked to the root. IMGUI_API void DockBuilderSetNodePos(ImGuiID node_id, ImVec2 pos); IMGUI_API void DockBuilderSetNodeSize(ImGuiID node_id, ImVec2 size); - IMGUI_API ImGuiID DockBuilderSplitNode(ImGuiID node_id, ImGuiDir split_dir, float size_ratio_for_node_at_dir, ImGuiID* out_id_at_dir, ImGuiID* out_id_at_opposite_dir); + IMGUI_API ImGuiID DockBuilderSplitNode(ImGuiID node_id, ImGuiDir split_dir, float size_ratio_for_node_at_dir, ImGuiID* out_id_at_dir, ImGuiID* out_id_at_opposite_dir); // Create 2 child nodes in this parent node. IMGUI_API void DockBuilderCopyDockSpace(ImGuiID src_dockspace_id, ImGuiID dst_dockspace_id, ImVector* in_window_remap_pairs); IMGUI_API void DockBuilderCopyNode(ImGuiID src_node_id, ImGuiID dst_node_id, ImVector* out_node_remap_pairs); IMGUI_API void DockBuilderCopyWindowSettings(const char* src_name, const char* dst_name); From e8568f3ec967106ac393a924ac2fde797f496d97 Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Tue, 24 Mar 2020 10:13:41 +0200 Subject: [PATCH 599/828] Menus: Fix appending to main menubar (#3068). --- imgui_widgets.cpp | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index d86d057ddc58..353fab09cb2f 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6150,18 +6150,23 @@ bool ImGui::BeginMainMenuBar() { ImGuiContext& g = *GImGui; ImGuiViewportP* viewport = g.Viewports[0]; + ImGuiWindow* menu_bar_window = FindWindowByName("##MainMenuBar"); // For the main menu bar, which cannot be moved, we honor g.Style.DisplaySafeAreaPadding to ensure text can be visible on a TV set. g.NextWindowData.MenuBarOffsetMinVal = ImVec2(g.Style.DisplaySafeAreaPadding.x, ImMax(g.Style.DisplaySafeAreaPadding.y - g.Style.FramePadding.y, 0.0f)); - // Get our rectangle in the work area, and report the size we need for next frame. - // We don't attempt to calculate our height ahead, as it depends on the per-viewport font size. However menu-bar will affect the minimum window size so we'll get the right height. - ImVec2 menu_bar_pos = viewport->Pos + viewport->CurrWorkOffsetMin; - ImVec2 menu_bar_size = ImVec2(viewport->Size.x - viewport->CurrWorkOffsetMin.x + viewport->CurrWorkOffsetMax.x, 1.0f); + // Get our rectangle at the top of the work area + if (menu_bar_window == NULL || menu_bar_window->BeginCount == 0) + { + // Set window position + // We don't attempt to calculate our height ahead, as it depends on the per-viewport font size. However menu-bar will affect the minimum window size so we'll get the right height. + ImVec2 menu_bar_pos = viewport->Pos + viewport->CurrWorkOffsetMin; + ImVec2 menu_bar_size = ImVec2(viewport->Size.x - viewport->CurrWorkOffsetMin.x + viewport->CurrWorkOffsetMax.x, 1.0f); + SetNextWindowPos(menu_bar_pos); + SetNextWindowSize(menu_bar_size); + } // Create window - SetNextWindowPos(menu_bar_pos); - SetNextWindowSize(menu_bar_size); SetNextWindowViewport(viewport->ID); // Enforce viewport so we don't create our own viewport when ImGuiConfigFlags_ViewportsNoMerge is set. PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0, 0)); // Lift normal size constraint, however the presence of a menu-bar will give us the minimum height we want. @@ -6169,8 +6174,10 @@ bool ImGui::BeginMainMenuBar() bool is_open = Begin("##MainMenuBar", NULL, window_flags) && BeginMenuBar(); PopStyleVar(2); - // Feed back into work area using actual window size - viewport->CurrWorkOffsetMin.y += GetCurrentWindow()->Size.y; + // Report our size into work area (for next frame) using actual window size + menu_bar_window = GetCurrentWindow(); + if (menu_bar_window->BeginCount == 1) + viewport->CurrWorkOffsetMin.y += menu_bar_window->Size.y; g.NextWindowData.MenuBarOffsetMinVal = ImVec2(0.0f, 0.0f); if (!is_open) From 9085c2cae27571d4dc83aa80f953737458584d31 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 24 Mar 2020 18:51:04 +0100 Subject: [PATCH 600/828] Docking: Fixed unused definition leftover from a hasty merge. --- imgui_internal.h | 1 - 1 file changed, 1 deletion(-) diff --git a/imgui_internal.h b/imgui_internal.h index 20c777eadc9f..c42f595f0094 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2072,7 +2072,6 @@ namespace ImGui IMGUI_API void RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding = 0.0f); IMGUI_API void RenderColorRectWithAlphaCheckerboard(ImDrawList* draw_list, ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, float grid_step, ImVec2 grid_off, float rounding = 0.0f, int rounding_corners_flags = ~0); IMGUI_API void RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags = ImGuiNavHighlightFlags_TypeDefault); // Navigation highlight - IMGUI_API void RenderMouseCursor(ImVec2 pos, float scale, ImGuiMouseCursor mouse_cursor = ImGuiMouseCursor_Arrow); IMGUI_API const char* FindRenderedTextEnd(const char* text, const char* text_end = NULL); // Find the optional ## from which we stop displaying text. IMGUI_API void LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end = NULL); From 6d03f93067223b2f1e63e50649308eb5f85a8fea Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 6 Apr 2020 15:03:01 +0200 Subject: [PATCH 601/828] Docking: Fixed assert preventing dockspace from being stored into a tab. (#3101) --- imgui_widgets.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 5081650c8a63..64c3e7eaa27d 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6849,7 +6849,7 @@ void ImGui::TabBarAddTab(ImGuiTabBar* tab_bar, ImGuiTabItemFlags tab_flags, ImGu { ImGuiContext& g = *GImGui; IM_ASSERT(TabBarFindTabByID(tab_bar, window->ID) == NULL); - IM_ASSERT(g.CurrentTabBar == NULL); // Can't work while the tab bar is active as our tab doesn't have an X offset yet + IM_ASSERT(g.CurrentTabBar != tab_bar); // Can't work while the tab bar is active as our tab doesn't have an X offset yet, in theory we could/should test something like (tab_bar->CurrFrameVisible < g.FrameCount) but we'd need to solve why triggers the commented early-out assert in BeginTabBarEx() (probably dock node going from implicit to explicit in same frame) ImGuiTabItem new_tab; new_tab.ID = window->ID; From c142540705702623e0a60a866be60e9de5143491 Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Thu, 26 Mar 2020 09:42:04 +0200 Subject: [PATCH 602/828] Viewports, Backends: GLFW: Avoid using window positioning workaround for glfw versions that have it fixed. Amend 09780b8. --- examples/imgui_impl_glfw.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index bcbee43655d7..495796fd2873 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -57,6 +57,7 @@ #define GLFW_HAS_FOCUS_WINDOW (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ glfwFocusWindow #define GLFW_HAS_FOCUS_ON_SHOW (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ GLFW_FOCUS_ON_SHOW #define GLFW_HAS_MONITOR_WORK_AREA (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ glfwGetMonitorWorkarea +#define GLFW_HAS_OSX_WINDOW_POS_FIX (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 + GLFW_VERSION_REVISION * 10 >= 3310) // 3.3.1+ Fixed: Resizing window repositions it on MacOS #1553 #ifdef GLFW_RESIZE_NESW_CURSOR // Let's be nice to people who pulled GLFW between 2019-04-16 (3.4 define) and 2019-11-29 (cursors defines) // FIXME: Remove when GLFW 3.4 is released? #define GLFW_HAS_NEW_CURSORS (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3400) // 3.4+ GLFW_RESIZE_ALL_CURSOR, GLFW_RESIZE_NESW_CURSOR, GLFW_RESIZE_NWSE_CURSOR, GLFW_NOT_ALLOWED_CURSOR #else @@ -671,7 +672,7 @@ static ImVec2 ImGui_ImplGlfw_GetWindowSize(ImGuiViewport* viewport) static void ImGui_ImplGlfw_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) { ImGuiViewportDataGlfw* data = (ImGuiViewportDataGlfw*)viewport->PlatformUserData; -#if __APPLE__ +#if __APPLE__ && !GLFW_HAS_OSX_WINDOW_POS_FIX // Native OS windows are positioned from the bottom-left corner on macOS, whereas on other platforms they are // positioned from the upper-left corner. GLFW makes an effort to convert macOS style coordinates, however it // doesn't handle it when changing size. We are manually moving the window in order for changes of size to be based From 71eb4034eba128f01aebc856e9ce65fd38edea86 Mon Sep 17 00:00:00 2001 From: omar Date: Sun, 12 Apr 2020 17:48:24 +0200 Subject: [PATCH 603/828] Viewports: Fixed viewport merge code not testing the Minimized flag correctly. (#3118) --- imgui.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 4fcfd0558d04..31d7a41623be 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3552,7 +3552,7 @@ void ImGui::UpdateMouseMovingWindowEndFrame() if (g.NavWindow && g.NavWindow->Appearing) return; - // Click on void to focus window and start moving + // Click on void to focus window and start moving // (after we're done with all our widgets, so e.g. clicking on docking tab-bar which have set HoveredId already and not get us here!) if (g.IO.MouseClicked[0]) { @@ -4424,7 +4424,7 @@ void ImGui::EndFrame() { ImGuiContext& g = *GImGui; IM_ASSERT(g.Initialized); - + // Don't process EndFrame() multiple times. if (g.FrameCountEnded == g.FrameCount) return; @@ -10679,7 +10679,11 @@ static bool ImGui::GetWindowAlwaysWantOwnViewport(ImGuiWindow* window) static bool ImGui::UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* viewport) { ImGuiContext& g = *GImGui; - if (!(viewport->Flags & (ImGuiViewportFlags_CanHostOtherWindows | ImGuiViewportFlags_Minimized)) || window->Viewport == viewport) + if (window->Viewport == viewport) + return false; + if ((viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) == 0) + return false; + if ((viewport->Flags & ImGuiViewportFlags_Minimized) != 0) return false; if (!viewport->GetMainRect().Contains(window->Rect())) return false; @@ -10793,7 +10797,7 @@ static void ImGui::UpdateViewportsNewFrame() } } - // Create/update main viewport with current platform position. + // Create/update main viewport with current platform position. // FIXME-VIEWPORT: Size is driven by back-end/user code for backward-compatibility but we should aim to make this more consistent. ImGuiViewportP* main_viewport = g.Viewports[0]; IM_ASSERT(main_viewport->ID == IMGUI_VIEWPORT_DEFAULT_ID); From 013013737fa729619534ade16915619b2683e91f Mon Sep 17 00:00:00 2001 From: SergeyN Date: Mon, 13 Apr 2020 15:22:27 +0200 Subject: [PATCH 604/828] Viewports, Backends: DX12: Fix for crash caused by early resource release. (#3121) --- examples/imgui_impl_dx12.cpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/examples/imgui_impl_dx12.cpp b/examples/imgui_impl_dx12.cpp index 3ead29c5affd..446546fb8661 100644 --- a/examples/imgui_impl_dx12.cpp +++ b/examples/imgui_impl_dx12.cpp @@ -815,7 +815,8 @@ static void ImGui_ImplDX12_CreateWindow(ImGuiViewport* viewport) desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; desc.NodeMask = 1; - IM_ASSERT(g_pd3dDevice->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&data->RtvDescHeap)) == S_OK); + HRESULT hr = g_pd3dDevice->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&data->RtvDescHeap)); + IM_ASSERT(hr == S_OK); SIZE_T rtv_descriptor_size = g_pd3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV); D3D12_CPU_DESCRIPTOR_HANDLE rtv_handle = data->RtvDescHeap->GetCPUDescriptorHandleForHeapStart(); @@ -847,12 +848,24 @@ static void ImGui_ImplDX12_DestroyWindow(ImGuiViewport* viewport) // The main viewport (owned by the application) will always have RendererUserData == NULL since we didn't create the data for it. if (ImGuiViewportDataDx12* data = (ImGuiViewportDataDx12*)viewport->RendererUserData) { + // Wait for pending operations to complete to safely release objects below + HRESULT hr; + if (data->CommandQueue && data->Fence && data->FenceEvent) + { + hr = data->CommandQueue->Signal(data->Fence, ++data->FenceSignaledValue); + IM_ASSERT(hr == S_OK); + ::WaitForSingleObject(data->FenceEvent, 0); // Reset any forgotten waits + hr = data->Fence->SetEventOnCompletion(data->FenceSignaledValue, data->FenceEvent); + IM_ASSERT(hr == S_OK); + ::WaitForSingleObject(data->FenceEvent, INFINITE); + } + SafeRelease(data->CommandQueue); SafeRelease(data->CommandList); SafeRelease(data->SwapChain); SafeRelease(data->RtvDescHeap); SafeRelease(data->Fence); - ::CloseHandle(data->FenceEvent); + ::CloseHandle(data->FenceEvent); data->FenceEvent = NULL; for (UINT i = 0; i < g_numFramesInFlight; i++) From 36ac557df077530f540e58778fedd8e3ef24cd31 Mon Sep 17 00:00:00 2001 From: Patryk Czachurski Date: Sat, 18 Apr 2020 11:09:44 +0200 Subject: [PATCH 605/828] Docking: Fix unused variable warning. (#3135) --- imgui.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/imgui.cpp b/imgui.cpp index 4ca0c92fbdfb..23ce906c6250 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -14222,6 +14222,7 @@ void ImGui::DockBuilderRemoveNodeDockedWindows(ImGuiID root_id, bool clear_persi if (want_removal) { const ImGuiID backup_dock_id = window->DockId; + IM_UNUSED(backup_dock_id); DockContextProcessUndockWindow(ctx, window, clear_persistent_docking_references); if (!clear_persistent_docking_references) IM_ASSERT(window->DockId == backup_dock_id); From 7f8b076f2b5042aa88a95e0d334e599051176e44 Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Wed, 15 Apr 2020 15:00:37 +0300 Subject: [PATCH 606/828] Viewports, Backends: GLFW: Fix windows resizing incorrectly due to GLFW firing window positioning callbacks on next frame after window is resized manually. (#2117) + Docking fixed PVS warning --- examples/imgui_impl_glfw.cpp | 27 +++++++++++++++++++-------- imgui.cpp | 4 ++-- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index 495796fd2873..f1d38701f46b 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -491,9 +491,10 @@ struct ImGuiViewportDataGlfw { GLFWwindow* Window; bool WindowOwned; + int IgnoreWindowPosEventFrame; int IgnoreWindowSizeEventFrame; - ImGuiViewportDataGlfw() { Window = NULL; WindowOwned = false; IgnoreWindowSizeEventFrame = -1; } + ImGuiViewportDataGlfw() { Window = NULL; WindowOwned = false; IgnoreWindowSizeEventFrame = IgnoreWindowPosEventFrame = -1; } ~ImGuiViewportDataGlfw() { IM_ASSERT(Window == NULL); } }; @@ -503,10 +504,25 @@ static void ImGui_ImplGlfw_WindowCloseCallback(GLFWwindow* window) viewport->PlatformRequestClose = true; } +// GLFW may dispatch window pos/size events after calling glfwSetWindowPos()/glfwSetWindowSize(). +// However: depending on the platform the callback may be invoked at different time: +// - on Windows it appears to be called within the glfwSetWindowPos()/glfwSetWindowSize() call +// - on Linux it is queued and invoked during glfwPollEvents() +// Because the event doesn't always fire on glfwSetWindowXXX() we use a frame counter tag to only +// ignore recent glfwSetWindowXXX() calls. static void ImGui_ImplGlfw_WindowPosCallback(GLFWwindow* window, int, int) { if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle(window)) + { + if (ImGuiViewportDataGlfw* data = (ImGuiViewportDataGlfw*)viewport->PlatformUserData) + { + bool ignore_event = (ImGui::GetFrameCount() <= data->IgnoreWindowPosEventFrame + 1); + //data->IgnoreWindowPosEventFrame = -1; + if (ignore_event) + return; + } viewport->PlatformRequestMove = true; + } } static void ImGui_ImplGlfw_WindowSizeCallback(GLFWwindow* window, int, int) @@ -515,14 +531,8 @@ static void ImGui_ImplGlfw_WindowSizeCallback(GLFWwindow* window, int, int) { if (ImGuiViewportDataGlfw* data = (ImGuiViewportDataGlfw*)viewport->PlatformUserData) { - // GLFW may dispatch window size event after calling glfwSetWindowSize(). - // However depending on the platform the callback may be invoked at different time: on Windows it - // appears to be called within the glfwSetWindowSize() call whereas on Linux it is queued and invoked - // during glfwPollEvents(). - // Because the event doesn't always fire on glfwSetWindowSize() we use a frame counter tag to only - // ignore recent glfwSetWindowSize() calls. bool ignore_event = (ImGui::GetFrameCount() <= data->IgnoreWindowSizeEventFrame + 1); - data->IgnoreWindowSizeEventFrame = -1; + //data->IgnoreWindowSizeEventFrame = -1; if (ignore_event) return; } @@ -658,6 +668,7 @@ static ImVec2 ImGui_ImplGlfw_GetWindowPos(ImGuiViewport* viewport) static void ImGui_ImplGlfw_SetWindowPos(ImGuiViewport* viewport, ImVec2 pos) { ImGuiViewportDataGlfw* data = (ImGuiViewportDataGlfw*)viewport->PlatformUserData; + data->IgnoreWindowPosEventFrame = ImGui::GetFrameCount(); glfwSetWindowPos(data->Window, (int)pos.x, (int)pos.y); } diff --git a/imgui.cpp b/imgui.cpp index 23ce906c6250..54743e34da34 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -13350,8 +13350,8 @@ static void ImGui::DockNodePreviewDockSetup(ImGuiWindow* host_window, ImGuiDockN // Build a tentative future node (reuse same structure because it is practical. Shape will be readjusted when previewing a split) data->FutureNode.HasCloseButton = (host_node ? host_node->HasCloseButton : host_window->HasCloseButton) || (root_payload->HasCloseButton); data->FutureNode.HasWindowMenuButton = host_node ? true : ((host_window->Flags & ImGuiWindowFlags_NoCollapse) == 0); - data->FutureNode.Pos = host_node ? ref_node_for_rect->Pos : host_window->Pos; - data->FutureNode.Size = host_node ? ref_node_for_rect->Size : host_window->Size; + data->FutureNode.Pos = ref_node_for_rect ? ref_node_for_rect->Pos : host_window->Pos; + data->FutureNode.Size = ref_node_for_rect ? ref_node_for_rect->Size : host_window->Size; // Calculate drop shapes geometry for allowed splitting directions IM_ASSERT(ImGuiDir_None == -1); From 7ddc1adefbdca94e7de350653def7a647ff45612 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 22 Apr 2020 16:30:22 +0200 Subject: [PATCH 607/828] Internals: FIxed commented out IMGUI_DEBUG_LOG_xxx macros, added extra. Revert incorrect 103c5edaa. --- imgui.cpp | 4 +++- imgui_internal.h | 11 +++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 8a2b3f6964fc..970fc9cc7f67 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -12154,7 +12154,8 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) void ImGui::DockContextProcessUndockWindow(ImGuiContext* ctx, ImGuiWindow* window, bool clear_persistent_docking_ref) { - (void)ctx; + IMGUI_DEBUG_LOG_DOCKING("DockContextProcessUndockWindow window '%s', clear_persistent_docking_ref = %d\n", window->Name, clear_persistent_docking_ref); + IM_UNUSED(ctx); if (window->DockNode) DockNodeRemoveWindow(window->DockNode, window, clear_persistent_docking_ref ? 0 : window->DockId); else @@ -12167,6 +12168,7 @@ void ImGui::DockContextProcessUndockWindow(ImGuiContext* ctx, ImGuiWindow* windo void ImGui::DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node) { + IMGUI_DEBUG_LOG_DOCKING("DockContextProcessUndockNode node %08X\n", node->ID); IM_ASSERT(node->IsLeafNode()); IM_ASSERT(node->Windows.Size >= 1); diff --git a/imgui_internal.h b/imgui_internal.h index dd0ea528c8cd..13abd67a036e 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -158,9 +158,12 @@ extern IMGUI_API ImGuiContext* GImGui; // Current implicit context pointer #endif // Debug Logging for selected systems. Remove the '((void)0) //' to enable. -#define IMGUI_DEBUG_LOG_POPUP(...) ((void)0) // IMGUI_DEBUG_LOG(__VA_ARGS__) -#define IMGUI_DEBUG_LOG_VIEWPORT(...) ((void)0) // IMGUI_DEBUG_LOG(__VA_ARGS__) -#define IMGUI_DEBUG_LOG_DOCKING(...) ((void)0) // IMGUI_DEBUG_LOG(__VA_ARGS__) +//#define IMGUI_DEBUG_LOG_POPUP IMGUI_DEBUG_LOG // Enable log +//#define IMGUI_DEBUG_LOG_VIEWPORT IMGUI_DEBUG_LOG // Enable log +//#define IMGUI_DEBUG_LOG_DOCKING IMGUI_DEBUG_LOG // Enable log +#define IMGUI_DEBUG_LOG_POPUP(...) ((void)0) // Disable log +#define IMGUI_DEBUG_LOG_VIEWPORT(...) ((void)0) // Disable log +#define IMGUI_DEBUG_LOG_DOCKING(...) ((void)0) // Disable log // Static Asserts #if (__cplusplus >= 201100) @@ -1751,7 +1754,7 @@ struct IMGUI_API ImGuiWindow ImGuiItemStatusFlags DockTabItemStatusFlags; ImRect DockTabItemRect; short DockOrder; // Order of the last time the window was visible within its DockNode. This is used to reorder windows that are reappearing on the same frame. Same value between windows that were active and windows that were none are possible. - bool DockIsActive :1; // When docking artifacts are actually visible. When this is set, DockNode is guaranteed to be != NULL. ~~ (DockNode != NULL) && (DockNode->Windows.Size > 1). + bool DockIsActive :1; // When docking artifacts are actually visible. When this is set, DockNode is guaranteed to be != NULL. ~~ (DockNode != NULL) && (DockNode->Windows.Size > 1). bool DockTabIsVisible :1; // Is our window visible this frame? ~~ is the corresponding tab selected? bool DockTabWantClose :1; From 51e568f9dc1dec33017376a6fa6a71b9ced6cda9 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 7 May 2020 22:46:06 +0200 Subject: [PATCH 608/828] Docking: Fix to allow basic reload of non-docking .ini data (following d33021d8) + moved settings blocks --- imgui.cpp | 113 ++++++++++++++++++++++------------------------- imgui_internal.h | 2 +- 2 files changed, 55 insertions(+), 60 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 27c713379b4b..f0e76d5a5955 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5206,6 +5206,8 @@ ImGuiWindow* ImGui::FindWindowByName(const char* name) static void ApplyWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settings) { + ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + window->ViewportPos = main_viewport->Pos; if (settings->ViewportId) { window->ViewportId = settings->ViewportId; @@ -5217,7 +5219,6 @@ static void ApplyWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settin window->Collapsed = settings->Collapsed; window->DockId = settings->DockId; window->DockOrder = settings->DockOrder; - } static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags) @@ -5233,6 +5234,7 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags) // Default/arbitrary window position. Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window. ImGuiViewport* main_viewport = ImGui::GetMainViewport(); window->Pos = main_viewport->Pos + ImVec2(60, 60); + window->ViewportPos = main_viewport->Pos; // User can disable loading and saving of settings. Tooltip and child windows also don't store settings. if (!(flags & ImGuiWindowFlags_NoSavedSettings)) @@ -5241,7 +5243,6 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags) // Retrieve settings from .ini file window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings); SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false); - window->ViewportPos = main_viewport->Pos; ApplyWindowSettings(window, settings); } window->DC.CursorStartPos = window->DC.CursorMaxPos = window->Pos; // So first call to CalcContentSize() doesn't return crazy values @@ -10564,8 +10565,6 @@ void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size) for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++) if (g.SettingsHandlers[handler_n].ApplyAllFn) g.SettingsHandlers[handler_n].ApplyAllFn(&g, &g.SettingsHandlers[handler_n]); - - DockContextOnLoadSettings(&g); } void ImGui::SaveIniSettingsToDisk(const char* ini_filename) @@ -10624,9 +10623,10 @@ static void WindowSettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettingsHandl static void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name) { - ImGuiWindowSettings* settings = ImGui::FindWindowSettings(ImHashStr(name)); - if (!settings) - settings = ImGui::CreateNewWindowSettings(name); + ImGuiWindowSettings* settings = ImGui::FindOrCreateWindowSettings(name); + ImGuiID id = settings->ID; + *settings = ImGuiWindowSettings(); + settings->ID = id; settings->WantApply = true; return (void*)settings; } @@ -11683,6 +11683,7 @@ namespace ImGui static void DockSettingsRenameNodeReferences(ImGuiID old_node_id, ImGuiID new_node_id); static void DockSettingsRemoveNodeReferences(ImGuiID* node_ids, int node_ids_count); static ImGuiDockNodeSettings* DockSettingsFindNodeSettings(ImGuiContext* ctx, ImGuiID node_id); + static void DockSettingsHandler_ApplyAll(ImGuiContext*, ImGuiSettingsHandler*); static void* DockSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name); static void DockSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line); static void DockSettingsHandler_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf); @@ -11700,7 +11701,6 @@ namespace ImGui //----------------------------------------------------------------------------- // - DockContextInitialize() // - DockContextShutdown() -// - DockContextOnLoadSettings() // - DockContextClearNodes() // - DockContextRebuildNodes() // - DockContextUpdateUndocking() @@ -11726,6 +11726,7 @@ void ImGui::DockContextInitialize(ImGuiContext* ctx) ImGuiSettingsHandler ini_handler; ini_handler.TypeName = "Docking"; ini_handler.TypeHash = ImHashStr("Docking"); + ini_handler.ApplyAllFn = DockSettingsHandler_ApplyAll; ini_handler.ReadOpenFn = DockSettingsHandler_ReadOpen; ini_handler.ReadLineFn = DockSettingsHandler_ReadLine; ini_handler.WriteAllFn = DockSettingsHandler_WriteAll; @@ -11743,13 +11744,6 @@ void ImGui::DockContextShutdown(ImGuiContext* ctx) g.DockContext = NULL; } -void ImGui::DockContextOnLoadSettings(ImGuiContext* ctx) -{ - ImGuiDockContext* dc = ctx->DockContext; - DockContextPruneUnusedSettingsNodes(ctx); - DockContextBuildNodesFromSettings(ctx, dc->SettingsNodes.Data, dc->SettingsNodes.Size); -} - void ImGui::DockContextClearNodes(ImGuiContext* ctx, ImGuiID root_id, bool clear_persistent_docking_references) { IM_UNUSED(ctx); @@ -14251,6 +14245,7 @@ void ImGui::DockBuilderRemoveNode(ImGuiID node_id) DockContextRemoveNode(ctx, node, true); } +// root_id = 0 to remove all, root_id != 0 to remove child of given node. void ImGui::DockBuilderRemoveNodeChildNodes(ImGuiID root_id) { ImGuiContext* ctx = GImGui; @@ -14821,6 +14816,7 @@ void ImGui::BeginDockableDragDropTarget(ImGuiWindow* window) // - DockSettingsRenameNodeReferences() // - DockSettingsRemoveNodeReferences() // - DockSettingsFindNodeSettings() +// - DockSettingsHandler_ApplyAll() // - DockSettingsHandler_ReadOpen() // - DockSettingsHandler_ReadLine() // - DockSettingsHandler_DockNodeToSettings() @@ -14871,6 +14867,15 @@ static ImGuiDockNodeSettings* ImGui::DockSettingsFindNodeSettings(ImGuiContext* return NULL; } +static void ImGui::DockSettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettingsHandler*) +{ + // Prune settings at boot time only + ImGuiDockContext* dc = ctx->DockContext; + if (ctx->Windows.Size == 0) + DockContextPruneUnusedSettingsNodes(ctx); + DockContextBuildNodesFromSettings(ctx, dc->SettingsNodes.Data, dc->SettingsNodes.Size); +} + static void* ImGui::DockSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name) { if (strcmp(name, "Data") != 0) @@ -15690,52 +15695,17 @@ void ImGui::ShowMetricsWindow(bool* p_open) // Details for Docking #ifdef IMGUI_HAS_DOCK - if (ImGui::TreeNode("Docking")) + if (ImGui::TreeNode("Dock nodes")) { ImGuiDockContext* dc = g.DockContext; ImGui::Checkbox("Ctrl shows window dock info", &show_docking_nodes); - - if (ImGui::TreeNode("Dock nodes")) - { - if (ImGui::SmallButton("Clear settings")) { DockContextClearNodes(&g, 0, true); } - ImGui::SameLine(); - if (ImGui::SmallButton("Rebuild all")) { dc->WantFullRebuild = true; } - for (int n = 0; n < dc->Nodes.Data.Size; n++) - if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p) - if (node->IsRootNode()) - Funcs::NodeDockNode(node, "Node"); - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Settings")) - { - if (ImGui::SmallButton("Refresh")) - SaveIniSettingsToMemory(); - ImGui::SameLine(); - if (ImGui::SmallButton("Save to disk")) - SaveIniSettingsToDisk(g.IO.IniFilename); - ImGui::Separator(); - ImGui::Text("Docked Windows:"); - for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings)) - if (settings->DockId != 0) - ImGui::BulletText("Window '%s' -> DockId %08X", settings->GetName(), settings->DockId); - ImGui::Separator(); - ImGui::Text("Dock Nodes:"); - for (int n = 0; n < dc->SettingsNodes.Size; n++) - { - ImGuiDockNodeSettings* settings = &dc->SettingsNodes[n]; - const char* selected_tab_name = NULL; - if (settings->SelectedWindowId) - { - if (ImGuiWindow* window = FindWindowByID(settings->SelectedWindowId)) - selected_tab_name = window->Name; - else if (ImGuiWindowSettings* window_settings = FindWindowSettings(settings->SelectedWindowId)) - selected_tab_name = window_settings->GetName(); - } - ImGui::BulletText("Node %08X, Parent %08X, SelectedTab %08X ('%s')", settings->ID, settings->ParentNodeId, settings->SelectedWindowId, selected_tab_name ? selected_tab_name : settings->SelectedWindowId ? "N/A" : ""); - } - ImGui::TreePop(); - } + if (ImGui::SmallButton("Clear nodes")) { DockContextClearNodes(&g, 0, true); } + ImGui::SameLine(); + if (ImGui::SmallButton("Rebuild all")) { dc->WantFullRebuild = true; } + for (int n = 0; n < dc->Nodes.Data.Size; n++) + if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p) + if (node->IsRootNode()) + Funcs::NodeDockNode(node, "Node"); ImGui::TreePop(); } #endif // #define IMGUI_HAS_DOCK @@ -15746,6 +15716,9 @@ void ImGui::ShowMetricsWindow(bool* p_open) if (ImGui::SmallButton("Clear")) ImGui::ClearIniSettings(); ImGui::SameLine(); + if (ImGui::SmallButton("Save to memory")) + ImGui::SaveIniSettingsToMemory(); + ImGui::SameLine(); if (ImGui::SmallButton("Save to disk")) ImGui::SaveIniSettingsToDisk(g.IO.IniFilename); ImGui::SameLine(); @@ -15757,7 +15730,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) if (ImGui::TreeNode("SettingsHandlers", "Settings handlers: (%d)", g.SettingsHandlers.Size)) { for (int n = 0; n < g.SettingsHandlers.Size; n++) - ImGui::TextUnformatted(g.SettingsHandlers[n].TypeName); + ImGui::BulletText("%s", g.SettingsHandlers[n].TypeName); ImGui::TreePop(); } if (ImGui::TreeNode("SettingsWindows", "Settings packed data: Windows: %d bytes", g.SettingsWindows.size())) @@ -15766,6 +15739,28 @@ void ImGui::ShowMetricsWindow(bool* p_open) Funcs::NodeWindowSettings(settings); ImGui::TreePop(); } + if (ImGui::TreeNode("SettingsDocking", "Settings packed data: Docking")) + { + ImGui::Text("In SettingsWindows:"); + for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings)) + if (settings->DockId != 0) + ImGui::BulletText("Window '%s' -> DockId %08X", settings->GetName(), settings->DockId); + ImGui::Text("In SettingsNodes:"); + for (int n = 0; n < g.DockContext->SettingsNodes.Size; n++) + { + ImGuiDockNodeSettings* settings = &g.DockContext->SettingsNodes[n]; + const char* selected_tab_name = NULL; + if (settings->SelectedWindowId) + { + if (ImGuiWindow* window = FindWindowByID(settings->SelectedWindowId)) + selected_tab_name = window->Name; + else if (ImGuiWindowSettings* window_settings = FindWindowSettings(settings->SelectedWindowId)) + selected_tab_name = window_settings->GetName(); + } + ImGui::BulletText("Node %08X, Parent %08X, SelectedTab %08X ('%s')", settings->ID, settings->ParentNodeId, settings->SelectedWindowId, selected_tab_name ? selected_tab_name : settings->SelectedWindowId ? "N/A" : ""); + } + ImGui::TreePop(); + } if (ImGui::TreeNode("SettingsIniData", "Settings unpacked data (.ini): %d bytes", g.SettingsIniData.size())) { char* buf = (char*)(void*)(g.SettingsIniData.Buf.Data ? g.SettingsIniData.Buf.Data : ""); diff --git a/imgui_internal.h b/imgui_internal.h index a3b7668832c0..2f43e9b5de6f 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2058,7 +2058,7 @@ namespace ImGui IMGUI_API ImGuiID DockBuilderAddNode(ImGuiID node_id = 0, ImGuiDockNodeFlags flags = 0); IMGUI_API void DockBuilderRemoveNode(ImGuiID node_id); // Remove node and all its child, undock all windows IMGUI_API void DockBuilderRemoveNodeDockedWindows(ImGuiID node_id, bool clear_persistent_docking_references = true); - IMGUI_API void DockBuilderRemoveNodeChildNodes(ImGuiID node_id); // Remove all split/hierarchy. All remaining docked windows will be re-docked to the root. + IMGUI_API void DockBuilderRemoveNodeChildNodes(ImGuiID node_id); // Remove all split/hierarchy. All remaining docked windows will be re-docked to the remaining root node (node_id). IMGUI_API void DockBuilderSetNodePos(ImGuiID node_id, ImVec2 pos); IMGUI_API void DockBuilderSetNodeSize(ImGuiID node_id, ImVec2 size); IMGUI_API ImGuiID DockBuilderSplitNode(ImGuiID node_id, ImGuiDir split_dir, float size_ratio_for_node_at_dir, ImGuiID* out_id_at_dir, ImGuiID* out_id_at_opposite_dir); // Create 2 child nodes in this parent node. From 574ff0a280d09b5115c489f5e204ba1af13c9c0a Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 7 May 2020 23:28:29 +0200 Subject: [PATCH 609/828] Docking, Settings: Allow reload of settings data at runtime. (#2573) --- imgui.cpp | 24 ++++++++++++++++++++++-- imgui_internal.h | 3 ++- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index f0e76d5a5955..08090188e6aa 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10520,6 +10520,12 @@ void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size) memcpy(buf, ini_data, ini_size); buf_end[0] = 0; + // Call pre-read handlers + // Some types will clear their data (e.g. dock information) some types will allow merge/override (window) + for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++) + if (g.SettingsHandlers[handler_n].ReadInitFn) + g.SettingsHandlers[handler_n].ReadInitFn(&g, &g.SettingsHandlers[handler_n]); + void* entry_data = NULL; ImGuiSettingsHandler* entry_handler = NULL; @@ -11683,6 +11689,7 @@ namespace ImGui static void DockSettingsRenameNodeReferences(ImGuiID old_node_id, ImGuiID new_node_id); static void DockSettingsRemoveNodeReferences(ImGuiID* node_ids, int node_ids_count); static ImGuiDockNodeSettings* DockSettingsFindNodeSettings(ImGuiContext* ctx, ImGuiID node_id); + static void DockSettingsHandler_ClearAll(ImGuiContext*, ImGuiSettingsHandler*); static void DockSettingsHandler_ApplyAll(ImGuiContext*, ImGuiSettingsHandler*); static void* DockSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name); static void DockSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line); @@ -11726,9 +11733,11 @@ void ImGui::DockContextInitialize(ImGuiContext* ctx) ImGuiSettingsHandler ini_handler; ini_handler.TypeName = "Docking"; ini_handler.TypeHash = ImHashStr("Docking"); - ini_handler.ApplyAllFn = DockSettingsHandler_ApplyAll; + ini_handler.ClearAllFn = DockSettingsHandler_ClearAll; + ini_handler.ReadInitFn = DockSettingsHandler_ClearAll; // Also clear on read ini_handler.ReadOpenFn = DockSettingsHandler_ReadOpen; ini_handler.ReadLineFn = DockSettingsHandler_ReadLine; + ini_handler.ApplyAllFn = DockSettingsHandler_ApplyAll; ini_handler.WriteAllFn = DockSettingsHandler_WriteAll; g.SettingsHandlers.push_back(ini_handler); } @@ -11752,7 +11761,8 @@ void ImGui::DockContextClearNodes(ImGuiContext* ctx, ImGuiID root_id, bool clear DockBuilderRemoveNodeChildNodes(root_id); } -// This function also acts as a defacto test to make sure we can rebuild from scratch without a glitch +// [DEBUG] This function also acts as a defacto test to make sure we can rebuild from scratch without a glitch +// (Different from DockSettingsHandler_ClearAll() + DockSettingsHandler_ApplyAll() because this reuses current settings!) void ImGui::DockContextRebuildNodes(ImGuiContext* ctx) { IMGUI_DEBUG_LOG_DOCKING("DockContextRebuild()\n"); @@ -14867,6 +14877,15 @@ static ImGuiDockNodeSettings* ImGui::DockSettingsFindNodeSettings(ImGuiContext* return NULL; } +// Clear settings data +static void ImGui::DockSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*) +{ + ImGuiDockContext* dc = ctx->DockContext; + dc->SettingsNodes.clear(); + DockContextClearNodes(ctx, 0, true); +} + +// Recreate dones based on settings data static void ImGui::DockSettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettingsHandler*) { // Prune settings at boot time only @@ -14874,6 +14893,7 @@ static void ImGui::DockSettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettings if (ctx->Windows.Size == 0) DockContextPruneUnusedSettingsNodes(ctx); DockContextBuildNodesFromSettings(ctx, dc->SettingsNodes.Data, dc->SettingsNodes.Size); + DockContextBuildAddWindowsToNodes(ctx, 0); } static void* ImGui::DockSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name) diff --git a/imgui_internal.h b/imgui_internal.h index 2f43e9b5de6f..bf443573b549 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -855,9 +855,10 @@ struct ImGuiSettingsHandler const char* TypeName; // Short description stored in .ini file. Disallowed characters: '[' ']' ImGuiID TypeHash; // == ImHashStr(TypeName) void (*ClearAllFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler); // Clear all settings data - void (*ApplyAllFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler); // Read: Called after reading (in registration order) + void (*ReadInitFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler); // Read: Called before reading (in registration order) void* (*ReadOpenFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, const char* name); // Read: Called when entering into a new ini entry e.g. "[Window][Name]" void (*ReadLineFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, void* entry, const char* line); // Read: Called for every line of text within an ini entry + void (*ApplyAllFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler); // Read: Called after reading (in registration order) void (*WriteAllFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* out_buf); // Write: Output every entries into 'out_buf' void* UserData; From 1e7672acf46a81e36b3cf35734c015326dd97d58 Mon Sep 17 00:00:00 2001 From: Ivan Zinkevich Date: Thu, 7 May 2020 23:50:51 +0300 Subject: [PATCH 610/828] Backends: DX12: Fixed OBJECT_DELETED_WHILE_STILL_IN_USE on viewport resizing. (#3210) Tested with detaching/attaching a viewport and resizing it. DX12 debug layer is clean. --- examples/imgui_impl_dx12.cpp | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/examples/imgui_impl_dx12.cpp b/examples/imgui_impl_dx12.cpp index bbe026e49dbb..b10e8c65a761 100644 --- a/examples/imgui_impl_dx12.cpp +++ b/examples/imgui_impl_dx12.cpp @@ -846,22 +846,26 @@ static void ImGui_ImplDX12_CreateWindow(ImGuiViewport* viewport) } } +static void ImGui_WaitForPendingOperations(ImGuiViewportDataDx12* data) +{ + HRESULT hr = S_FALSE; + if (data && data->CommandQueue && data->Fence && data->FenceEvent) + { + hr = data->CommandQueue->Signal(data->Fence, ++data->FenceSignaledValue); + IM_ASSERT(hr == S_OK); + ::WaitForSingleObject(data->FenceEvent, 0); // Reset any forgotten waits + hr = data->Fence->SetEventOnCompletion(data->FenceSignaledValue, data->FenceEvent); + IM_ASSERT(hr == S_OK); + ::WaitForSingleObject(data->FenceEvent, INFINITE); + } +} + static void ImGui_ImplDX12_DestroyWindow(ImGuiViewport* viewport) { // The main viewport (owned by the application) will always have RendererUserData == NULL since we didn't create the data for it. if (ImGuiViewportDataDx12* data = (ImGuiViewportDataDx12*)viewport->RendererUserData) { - // Wait for pending operations to complete to safely release objects below - HRESULT hr; - if (data->CommandQueue && data->Fence && data->FenceEvent) - { - hr = data->CommandQueue->Signal(data->Fence, ++data->FenceSignaledValue); - IM_ASSERT(hr == S_OK); - ::WaitForSingleObject(data->FenceEvent, 0); // Reset any forgotten waits - hr = data->Fence->SetEventOnCompletion(data->FenceSignaledValue, data->FenceEvent); - IM_ASSERT(hr == S_OK); - ::WaitForSingleObject(data->FenceEvent, INFINITE); - } + ImGui_WaitForPendingOperations(data); SafeRelease(data->CommandQueue); SafeRelease(data->CommandList); @@ -887,6 +891,8 @@ static void ImGui_ImplDX12_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) { ImGuiViewportDataDx12* data = (ImGuiViewportDataDx12*)viewport->RendererUserData; + ImGui_WaitForPendingOperations(data); + for (UINT i = 0; i < g_numFramesInFlight; i++) SafeRelease(data->FrameCtx[i].RenderTarget); From 6636cb9f2f9dd607a93b8e46a4cb2255703851e3 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 11 May 2020 17:29:50 +0200 Subject: [PATCH 611/828] Viewports: Don't set ImGuiViewportFlags_NoRendererClear when ImGuiWindowFlags_NoBackground is set. (#3213) --- imgui.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index e1f51a774f4f..db1fb892761e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6236,7 +6236,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) UpdateViewportPlatformMonitor(window->Viewport); // Update common viewport flags - ImGuiViewportFlags viewport_flags = (window->Viewport->Flags) & ~(ImGuiViewportFlags_TopMost | ImGuiViewportFlags_NoTaskBarIcon | ImGuiViewportFlags_NoDecoration); + const ImGuiViewportFlags viewport_flags_to_clear = ImGuiViewportFlags_TopMost | ImGuiViewportFlags_NoTaskBarIcon | ImGuiViewportFlags_NoDecoration | ImGuiViewportFlags_NoRendererClear; + ImGuiViewportFlags viewport_flags = window->Viewport->Flags & ~viewport_flags_to_clear; const bool is_short_lived_floating_window = (flags & (ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) != 0; if (flags & ImGuiWindowFlags_Tooltip) viewport_flags |= ImGuiViewportFlags_TopMost; @@ -6266,7 +6267,9 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) viewport_flags &= ~window->WindowClass.ViewportFlagsOverrideClear; // We also tell the back-end that clearing the platform window won't be necessary, as our window is filling the viewport and we have disabled BgAlpha - viewport_flags |= ImGuiViewportFlags_NoRendererClear; + if (!(flags & ImGuiWindowFlags_NoBackground)) + viewport_flags &= ~ImGuiViewportFlags_NoRendererClear; + window->Viewport->Flags = viewport_flags; } From c46b79846c18d131f00ee299c811bb7afd125e66 Mon Sep 17 00:00:00 2001 From: Chris Savoie Date: Sat, 9 May 2020 22:18:10 -0700 Subject: [PATCH 612/828] Metrics: Fix metrics crash with viewports. --- imgui.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index db1fb892761e..f16aa3d3063b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -15318,10 +15318,9 @@ void ImGui::ShowMetricsWindow(bool* p_open) return ImRect(); } - static void NodeDrawCmdShowMeshAndBoundingBox(ImGuiWindow* window, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, int elem_offset, bool show_mesh, bool show_aabb) + static void NodeDrawCmdShowMeshAndBoundingBox(ImDrawList* fg_draw_list, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, int elem_offset, bool show_mesh, bool show_aabb) { IM_ASSERT(show_mesh || show_aabb); - ImDrawList* fg_draw_list = GetForegroundDrawList(window); // Render additional visuals into the top-most draw list ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL; // Draw wire-frame version of all triangles @@ -15350,6 +15349,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) fg_draw_list->Flags = backup_flags; } + // Note that both 'window' and 'viewport' may be NULL here. Viewport is generally null of destroyed popups which previously owned a viewport. static void NodeDrawList(ImGuiWindow* window, ImGuiViewportP* viewport, ImDrawList* draw_list, const char* label) { bool node_open = ImGui::TreeNode(draw_list, "%s: '%s' %d vtx, %d indices, %d cmds", label, draw_list->_OwnerName ? draw_list->_OwnerName : "", draw_list->VtxBuffer.Size, draw_list->IdxBuffer.Size, draw_list->CmdBuffer.Size); @@ -15388,7 +15388,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w); bool pcmd_node_open = ImGui::TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "%s", buf); if (ImGui::IsItemHovered() && (show_drawcmd_mesh || show_drawcmd_aabb) && fg_draw_list) - NodeDrawCmdShowMeshAndBoundingBox(window, draw_list, pcmd, elem_offset, show_drawcmd_mesh, show_drawcmd_aabb); + NodeDrawCmdShowMeshAndBoundingBox(fg_draw_list, draw_list, pcmd, elem_offset, show_drawcmd_mesh, show_drawcmd_aabb); if (!pcmd_node_open) continue; @@ -15407,7 +15407,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImFormatString(buf, IM_ARRAYSIZE(buf), "Mesh: ElemCount: %d, VtxOffset: +%d, IdxOffset: +%d, Area: ~%0.f px", pcmd->ElemCount, pcmd->VtxOffset, pcmd->IdxOffset, total_area); ImGui::Selectable(buf); if (ImGui::IsItemHovered() && fg_draw_list) - NodeDrawCmdShowMeshAndBoundingBox(window, draw_list, pcmd, elem_offset, true, false); + NodeDrawCmdShowMeshAndBoundingBox(fg_draw_list, draw_list, pcmd, elem_offset, true, false); // Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted. ImGuiListClipper clipper(pcmd->ElemCount/3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible. From 03ea87ea287a7391603bcf85a441412d973f65f1 Mon Sep 17 00:00:00 2001 From: Chris Savoie Date: Fri, 8 May 2020 13:02:15 -0700 Subject: [PATCH 613/828] Backends, Win32: Request monitor update when dpi awarness is enabled to make sure they have the correct dpi settings. --- examples/imgui_impl_win32.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index b7a38785d7fd..207e54fe2502 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -473,6 +473,9 @@ typedef DPI_AWARENESS_CONTEXT(WINAPI* PFN_SetThreadDpiAwarenessContext)(DPI_AWAR // Helper function to enable DPI awareness without setting up a manifest void ImGui_ImplWin32_EnableDpiAwareness() { + // Make sure monitors will be updated with latest correct scaling + g_WantUpdateMonitors = true; + // if (IsWindows10OrGreater()) // This needs a manifest to succeed. Instead we try to grab the function pointer! { static HINSTANCE user32_dll = ::LoadLibraryA("user32.dll"); // Reference counted per-process From 1cd32d3afe8cc70338e02652795ce7a62fe680ea Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 14 May 2020 00:15:14 +0200 Subject: [PATCH 614/828] Docking: moving small docking context to imgui_internal.h, removed unnecessary indirection, renaming. --- imgui.cpp | 111 ++++++++++++++++++++--------------------------- imgui_internal.h | 14 ++++-- 2 files changed, 59 insertions(+), 66 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index f16aa3d3063b..5cc867de7503 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4164,7 +4164,6 @@ void ImGui::Initialize(ImGuiContext* context) g.PlatformIO.Viewports.push_back(g.Viewports[0]); // Extensions - IM_ASSERT(g.DockContext == NULL); DockContextInitialize(&g); #endif // #ifdef IMGUI_HAS_DOCK @@ -4203,7 +4202,6 @@ void ImGui::Shutdown(ImGuiContext* context) SetCurrentContext(backup_context); // Shutdown extensions - IM_ASSERT(g.DockContext != NULL); DockContextShutdown(&g); // Clear everything else @@ -11626,15 +11624,6 @@ struct ImGuiDockNodeSettings ImGuiDockNodeSettings() { ID = ParentNodeId = ParentWindowId = SelectedWindowId = 0; SplitAxis = ImGuiAxis_None; Depth = 0; Flags = ImGuiDockNodeFlags_None; } }; -struct ImGuiDockContext -{ - ImGuiStorage Nodes; // Map ID -> ImGuiDockNode*: Active nodes - ImVector Requests; - ImVector SettingsNodes; - bool WantFullRebuild; - ImGuiDockContext() { WantFullRebuild = false; } -}; - //----------------------------------------------------------------------------- // Docking: Forward Declarations //----------------------------------------------------------------------------- @@ -11731,8 +11720,6 @@ namespace ImGui void ImGui::DockContextInitialize(ImGuiContext* ctx) { ImGuiContext& g = *ctx; - IM_ASSERT(g.DockContext == NULL); - g.DockContext = IM_NEW(ImGuiDockContext)(); // Add .ini handle for persistent docking data ImGuiSettingsHandler ini_handler; @@ -11749,13 +11736,10 @@ void ImGui::DockContextInitialize(ImGuiContext* ctx) void ImGui::DockContextShutdown(ImGuiContext* ctx) { - ImGuiContext& g = *ctx; - ImGuiDockContext* dc = ctx->DockContext; + ImGuiDockContext* dc = &ctx->DockContext; for (int n = 0; n < dc->Nodes.Data.Size; n++) if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p) IM_DELETE(node); - IM_DELETE(g.DockContext); - g.DockContext = NULL; } void ImGui::DockContextClearNodes(ImGuiContext* ctx, ImGuiID root_id, bool clear_persistent_docking_references) @@ -11771,11 +11755,11 @@ void ImGui::DockContextClearNodes(ImGuiContext* ctx, ImGuiID root_id, bool clear void ImGui::DockContextRebuildNodes(ImGuiContext* ctx) { IMGUI_DEBUG_LOG_DOCKING("DockContextRebuild()\n"); - ImGuiDockContext* dc = ctx->DockContext; + ImGuiDockContext* dc = &ctx->DockContext; SaveIniSettingsToMemory(); ImGuiID root_id = 0; // Rebuild all DockContextClearNodes(ctx, root_id, false); - DockContextBuildNodesFromSettings(ctx, dc->SettingsNodes.Data, dc->SettingsNodes.Size); + DockContextBuildNodesFromSettings(ctx, dc->NodesSettings.Data, dc->NodesSettings.Size); DockContextBuildAddWindowsToNodes(ctx, root_id); } @@ -11783,7 +11767,7 @@ void ImGui::DockContextRebuildNodes(ImGuiContext* ctx) void ImGui::DockContextUpdateUndocking(ImGuiContext* ctx) { ImGuiContext& g = *ctx; - ImGuiDockContext* dc = ctx->DockContext; + ImGuiDockContext* dc = &ctx->DockContext; if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable)) { if (dc->Nodes.Data.Size > 0 || dc->Requests.Size > 0) @@ -11827,7 +11811,7 @@ void ImGui::DockContextUpdateUndocking(ImGuiContext* ctx) void ImGui::DockContextUpdateDocking(ImGuiContext* ctx) { ImGuiContext& g = *ctx; - ImGuiDockContext* dc = ctx->DockContext; + ImGuiDockContext* dc = &ctx->DockContext; if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable)) return; @@ -11847,7 +11831,7 @@ void ImGui::DockContextUpdateDocking(ImGuiContext* ctx) static ImGuiDockNode* ImGui::DockContextFindNodeByID(ImGuiContext* ctx, ImGuiID id) { - return (ImGuiDockNode*)ctx->DockContext->Nodes.GetVoidPtr(id); + return (ImGuiDockNode*)ctx->DockContext.Nodes.GetVoidPtr(id); } ImGuiID ImGui::DockContextGenNodeID(ImGuiContext* ctx) @@ -11871,14 +11855,14 @@ static ImGuiDockNode* ImGui::DockContextAddNode(ImGuiContext* ctx, ImGuiID id) // We don't set node->LastFrameAlive on construction. Nodes are always created at all time to reflect .ini settings! IMGUI_DEBUG_LOG_DOCKING("DockContextAddNode 0x%08X\n", id); ImGuiDockNode* node = IM_NEW(ImGuiDockNode)(id); - ctx->DockContext->Nodes.SetVoidPtr(node->ID, node); + ctx->DockContext.Nodes.SetVoidPtr(node->ID, node); return node; } static void ImGui::DockContextRemoveNode(ImGuiContext* ctx, ImGuiDockNode* node, bool merge_sibling_into_parent_node) { ImGuiContext& g = *ctx; - ImGuiDockContext* dc = ctx->DockContext; + ImGuiDockContext* dc = &ctx->DockContext; IMGUI_DEBUG_LOG_DOCKING("DockContextRemoveNode 0x%08X\n", node->ID); IM_ASSERT(DockContextFindNodeByID(ctx, node->ID) == node); @@ -11925,16 +11909,16 @@ struct ImGuiDockContextPruneNodeData static void ImGui::DockContextPruneUnusedSettingsNodes(ImGuiContext* ctx) { ImGuiContext& g = *ctx; - ImGuiDockContext* dc = ctx->DockContext; + ImGuiDockContext* dc = &ctx->DockContext; IM_ASSERT(g.Windows.Size == 0); ImPool pool; - pool.Reserve(dc->SettingsNodes.Size); + pool.Reserve(dc->NodesSettings.Size); // Count child nodes and compute RootID - for (int settings_n = 0; settings_n < dc->SettingsNodes.Size; settings_n++) + for (int settings_n = 0; settings_n < dc->NodesSettings.Size; settings_n++) { - ImGuiDockNodeSettings* settings = &dc->SettingsNodes[settings_n]; + ImGuiDockNodeSettings* settings = &dc->NodesSettings[settings_n]; ImGuiDockContextPruneNodeData* parent_data = settings->ParentNodeId ? pool.GetByKey(settings->ParentNodeId) : 0; pool.GetOrAddByKey(settings->ID)->RootId = parent_data ? parent_data->RootId : settings->ID; if (settings->ParentNodeId) @@ -11943,9 +11927,9 @@ static void ImGui::DockContextPruneUnusedSettingsNodes(ImGuiContext* ctx) // Count reference to dock ids from dockspaces // We track the 'auto-DockNode <- manual-Window <- manual-DockSpace' in order to avoid 'auto-DockNode' being ditched by DockContextPruneUnusedSettingsNodes() - for (int settings_n = 0; settings_n < dc->SettingsNodes.Size; settings_n++) + for (int settings_n = 0; settings_n < dc->NodesSettings.Size; settings_n++) { - ImGuiDockNodeSettings* settings = &dc->SettingsNodes[settings_n]; + ImGuiDockNodeSettings* settings = &dc->NodesSettings[settings_n]; if (settings->ParentWindowId != 0) if (ImGuiWindowSettings* window_settings = FindWindowSettings(settings->ParentWindowId)) if (window_settings->DockId) @@ -11965,9 +11949,9 @@ static void ImGui::DockContextPruneUnusedSettingsNodes(ImGuiContext* ctx) } // Prune - for (int settings_n = 0; settings_n < dc->SettingsNodes.Size; settings_n++) + for (int settings_n = 0; settings_n < dc->NodesSettings.Size; settings_n++) { - ImGuiDockNodeSettings* settings = &dc->SettingsNodes[settings_n]; + ImGuiDockNodeSettings* settings = &dc->NodesSettings[settings_n]; ImGuiDockContextPruneNodeData* data = pool.GetByKey(settings->ID); if (data->CountWindows > 1) continue; @@ -12059,7 +12043,7 @@ void ImGui::DockContextQueueDock(ImGuiContext* ctx, ImGuiWindow* target, ImGuiDo req.DockSplitDir = split_dir; req.DockSplitRatio = split_ratio; req.DockSplitOuter = split_outer; - ctx->DockContext->Requests.push_back(req); + ctx->DockContext.Requests.push_back(req); } void ImGui::DockContextQueueUndockWindow(ImGuiContext* ctx, ImGuiWindow* window) @@ -12067,7 +12051,7 @@ void ImGui::DockContextQueueUndockWindow(ImGuiContext* ctx, ImGuiWindow* window) ImGuiDockRequest req; req.Type = ImGuiDockRequestType_Undock; req.UndockTargetWindow = window; - ctx->DockContext->Requests.push_back(req); + ctx->DockContext.Requests.push_back(req); } void ImGui::DockContextQueueUndockNode(ImGuiContext* ctx, ImGuiDockNode* node) @@ -12075,12 +12059,12 @@ void ImGui::DockContextQueueUndockNode(ImGuiContext* ctx, ImGuiDockNode* node) ImGuiDockRequest req; req.Type = ImGuiDockRequestType_Undock; req.UndockTargetNode = node; - ctx->DockContext->Requests.push_back(req); + ctx->DockContext.Requests.push_back(req); } void ImGui::DockContextQueueNotifyRemovedNode(ImGuiContext* ctx, ImGuiDockNode* node) { - ImGuiDockContext* dc = ctx->DockContext; + ImGuiDockContext* dc = &ctx->DockContext; for (int n = 0; n < dc->Requests.Size; n++) if (dc->Requests[n].DockTargetNode == node) dc->Requests[n].Type = ImGuiDockRequestType_None; @@ -13725,12 +13709,12 @@ void ImGui::DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImG if (child_0) { - ctx->DockContext->Nodes.SetVoidPtr(child_0->ID, NULL); + ctx->DockContext.Nodes.SetVoidPtr(child_0->ID, NULL); IM_DELETE(child_0); } if (child_1) { - ctx->DockContext->Nodes.SetVoidPtr(child_1->ID, NULL); + ctx->DockContext.Nodes.SetVoidPtr(child_1->ID, NULL); IM_DELETE(child_1); } } @@ -14264,7 +14248,7 @@ void ImGui::DockBuilderRemoveNode(ImGuiID node_id) void ImGui::DockBuilderRemoveNodeChildNodes(ImGuiID root_id) { ImGuiContext* ctx = GImGui; - ImGuiDockContext* dc = ctx->DockContext; + ImGuiDockContext* dc = &ctx->DockContext; ImGuiDockNode* root_node = root_id ? DockContextFindNodeByID(ctx, root_id) : NULL; if (root_id && root_node == NULL) @@ -14875,29 +14859,29 @@ static void ImGui::DockSettingsRemoveNodeReferences(ImGuiID* node_ids, int node_ static ImGuiDockNodeSettings* ImGui::DockSettingsFindNodeSettings(ImGuiContext* ctx, ImGuiID id) { // FIXME-OPT - ImGuiDockContext* dc = ctx->DockContext; - for (int n = 0; n < dc->SettingsNodes.Size; n++) - if (dc->SettingsNodes[n].ID == id) - return &dc->SettingsNodes[n]; + ImGuiDockContext* dc = &ctx->DockContext; + for (int n = 0; n < dc->NodesSettings.Size; n++) + if (dc->NodesSettings[n].ID == id) + return &dc->NodesSettings[n]; return NULL; } // Clear settings data static void ImGui::DockSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*) { - ImGuiDockContext* dc = ctx->DockContext; - dc->SettingsNodes.clear(); + ImGuiDockContext* dc = &ctx->DockContext; + dc->NodesSettings.clear(); DockContextClearNodes(ctx, 0, true); } -// Recreate dones based on settings data +// Recreate nodes based on settings data static void ImGui::DockSettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettingsHandler*) { // Prune settings at boot time only - ImGuiDockContext* dc = ctx->DockContext; + ImGuiDockContext* dc = &ctx->DockContext; if (ctx->Windows.Size == 0) DockContextPruneUnusedSettingsNodes(ctx); - DockContextBuildNodesFromSettings(ctx, dc->SettingsNodes.Data, dc->SettingsNodes.Size); + DockContextBuildNodesFromSettings(ctx, dc->NodesSettings.Data, dc->NodesSettings.Size); DockContextBuildAddWindowsToNodes(ctx, 0); } @@ -14943,11 +14927,10 @@ static void ImGui::DockSettingsHandler_ReadLine(ImGuiContext* ctx, ImGuiSettings if (sscanf(line, " NoWindowMenuButton=%d%n", &x, &r) == 1) { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoWindowMenuButton; } if (sscanf(line, " NoCloseButton=%d%n", &x, &r) == 1) { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoCloseButton; } if (sscanf(line, " Selected=0x%08X%n", &node.SelectedWindowId,&r) == 1) { line += r; } - ImGuiDockContext* dc = ctx->DockContext; if (node.ParentNodeId != 0) if (ImGuiDockNodeSettings* parent_settings = DockSettingsFindNodeSettings(ctx, node.ParentNodeId)) node.Depth = parent_settings->Depth + 1; - dc->SettingsNodes.push_back(node); + ctx->DockContext.NodesSettings.push_back(node); } static void DockSettingsHandler_DockNodeToSettings(ImGuiDockContext* dc, ImGuiDockNode* node, int depth) @@ -14964,7 +14947,7 @@ static void DockSettingsHandler_DockNodeToSettings(ImGuiDockContext* dc, ImGuiDo node_settings.Pos = ImVec2ih(node->Pos); node_settings.Size = ImVec2ih(node->Size); node_settings.SizeRef = ImVec2ih(node->SizeRef); - dc->SettingsNodes.push_back(node_settings); + dc->NodesSettings.push_back(node_settings); if (node->ChildNodes[0]) DockSettingsHandler_DockNodeToSettings(dc, node->ChildNodes[0], depth + 1); if (node->ChildNodes[1]) @@ -14974,29 +14957,29 @@ static void DockSettingsHandler_DockNodeToSettings(ImGuiDockContext* dc, ImGuiDo static void ImGui::DockSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf) { ImGuiContext& g = *ctx; - ImGuiDockContext* dc = g.DockContext; + ImGuiDockContext* dc = &ctx->DockContext; if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable)) return; // Gather settings data // (unlike our windows settings, because nodes are always built we can do a full rewrite of the SettingsNode buffer) - dc->SettingsNodes.resize(0); - dc->SettingsNodes.reserve(dc->Nodes.Data.Size); + dc->NodesSettings.resize(0); + dc->NodesSettings.reserve(dc->Nodes.Data.Size); for (int n = 0; n < dc->Nodes.Data.Size; n++) if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p) if (node->IsRootNode()) DockSettingsHandler_DockNodeToSettings(dc, node, 0); int max_depth = 0; - for (int node_n = 0; node_n < dc->SettingsNodes.Size; node_n++) - max_depth = ImMax((int)dc->SettingsNodes[node_n].Depth, max_depth); + for (int node_n = 0; node_n < dc->NodesSettings.Size; node_n++) + max_depth = ImMax((int)dc->NodesSettings[node_n].Depth, max_depth); // Write to text buffer buf->appendf("[%s][Data]\n", handler->TypeName); - for (int node_n = 0; node_n < dc->SettingsNodes.Size; node_n++) + for (int node_n = 0; node_n < dc->NodesSettings.Size; node_n++) { const int line_start_pos = buf->size(); (void)line_start_pos; - const ImGuiDockNodeSettings* node_settings = &dc->SettingsNodes[node_n]; + const ImGuiDockNodeSettings* node_settings = &dc->NodesSettings[node_n]; buf->appendf("%*s%s%*s", node_settings->Depth * 2, "", (node_settings->Flags & ImGuiDockNodeFlags_DockSpace) ? "DockSpace" : "DockNode ", (max_depth - node_settings->Depth) * 2, ""); // Text align nodes to facilitate looking at .ini file buf->appendf(" ID=0x%08X", node_settings->ID); if (node_settings->ParentNodeId) @@ -15722,7 +15705,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) #ifdef IMGUI_HAS_DOCK if (ImGui::TreeNode("Dock nodes")) { - ImGuiDockContext* dc = g.DockContext; + ImGuiDockContext* dc = &g.DockContext; ImGui::Checkbox("Ctrl shows window dock info", &show_docking_nodes); if (ImGui::SmallButton("Clear nodes")) { DockContextClearNodes(&g, 0, true); } ImGui::SameLine(); @@ -15777,14 +15760,15 @@ void ImGui::ShowMetricsWindow(bool* p_open) #ifdef IMGUI_HAS_DOCK if (ImGui::TreeNode("SettingsDocking", "Settings packed data: Docking")) { + ImGuiDockContext* dc = &g.DockContext; ImGui::Text("In SettingsWindows:"); for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings)) if (settings->DockId != 0) ImGui::BulletText("Window '%s' -> DockId %08X", settings->GetName(), settings->DockId); ImGui::Text("In SettingsNodes:"); - for (int n = 0; n < g.DockContext->SettingsNodes.Size; n++) + for (int n = 0; n < dc->NodesSettings.Size; n++) { - ImGuiDockNodeSettings* settings = &g.DockContext->SettingsNodes[n]; + ImGuiDockNodeSettings* settings = &dc->NodesSettings[n]; const char* selected_tab_name = NULL; if (settings->SelectedWindowId) { @@ -15871,8 +15855,9 @@ void ImGui::ShowMetricsWindow(bool* p_open) // Overlay: Display Docking info if (show_docking_nodes && g.IO.KeyCtrl) { - for (int n = 0; n < g.DockContext->Nodes.Data.Size; n++) - if (ImGuiDockNode* node = (ImGuiDockNode*)g.DockContext->Nodes.Data[n].val_p) + ImGuiDockContext* dc = &g.DockContext; + for (int n = 0; n < dc->Nodes.Data.Size; n++) + if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p) { ImGuiDockNode* root_node = DockNodeGetRootNode(node); if (ImGuiDockNode* hovered_node = DockNodeTreeFindNodeByPos(root_node, g.IO.MousePos)) diff --git a/imgui_internal.h b/imgui_internal.h index 489e501107f2..54d731cae12e 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -94,6 +94,7 @@ struct ImGuiColumns; // Storage data for a columns set struct ImGuiContext; // Main Dear ImGui context struct ImGuiDataTypeInfo; // Type information associated to a ImGuiDataType enum struct ImGuiDockContext; // Docking system context +struct ImGuiDockRequest; // Docking system dock/undock queued request struct ImGuiDockNode; // Docking system node (hold a list of Windows OR two child dock nodes) struct ImGuiDockNodeSettings; // Storage for a dock node in .ini file (we preserve those even if the associated dock node isn't active during the session) struct ImGuiGroupData; // Stacked storage data for BeginGroup()/EndGroup() @@ -1161,6 +1162,15 @@ struct ImGuiDockNode ImRect Rect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } }; +struct ImGuiDockContext +{ + ImGuiStorage Nodes; // Map ID -> ImGuiDockNode*: Active nodes + ImVector Requests; + ImVector NodesSettings; + bool WantFullRebuild; + ImGuiDockContext() { WantFullRebuild = false; } +}; + #endif // #ifdef IMGUI_HAS_DOCK //----------------------------------------------------------------------------- @@ -1443,7 +1453,7 @@ struct ImGuiContext // Extensions // FIXME: We could provide an API to register one slot in an array held in ImGuiContext? - ImGuiDockContext* DockContext; + ImGuiDockContext DockContext; // Settings bool SettingsLoaded; @@ -1597,8 +1607,6 @@ struct ImGuiContext PlatformImePos = PlatformImeLastPos = ImVec2(FLT_MAX, FLT_MAX); PlatformImePosViewport = 0; - DockContext = NULL; - SettingsLoaded = false; SettingsDirtyTimer = 0.0f; From 3b3af6b73136a03dfcdb84872fc08542900a1aa8 Mon Sep 17 00:00:00 2001 From: omar Date: Sat, 16 May 2020 16:11:42 +0200 Subject: [PATCH 615/828] Docking: Fix extraneous function declaration (#3236) + moved some other declarations in imgui_internal to facilitate moving docking code. --- imgui.cpp | 17 +++++++---------- imgui_internal.h | 10 ++++++---- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index fcebd5c0ff38..31b5e10e9896 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -921,7 +921,6 @@ static const float DOCKING_TRANSPARENT_PAYLOAD_ALPHA = 0.50f; // For u //------------------------------------------------------------------------- static void SetCurrentWindow(ImGuiWindow* window); -static void SetWindowHitTestHole(ImGuiWindow* window, const ImVec2& pos, const ImVec2& size); static void FindHoveredWindow(); static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags); static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window, bool snap_on_edges); @@ -7137,7 +7136,7 @@ void ImGui::SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond co window->Collapsed = collapsed; } -static void SetWindowHitTestHole(ImGuiWindow* window, const ImVec2& pos, const ImVec2& size) +void ImGui::SetWindowHitTestHole(ImGuiWindow* window, const ImVec2& pos, const ImVec2& size) { IM_ASSERT(window->HitTestHoleSize.x == 0); // We don't support multiple holes/hit test filters window->HitTestHoleSize = ImVec2ih(size); @@ -11679,7 +11678,6 @@ namespace ImGui static void DockContextPruneUnusedSettingsNodes(ImGuiContext* ctx); static ImGuiDockNode* DockContextFindNodeByID(ImGuiContext* ctx, ImGuiID id); static ImGuiDockNode* DockContextBindNodeToWindow(ImGuiContext* ctx, ImGuiWindow* window); - static void DockContextClearNodes(ImGuiContext* ctx, ImGuiID root_id, bool clear_persistent_docking_refs); // Use root_id==0 to clear all static void DockContextBuildNodesFromSettings(ImGuiContext* ctx, ImGuiDockNodeSettings* node_settings_array, int node_settings_count); static void DockContextBuildAddWindowsToNodes(ImGuiContext* ctx, ImGuiID root_id); // Use root_id==0 to add all @@ -11707,7 +11705,6 @@ namespace ImGui static void DockNodeCalcSplitRects(ImVec2& pos_old, ImVec2& size_old, ImVec2& pos_new, ImVec2& size_new, ImGuiDir dir, ImVec2 size_new_desired); static bool DockNodeCalcDropRectsAndTestMousePos(const ImRect& parent, ImGuiDir dir, ImRect& out_draw, bool outer_docking, ImVec2* test_mouse_pos); static const char* DockNodeGetHostWindowTitle(ImGuiDockNode* node, char* buf, int buf_size) { ImFormatString(buf, buf_size, "##DockNode_%02X", node->ID); return buf; } - static int DockNodeGetDepth(const ImGuiDockNode* node) { int depth = 0; while (node->ParentNode) { node = node->ParentNode; depth++; } return depth; } static int DockNodeGetTabOrder(ImGuiWindow* window); // ImGuiDockNode tree manipulations @@ -11781,11 +11778,11 @@ void ImGui::DockContextShutdown(ImGuiContext* ctx) IM_DELETE(node); } -void ImGui::DockContextClearNodes(ImGuiContext* ctx, ImGuiID root_id, bool clear_persistent_docking_references) +void ImGui::DockContextClearNodes(ImGuiContext* ctx, ImGuiID root_id, bool clear_settings_refs) { IM_UNUSED(ctx); IM_ASSERT(ctx == GImGui); - DockBuilderRemoveNodeDockedWindows(root_id, clear_persistent_docking_references); + DockBuilderRemoveNodeDockedWindows(root_id, clear_settings_refs); DockBuilderRemoveNodeChildNodes(root_id); } @@ -14351,12 +14348,12 @@ void ImGui::DockBuilderRemoveNodeChildNodes(ImGuiID root_id) } } -void ImGui::DockBuilderRemoveNodeDockedWindows(ImGuiID root_id, bool clear_persistent_docking_references) +void ImGui::DockBuilderRemoveNodeDockedWindows(ImGuiID root_id, bool clear_settings_refs) { // Clear references in settings ImGuiContext* ctx = GImGui; ImGuiContext& g = *ctx; - if (clear_persistent_docking_references) + if (clear_settings_refs) { for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings)) { @@ -14379,8 +14376,8 @@ void ImGui::DockBuilderRemoveNodeDockedWindows(ImGuiID root_id, bool clear_persi { const ImGuiID backup_dock_id = window->DockId; IM_UNUSED(backup_dock_id); - DockContextProcessUndockWindow(ctx, window, clear_persistent_docking_references); - if (!clear_persistent_docking_references) + DockContextProcessUndockWindow(ctx, window, clear_settings_refs); + if (!clear_settings_refs) IM_ASSERT(window->DockId == backup_dock_id); } } diff --git a/imgui_internal.h b/imgui_internal.h index 9737578c7fa8..f7a2fbca38f5 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1978,6 +1978,7 @@ namespace ImGui IMGUI_API void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond = 0); IMGUI_API void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond = 0); IMGUI_API void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond = 0); + IMGUI_API void SetWindowHitTestHole(ImGuiWindow* window, const ImVec2& pos, const ImVec2& size); // Windows: Display Order and Focus Order IMGUI_API void FocusWindow(ImGuiWindow* window); @@ -2107,7 +2108,7 @@ namespace ImGui // (some functions are only declared in imgui.cpp, see Docking section) IMGUI_API void DockContextInitialize(ImGuiContext* ctx); IMGUI_API void DockContextShutdown(ImGuiContext* ctx); - IMGUI_API void DockContextOnLoadSettings(ImGuiContext* ctx); + IMGUI_API void DockContextClearNodes(ImGuiContext* ctx, ImGuiID root_id, bool clear_settings_refs); // Use root_id==0 to clear all IMGUI_API void DockContextRebuildNodes(ImGuiContext* ctx); IMGUI_API void DockContextUpdateUndocking(ImGuiContext* ctx); IMGUI_API void DockContextUpdateDocking(ImGuiContext* ctx); @@ -2116,8 +2117,9 @@ namespace ImGui IMGUI_API void DockContextQueueUndockWindow(ImGuiContext* ctx, ImGuiWindow* window); IMGUI_API void DockContextQueueUndockNode(ImGuiContext* ctx, ImGuiDockNode* node); IMGUI_API bool DockContextCalcDropPosForDocking(ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, bool split_outer, ImVec2* out_pos); - inline ImGuiDockNode* DockNodeGetRootNode(ImGuiDockNode* node) { while (node->ParentNode) node = node->ParentNode; return node; } - inline ImGuiDockNode* GetWindowDockNode() { ImGuiContext& g = *GImGui; return g.CurrentWindow->DockNode; } + inline ImGuiDockNode* DockNodeGetRootNode(ImGuiDockNode* node) { while (node->ParentNode) node = node->ParentNode; return node; } + inline int DockNodeGetDepth(const ImGuiDockNode* node) { int depth = 0; while (node->ParentNode) { node = node->ParentNode; depth++; } return depth; } + inline ImGuiDockNode* GetWindowDockNode() { ImGuiContext& g = *GImGui; return g.CurrentWindow->DockNode; } IMGUI_API bool GetWindowAlwaysWantOwnTabBar(ImGuiWindow* window); IMGUI_API void BeginDocked(ImGuiWindow* window, bool* p_open); IMGUI_API void BeginDockableDragDropSource(ImGuiWindow* window); @@ -2138,7 +2140,7 @@ namespace ImGui inline ImGuiDockNode* DockBuilderGetCentralNode(ImGuiID node_id) { ImGuiDockNode* node = DockBuilderGetNode(node_id); if (!node) return NULL; return DockNodeGetRootNode(node)->CentralNode; } IMGUI_API ImGuiID DockBuilderAddNode(ImGuiID node_id = 0, ImGuiDockNodeFlags flags = 0); IMGUI_API void DockBuilderRemoveNode(ImGuiID node_id); // Remove node and all its child, undock all windows - IMGUI_API void DockBuilderRemoveNodeDockedWindows(ImGuiID node_id, bool clear_persistent_docking_references = true); + IMGUI_API void DockBuilderRemoveNodeDockedWindows(ImGuiID node_id, bool clear_settings_refs = true); IMGUI_API void DockBuilderRemoveNodeChildNodes(ImGuiID node_id); // Remove all split/hierarchy. All remaining docked windows will be re-docked to the remaining root node (node_id). IMGUI_API void DockBuilderSetNodePos(ImGuiID node_id, ImVec2 pos); IMGUI_API void DockBuilderSetNodeSize(ImGuiID node_id, ImVec2 size); From 615e9ae34537df5324df2f71253eafd887a848d1 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 18 May 2020 15:08:47 +0200 Subject: [PATCH 616/828] Docking: Fix undocking (#3243), amend 7b3d3798 (#1738) --- imgui.cpp | 15 ++++++++++----- imgui_widgets.cpp | 5 +++++ 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 31b5e10e9896..bec6741353ea 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6796,9 +6796,14 @@ void ImGui::FocusWindow(ImGuiWindow* window) IM_ASSERT(window == NULL || window->RootWindow != NULL); ImGuiWindow* focus_front_window = window ? window->RootWindowDockStop : NULL; ImGuiWindow* display_front_window = window ? window->RootWindow : NULL; - - // Steal focus on active widgets - if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindowDockStop != focus_front_window) + ImGuiDockNode* dock_node = window ? window->DockNode : NULL; + bool active_id_window_is_dock_node_host = (g.ActiveIdWindow && dock_node && dock_node->HostWindow == g.ActiveIdWindow); + + // Steal active widgets. Some of the cases it triggers includes: + // - Focus a window while an InputText in another window is active, if focus happens before the old InputText can run. + // - When using Nav to activate menu items (due to timing of activating on press->new window appears->losing ActiveId) + // - Using dock host items (tab, collapse button) can trigger this before we redirect the ActiveIdWindow toward the child window. + if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindowDockStop != focus_front_window && !active_id_window_is_dock_node_host) ClearActiveID(); // Passing NULL allow to disable keyboard focus @@ -6807,8 +6812,8 @@ void ImGui::FocusWindow(ImGuiWindow* window) window->LastFrameJustFocused = g.FrameCount; // Select in dock node - if (window->DockNode && window->DockNode->TabBar) - window->DockNode->TabBar->SelectedTabId = window->DockNode->TabBar->NextSelectedTabId = window->ID; + if (dock_node && dock_node->TabBar) + dock_node->TabBar->SelectedTabId = dock_node->TabBar->NextSelectedTabId = window->ID; // Bring to front BringWindowToFocusFront(focus_front_window); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 74df48655d6d..2c1c04e1dc8d 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7289,6 +7289,11 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, tab_bar->NextSelectedTabId = id; hovered |= (g.HoveredId == id); + // Transfer active id window so the active id is not owned by the dock host (as StartMouseMovingWindow() + // will only do it on the drag). This allows FocusWindow() to be more conservative in how it clears active id. + if (held && docked_window && g.ActiveId == id && g.ActiveIdIsJustActivated) + g.ActiveIdWindow = docked_window; + // Allow the close button to overlap unless we are dragging (in which case we don't want any overlapping tabs to be hovered) if (!held) SetItemAllowOverlap(); From 0fe5170bc400b789dcb3038eb0ead9d540927dfd Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 25 May 2020 16:28:55 +0200 Subject: [PATCH 617/828] Viewports: Report minimized viewports as zero DisplaySize to be consistent with main branch + comments (#1542) --- imgui.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index bec6741353ea..f580bc225366 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4363,6 +4363,13 @@ void ImDrawDataBuilder::FlattenIntoSingleLayer() static void SetupViewportDrawData(ImGuiViewportP* viewport, ImVector* draw_lists) { + // When minimized, we report draw_data->DisplaySize as zero to be consistent with non-viewport mode, + // and to allow applications/back-ends to easily skip rendering. + // FIXME: Note that we however do NOT attempt to report "zero drawlist / vertices" into the ImDrawData structure. + // This is because the work has been done already, and its wasted! We should fix that and add optimizations for + // it earlier in the pipeline, rather than pretend to hide the data at the end of the pipeline. + const bool is_minimized = (viewport->Flags & ImGuiViewportFlags_Minimized) != 0; + ImDrawData* draw_data = &viewport->DrawDataP; viewport->DrawData = draw_data; // Make publicly accessible draw_data->Valid = true; @@ -4370,7 +4377,7 @@ static void SetupViewportDrawData(ImGuiViewportP* viewport, ImVectorCmdListsCount = draw_lists->Size; draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0; draw_data->DisplayPos = viewport->Pos; - draw_data->DisplaySize = viewport->Size; + draw_data->DisplaySize = is_minimized ? ImVec2(0.0f, 0.0f) : viewport->Size; draw_data->FramebufferScale = ImGui::GetIO().DisplayFramebufferScale; // FIXME-VIEWPORT: This may vary on a per-monitor/viewport basis? draw_data->OwnerViewport = viewport; for (int n = 0; n < draw_lists->Size; n++) From 76e40fe5d1059733ddcdd88236ee5c5743ed64b7 Mon Sep 17 00:00:00 2001 From: omar Date: Sat, 20 Jun 2020 22:06:44 +0200 Subject: [PATCH 618/828] Docking: Fix misuse of PushClipRect in UpdateWindowManualResize(). (#3311) --- imgui.cpp | 5 ++--- imgui.h | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index ff6722af86cf..08c86496dde1 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5502,9 +5502,10 @@ static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& s // This is however not the case with current back-ends under Win32, but a custom borderless window implementation would benefit from it. // - When decoration are enabled we typically benefit from that distance, but then our resize elements would be conflicting with OS resize elements, so we also narrow. // - Note that we are unable to tell if the platform setup allows hovering with a distance threshold (on Win32, decorated window have such threshold). + // We only clip interaction so we overwrite window->ClipRect, cannot call PushClipRect() yet as DrawList is not yet setup. const bool clip_with_viewport_rect = !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport) || (g.IO.MouseHoveredViewport != window->ViewportId) || !(window->Viewport->Flags & ImGuiViewportFlags_NoDecoration); if (clip_with_viewport_rect) - PushClipRect(window->Viewport->Pos, window->Viewport->Pos + window->Viewport->Size, true); // Won't incur a draw command as we are not drawing here. + window->ClipRect = window->Viewport->GetMainRect(); // Resize grips and borders are on layer 1 window->DC.NavLayerCurrent = ImGuiNavLayer_Menu; @@ -5568,8 +5569,6 @@ static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& s } } PopID(); - if (clip_with_viewport_rect) - PopClipRect(); // Restore nav layer window->DC.NavLayerCurrent = ImGuiNavLayer_Main; diff --git a/imgui.h b/imgui.h index 980fbf1657c0..c283070f7e2c 100644 --- a/imgui.h +++ b/imgui.h @@ -1697,7 +1697,7 @@ struct ImGuiSizeCallbackData // Important: the content of this class is still highly WIP and likely to change and be refactored // before we stabilize Docking features. Please be mindful if using this. // Provide hints: -// - To the platform back-end via altered viewport flags (enable/disable OS decoration, OS task bar icons, etc.) +// - To the platform back-end via altered viewport flags (enable/disable OS decoration, OS task bar icons, etc.) // - To the platform back-end for OS level parent/child relationships of viewport. // - To the docking system for various options and filtering. struct ImGuiWindowClass @@ -1707,7 +1707,7 @@ struct ImGuiWindowClass ImGuiViewportFlags ViewportFlagsOverrideSet; // Viewport flags to set when a window of this class owns a viewport. This allows you to enforce OS decoration or task bar icon, override the defaults on a per-window basis. ImGuiViewportFlags ViewportFlagsOverrideClear; // Viewport flags to clear when a window of this class owns a viewport. This allows you to enforce OS decoration or task bar icon, override the defaults on a per-window basis. ImGuiDockNodeFlags DockNodeFlagsOverrideSet; // [EXPERIMENTAL] Dock node flags to set when a window of this class is hosted by a dock node (it doesn't have to be selected!) - ImGuiDockNodeFlags DockNodeFlagsOverrideClear; // [EXPERIMENTAL] + ImGuiDockNodeFlags DockNodeFlagsOverrideClear; // [EXPERIMENTAL] bool DockingAlwaysTabBar; // Set to true to enforce single floating windows of this class always having their own docking node (equivalent of setting the global io.ConfigDockingAlwaysTabBar) bool DockingAllowUnclassed; // Set to true to allow windows of this class to be docked/merged with an unclassed window. From a616ff5d4a7bb72ccbe9906d3220d0938c1915cf Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 23 Jun 2020 17:52:13 +0200 Subject: [PATCH 619/828] BeginPopupModal() doesn't set the ImGuiWindowFlags_NoSavedSettings flag anymore, and will not always be auto-centered. (#915, #3091) # Conflicts: # imgui.cpp --- docs/CHANGELOG.txt | 4 ++++ imgui.cpp | 10 ++++++---- imgui_demo.cpp | 4 ++++ 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index ead04e531310..11f0c052eb46 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -127,6 +127,10 @@ Other Changes: would attempt to focus it and close other popups. (#2880) - Popups: Fix BeginPopupContextVoid() when clicking over the area made unavailable by a modal. (#1636) - Popups: Clarified some of the comments and function prototypes. +- Modals: BeginPopupModal() doesn't set the ImGuiWindowFlags_NoSavedSettings flag anymore, and will + not always be auto-centered. Note that modals are more similar to regular windows than they are to + popups, so api and behavior may evolve further toward embracing this. (#915, #3091) + Enforce centering using e.g. SetNextWindowPos(io.DisplaySize * 0.5f, ImGuiCond_Appearing, ImVec2(0.5f,0.5f)). - Metrics: Added a "Settings" section with some details about persistent ini settings. - Nav, Menus: Fix vertical wrap-around in menus or popups created with multiple appending calls to BeginMenu()/EndMenu() or BeginPopup/EndPopup(). (#3223, #1207) [@rokups] diff --git a/imgui.cpp b/imgui.cpp index 08c86496dde1..4a57b4783df8 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6191,7 +6191,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if (window_just_activated_by_user) { window->AutoPosLastDirection = ImGuiDir_None; - if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api) + if ((flags & ImGuiWindowFlags_Popup) != 0 && !(flags & ImGuiWindowFlags_Modal) && !window_pos_set_by_api) // FIXME: BeginPopup() could use SetNextWindowPos() window->Pos = g.BeginPopupStack.back().OpenPopupPos; } @@ -8547,6 +8547,7 @@ void ImGui::CloseCurrentPopup() window->DC.NavHideHighlightOneFrame = true; } +// Attention! BeginPopup() adds default flags which BeginPopupEx()! bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags flags) { ImGuiContext& g = *GImGui; @@ -8595,15 +8596,16 @@ bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags fla return false; } - // Center modal windows by default + // Center modal windows by default for increased visibility + // (this won't really last as settings will kick in, and is mostly for backward compatibility. user may do the same themselves) // FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window. if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) == 0) { ImGuiViewportP* viewport = window->WasActive ? window->Viewport : (ImGuiViewportP*)GetMainViewport(); // FIXME-VIEWPORT: What may be our reference viewport? - SetNextWindowPos(viewport->GetMainRect().GetCenter(), ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); + SetNextWindowPos(viewport->GetMainRect().GetCenter(), ImGuiCond_FirstUseEver, ImVec2(0.5f, 0.5f)); } - flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoDocking; + flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoDocking; const bool is_open = Begin(name, p_open, flags); if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display) { diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 42a6320d692c..2202cbf3d5bc 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2922,6 +2922,10 @@ static void ShowDemoWindowPopups() if (ImGui::Button("Delete..")) ImGui::OpenPopup("Delete?"); + // Always center this window when appearing + ImVec2 center(ImGui::GetIO().DisplaySize.x * 0.5f, ImGui::GetIO().DisplaySize.y * 0.5f); + ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); + if (ImGui::BeginPopupModal("Delete?", NULL, ImGuiWindowFlags_AlwaysAutoResize)) { ImGui::Text("All those beautiful files will be deleted.\nThis operation cannot be undone!\n\n"); From e1d7e1471702ed0f50cf11bccb3877df05f45134 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 23 Jun 2020 18:31:44 +0200 Subject: [PATCH 620/828] Viewports: used main viewport for centering (wip), clarified the meaning of how ImGuiPlatformMonitor WorkPos/WorkSize should be set if unknown, added asserts. --- imgui.cpp | 5 ++--- imgui.h | 5 +++-- imgui_demo.cpp | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 4a57b4783df8..9e19f946d2ae 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7590,9 +7590,8 @@ static void ImGui::ErrorCheckNewFrameSanityChecks() for (int monitor_n = 0; monitor_n < g.PlatformIO.Monitors.Size; monitor_n++) { ImGuiPlatformMonitor& mon = g.PlatformIO.Monitors[monitor_n]; - IM_UNUSED(mon); - IM_ASSERT(mon.MainSize.x > 0.0f && mon.MainSize.y > 0.0f && "Monitor bounds not setup properly."); - IM_ASSERT(mon.WorkSize.x > 0.0f && mon.WorkSize.y > 0.0f && "Monitor bounds not setup properly. If you don't have work area information, just copy Min/Max into them."); + IM_ASSERT(mon.MainSize.x > 0.0f && mon.MainSize.y > 0.0f && "Monitor main bounds not setup properly."); + IM_ASSERT(ImRect(mon.MainPos, mon.MainPos + mon.MainSize).Contains(ImRect(mon.WorkPos, mon.WorkPos + mon.WorkSize)) && "Monitor work bounds not setup properly. If you don't have work area information, just copy MainPos/MainSize into them."); IM_ASSERT(mon.DpiScale != 0.0f); } } diff --git a/imgui.h b/imgui.h index c283070f7e2c..dc37773ce596 100644 --- a/imgui.h +++ b/imgui.h @@ -2570,7 +2570,7 @@ struct ImGuiPlatformIO struct ImGuiPlatformMonitor { ImVec2 MainPos, MainSize; // Coordinates of the area displayed on this monitor (Min = upper left, Max = bottom right) - ImVec2 WorkPos, WorkSize; // (Optional) Coordinates without task bars / side bars / menu bars. imgui uses this to avoid positioning popups/tooltips inside this region. + ImVec2 WorkPos, WorkSize; // Coordinates without task bars / side bars / menu bars. Used to avoid positioning popups/tooltips inside this region. If you don't have this info, please copy the value for MainPos/MainSize. float DpiScale; // 1.0f = 96 DPI ImGuiPlatformMonitor() { MainPos = MainSize = WorkPos = WorkSize = ImVec2(0, 0); DpiScale = 1.0f; } }; @@ -2621,7 +2621,8 @@ struct ImGuiViewport ImGuiViewport() { ID = 0; Flags = 0; DpiScale = 0.0f; DrawData = NULL; ParentViewportId = 0; RendererUserData = PlatformUserData = PlatformHandle = PlatformHandleRaw = NULL; PlatformRequestMove = PlatformRequestResize = PlatformRequestClose = false; } ~ImGuiViewport() { IM_ASSERT(PlatformUserData == NULL && RendererUserData == NULL); } - // Access work-area rectangle + // Access work-area rectangle with GetWorkXXX functions (see comments above) + ImVec2 GetCenter() { return ImVec2(Pos.x + Size.x * 0.5f, Pos.y + Size.y * 0.5f); } ImVec2 GetWorkPos() { return ImVec2(Pos.x + WorkOffsetMin.x, Pos.y + WorkOffsetMin.y); } ImVec2 GetWorkSize() { return ImVec2(Size.x - WorkOffsetMin.x + WorkOffsetMax.x, Size.y - WorkOffsetMin.y + WorkOffsetMax.y); } // This not clamped }; diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 2202cbf3d5bc..c6dec7a25221 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2923,8 +2923,8 @@ static void ShowDemoWindowPopups() ImGui::OpenPopup("Delete?"); // Always center this window when appearing - ImVec2 center(ImGui::GetIO().DisplaySize.x * 0.5f, ImGui::GetIO().DisplaySize.y * 0.5f); - ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); + ImGuiViewport* viewport = ImGui::GetMainViewport(); + ImGui::SetNextWindowPos(viewport->GetCenter(), ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); if (ImGui::BeginPopupModal("Delete?", NULL, ImGuiWindowFlags_AlwaysAutoResize)) { From 7538bbabb62da3105b62ca596827407f638c2e27 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 23 Jun 2020 19:01:40 +0200 Subject: [PATCH 621/828] Demo: Commented out ideas on another way to center a window. --- imgui_demo.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index c6dec7a25221..b43f5981cc9a 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2923,8 +2923,11 @@ static void ShowDemoWindowPopups() ImGui::OpenPopup("Delete?"); // Always center this window when appearing - ImGuiViewport* viewport = ImGui::GetMainViewport(); - ImGui::SetNextWindowPos(viewport->GetCenter(), ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); + ImVec2 center = ImGui::GetMainViewport()->GetCenter(); + //ImVec2 parent_pos = ImGui::GetWindowPos(); + //ImVec2 parent_size = ImGui::GetWindowSize(); + //ImVec2 center(parent_pos.x + parent_size.x * 0.5f, parent_pos.y + parent_size.y * 0.5f); + ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); if (ImGui::BeginPopupModal("Delete?", NULL, ImGuiWindowFlags_AlwaysAutoResize)) { From d7ef56dca211fce88c797068b29e1112867c25e4 Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Wed, 24 Jun 2020 14:00:35 +0300 Subject: [PATCH 622/828] Windows: Fix unintended window size changes when resizing windows close to main viewport edges. --- docs/CHANGELOG.txt | 13 +++++++------ imgui.cpp | 7 +++++++ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index f7128b4ae3b9..8d5a69bd95bd 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -123,17 +123,18 @@ Other Changes: Set to 0.0f (default) to always make a close button appear on hover (same as Chrome, VS). Set to FLT_MAX to only display a close button when selected (merely hovering is not enough). Set to an intermediary value to toggle behavior based on width (same as Firefox). -- Tab: Added a ImGuiTabItemFlags_NoTooltip flag to disable the tooltip for individual tab item +- Tab: Added a ImGuiTabItemFlags_NoTooltip flag to disable the tooltip for individual tab item (vs ImGuiTabBarFlags_NoTooltip for entire tab bar). [@Xipiryon] -- Popups: All functions capable of opening popups (OpenPopup*, BeginPopupContext*) now take a new - ImGuiPopupFlags sets of flags instead of a mouse button index. The API is automatically backward +- Windows: Fix unintended feedback loops when resizing windows close to main viewport edges. [@rokups] +- Popups: All functions capable of opening popups (OpenPopup*, BeginPopupContext*) now take a new + ImGuiPopupFlags sets of flags instead of a mouse button index. The API is automatically backward compatible as ImGuiPopupFlags is guaranteed to hold mouse button index in the lower bits. - Popups: Added ImGuiPopupFlags_NoOpenOverExistingPopup for OpenPopup*/BeginPopupContext* functions to first test for the presence of another popup at the same level. - Popups: Added ImGuiPopupFlags_NoOpenOverItems for BeginPopupContextWindow() - similar to testing for !IsAnyItemHovered() prior to doing an OpenPopup. -- Popups: Added ImGuiPopupFlags_AnyPopupId and ImGuiPopupFlags_AnyPopupLevel flags for IsPopupOpen(), - allowing to check if any popup is open at the current level, if a given popup is open at any popup +- Popups: Added ImGuiPopupFlags_AnyPopupId and ImGuiPopupFlags_AnyPopupLevel flags for IsPopupOpen(), + allowing to check if any popup is open at the current level, if a given popup is open at any popup level, if any popup is open at all. - Popups: Fix an edge case where programatically closing a popup while clicking on its empty space would attempt to focus it and close other popups. (#2880) @@ -141,7 +142,7 @@ Other Changes: - Popups: Clarified some of the comments and function prototypes. - Modals: BeginPopupModal() doesn't set the ImGuiWindowFlags_NoSavedSettings flag anymore, and will not always be auto-centered. Note that modals are more similar to regular windows than they are to - popups, so api and behavior may evolve further toward embracing this. (#915, #3091) + popups, so api and behavior may evolve further toward embracing this. (#915, #3091) Enforce centering using e.g. SetNextWindowPos(io.DisplaySize * 0.5f, ImGuiCond_Appearing, ImVec2(0.5f,0.5f)). - Metrics: Added a "Settings" section with some details about persistent ini settings. - Nav, Menus: Fix vertical wrap-around in menus or popups created with multiple appending calls to diff --git a/imgui.cpp b/imgui.cpp index 52bb77907c78..0666b4041c48 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5551,6 +5551,9 @@ static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& s // Resize from any of the four corners // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + ImLerp(grip.InnerDir * grip_hover_outer_size, grip.InnerDir * -grip_hover_inner_size, grip.CornerPosN); // Corner of the window corresponding to our corner grip + ImVec2 clamp_min = ImVec2(grip.CornerPosN.x == 1 ? g.Style.DisplayWindowPadding.x : -FLT_MAX, grip.CornerPosN.y == 1 ? g.Style.DisplayWindowPadding.y : -FLT_MAX); + ImVec2 clamp_max = ImVec2(grip.CornerPosN.x == 0 ? g.IO.DisplaySize.x - g.Style.DisplayWindowPadding.x : FLT_MAX, grip.CornerPosN.y == 0 ? g.IO.DisplaySize.y - g.Style.DisplayWindowPadding.y : FLT_MAX); + corner_target = ImClamp(corner_target, clamp_min, clamp_max); CalcResizePosSizeFromAnyCorner(window, corner_target, grip.CornerPosN, &pos_target, &size_target); } if (resize_grip_n == 0 || held || hovered) @@ -5576,6 +5579,9 @@ static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& s if (border_n == 1) { border_posn = ImVec2(1, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Right if (border_n == 2) { border_posn = ImVec2(0, 1); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Bottom if (border_n == 3) { border_posn = ImVec2(0, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Left + ImVec2 clamp_min = ImVec2(border_n == 1 ? g.Style.DisplayWindowPadding.x : -FLT_MAX, border_n == 2 ? g.Style.DisplayWindowPadding.y : -FLT_MAX); + ImVec2 clamp_max = ImVec2(border_n == 3 ? g.IO.DisplaySize.x - g.Style.DisplayWindowPadding.x : FLT_MAX, border_n == 0 ? g.IO.DisplaySize.y - g.Style.DisplayWindowPadding.y : FLT_MAX); + border_target = ImClamp(border_target, clamp_min, clamp_max); CalcResizePosSizeFromAnyCorner(window, border_target, border_posn, &pos_target, &size_target); } } @@ -5597,6 +5603,7 @@ static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& s { const float NAV_RESIZE_SPEED = 600.0f; nav_resize_delta *= ImFloor(NAV_RESIZE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y)); + nav_resize_delta = ImClamp(nav_resize_delta, g.Style.DisplayWindowPadding - window->Pos - window->Size, ImVec2(FLT_MAX, FLT_MAX)); g.NavWindowingToggleLayer = false; g.NavDisableMouseHover = true; resize_grip_col[0] = GetColorU32(ImGuiCol_ResizeGripActive); From f4f04cb5ec83168a9d1917ebeed11ac185b37a76 Mon Sep 17 00:00:00 2001 From: omar Date: Thu, 25 Jun 2020 22:58:23 +0200 Subject: [PATCH 623/828] Windows: Amend 6b0cf2e6 to facilitate working in viewport branch + handle safe area padding and ConfigWindowsMoveFromTitleBarOnly. # Conflicts: # imgui.cpp --- imgui.cpp | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 0666b4041c48..d265886cf6f7 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -975,7 +975,7 @@ static void UpdateMouseInputs(); static void UpdateMouseWheel(); static void UpdateTabFocus(); static void UpdateDebugToolItemPicker(); -static bool UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4]); +static bool UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect); static void RenderWindowOuterBorders(ImGuiWindow* window); static void RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, bool handle_borders_and_resize_grips, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size); static void RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open); @@ -5489,7 +5489,7 @@ ImGuiID ImGui::GetWindowResizeID(ImGuiWindow* window, int n) // Handle resize for: Resize Grips, Borders, Gamepad // Return true when using auto-fit (double click on resize grip) -static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4]) +static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect) { ImGuiContext& g = *GImGui; ImGuiWindowFlags flags = window->Flags; @@ -5551,8 +5551,8 @@ static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& s // Resize from any of the four corners // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + ImLerp(grip.InnerDir * grip_hover_outer_size, grip.InnerDir * -grip_hover_inner_size, grip.CornerPosN); // Corner of the window corresponding to our corner grip - ImVec2 clamp_min = ImVec2(grip.CornerPosN.x == 1 ? g.Style.DisplayWindowPadding.x : -FLT_MAX, grip.CornerPosN.y == 1 ? g.Style.DisplayWindowPadding.y : -FLT_MAX); - ImVec2 clamp_max = ImVec2(grip.CornerPosN.x == 0 ? g.IO.DisplaySize.x - g.Style.DisplayWindowPadding.x : FLT_MAX, grip.CornerPosN.y == 0 ? g.IO.DisplaySize.y - g.Style.DisplayWindowPadding.y : FLT_MAX); + ImVec2 clamp_min = ImVec2(grip.CornerPosN.x == 1.0f ? visibility_rect.Min.x : -FLT_MAX, grip.CornerPosN.y == 1.0f ? visibility_rect.Min.y : -FLT_MAX); + ImVec2 clamp_max = ImVec2(grip.CornerPosN.x == 0.0f ? visibility_rect.Max.x : +FLT_MAX, grip.CornerPosN.y == 0.0f ? visibility_rect.Max.y : +FLT_MAX); corner_target = ImClamp(corner_target, clamp_min, clamp_max); CalcResizePosSizeFromAnyCorner(window, corner_target, grip.CornerPosN, &pos_target, &size_target); } @@ -5579,8 +5579,8 @@ static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& s if (border_n == 1) { border_posn = ImVec2(1, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Right if (border_n == 2) { border_posn = ImVec2(0, 1); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Bottom if (border_n == 3) { border_posn = ImVec2(0, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS); } // Left - ImVec2 clamp_min = ImVec2(border_n == 1 ? g.Style.DisplayWindowPadding.x : -FLT_MAX, border_n == 2 ? g.Style.DisplayWindowPadding.y : -FLT_MAX); - ImVec2 clamp_max = ImVec2(border_n == 3 ? g.IO.DisplaySize.x - g.Style.DisplayWindowPadding.x : FLT_MAX, border_n == 0 ? g.IO.DisplaySize.y - g.Style.DisplayWindowPadding.y : FLT_MAX); + ImVec2 clamp_min = ImVec2(border_n == 1 ? visibility_rect.Min.x : -FLT_MAX, border_n == 2 ? visibility_rect.Min.y : -FLT_MAX); + ImVec2 clamp_max = ImVec2(border_n == 3 ? visibility_rect.Max.x : +FLT_MAX, border_n == 0 ? visibility_rect.Max.y : +FLT_MAX); border_target = ImClamp(border_target, clamp_min, clamp_max); CalcResizePosSizeFromAnyCorner(window, border_target, border_posn, &pos_target, &size_target); } @@ -5603,7 +5603,7 @@ static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& s { const float NAV_RESIZE_SPEED = 600.0f; nav_resize_delta *= ImFloor(NAV_RESIZE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y)); - nav_resize_delta = ImClamp(nav_resize_delta, g.Style.DisplayWindowPadding - window->Pos - window->Size, ImVec2(FLT_MAX, FLT_MAX)); + nav_resize_delta = ImMax(nav_resize_delta, visibility_rect.Min - window->Pos - window->Size); g.NavWindowingToggleLayer = false; g.NavDisableMouseHover = true; resize_grip_col[0] = GetColorU32(ImGuiCol_ResizeGripActive); @@ -5628,13 +5628,13 @@ static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& s return ret_auto_fit; } -static inline void ClampWindowRect(ImGuiWindow* window, const ImRect& viewport_rect, const ImVec2& padding) +static inline void ClampWindowRect(ImGuiWindow* window, const ImRect& visibility_rect) { ImGuiContext& g = *GImGui; ImVec2 size_for_clamping = window->Size; if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(window->Flags & ImGuiWindowFlags_NoTitleBar)) size_for_clamping.y = window->TitleBarHeight(); - window->Pos = ImClamp(window->Pos, viewport_rect.Min + padding - size_for_clamping, viewport_rect.Max - padding); + window->Pos = ImClamp(window->Pos, visibility_rect.Min - size_for_clamping, visibility_rect.Max); } static void ImGui::RenderWindowOuterBorders(ImGuiWindow* window) @@ -6318,16 +6318,21 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->Viewport->Flags = viewport_flags; } + // Calculate the range of allowed position for that window (to be movable and visible past safe area padding) + // When clamping to stay visible, we will enforce that window->Pos stays inside of visibility_rect. + ImRect viewport_rect(window->Viewport->GetMainRect()); + ImRect viewport_work_rect(window->Viewport->GetWorkRect()); + ImVec2 visibility_padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding); + ImRect visibility_rect(viewport_work_rect.Min + visibility_padding, viewport_work_rect.Max - visibility_padding); + // Clamp position/size so window stays visible within its viewport or monitor // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing. // FIXME: Similar to code in GetWindowAllowedExtentRect() - ImRect viewport_rect = window->Viewport->GetMainRect(); if (!window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) { - ImVec2 clamp_padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding); if (!window->ViewportOwned && viewport_rect.GetWidth() > 0 && viewport_rect.GetHeight() > 0.0f) { - ClampWindowRect(window, window->Viewport->GetWorkRect(), clamp_padding); + ClampWindowRect(window, visibility_rect); } else if (window->ViewportOwned && g.PlatformIO.Monitors.Size > 0) { @@ -6339,7 +6344,9 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) else { ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[window->Viewport->PlatformMonitor]; - ClampWindowRect(window, ImRect(monitor.WorkPos, monitor.WorkPos + monitor.WorkSize), clamp_padding); + visibility_rect.Min = monitor.WorkPos + visibility_padding; + visibility_rect.Max = monitor.WorkPos + monitor.WorkSize - visibility_padding; + ClampWindowRect(window, visibility_rect); } } } @@ -6375,7 +6382,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) const int resize_grip_count = g.IO.ConfigWindowsResizeFromEdges ? 2 : 1; // Allow resize from lower-left if we have the mouse cursor feedback for it. const float resize_grip_draw_size = IM_FLOOR(ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f)); if (handle_borders_and_resize_grips && !window->Collapsed) - if (UpdateWindowManualResize(window, size_auto_fit, &border_held, resize_grip_count, &resize_grip_col[0])) + if (UpdateWindowManualResize(window, size_auto_fit, &border_held, resize_grip_count, &resize_grip_col[0], visibility_rect)) use_current_size_for_scrollbar_x = use_current_size_for_scrollbar_y = true; window->ResizeBorderHeld = (signed char)border_held; From 2b9d88196e76c988bc28e615aa4fe12dc51c8fc8 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 30 Jun 2020 18:52:02 +0200 Subject: [PATCH 624/828] Docking: Rework size allocations to recover when there's no enough room for nodes + do not hold on WantLockSizeOnce forever (#3328) (Ensure if the fact that WantLockSizeOnce was kept when only 1 child is visible was desired/desirable) --- imgui.cpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index cf5f13b519dd..3936fd321d46 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -13793,16 +13793,14 @@ void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 si IM_ASSERT(!(child_0->WantLockSizeOnce && child_1->WantLockSizeOnce)); if (child_0->WantLockSizeOnce) { - child_0->WantLockSizeOnce = false; - child_0_size[axis] = child_0->SizeRef[axis] = child_0->Size[axis]; + child_0_size[axis] = child_0->SizeRef[axis] = ImMin(size_avail - 1.0f, child_0->Size[axis]); child_1_size[axis] = child_1->SizeRef[axis] = (size_avail - child_0_size[axis]); IM_ASSERT(child_0->SizeRef[axis] > 0.0f && child_1->SizeRef[axis] > 0.0f); } else if (child_1->WantLockSizeOnce) { - child_1->WantLockSizeOnce = false; - child_1_size[axis] = child_1->SizeRef[axis] = child_1->Size[axis]; + child_1_size[axis] = child_1->SizeRef[axis] = ImMin(size_avail - 1.0f, child_1->Size[axis]); child_0_size[axis] = child_0->SizeRef[axis] = (size_avail - child_1_size[axis]); IM_ASSERT(child_0->SizeRef[axis] > 0.0f && child_1->SizeRef[axis] > 0.0f); } @@ -13825,8 +13823,11 @@ void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 si child_0_size[axis] = ImMax(size_min_each, ImFloor(size_avail * split_ratio + 0.5F)); child_1_size[axis] = (size_avail - child_0_size[axis]); } + child_1_pos[axis] += spacing + child_0_size[axis]; } + child_0->WantLockSizeOnce = child_1->WantLockSizeOnce = false; + if (child_0->IsVisible) DockNodeTreeUpdatePosSize(child_0, child_0_pos, child_0_size); if (child_1->IsVisible) @@ -15584,7 +15585,12 @@ void ImGui::ShowMetricsWindow(bool* p_open) NodeWindow(node->HostWindow, "HostWindow"); NodeWindow(node->VisibleWindow, "VisibleWindow"); ImGui::BulletText("SelectedTabID: 0x%08X, LastFocusedNodeID: 0x%08X", node->SelectedTabId, node->LastFocusedNodeId); - ImGui::BulletText("Misc:%s%s%s%s", node->IsDockSpace() ? " IsDockSpace" : "", node->IsCentralNode() ? " IsCentralNode" : "", (g.FrameCount - node->LastFrameAlive < 2) ? " IsAlive" : "", (g.FrameCount - node->LastFrameActive < 2) ? " IsActive" : ""); + ImGui::BulletText("Misc:%s%s%s%s%s", + node->IsDockSpace() ? " IsDockSpace" : "", + node->IsCentralNode() ? " IsCentralNode" : "", + (g.FrameCount - node->LastFrameAlive < 2) ? " IsAlive" : "", + (g.FrameCount - node->LastFrameActive < 2) ? " IsActive" : "", + node->WantLockSizeOnce ? " WantLockSizeOnce" : ""); if (ImGui::TreeNode("flags", "LocalFlags: 0x%04X SharedFlags: 0x%04X", node->LocalFlags, node->SharedFlags)) { ImGui::CheckboxFlags("LocalFlags: NoDocking", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_NoDocking); From 4bdbea8375557fcd3c8b46abde7cc0119c2c57d4 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 30 Jun 2020 18:53:13 +0200 Subject: [PATCH 625/828] Docking: Rework size allocation to allow user code to override node sizes. Not all edge cases will be properly handled but this is a step toward toolbar emitting size constraints. --- imgui.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 3936fd321d46..efcfc3c59405 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -13790,20 +13790,27 @@ void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 si const float size_min_each = ImFloor(ImMin(size_avail, g.Style.WindowMinSize[axis] * 2.0f) * 0.5f); // 2) Process locked absolute size (during a splitter resize we preserve the child of nodes not touching the splitter edge) - IM_ASSERT(!(child_0->WantLockSizeOnce && child_1->WantLockSizeOnce)); - if (child_0->WantLockSizeOnce) + if (child_0->WantLockSizeOnce && !child_1->WantLockSizeOnce) { child_0_size[axis] = child_0->SizeRef[axis] = ImMin(size_avail - 1.0f, child_0->Size[axis]); child_1_size[axis] = child_1->SizeRef[axis] = (size_avail - child_0_size[axis]); IM_ASSERT(child_0->SizeRef[axis] > 0.0f && child_1->SizeRef[axis] > 0.0f); - } - else if (child_1->WantLockSizeOnce) + else if (child_1->WantLockSizeOnce && !child_0->WantLockSizeOnce) { child_1_size[axis] = child_1->SizeRef[axis] = ImMin(size_avail - 1.0f, child_1->Size[axis]); child_0_size[axis] = child_0->SizeRef[axis] = (size_avail - child_1_size[axis]); IM_ASSERT(child_0->SizeRef[axis] > 0.0f && child_1->SizeRef[axis] > 0.0f); } + else if (child_0->WantLockSizeOnce && child_1->WantLockSizeOnce) + { + // FIXME-DOCK: We cannot honor the requested size, so apply ratio. + // Currently this path will only be taken if code programmatically sets WantLockSizeOnce + float ratio_0 = child_0_size[axis] / (child_0_size[axis] + child_1_size[axis]); + child_0_size[axis] = child_0->SizeRef[axis] = ImFloor(size_avail * ratio_0); + child_1_size[axis] = child_1->SizeRef[axis] = (size_avail - child_0_size[axis]); + IM_ASSERT(child_0->SizeRef[axis] > 0.0f && child_1->SizeRef[axis] > 0.0f); + } // 3) If one window is the central node (~ use remaining space, should be made explicit!), use explicit size from the other, and remainder for the central node else if (child_1->IsCentralNode() && child_0->SizeRef[axis] != 0.0f) From fc9d6b6cb5956eb5a07e09c1d5e4685b1037109e Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 29 Nov 2019 17:52:30 +0100 Subject: [PATCH 626/828] Docking: Added experimental flags to perform more docking filtering and disable resize per axis. Designed for toolbar patterns. The local/shared flags specs, saving and inheriting rules are pretty inconsistent at the moment. --- imgui.cpp | 19 ++++++++++++++----- imgui.h | 2 +- imgui_internal.h | 11 +++++++++-- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index efcfc3c59405..08fbff28f154 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -13486,22 +13486,27 @@ static void ImGui::DockNodePreviewDockSetup(ImGuiWindow* host_window, ImGuiDockN IM_ASSERT(ref_node_for_rect->IsVisible); // Filter, figure out where we are allowed to dock - ImGuiDockNodeFlags host_node_flags = host_node ? host_node->GetMergedFlags() : 0; + ImGuiDockNodeFlags src_node_flags = root_payload_as_host ? root_payload_as_host->GetMergedFlags() : root_payload->WindowClass.DockNodeFlagsOverrideSet; + ImGuiDockNodeFlags dst_node_flags = host_node ? host_node->GetMergedFlags() : host_window->WindowClass.DockNodeFlagsOverrideSet; data->IsCenterAvailable = true; if (is_outer_docking) data->IsCenterAvailable = false; - else if (host_node && (host_node_flags & ImGuiDockNodeFlags_NoDocking)) + else if (dst_node_flags & ImGuiDockNodeFlags_NoDocking) data->IsCenterAvailable = false; - else if (host_node && (host_node_flags & ImGuiDockNodeFlags_NoDockingInCentralNode) && host_node->IsCentralNode()) + else if (host_node && (dst_node_flags & ImGuiDockNodeFlags_NoDockingInCentralNode) && host_node->IsCentralNode()) data->IsCenterAvailable = false; else if ((!host_node || !host_node->IsEmpty()) && root_payload_as_host && root_payload_as_host->IsSplitNode() && (root_payload_as_host->OnlyNodeWithWindows == NULL)) // Is _visibly_ split? data->IsCenterAvailable = false; + else if ((dst_node_flags & ImGuiDockNodeFlags_NoDockingOverMe) || (src_node_flags & ImGuiDockNodeFlags_NoDockingOverOther)) + data->IsCenterAvailable = false; data->IsSidesAvailable = true; - if ((host_node && (host_node_flags & ImGuiDockNodeFlags_NoSplit)) || g.IO.ConfigDockingNoSplit) + if ((dst_node_flags & ImGuiDockNodeFlags_NoSplit) || g.IO.ConfigDockingNoSplit) data->IsSidesAvailable = false; else if (!is_outer_docking && host_node && host_node->ParentNode == NULL && host_node->IsCentralNode()) data->IsSidesAvailable = false; + else if ((dst_node_flags & ImGuiDockNodeFlags_NoDockingSplitMe) || (src_node_flags & ImGuiDockNodeFlags_NoDockingSplitOther)) + data->IsSidesAvailable = false; // Build a tentative future node (reuse same structure because it is practical. Shape will be readjusted when previewing a split) data->FutureNode.HasCloseButton = (host_node ? host_node->HasCloseButton : host_window->HasCloseButton) || (root_payload->HasCloseButton); @@ -13877,7 +13882,9 @@ void ImGui::DockNodeTreeUpdateSplitter(ImGuiDockNode* node) bb.Max[axis ^ 1] += child_1->Size[axis ^ 1]; //if (g.IO.KeyCtrl) GetForegroundDrawList(g.CurrentWindow->Viewport)->AddRect(bb.Min, bb.Max, IM_COL32(255,0,255,255)); - if ((child_0->GetMergedFlags() | child_1->GetMergedFlags()) & ImGuiDockNodeFlags_NoResize) + const ImGuiDockNodeFlags merged_flags = child_0->GetMergedFlags() | child_1->GetMergedFlags(); + const ImGuiDockNodeFlags no_resize_axis_flag = (axis == ImGuiAxis_X) ? ImGuiDockNodeFlags_NoResizeX : ImGuiDockNodeFlags_NoResizeY; + if ((merged_flags & ImGuiDockNodeFlags_NoResize) || (merged_flags & no_resize_axis_flag)) { ImGuiWindow* window = g.CurrentWindow; window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_Separator), g.Style.FrameRounding); @@ -15603,6 +15610,8 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::CheckboxFlags("LocalFlags: NoDocking", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_NoDocking); ImGui::CheckboxFlags("LocalFlags: NoSplit", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_NoSplit); ImGui::CheckboxFlags("LocalFlags: NoResize", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_NoResize); + ImGui::CheckboxFlags("LocalFlags: NoResizeX", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_NoResizeX); + ImGui::CheckboxFlags("LocalFlags: NoResizeY", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_NoResizeY); ImGui::CheckboxFlags("LocalFlags: NoTabBar", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_NoTabBar); ImGui::CheckboxFlags("LocalFlags: HiddenTabBar", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_HiddenTabBar); ImGui::CheckboxFlags("LocalFlags: NoWindowMenuButton", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_NoWindowMenuButton); diff --git a/imgui.h b/imgui.h index d91a343f0d26..233d2841f8c1 100644 --- a/imgui.h +++ b/imgui.h @@ -1739,7 +1739,7 @@ struct ImGuiWindowClass ImGuiDockNodeFlags DockNodeFlagsOverrideSet; // [EXPERIMENTAL] Dock node flags to set when a window of this class is hosted by a dock node (it doesn't have to be selected!) ImGuiDockNodeFlags DockNodeFlagsOverrideClear; // [EXPERIMENTAL] bool DockingAlwaysTabBar; // Set to true to enforce single floating windows of this class always having their own docking node (equivalent of setting the global io.ConfigDockingAlwaysTabBar) - bool DockingAllowUnclassed; // Set to true to allow windows of this class to be docked/merged with an unclassed window. + bool DockingAllowUnclassed; // Set to true to allow windows of this class to be docked/merged with an unclassed window. // FIXME-DOCK: Move to DockNodeFlags override? ImGuiWindowClass() { ClassId = 0; ParentViewportId = 0; ViewportFlagsOverrideSet = ViewportFlagsOverrideClear = 0x00; DockNodeFlagsOverrideSet = DockNodeFlagsOverrideClear = 0x00; DockingAlwaysTabBar = false; DockingAllowUnclassed = true; } }; diff --git a/imgui_internal.h b/imgui_internal.h index 16b41d11a437..3934034b32a2 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1086,10 +1086,17 @@ enum ImGuiDockNodeFlagsPrivate_ ImGuiDockNodeFlags_NoWindowMenuButton = 1 << 14, // Local, Saved // Disable window/docking menu (that one that appears instead of the collapse button) ImGuiDockNodeFlags_NoCloseButton = 1 << 15, // Local, Saved // ImGuiDockNodeFlags_NoDocking = 1 << 16, // Local, Saved // Disable any form of docking in this dockspace or individual node. (On a whole dockspace, this pretty much defeat the purpose of using a dockspace at all). Note: when turned on, existing docked nodes will be preserved. + ImGuiDockNodeFlags_NoDockingSplitMe = 1 << 17, // [EXPERIMENTAL] Prevent another window/node from splitting this node. + ImGuiDockNodeFlags_NoDockingSplitOther = 1 << 18, // [EXPERIMENTAL] Prevent this node from splitting another window/node. + ImGuiDockNodeFlags_NoDockingOverMe = 1 << 19, // [EXPERIMENTAL] Prevent another window/node to be docked over this node. + ImGuiDockNodeFlags_NoDockingOverOther = 1 << 20, // [EXPERIMENTAL] Prevent this node to be docked over another window/node. + ImGuiDockNodeFlags_NoResizeX = 1 << 21, // [EXPERIMENTAL] + ImGuiDockNodeFlags_NoResizeY = 1 << 22, // [EXPERIMENTAL] ImGuiDockNodeFlags_SharedFlagsInheritMask_ = ~0, - ImGuiDockNodeFlags_LocalFlagsMask_ = ImGuiDockNodeFlags_NoSplit | ImGuiDockNodeFlags_NoResize | ImGuiDockNodeFlags_AutoHideTabBar | ImGuiDockNodeFlags_DockSpace | ImGuiDockNodeFlags_CentralNode | ImGuiDockNodeFlags_NoTabBar | ImGuiDockNodeFlags_HiddenTabBar | ImGuiDockNodeFlags_NoWindowMenuButton | ImGuiDockNodeFlags_NoCloseButton | ImGuiDockNodeFlags_NoDocking, + ImGuiDockNodeFlags_NoResizeFlagsMask_ = ImGuiDockNodeFlags_NoResize | ImGuiDockNodeFlags_NoResizeX | ImGuiDockNodeFlags_NoResizeY, + ImGuiDockNodeFlags_LocalFlagsMask_ = ImGuiDockNodeFlags_NoSplit | ImGuiDockNodeFlags_NoResizeFlagsMask_ | ImGuiDockNodeFlags_AutoHideTabBar | ImGuiDockNodeFlags_DockSpace | ImGuiDockNodeFlags_CentralNode | ImGuiDockNodeFlags_NoTabBar | ImGuiDockNodeFlags_HiddenTabBar | ImGuiDockNodeFlags_NoWindowMenuButton | ImGuiDockNodeFlags_NoCloseButton | ImGuiDockNodeFlags_NoDocking, ImGuiDockNodeFlags_LocalFlagsTransferMask_ = ImGuiDockNodeFlags_LocalFlagsMask_ & ~ImGuiDockNodeFlags_DockSpace, // When splitting those flags are moved to the inheriting child, never duplicated - ImGuiDockNodeFlags_SavedFlagsMask_ = ImGuiDockNodeFlags_NoResize | ImGuiDockNodeFlags_DockSpace | ImGuiDockNodeFlags_CentralNode | ImGuiDockNodeFlags_NoTabBar | ImGuiDockNodeFlags_HiddenTabBar | ImGuiDockNodeFlags_NoWindowMenuButton | ImGuiDockNodeFlags_NoCloseButton | ImGuiDockNodeFlags_NoDocking + ImGuiDockNodeFlags_SavedFlagsMask_ = ImGuiDockNodeFlags_NoResizeFlagsMask_ | ImGuiDockNodeFlags_DockSpace | ImGuiDockNodeFlags_CentralNode | ImGuiDockNodeFlags_NoTabBar | ImGuiDockNodeFlags_HiddenTabBar | ImGuiDockNodeFlags_NoWindowMenuButton | ImGuiDockNodeFlags_NoCloseButton | ImGuiDockNodeFlags_NoDocking }; // Store the source authority (dock node vs window) of a field From 4f5aac319e3561284833db90f35d218de8b282c1 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 3 Jul 2020 15:51:05 +0200 Subject: [PATCH 627/828] Docking: moved local-ish IMGUI_DOCK_SPLITTER_SIZE to DOCKING_SPLITTER_SIZE at the top of the file. --- imgui.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 08fbff28f154..7b31979c1afb 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -811,6 +811,7 @@ static const float WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER = 2.00f; // Lock // Docking static const float DOCKING_TRANSPARENT_PAYLOAD_ALPHA = 0.50f; // For use with io.ConfigDockingTransparentPayload. Apply to Viewport _or_ WindowBg in host viewport. +static const float DOCKING_SPLITTER_SIZE = 2.0f; //------------------------------------------------------------------------- // [SECTION] FORWARD DECLARATIONS @@ -11602,8 +11603,6 @@ void ImGui::DestroyPlatformWindows() // - ImGuiDockContext //----------------------------------------------------------------------------- -static float IMGUI_DOCK_SPLITTER_SIZE = 2.0f; - enum ImGuiDockRequestType { ImGuiDockRequestType_None = 0, @@ -13697,7 +13696,7 @@ void ImGui::DockNodeTreeSplit(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImG parent_node->VisibleWindow = NULL; parent_node->AuthorityForPos = parent_node->AuthorityForSize = ImGuiDataAuthority_DockNode; - float size_avail = (parent_node->Size[split_axis] - IMGUI_DOCK_SPLITTER_SIZE); + float size_avail = (parent_node->Size[split_axis] - DOCKING_SPLITTER_SIZE); size_avail = ImMax(size_avail, g.Style.WindowMinSize[split_axis] * 2.0f); IM_ASSERT(size_avail > 0.0f); // If you created a node manually with DockBuilderAddNode(), you need to also call DockBuilderSetNodeSize() before splitting. child_0->SizeRef = child_1->SizeRef = parent_node->Size; @@ -13785,7 +13784,7 @@ void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 si ImVec2 child_0_size = size, child_1_size = size; if (child_0->IsVisible && child_1->IsVisible) { - const float spacing = IMGUI_DOCK_SPLITTER_SIZE; + const float spacing = DOCKING_SPLITTER_SIZE; const ImGuiAxis axis = (ImGuiAxis)node->SplitAxis; const float size_avail = ImMax(size[axis] - spacing, 0.0f); From cb1d5784707503b2a1a82be81dabcbe5529e4e5f Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 10 Jul 2020 12:09:24 +0200 Subject: [PATCH 628/828] Backends: DX12, Viewports: Fixed issue on shutdown when viewports are disabled. (#3347) --- examples/imgui_impl_dx12.cpp | 38 ++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/examples/imgui_impl_dx12.cpp b/examples/imgui_impl_dx12.cpp index 40559b71f621..e4480e999a30 100644 --- a/examples/imgui_impl_dx12.cpp +++ b/examples/imgui_impl_dx12.cpp @@ -49,6 +49,7 @@ static D3D12_GPU_DESCRIPTOR_HANDLE g_hFontSrvGpuDescHandle = {}; static ID3D12DescriptorHeap* g_pd3dSrvDescHeap = NULL; static UINT g_numFramesInFlight = 0; +// Buffers used during the rendering of a frame struct FrameResources { ID3D12Resource* IndexBuffer; @@ -57,6 +58,7 @@ struct FrameResources int VertexBufferSize; }; +// Buffers used for secondary viewports created by the multi-viewports systems struct FrameContext { ID3D12CommandAllocator* CommandAllocator; @@ -64,7 +66,7 @@ struct FrameContext D3D12_CPU_DESCRIPTOR_HANDLE RenderTargetCpuDescriptors; }; -// Helper structure we store in the void* RenderUserData field of each ImGuiViewport to easily retrieve our backend data. +// Helper structure we store in the void* RendererUserData field of each ImGuiViewport to easily retrieve our backend data. struct ImGuiViewportDataDx12 { ID3D12CommandQueue* CommandQueue; @@ -133,6 +135,13 @@ static void SafeRelease(T*& res) res = NULL; } +static void ImGui_ImplDX12_DestroyFrameResources(FrameResources* resources) +{ + SafeRelease(resources->IndexBuffer); + SafeRelease(resources->VertexBuffer); + resources->IndexBufferSize = resources->VertexBufferSize = 0; +} + struct VERTEX_CONSTANT_BUFFER { float mvp[4][4]; @@ -699,6 +708,8 @@ bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FO g_numFramesInFlight = num_frames_in_flight; g_pd3dSrvDescHeap = cbv_srv_heap; + // Create a dummy ImGuiViewportDataDx12 holder for the main viewport, + // Since this is created and managed by the application, we will only use the ->Resources[] fields. ImGuiViewport* main_viewport = ImGui::GetMainViewport(); main_viewport->RendererUserData = IM_NEW(ImGuiViewportDataDx12)(); @@ -712,6 +723,18 @@ bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FO void ImGui_ImplDX12_Shutdown() { + // Manually delete main viewport render resources in-case we haven't initialized for viewports + ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + if (ImGuiViewportDataDx12* data = (ImGuiViewportDataDx12*)main_viewport->RendererUserData) + { + // We could just call ImGui_ImplDX12_DestroyWindow(main_viewport) as a convenience but that would be misleading since we only use data->Resources[] + for (UINT i = 0; i < g_numFramesInFlight; i++) + ImGui_ImplDX12_DestroyFrameResources(&data->Resources[i]); + IM_DELETE(data); + main_viewport->RendererUserData = NULL; + } + + // Clean up windows and device objects ImGui_ImplDX12_ShutdownPlatformInterface(); ImGui_ImplDX12_InvalidateDeviceObjects(); @@ -720,11 +743,6 @@ void ImGui_ImplDX12_Shutdown() g_hFontSrvGpuDescHandle.ptr = 0; g_numFramesInFlight = 0; g_pd3dSrvDescHeap = NULL; - - ImGuiViewport* main_viewport = ImGui::GetMainViewport(); - if (ImGuiViewportDataDx12* data = (ImGuiViewportDataDx12*)main_viewport->RendererUserData) - IM_DELETE(data); - main_viewport->RendererUserData = NULL; } void ImGui_ImplDX12_NewFrame() @@ -841,10 +859,7 @@ static void ImGui_ImplDX12_CreateWindow(ImGuiViewport* viewport) } for (UINT i = 0; i < g_numFramesInFlight; i++) - { - SafeRelease(data->Resources[i].IndexBuffer); - SafeRelease(data->Resources[i].VertexBuffer); - } + ImGui_ImplDX12_DestroyFrameResources(&data->Resources[i]); } static void ImGui_WaitForPendingOperations(ImGuiViewportDataDx12* data) @@ -880,8 +895,7 @@ static void ImGui_ImplDX12_DestroyWindow(ImGuiViewport* viewport) { SafeRelease(data->FrameCtx[i].RenderTarget); SafeRelease(data->FrameCtx[i].CommandAllocator); - SafeRelease(data->Resources[i].IndexBuffer); - SafeRelease(data->Resources[i].VertexBuffer); + ImGui_ImplDX12_DestroyFrameResources(&data->Resources[i]); } IM_DELETE(data); } From b626dd57d3f11a2ea91ea2f0b7d9be50d7506d21 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 10 Jul 2020 12:20:03 +0200 Subject: [PATCH 629/828] Backends: DX12, Viewports: Tidying up, renaming. --- examples/imgui_impl_dx12.cpp | 74 ++++++++++++++++++------------------ 1 file changed, 38 insertions(+), 36 deletions(-) diff --git a/examples/imgui_impl_dx12.cpp b/examples/imgui_impl_dx12.cpp index e4480e999a30..690f3a8bcfe5 100644 --- a/examples/imgui_impl_dx12.cpp +++ b/examples/imgui_impl_dx12.cpp @@ -50,7 +50,7 @@ static ID3D12DescriptorHeap* g_pd3dSrvDescHeap = NULL; static UINT g_numFramesInFlight = 0; // Buffers used during the rendering of a frame -struct FrameResources +struct ImGui_ImplDX12_RenderBuffers { ID3D12Resource* IndexBuffer; ID3D12Resource* VertexBuffer; @@ -59,28 +59,31 @@ struct FrameResources }; // Buffers used for secondary viewports created by the multi-viewports systems -struct FrameContext +struct ImGui_ImplDX12_FrameContext { - ID3D12CommandAllocator* CommandAllocator; - ID3D12Resource* RenderTarget; - D3D12_CPU_DESCRIPTOR_HANDLE RenderTargetCpuDescriptors; + ID3D12CommandAllocator* CommandAllocator; + ID3D12Resource* RenderTarget; + D3D12_CPU_DESCRIPTOR_HANDLE RenderTargetCpuDescriptors; }; // Helper structure we store in the void* RendererUserData field of each ImGuiViewport to easily retrieve our backend data. +// Main viewport created by application will only use the Resources field. +// Secondary viewports created by this back-end will use all the fields (including Window fields), struct ImGuiViewportDataDx12 { - ID3D12CommandQueue* CommandQueue; - ID3D12GraphicsCommandList* CommandList; - ID3D12DescriptorHeap* RtvDescHeap; - IDXGISwapChain3* SwapChain; - - ID3D12Fence* Fence; - UINT64 FenceSignaledValue; - HANDLE FenceEvent; - - UINT FrameIndex; - FrameContext* FrameCtx; - FrameResources* Resources; + // Window + ID3D12CommandQueue* CommandQueue; + ID3D12GraphicsCommandList* CommandList; + ID3D12DescriptorHeap* RtvDescHeap; + IDXGISwapChain3* SwapChain; + ID3D12Fence* Fence; + UINT64 FenceSignaledValue; + HANDLE FenceEvent; + ImGui_ImplDX12_FrameContext* FrameCtx; + + // Render buffers + UINT FrameIndex; + ImGui_ImplDX12_RenderBuffers* FrameRenderBuffers; ImGuiViewportDataDx12() { @@ -88,13 +91,12 @@ struct ImGuiViewportDataDx12 CommandList = NULL; RtvDescHeap = NULL; SwapChain = NULL; - Fence = NULL; FenceSignaledValue = 0; FenceEvent = NULL; + FrameCtx = new ImGui_ImplDX12_FrameContext[g_numFramesInFlight]; FrameIndex = UINT_MAX; - FrameCtx = new FrameContext[g_numFramesInFlight]; - Resources = new FrameResources[g_numFramesInFlight]; + FrameRenderBuffers = new ImGui_ImplDX12_RenderBuffers[g_numFramesInFlight]; for (UINT i = 0; i < g_numFramesInFlight; ++i) { @@ -102,10 +104,10 @@ struct ImGuiViewportDataDx12 FrameCtx[i].RenderTarget = NULL; // Create buffers with a default size (they will later be grown as needed) - Resources[i].IndexBuffer = NULL; - Resources[i].VertexBuffer = NULL; - Resources[i].VertexBufferSize = 5000; - Resources[i].IndexBufferSize = 10000; + FrameRenderBuffers[i].IndexBuffer = NULL; + FrameRenderBuffers[i].VertexBuffer = NULL; + FrameRenderBuffers[i].VertexBufferSize = 5000; + FrameRenderBuffers[i].IndexBufferSize = 10000; } } ~ImGuiViewportDataDx12() @@ -119,11 +121,11 @@ struct ImGuiViewportDataDx12 for (UINT i = 0; i < g_numFramesInFlight; ++i) { IM_ASSERT(FrameCtx[i].CommandAllocator == NULL && FrameCtx[i].RenderTarget == NULL); - IM_ASSERT(Resources[i].IndexBuffer == NULL && Resources[i].VertexBuffer == NULL); + IM_ASSERT(FrameRenderBuffers[i].IndexBuffer == NULL && FrameRenderBuffers[i].VertexBuffer == NULL); } delete[] FrameCtx; FrameCtx = NULL; - delete[] Resources; Resources = NULL; + delete[] FrameRenderBuffers; FrameRenderBuffers = NULL; } }; @@ -135,11 +137,11 @@ static void SafeRelease(T*& res) res = NULL; } -static void ImGui_ImplDX12_DestroyFrameResources(FrameResources* resources) +static void ImGui_ImplDX12_DestroyRenderBuffers(ImGui_ImplDX12_RenderBuffers* render_buffers) { - SafeRelease(resources->IndexBuffer); - SafeRelease(resources->VertexBuffer); - resources->IndexBufferSize = resources->VertexBufferSize = 0; + SafeRelease(render_buffers->IndexBuffer); + SafeRelease(render_buffers->VertexBuffer); + render_buffers->IndexBufferSize = render_buffers->VertexBufferSize = 0; } struct VERTEX_CONSTANT_BUFFER @@ -151,7 +153,7 @@ struct VERTEX_CONSTANT_BUFFER static void ImGui_ImplDX12_InitPlatformInterface(); static void ImGui_ImplDX12_ShutdownPlatformInterface(); -static void ImGui_ImplDX12_SetupRenderState(ImDrawData* draw_data, ID3D12GraphicsCommandList* ctx, FrameResources* fr) +static void ImGui_ImplDX12_SetupRenderState(ImDrawData* draw_data, ID3D12GraphicsCommandList* ctx, ImGui_ImplDX12_RenderBuffers* fr) { // Setup orthographic projection matrix into our constant buffer // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). @@ -216,7 +218,7 @@ void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandL ImGuiViewportDataDx12* render_data = (ImGuiViewportDataDx12*)draw_data->OwnerViewport->RendererUserData; render_data->FrameIndex++; - FrameResources* fr = &render_data->Resources[render_data->FrameIndex % g_numFramesInFlight]; + ImGui_ImplDX12_RenderBuffers* fr = &render_data->FrameRenderBuffers[render_data->FrameIndex % g_numFramesInFlight]; // Create and grow vertex/index buffers if needed if (fr->VertexBuffer == NULL || fr->VertexBufferSize < draw_data->TotalVtxCount) @@ -729,7 +731,7 @@ void ImGui_ImplDX12_Shutdown() { // We could just call ImGui_ImplDX12_DestroyWindow(main_viewport) as a convenience but that would be misleading since we only use data->Resources[] for (UINT i = 0; i < g_numFramesInFlight; i++) - ImGui_ImplDX12_DestroyFrameResources(&data->Resources[i]); + ImGui_ImplDX12_DestroyRenderBuffers(&data->FrameRenderBuffers[i]); IM_DELETE(data); main_viewport->RendererUserData = NULL; } @@ -859,7 +861,7 @@ static void ImGui_ImplDX12_CreateWindow(ImGuiViewport* viewport) } for (UINT i = 0; i < g_numFramesInFlight; i++) - ImGui_ImplDX12_DestroyFrameResources(&data->Resources[i]); + ImGui_ImplDX12_DestroyRenderBuffers(&data->FrameRenderBuffers[i]); } static void ImGui_WaitForPendingOperations(ImGuiViewportDataDx12* data) @@ -895,7 +897,7 @@ static void ImGui_ImplDX12_DestroyWindow(ImGuiViewport* viewport) { SafeRelease(data->FrameCtx[i].RenderTarget); SafeRelease(data->FrameCtx[i].CommandAllocator); - ImGui_ImplDX12_DestroyFrameResources(&data->Resources[i]); + ImGui_ImplDX12_DestroyRenderBuffers(&data->FrameRenderBuffers[i]); } IM_DELETE(data); } @@ -928,7 +930,7 @@ static void ImGui_ImplDX12_RenderWindow(ImGuiViewport* viewport, void*) { ImGuiViewportDataDx12* data = (ImGuiViewportDataDx12*)viewport->RendererUserData; - FrameContext* frame_context = &data->FrameCtx[data->FrameIndex % g_numFramesInFlight]; + ImGui_ImplDX12_FrameContext* frame_context = &data->FrameCtx[data->FrameIndex % g_numFramesInFlight]; UINT back_buffer_idx = data->SwapChain->GetCurrentBackBufferIndex(); const ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); From cbade7b16d3b7f1bbcd257a9639209e937a88224 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 10 Jul 2020 19:04:58 +0200 Subject: [PATCH 630/828] Docking: Workaround recovery for node created without the _ockSpace flags later becoming DockSpace. (#3340) --- imgui.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 7b31979c1afb..36d2714596da 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -14145,6 +14145,17 @@ void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags fla node->OnlyNodeWithWindows = NULL; IM_ASSERT(node->IsRootNode()); + + // We need to handle the rare case were a central node is missing. + // This can happen if the node was first created manually with DockBuilderAddNode() but _without_ the ImGuiDockNodeFlags_Dockspace. + // Doing it correctly would set the _CentralNode flags, which would then propagate according to subsequent split. + // It would also be ambiguous to attempt to assign a central node while there are split nodes, so we wait until there's a single node remaining. + // The specific sub-property of _CentralNode we are interested in recovering here is the "Don't delete when empty" property, + // as it doesn't make sense for an empty dockspace to not have this property. + if (node->IsLeafNode() && !node->IsCentralNode()) + node->LocalFlags |= ImGuiDockNodeFlags_CentralNode; + + // Update the node DockNodeUpdate(node); g.WithinEndChild = true; @@ -15765,14 +15776,16 @@ void ImGui::ShowMetricsWindow(bool* p_open) #ifdef IMGUI_HAS_DOCK if (ImGui::TreeNode("Dock nodes")) { + static bool root_nodes_only = true; ImGuiDockContext* dc = &g.DockContext; + ImGui::Checkbox("List root nodes", &root_nodes_only); ImGui::Checkbox("Ctrl shows window dock info", &show_docking_nodes); if (ImGui::SmallButton("Clear nodes")) { DockContextClearNodes(&g, 0, true); } ImGui::SameLine(); if (ImGui::SmallButton("Rebuild all")) { dc->WantFullRebuild = true; } for (int n = 0; n < dc->Nodes.Data.Size; n++) if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p) - if (node->IsRootNode()) + if (!root_nodes_only || node->IsRootNode()) Funcs::NodeDockNode(node, "Node"); ImGui::TreePop(); } From cba52b66afa44a886f82b4c78e12e4ed389c7068 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 17 Jul 2020 18:11:01 +0200 Subject: [PATCH 631/828] Backends: GLFW: Fixed enabling ImGuiBackendFlags_HasMouseHoveredViewport broken by 950539b7. As it turns out, back-end passing NULL hovered with HasMouseHoveredViewport is also broken which defeats some of its purpose. --- examples/imgui_impl_glfw.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index 1fab76343dff..d163a72abd59 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -166,7 +166,7 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional) -#if GLFW_HAS_GLFW_HOVERED && defined(_WIN32) +#if GLFW_HAS_WINDOW_HOVERED && defined(_WIN32) io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can set io.MouseHoveredViewport correctly (optional, not easy) #endif io.BackendPlatformName = "imgui_impl_glfw"; @@ -348,7 +348,7 @@ static void ImGui_ImplGlfw_UpdateMousePosAndButtons() // rectangles and last focused time of every viewports it knows about. It will be unaware of other windows that may be sitting between or over your windows. // [GLFW] FIXME: This is currently only correct on Win32. See what we do below with the WM_NCHITTEST, missing an equivalent for other systems. // See https://github.com/glfw/glfw/issues/1236 if you want to help in making this a GLFW feature. -#if GLFW_HAS_GLFW_HOVERED && defined(_WIN32) +#if GLFW_HAS_WINDOW_HOVERED && defined(_WIN32) if (glfwGetWindowAttrib(window, GLFW_HOVERED) && !(viewport->Flags & ImGuiViewportFlags_NoInputs)) io.MouseHoveredViewport = viewport->ID; #endif @@ -586,7 +586,7 @@ static void ImGui_ImplGlfw_DestroyWindow(ImGuiViewport* viewport) { if (data->WindowOwned) { -#if GLFW_HAS_GLFW_HOVERED && defined(_WIN32) +#if GLFW_HAS_WINDOW_HOVERED && defined(_WIN32) HWND hwnd = (HWND)viewport->PlatformHandleRaw; ::RemovePropA(hwnd, "IMGUI_VIEWPORT"); #endif @@ -600,7 +600,7 @@ static void ImGui_ImplGlfw_DestroyWindow(ImGuiViewport* viewport) // We have submitted https://github.com/glfw/glfw/pull/1568 to allow GLFW to support "transparent inputs". // In the meanwhile we implement custom per-platform workarounds here (FIXME-VIEWPORT: Implement same work-around for Linux/OSX!) -#if defined(_WIN32) && GLFW_HAS_GLFW_HOVERED +#if GLFW_HAS_WINDOW_HOVERED && defined(_WIN32) static WNDPROC g_GlfwWndProc = NULL; static LRESULT CALLBACK WndProcNoInputs(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { @@ -634,7 +634,7 @@ static void ImGui_ImplGlfw_ShowWindow(ImGuiViewport* viewport) } // GLFW hack: install hook for WM_NCHITTEST message handler -#if GLFW_HAS_GLFW_HOVERED && defined(_WIN32) +#if GLFW_HAS_WINDOW_HOVERED && defined(_WIN32) ::SetPropA(hwnd, "IMGUI_VIEWPORT", viewport); if (g_GlfwWndProc == NULL) g_GlfwWndProc = (WNDPROC)::GetWindowLongPtr(hwnd, GWLP_WNDPROC); From 3d4af15d1d5f5d3746b69859308949c357e0c21e Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Fri, 17 Jul 2020 16:57:50 +0300 Subject: [PATCH 632/828] Backends GLFW: Use GLFW_MOUSE_PASSTHROUGH when available. --- examples/imgui_impl_glfw.cpp | 24 ++++++++++++++++++------ examples/imgui_impl_glfw.h | 3 +++ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index d163a72abd59..e7dc10ca0297 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -10,6 +10,9 @@ // [X] Platform: Keyboard arrays indexed using GLFW_KEY_* codes, e.g. ImGui::IsKeyPressed(GLFW_KEY_SPACE). // [X] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. +// Issues: +// [ ] Platform: Multi-viewport support: ParentViewportID not honored, and so io.ConfigViewportsNoDefaultParent has no effect (minor). + // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp. // https://github.com/ocornut/imgui @@ -63,6 +66,11 @@ #else #define GLFW_HAS_NEW_CURSORS (0) #endif +#ifdef GLFW_MOUSE_PASSTHROUGH // Let's be nice to people who pulled GLFW between 2019-04-16 (3.4 define) and 2020-07-17 (passthrough) +#define GLFW_HAS_MOUSE_PASSTHROUGH (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3400) // 3.4+ GLFW_MOUSE_PASSTHROUGH +#else +#define GLFW_HAS_MOUSE_PASSTHROUGH (0) +#endif // Data enum GlfwClientApi @@ -166,7 +174,7 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional) -#if GLFW_HAS_WINDOW_HOVERED && defined(_WIN32) +#if GLFW_HAS_MOUSE_PASSTHROUGH || (GLFW_HAS_WINDOW_HOVERED && defined(_WIN32)) io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can set io.MouseHoveredViewport correctly (optional, not easy) #endif io.BackendPlatformName = "imgui_impl_glfw"; @@ -348,8 +356,12 @@ static void ImGui_ImplGlfw_UpdateMousePosAndButtons() // rectangles and last focused time of every viewports it knows about. It will be unaware of other windows that may be sitting between or over your windows. // [GLFW] FIXME: This is currently only correct on Win32. See what we do below with the WM_NCHITTEST, missing an equivalent for other systems. // See https://github.com/glfw/glfw/issues/1236 if you want to help in making this a GLFW feature. -#if GLFW_HAS_WINDOW_HOVERED && defined(_WIN32) - if (glfwGetWindowAttrib(window, GLFW_HOVERED) && !(viewport->Flags & ImGuiViewportFlags_NoInputs)) +#if GLFW_HAS_MOUSE_PASSTHROUGH || (GLFW_HAS_WINDOW_HOVERED && defined(_WIN32)) + const bool window_no_input = (viewport->Flags & ImGuiViewportFlags_NoInputs) != 0; +#if GLFW_HAS_MOUSE_PASSTHROUGH + glfwSetWindowAttrib(window, GLFW_MOUSE_PASSTHROUGH, window_no_input); +#endif + if (glfwGetWindowAttrib(window, GLFW_HOVERED) && !window_no_input) io.MouseHoveredViewport = viewport->ID; #endif } @@ -586,7 +598,7 @@ static void ImGui_ImplGlfw_DestroyWindow(ImGuiViewport* viewport) { if (data->WindowOwned) { -#if GLFW_HAS_WINDOW_HOVERED && defined(_WIN32) +#if !GLFW_HAS_MOUSE_PASSTHROUGH && GLFW_HAS_WINDOW_HOVERED && defined(_WIN32) HWND hwnd = (HWND)viewport->PlatformHandleRaw; ::RemovePropA(hwnd, "IMGUI_VIEWPORT"); #endif @@ -600,7 +612,7 @@ static void ImGui_ImplGlfw_DestroyWindow(ImGuiViewport* viewport) // We have submitted https://github.com/glfw/glfw/pull/1568 to allow GLFW to support "transparent inputs". // In the meanwhile we implement custom per-platform workarounds here (FIXME-VIEWPORT: Implement same work-around for Linux/OSX!) -#if GLFW_HAS_WINDOW_HOVERED && defined(_WIN32) +#if !GLFW_HAS_MOUSE_PASSTHROUGH && GLFW_HAS_WINDOW_HOVERED && defined(_WIN32) static WNDPROC g_GlfwWndProc = NULL; static LRESULT CALLBACK WndProcNoInputs(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { @@ -634,7 +646,7 @@ static void ImGui_ImplGlfw_ShowWindow(ImGuiViewport* viewport) } // GLFW hack: install hook for WM_NCHITTEST message handler -#if GLFW_HAS_WINDOW_HOVERED && defined(_WIN32) +#if !GLFW_HAS_MOUSE_PASSTHROUGH && GLFW_HAS_WINDOW_HOVERED && defined(_WIN32) ::SetPropA(hwnd, "IMGUI_VIEWPORT", viewport); if (g_GlfwWndProc == NULL) g_GlfwWndProc = (WNDPROC)::GetWindowLongPtr(hwnd, GWLP_WNDPROC); diff --git a/examples/imgui_impl_glfw.h b/examples/imgui_impl_glfw.h index bea576a0f92c..9ad0d9b31b3d 100644 --- a/examples/imgui_impl_glfw.h +++ b/examples/imgui_impl_glfw.h @@ -9,6 +9,9 @@ // [X] Platform: Keyboard arrays indexed using GLFW_KEY_* codes, e.g. ImGui::IsKeyPressed(GLFW_KEY_SPACE). // [X] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. +// Issues: +// [ ] Platform: Multi-viewport support: ParentViewportID not honored, and so io.ConfigViewportsNoDefaultParent has no effect (minor). + // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp. // https://github.com/ocornut/imgui From acf043a67589e718b630e27df4ddcefe0693d320 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 10 Aug 2020 11:58:37 +0200 Subject: [PATCH 633/828] Docking: Moved code unjustly in DockNodeTreeFindNodeByPos() out of it and into caller (should have no side-effect ideally). Removed dupe in Begin() from earlier merge. --- imgui.cpp | 39 +++++++++++++++++++-------------------- imgui_internal.h | 4 ++-- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index fbb2bff61cd2..58d515abbf66 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6056,10 +6056,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DC.MenuBarOffset.x = ImMax(ImMax(window->WindowPadding.x, style.ItemSpacing.x), g.NextWindowData.MenuBarOffsetMinVal.x); window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y; - // Lock menu offset so size calculation can use it as menu-bar windows need a minimum size. - window->DC.MenuBarOffset.x = ImMax(ImMax(window->WindowPadding.x, style.ItemSpacing.x), g.NextWindowData.MenuBarOffsetMinVal.x); - window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y; - // Collapse window by double-clicking on title bar // At this point we don't have a clipping rectangle setup yet, so we can use the title bar area for hit detection and drawing if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse) && !window->DockIsActive) @@ -11757,7 +11753,7 @@ namespace ImGui static void DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiDockNode* merge_lead_child); static void DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 size, bool only_write_to_marked_nodes = false); static void DockNodeTreeUpdateSplitter(ImGuiDockNode* node); - static ImGuiDockNode* DockNodeTreeFindNodeByPos(ImGuiDockNode* node, ImVec2 pos); + static ImGuiDockNode* DockNodeTreeFindVisibleNodeByPos(ImGuiDockNode* node, ImVec2 pos); static ImGuiDockNode* DockNodeTreeFindFallbackLeafNode(ImGuiDockNode* node); // Settings @@ -14026,13 +14022,13 @@ ImGuiDockNode* ImGui::DockNodeTreeFindFallbackLeafNode(ImGuiDockNode* node) return NULL; } -ImGuiDockNode* ImGui::DockNodeTreeFindNodeByPos(ImGuiDockNode* node, ImVec2 pos) +ImGuiDockNode* ImGui::DockNodeTreeFindVisibleNodeByPos(ImGuiDockNode* node, ImVec2 pos) { if (!node->IsVisible) return NULL; ImGuiContext& g = *GImGui; - const float dock_spacing = g.Style.ItemInnerSpacing.x; + const float dock_spacing = g.Style.ItemInnerSpacing.x; // FIXME: Relation to DOCKING_SPLITTER_SIZE? ImRect r(node->Pos, node->Pos + node->Size); r.Expand(dock_spacing * 0.5f); bool inside = r.Contains(pos); @@ -14041,20 +14037,11 @@ ImGuiDockNode* ImGui::DockNodeTreeFindNodeByPos(ImGuiDockNode* node, ImVec2 pos) if (node->IsLeafNode()) return node; - if (ImGuiDockNode* hovered_node = DockNodeTreeFindNodeByPos(node->ChildNodes[0], pos)) + if (ImGuiDockNode* hovered_node = DockNodeTreeFindVisibleNodeByPos(node->ChildNodes[0], pos)) return hovered_node; - if (ImGuiDockNode* hovered_node = DockNodeTreeFindNodeByPos(node->ChildNodes[1], pos)) + if (ImGuiDockNode* hovered_node = DockNodeTreeFindVisibleNodeByPos(node->ChildNodes[1], pos)) return hovered_node; - // There is an edge case when docking into a dockspace which only has inactive nodes (because none of the windows are active) - // In this case we need to fallback into any leaf mode, possibly the central node. - if (node->IsDockSpace() && node->IsRootNode()) - { - if (node->CentralNode && node->IsLeafNode()) // FIXME-20181220: We should not have to test for IsLeafNode() here but we have another bug to fix first. - return node->CentralNode; - return DockNodeTreeFindFallbackLeafNode(node); - } - return NULL; } @@ -14876,7 +14863,19 @@ void ImGui::BeginDockableDragDropTarget(ImGuiWindow* window) ImGuiDockNode* node = NULL; bool allow_null_target_node = false; if (window->DockNodeAsHost) - node = DockNodeTreeFindNodeByPos(window->DockNodeAsHost, g.IO.MousePos); + { + node = DockNodeTreeFindVisibleNodeByPos(window->DockNodeAsHost, g.IO.MousePos); + + // There is an edge case when docking into a dockspace which only has inactive nodes (because none of the windows are active) + // In this case we need to fallback into any leaf mode, possibly the central node. + if (node && node->IsDockSpace() && node->IsRootNode()) + { + if (node->CentralNode && node->IsLeafNode()) // FIXME-20181220: We should not have to test for IsLeafNode() here but we have another bug to fix first. + node = node->CentralNode; + else + node = DockNodeTreeFindFallbackLeafNode(node); + } + } else if (window->DockNode) // && window->DockIsActive) node = window->DockNode; else @@ -15975,7 +15974,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p) { ImGuiDockNode* root_node = DockNodeGetRootNode(node); - if (ImGuiDockNode* hovered_node = DockNodeTreeFindNodeByPos(root_node, g.IO.MousePos)) + if (ImGuiDockNode* hovered_node = DockNodeTreeFindVisibleNodeByPos(root_node, g.IO.MousePos)) if (hovered_node != node) continue; char buf[64] = ""; diff --git a/imgui_internal.h b/imgui_internal.h index 8c7601c869f1..6f29af1e8977 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1085,8 +1085,8 @@ enum ImGuiDockNodeFlagsPrivate_ ImGuiDockNodeFlags_NoDockingSplitOther = 1 << 18, // [EXPERIMENTAL] Prevent this node from splitting another window/node. ImGuiDockNodeFlags_NoDockingOverMe = 1 << 19, // [EXPERIMENTAL] Prevent another window/node to be docked over this node. ImGuiDockNodeFlags_NoDockingOverOther = 1 << 20, // [EXPERIMENTAL] Prevent this node to be docked over another window/node. - ImGuiDockNodeFlags_NoResizeX = 1 << 21, // [EXPERIMENTAL] - ImGuiDockNodeFlags_NoResizeY = 1 << 22, // [EXPERIMENTAL] + ImGuiDockNodeFlags_NoResizeX = 1 << 21, // [EXPERIMENTAL] + ImGuiDockNodeFlags_NoResizeY = 1 << 22, // [EXPERIMENTAL] ImGuiDockNodeFlags_SharedFlagsInheritMask_ = ~0, ImGuiDockNodeFlags_NoResizeFlagsMask_ = ImGuiDockNodeFlags_NoResize | ImGuiDockNodeFlags_NoResizeX | ImGuiDockNodeFlags_NoResizeY, ImGuiDockNodeFlags_LocalFlagsMask_ = ImGuiDockNodeFlags_NoSplit | ImGuiDockNodeFlags_NoResizeFlagsMask_ | ImGuiDockNodeFlags_AutoHideTabBar | ImGuiDockNodeFlags_DockSpace | ImGuiDockNodeFlags_CentralNode | ImGuiDockNodeFlags_NoTabBar | ImGuiDockNodeFlags_HiddenTabBar | ImGuiDockNodeFlags_NoWindowMenuButton | ImGuiDockNodeFlags_NoCloseButton | ImGuiDockNodeFlags_NoDocking, From 85a661d276a27063b39209fd52deab1a793bfbed Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 10 Aug 2020 12:52:33 +0200 Subject: [PATCH 634/828] Docking: Storing HoveredDockNode in context which can be useful for easily detecting e.g. hovering an empty node. (#3398) --- imgui.cpp | 81 +++++++++++++++++++++++------------------------- imgui_internal.h | 2 ++ 2 files changed, 40 insertions(+), 43 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 58d515abbf66..30f31a72e1b2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11892,6 +11892,16 @@ void ImGui::DockContextUpdateDocking(ImGuiContext* ctx) if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable)) return; + // Store hovered dock node. We could in theory use DockNodeTreeFindVisibleNodeByPos() on the root host dock node, but using ->DockNode is a good shortcut. + g.HoveredDockNode = NULL; + if (ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow) + { + if (hovered_window->DockNode) + g.HoveredDockNode = hovered_window->DockNode; + else if (hovered_window->DockNodeAsHost) + g.HoveredDockNode = DockNodeTreeFindVisibleNodeByPos(hovered_window->DockNodeAsHost, g.IO.MousePos); + } + // Process Docking requests for (int n = 0; n < dc->Requests.Size; n++) if (dc->Requests[n].Type == ImGuiDockRequestType_Dock) @@ -14027,8 +14037,7 @@ ImGuiDockNode* ImGui::DockNodeTreeFindVisibleNodeByPos(ImGuiDockNode* node, ImVe if (!node->IsVisible) return NULL; - ImGuiContext& g = *GImGui; - const float dock_spacing = g.Style.ItemInnerSpacing.x; // FIXME: Relation to DOCKING_SPLITTER_SIZE? + const float dock_spacing = 0.0f;// g.Style.ItemInnerSpacing.x; // FIXME: Relation to DOCKING_SPLITTER_SIZE? ImRect r(node->Pos, node->Pos + node->Size); r.Expand(dock_spacing * 0.5f); bool inside = r.Contains(pos); @@ -14860,26 +14869,19 @@ void ImGui::BeginDockableDragDropTarget(ImGuiWindow* window) if (AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_WINDOW, ImGuiDragDropFlags_AcceptBeforeDelivery | ImGuiDragDropFlags_AcceptNoDrawDefaultRect)) { // Select target node - ImGuiDockNode* node = NULL; - bool allow_null_target_node = false; - if (window->DockNodeAsHost) - { - node = DockNodeTreeFindVisibleNodeByPos(window->DockNodeAsHost, g.IO.MousePos); + // (we should not assume that g.HoveredDockNode is != NULL when window is a host dock node: it depends on padding/spacing handled by DockNodeTreeFindVisibleNodeByPos) + ImGuiDockNode* node = g.HoveredDockNode; + const bool allow_null_target_node = window->DockNode == NULL && window->DockNodeAsHost == NULL; - // There is an edge case when docking into a dockspace which only has inactive nodes (because none of the windows are active) - // In this case we need to fallback into any leaf mode, possibly the central node. - if (node && node->IsDockSpace() && node->IsRootNode()) - { - if (node->CentralNode && node->IsLeafNode()) // FIXME-20181220: We should not have to test for IsLeafNode() here but we have another bug to fix first. - node = node->CentralNode; - else - node = DockNodeTreeFindFallbackLeafNode(node); - } + // There is an edge case when docking into a dockspace which only has inactive nodes (because none of the windows are active) + // In this case we need to fallback into any leaf mode, possibly the central node. + if (window->DockNodeAsHost && node && node->IsDockSpace() && node->IsRootNode()) + { + if (node->CentralNode && node->IsLeafNode()) // FIXME-20181220: We should not have to test for IsLeafNode() here but we have another bug to fix first. + node = node->CentralNode; + else + node = DockNodeTreeFindFallbackLeafNode(node); } - else if (window->DockNode) // && window->DockIsActive) - node = window->DockNode; - else - allow_null_target_node = true; // Dock into a regular window const ImRect explicit_target_rect = (node && node->TabBar && !node->IsHiddenTabBar() && !node->IsNoTabBar()) ? node->TabBar->BarRect : ImRect(window->Pos, window->Pos + ImVec2(window->Size.x, GetFrameHeight())); const bool is_explicit_target = g.IO.ConfigDockingWithShift || IsMouseHoveringRect(explicit_target_rect.Min, explicit_target_rect.Max); @@ -15824,6 +15826,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) if (ImGui::SmallButton("Clear nodes")) { DockContextClearNodes(&g, 0, true); } ImGui::SameLine(); if (ImGui::SmallButton("Rebuild all")) { dc->WantFullRebuild = true; } + ImGui::Text("HoveredDockNode: 0x%08X", g.HoveredDockNode ? g.HoveredDockNode->ID : 0); for (int n = 0; n < dc->Nodes.Data.Size; n++) if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p) if (!root_nodes_only || node->IsRootNode()) @@ -15967,29 +15970,21 @@ void ImGui::ShowMetricsWindow(bool* p_open) #ifdef IMGUI_HAS_DOCK // Overlay: Display Docking info - if (show_docking_nodes && g.IO.KeyCtrl) - { - ImGuiDockContext* dc = &g.DockContext; - for (int n = 0; n < dc->Nodes.Data.Size; n++) - if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p) - { - ImGuiDockNode* root_node = DockNodeGetRootNode(node); - if (ImGuiDockNode* hovered_node = DockNodeTreeFindVisibleNodeByPos(root_node, g.IO.MousePos)) - if (hovered_node != node) - continue; - char buf[64] = ""; - char* p = buf; - ImDrawList* overlay_draw_list = node->HostWindow ? GetForegroundDrawList(node->HostWindow) : GetForegroundDrawList((ImGuiViewportP*)GetMainViewport()); - p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "DockId: %X%s\n", node->ID, node->IsCentralNode() ? " *CentralNode*" : ""); - p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "WindowClass: %08X\n", node->WindowClass.ClassId); - p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "Size: (%.0f, %.0f)\n", node->Size.x, node->Size.y); - p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "SizeRef: (%.0f, %.0f)\n", node->SizeRef.x, node->SizeRef.y); - int depth = DockNodeGetDepth(node); - overlay_draw_list->AddRect(node->Pos + ImVec2(3, 3) * (float)depth, node->Pos + node->Size - ImVec2(3, 3) * (float)depth, IM_COL32(200, 100, 100, 255)); - ImVec2 pos = node->Pos + ImVec2(3, 3) * (float)depth; - overlay_draw_list->AddRectFilled(pos - ImVec2(1, 1), pos + CalcTextSize(buf) + ImVec2(1, 1), IM_COL32(200, 100, 100, 255)); - overlay_draw_list->AddText(NULL, 0.0f, pos, IM_COL32(255, 255, 255, 255), buf); - } + if (show_docking_nodes && g.IO.KeyCtrl && g.HoveredDockNode) + { + char buf[64] = ""; + char* p = buf; + ImGuiDockNode* node = g.HoveredDockNode; + ImDrawList* overlay_draw_list = node->HostWindow ? GetForegroundDrawList(node->HostWindow) : GetForegroundDrawList((ImGuiViewportP*)GetMainViewport()); + p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "DockId: %X%s\n", node->ID, node->IsCentralNode() ? " *CentralNode*" : ""); + p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "WindowClass: %08X\n", node->WindowClass.ClassId); + p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "Size: (%.0f, %.0f)\n", node->Size.x, node->Size.y); + p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "SizeRef: (%.0f, %.0f)\n", node->SizeRef.x, node->SizeRef.y); + int depth = DockNodeGetDepth(node); + overlay_draw_list->AddRect(node->Pos + ImVec2(3, 3) * (float)depth, node->Pos + node->Size - ImVec2(3, 3) * (float)depth, IM_COL32(200, 100, 100, 255)); + ImVec2 pos = node->Pos + ImVec2(3, 3) * (float)depth; + overlay_draw_list->AddRectFilled(pos - ImVec2(1, 1), pos + CalcTextSize(buf) + ImVec2(1, 1), IM_COL32(200, 100, 100, 255)); + overlay_draw_list->AddText(NULL, 0.0f, pos, IM_COL32(255, 255, 255, 255), buf); } #endif // #ifdef IMGUI_HAS_DOCK diff --git a/imgui_internal.h b/imgui_internal.h index 6f29af1e8977..e3253fb4f330 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1296,6 +1296,7 @@ struct ImGuiContext ImGuiWindow* HoveredWindow; // Will catch mouse inputs ImGuiWindow* HoveredRootWindow; // Will catch mouse inputs (for focus/move only) ImGuiWindow* HoveredWindowUnderMovingWindow; // Hovered window ignoring MovingWindow. Only set if MovingWindow is set. + ImGuiDockNode* HoveredDockNode; ImGuiWindow* MovingWindow; // Track the window we clicked on (in order to preserve focus). The actually window that is moved is generally MovingWindow->RootWindow. ImGuiWindow* WheelingWindow; // Track the window we started mouse-wheeling on. Until a timer elapse or mouse has moved, generally keep scrolling the same window even if during the course of scrolling the mouse ends up hovering a child window. ImVec2 WheelingWindowRefMousePos; @@ -1515,6 +1516,7 @@ struct ImGuiContext HoveredWindow = NULL; HoveredRootWindow = NULL; HoveredWindowUnderMovingWindow = NULL; + HoveredDockNode = NULL; MovingWindow = NULL; WheelingWindow = NULL; WheelingWindowTimer = 0.0f; From dbc70f21a90b0d7af93abf178ac6a0c6fa4b406b Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 10 Aug 2020 15:53:34 +0200 Subject: [PATCH 635/828] Docking: Fixed docking overlay bits appearing at (0,0), because of 43bd80a4. Most typically noticable when disabling multi-viewport. --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 30f31a72e1b2..f03f787a306b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11684,7 +11684,7 @@ struct ImGuiDockPreviewData float SplitRatio; ImRect DropRectsDraw[ImGuiDir_COUNT + 1]; // May be slightly different from hit-testing drop rects used in DockNodeCalcDropRects() - ImGuiDockPreviewData() : FutureNode(0) { IsDropAllowed = IsCenterAvailable = IsSidesAvailable = IsSplitDirExplicit = false; SplitNode = NULL; SplitDir = ImGuiDir_None; SplitRatio = 0.f; } + ImGuiDockPreviewData() : FutureNode(0) { IsDropAllowed = IsCenterAvailable = IsSidesAvailable = IsSplitDirExplicit = false; SplitNode = NULL; SplitDir = ImGuiDir_None; SplitRatio = 0.f; for (int n = 0; n < IM_ARRAYSIZE(DropRectsDraw); n++) DropRectsDraw[n] = ImRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX); } }; // Persistent Settings data, stored contiguously in SettingsNodes (sizeof() ~32 bytes) From a5ba26806f19bb85a7f146e166aa2f61e99e70a3 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 10 Aug 2020 16:28:46 +0200 Subject: [PATCH 636/828] Make moving window prevent its active id from being stolen (#3392, #3243, #1738) Amend 7b3d379, 615e9ae3 --- imgui.cpp | 9 ++++++--- imgui_internal.h | 1 + imgui_widgets.cpp | 3 ++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index f03f787a306b..9a8c8b3bc632 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2979,6 +2979,7 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window) } g.ActiveId = id; g.ActiveIdAllowOverlap = false; + g.ActiveIdNoClearOnFocusLoss = false; g.ActiveIdWindow = window; g.ActiveIdHasBeenEditedThisFrame = false; if (id) @@ -2996,7 +2997,7 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window) void ImGui::ClearActiveID() { - SetActiveID(0, NULL); + SetActiveID(0, NULL); // g.ActiveId = 0; } void ImGui::SetHoveredID(ImGuiID id) @@ -3401,6 +3402,7 @@ void ImGui::StartMouseMovingWindow(ImGuiWindow* window) FocusWindow(window); SetActiveID(window->MoveId, window); g.NavDisableHighlight = true; + g.ActiveIdNoClearOnFocusLoss = true; g.ActiveIdClickOffset = g.IO.MousePos - window->RootWindow->Pos; bool can_move_window = true; @@ -6775,8 +6777,9 @@ void ImGui::FocusWindow(ImGuiWindow* window) // - Focus a window while an InputText in another window is active, if focus happens before the old InputText can run. // - When using Nav to activate menu items (due to timing of activating on press->new window appears->losing ActiveId) // - Using dock host items (tab, collapse button) can trigger this before we redirect the ActiveIdWindow toward the child window. - if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindowDockStop != focus_front_window && !active_id_window_is_dock_node_host) - ClearActiveID(); + if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindowDockStop != focus_front_window) + if (!g.ActiveIdNoClearOnFocusLoss && !active_id_window_is_dock_node_host) + ClearActiveID(); // Passing NULL allow to disable keyboard focus if (!window) diff --git a/imgui_internal.h b/imgui_internal.h index e3253fb4f330..2dada7afdba6 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1314,6 +1314,7 @@ struct ImGuiContext float ActiveIdTimer; bool ActiveIdIsJustActivated; // Set at the time of activation for one frame bool ActiveIdAllowOverlap; // Active widget allows another widget to steal active id (generally for overlapping widgets, but not always) + bool ActiveIdNoClearOnFocusLoss; // Disable losing active id if the active id window gets unfocused. bool ActiveIdHasBeenPressedBefore; // Track whether the active id led to a press (this is to allow changing between PressOnClick and PressOnRelease without pressing twice). Used by range_select branch. bool ActiveIdHasBeenEditedBefore; // Was the value associated to the widget Edited over the course of the Active state. bool ActiveIdHasBeenEditedThisFrame; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 374e6b5d8c85..1103167eaae9 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7395,8 +7395,9 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, // Undock DockContextQueueUndockWindow(&g, docked_window); g.MovingWindow = docked_window; - g.ActiveId = g.MovingWindow->MoveId; + SetActiveID(g.MovingWindow->MoveId, g.MovingWindow); g.ActiveIdClickOffset -= g.MovingWindow->Pos - bb.Min; + g.ActiveIdNoClearOnFocusLoss = true; } } } From a9626e1162c3742307553108aff8a443c20bb73c Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 11 Aug 2020 11:51:47 +0200 Subject: [PATCH 637/828] Docking: Made DockBuilderAddNode() automatically call DockBuilderRemoveNode(). (#3399, #2109) --- imgui.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 9a8c8b3bc632..13dfcc42394c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -14316,10 +14316,15 @@ void ImGui::DockBuilderSetNodeSize(ImGuiID node_id, ImVec2 size) // - If you intend to split a node immediately after creation using DockBuilderSplitNode(), make sure to call DockBuilderSetNodeSize() beforehand! // For various reason, the splitting code currently needs a base size otherwise space may not be allocated as precisely as you would expect. // - Use (id == 0) to let the system allocate a node identifier. +// - Existing node with a same id will be removed. ImGuiID ImGui::DockBuilderAddNode(ImGuiID id, ImGuiDockNodeFlags flags) { ImGuiContext* ctx = GImGui; ImGuiDockNode* node = NULL; + + if (id != 0) + DockBuilderRemoveNode(id); + if (flags & ImGuiDockNodeFlags_DockSpace) { DockSpace(id, ImVec2(0, 0), (flags & ~ImGuiDockNodeFlags_DockSpace) | ImGuiDockNodeFlags_KeepAliveOnly); @@ -14327,10 +14332,7 @@ ImGuiID ImGui::DockBuilderAddNode(ImGuiID id, ImGuiDockNodeFlags flags) } else { - if (id != 0) - node = DockContextFindNodeByID(ctx, id); - if (!node) - node = DockContextAddNode(ctx, id); + node = DockContextAddNode(ctx, id); node->LocalFlags = flags; } node->LastFrameAlive = ctx->FrameCount; // Set this otherwise BeginDocked will undock during the same frame. From b36d1d465dc25488455a6ab728d322e57b2fed3e Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 11 Aug 2020 11:53:04 +0200 Subject: [PATCH 638/828] Docking: Untangle a little bit of the ActiveIdClickOffset mess. --- imgui.cpp | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 756991812314..fdc76f7a69ab 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3414,7 +3414,7 @@ void ImGui::StartMouseMovingWindow(ImGuiWindow* window) SetActiveID(window->MoveId, window); g.NavDisableHighlight = true; g.ActiveIdNoClearOnFocusLoss = true; - g.ActiveIdClickOffset = g.IO.MousePos - window->RootWindow->Pos; + g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - window->RootWindow->Pos; bool can_move_window = true; if ((window->Flags & ImGuiWindowFlags_NoMove) || (window->RootWindow->Flags & ImGuiWindowFlags_NoMove)) @@ -3447,15 +3447,9 @@ void ImGui::StartMouseMovingWindowOrNode(ImGuiWindow* window, ImGuiDockNode* nod const bool clicked = IsMouseClicked(0); const bool dragging = IsMouseDragging(0, g.IO.MouseDragThreshold * 1.70f); if (can_undock_node && dragging) - { - DockContextQueueUndockNode(&g, node); - g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - node->Pos; - } + DockContextQueueUndockNode(&g, node); // Will lead to DockNodeStartMouseMovingWindow() -> StartMouseMovingWindow() being called next frame else if (!can_undock_node && (clicked || dragging) && g.MovingWindow != window) - { StartMouseMovingWindow(window); - g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - window->RootWindow->Pos; - } } // Handle mouse moving window @@ -12775,11 +12769,10 @@ static void ImGui::DockNodeStartMouseMovingWindow(ImGuiDockNode* node, ImGuiWind { ImGuiContext& g = *GImGui; IM_ASSERT(node->WantMouseMove == true); - ImVec2 backup_active_click_offset = g.ActiveIdClickOffset; StartMouseMovingWindow(window); + g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - node->Pos; g.MovingWindow = window; // If we are docked into a non moveable root window, StartMouseMovingWindow() won't set g.MovingWindow. Override that decision. node->WantMouseMove = false; - g.ActiveIdClickOffset = backup_active_click_offset; } // Update CentralNode, OnlyNodeWithWindows, LastFocusedNodeID. Copy window class. @@ -13296,7 +13289,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w window->DockTabItemRect = host_window->DC.LastItemRect; // Update navigation ID on menu layer - if (g.NavWindow && g.NavWindow->RootWindowDockStop == window && (window->DC.NavLayerActiveMask & (1 << 1)) == 0) + if (g.NavWindow && g.NavWindow->RootWindowDockStop == window && (window->DC.NavLayerActiveMask & (1 << ImGuiNavLayer_Menu)) == 0) host_window->NavLastIds[1] = window->ID; } } From 9d20a5f0a515dba3ade46cfcf90953133e34dcbc Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 20 Aug 2020 16:21:48 +0200 Subject: [PATCH 639/828] Docking: DockSpace() emits ItemSize() properly + dockspace demo (works now since 05a25e5f3) --- imgui.cpp | 2 ++ imgui_demo.cpp | 33 +++++++++++++++++++++------------ 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 51699b0c85f3..5babd781ee21 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -14183,6 +14183,7 @@ void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags fla char title[256]; ImFormatString(title, IM_ARRAYSIZE(title), "%s/DockSpace_%08X", window->Name, id); + // FIXME-DOCK: What is the reason for not simply calling BeginChild()? if (node->Windows.Size > 0 || node->IsSplitNode()) PushStyleColor(ImGuiCol_ChildBg, IM_COL32(0, 0, 0, 0)); PushStyleVar(ImGuiStyleVar_ChildBorderSize, 0.0f); @@ -14213,6 +14214,7 @@ void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags fla g.WithinEndChild = true; End(); + ItemSize(size); g.WithinEndChild = false; } diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 2d6bc7a826f2..4555346567d6 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -5303,8 +5303,8 @@ static void ShowExampleAppCustomRendering(bool* p_open) // DockSpace() is only useful to construct to a central location for your application. void ShowExampleAppDockSpace(bool* p_open) { - static bool opt_fullscreen_persistant = true; - bool opt_fullscreen = opt_fullscreen_persistant; + static bool opt_fullscreen = true; + static bool opt_padding = false; static ImGuiDockNodeFlags dockspace_flags = ImGuiDockNodeFlags_None; // We are using the ImGuiWindowFlags_NoDocking flag to make the parent window not dockable into, @@ -5321,6 +5321,10 @@ void ShowExampleAppDockSpace(bool* p_open) window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove; window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus; } + else + { + dockspace_flags &= ~ImGuiDockNodeFlags_PassthruCentralNode; + } // When using ImGuiDockNodeFlags_PassthruCentralNode, DockSpace() will render our background // and handle the pass-thru hole, so we ask Begin() to not render a background. @@ -5332,9 +5336,11 @@ void ShowExampleAppDockSpace(bool* p_open) // all active windows docked into it will lose their parent and become undocked. // We cannot preserve the docking relationship between an active window and an inactive docking, otherwise // any change of dockspace/settings would lead to windows being stuck in limbo and never being visible. - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); + if (!opt_padding) + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); ImGui::Begin("DockSpace Demo", p_open, window_flags); - ImGui::PopStyleVar(); + if (!opt_padding) + ImGui::PopStyleVar(); if (opt_fullscreen) ImGui::PopStyleVar(2); @@ -5353,19 +5359,22 @@ void ShowExampleAppDockSpace(bool* p_open) if (ImGui::BeginMenuBar()) { - if (ImGui::BeginMenu("Docking")) + if (ImGui::BeginMenu("Options")) { // Disabling fullscreen would allow the window to be moved to the front of other windows, // which we can't undo at the moment without finer window depth/z control. - //ImGui::MenuItem("Fullscreen", NULL, &opt_fullscreen_persistant); + ImGui::MenuItem("Fullscreen", NULL, &opt_fullscreen); + ImGui::MenuItem("Padding", NULL, &opt_padding); + ImGui::Separator(); - if (ImGui::MenuItem("Flag: NoSplit", "", (dockspace_flags & ImGuiDockNodeFlags_NoSplit) != 0)) dockspace_flags ^= ImGuiDockNodeFlags_NoSplit; - if (ImGui::MenuItem("Flag: NoResize", "", (dockspace_flags & ImGuiDockNodeFlags_NoResize) != 0)) dockspace_flags ^= ImGuiDockNodeFlags_NoResize; - if (ImGui::MenuItem("Flag: NoDockingInCentralNode", "", (dockspace_flags & ImGuiDockNodeFlags_NoDockingInCentralNode) != 0)) dockspace_flags ^= ImGuiDockNodeFlags_NoDockingInCentralNode; - if (ImGui::MenuItem("Flag: PassthruCentralNode", "", (dockspace_flags & ImGuiDockNodeFlags_PassthruCentralNode) != 0)) dockspace_flags ^= ImGuiDockNodeFlags_PassthruCentralNode; - if (ImGui::MenuItem("Flag: AutoHideTabBar", "", (dockspace_flags & ImGuiDockNodeFlags_AutoHideTabBar) != 0)) dockspace_flags ^= ImGuiDockNodeFlags_AutoHideTabBar; + if (ImGui::MenuItem("Flag: NoSplit", "", (dockspace_flags & ImGuiDockNodeFlags_NoSplit) != 0)) { dockspace_flags ^= ImGuiDockNodeFlags_NoSplit; } + if (ImGui::MenuItem("Flag: NoResize", "", (dockspace_flags & ImGuiDockNodeFlags_NoResize) != 0)) { dockspace_flags ^= ImGuiDockNodeFlags_NoResize; } + if (ImGui::MenuItem("Flag: NoDockingInCentralNode", "", (dockspace_flags & ImGuiDockNodeFlags_NoDockingInCentralNode) != 0)) { dockspace_flags ^= ImGuiDockNodeFlags_NoDockingInCentralNode; } + if (ImGui::MenuItem("Flag: AutoHideTabBar", "", (dockspace_flags & ImGuiDockNodeFlags_AutoHideTabBar) != 0)) { dockspace_flags ^= ImGuiDockNodeFlags_AutoHideTabBar; } + if (ImGui::MenuItem("Flag: PassthruCentralNode", "", (dockspace_flags & ImGuiDockNodeFlags_PassthruCentralNode) != 0, opt_fullscreen)) { dockspace_flags ^= ImGuiDockNodeFlags_PassthruCentralNode; } ImGui::Separator(); - if (ImGui::MenuItem("Close DockSpace", NULL, false, p_open != NULL)) + + if (ImGui::MenuItem("Close", NULL, false, p_open != NULL)) *p_open = false; ImGui::EndMenu(); } From d6f3a8848d005e00748b8e8f9a93ba84620160a3 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 21 Aug 2020 15:02:52 +0200 Subject: [PATCH 640/828] Viewports: Backends: DirectX9: Allow D3DERR_DEVICELOST on secondary viewports. (#3424) --- examples/imgui_impl_dx9.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/imgui_impl_dx9.cpp b/examples/imgui_impl_dx9.cpp index 6d726af889fb..f8abc61a15e7 100644 --- a/examples/imgui_impl_dx9.cpp +++ b/examples/imgui_impl_dx9.cpp @@ -408,7 +408,8 @@ static void ImGui_ImplDX9_SwapBuffers(ImGuiViewport* viewport, void*) { ImGuiViewportDataDx9* data = (ImGuiViewportDataDx9*)viewport->RendererUserData; HRESULT hr = data->SwapChain->Present(NULL, NULL, data->d3dpp.hDeviceWindow, NULL, NULL); - IM_ASSERT(hr == D3D_OK); + // Let main application handle D3DERR_DEVICELOST by resetting the device. + IM_ASSERT(hr == D3D_OK || hr == D3DERR_DEVICELOST); } static void ImGui_ImplDX9_InitPlatformInterface() From cf312545e87146000bd96a1827d94a97ca7b17a0 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 21 Aug 2020 16:21:51 +0200 Subject: [PATCH 641/828] Docking: Fixed docking while hovering a child window. (#3420) broken by 85a661d27. Improve metrics debugging. --- imgui.cpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 5babd781ee21..ac94fc2e06cb 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11912,10 +11912,10 @@ void ImGui::DockContextUpdateDocking(ImGuiContext* ctx) g.HoveredDockNode = NULL; if (ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow) { - if (hovered_window->DockNode) - g.HoveredDockNode = hovered_window->DockNode; - else if (hovered_window->DockNodeAsHost) + if (hovered_window->DockNodeAsHost) g.HoveredDockNode = DockNodeTreeFindVisibleNodeByPos(hovered_window->DockNodeAsHost, g.IO.MousePos); + else if (hovered_window->RootWindowDockStop->DockNode) + g.HoveredDockNode = hovered_window->RootWindowDockStop->DockNode; } // Process Docking requests @@ -14872,6 +14872,7 @@ void ImGui::BeginDockableDragDropTarget(ImGuiWindow* window) IM_ASSERT((window->Flags & ImGuiWindowFlags_NoDocking) == 0); if (!g.DragDropActive) return; + //GetForegroundDrawList(window)->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255)); if (!BeginDragDropTargetCustom(window->Rect(), window->ID)) return; @@ -15662,11 +15663,17 @@ void ImGui::ShowMetricsWindow(bool* p_open) static void NodeDockNode(ImGuiDockNode* node, const char* label) { ImGuiContext& g = *GImGui; + const bool is_alive = (g.FrameCount - node->LastFrameAlive < 2); // Submitted with ImGuiDockNodeFlags_KeepAliveOnly + const bool is_active = (g.FrameCount - node->LastFrameActive < 2); // Submitted + if (!is_alive) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); } bool open; if (node->Windows.Size > 0) open = ImGui::TreeNode((void*)(intptr_t)node->ID, "%s 0x%04X%s: %d windows (vis: '%s')", label, node->ID, node->IsVisible ? "" : " (hidden)", node->Windows.Size, node->VisibleWindow ? node->VisibleWindow->Name : "NULL"); else open = ImGui::TreeNode((void*)(intptr_t)node->ID, "%s 0x%04X%s: %s split (vis: '%s')", label, node->ID, node->IsVisible ? "" : " (hidden)", (node->SplitAxis == ImGuiAxis_X) ? "horizontal" : (node->SplitAxis == ImGuiAxis_Y) ? "vertical" : "n/a", node->VisibleWindow ? node->VisibleWindow->Name : "NULL"); + if (!is_alive) { PopStyleColor(); } + if (is_active && ImGui::IsItemHovered()) + GetForegroundDrawList(node->HostWindow ? node->HostWindow : node->VisibleWindow)->AddRect(node->Pos, node->Pos + node->Size, IM_COL32(255, 255, 0, 255)); if (open) { IM_ASSERT(node->ChildNodes[0] == NULL || node->ChildNodes[0]->ParentNode == node); @@ -15679,8 +15686,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::BulletText("Misc:%s%s%s%s%s", node->IsDockSpace() ? " IsDockSpace" : "", node->IsCentralNode() ? " IsCentralNode" : "", - (g.FrameCount - node->LastFrameAlive < 2) ? " IsAlive" : "", - (g.FrameCount - node->LastFrameActive < 2) ? " IsActive" : "", + is_alive ? " IsAlive" : "", is_active ? " IsActive" : "", node->WantLockSizeOnce ? " WantLockSizeOnce" : ""); if (ImGui::TreeNode("flags", "LocalFlags: 0x%04X SharedFlags: 0x%04X", node->LocalFlags, node->SharedFlags)) { @@ -15854,7 +15860,6 @@ void ImGui::ShowMetricsWindow(bool* p_open) if (ImGui::SmallButton("Clear nodes")) { DockContextClearNodes(&g, 0, true); } ImGui::SameLine(); if (ImGui::SmallButton("Rebuild all")) { dc->WantFullRebuild = true; } - ImGui::Text("HoveredDockNode: 0x%08X", g.HoveredDockNode ? g.HoveredDockNode->ID : 0); for (int n = 0; n < dc->Nodes.Data.Size; n++) if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p) if (!root_nodes_only || node->IsRootNode()) @@ -15946,6 +15951,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL"); ImGui::Text("HoveredRootWindow: '%s'", g.HoveredRootWindow ? g.HoveredRootWindow->Name : "NULL"); ImGui::Text("HoveredWindowUnderMovingWindow: '%s'", g.HoveredWindowUnderMovingWindow ? g.HoveredWindowUnderMovingWindow->Name : "NULL"); + ImGui::Text("HoveredDockNode: 0x%08X", g.HoveredDockNode ? g.HoveredDockNode->ID : 0); ImGui::Text("MovingWindow: '%s'", g.MovingWindow ? g.MovingWindow->Name : "NULL"); ImGui::Text("MouseViewport: 0x%08X (UserHovered 0x%08X, LastHovered 0x%08X)", g.MouseViewport->ID, g.IO.MouseHoveredViewport, g.MouseLastHoveredViewport ? g.MouseLastHoveredViewport->ID : 0); ImGui::Unindent(); From 831e2c920edf62b1254587418a4f1968de3dc37e Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 21 Aug 2020 18:44:56 +0200 Subject: [PATCH 642/828] Docking, Viewport: Fixed a rare edge-case if the window targetted by CTRL+Tab stops being rendered. --- imgui.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index ac94fc2e06cb..110b73d2ee08 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4386,9 +4386,10 @@ static void ImGui::EndFrameDrawDimmedBackgrounds() } // Draw modal whitening background between CTRL-TAB list - if (dim_bg_for_window_list) + if (dim_bg_for_window_list && g.NavWindowingTargetAnim->Active) { // Choose a draw list that will be front-most across all our children + // In the unlikely case that the window wasn't made active we can't rely on its drawlist and skip rendering all-together. ImGuiWindow* window = g.NavWindowingTargetAnim; ImDrawList* draw_list = FindFrontMostVisibleChildWindow(window->RootWindow)->DrawList; draw_list->PushClipRectFullScreen(); From 30f0900b1c5c840b817f73d0dadd1511e21f224c Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 25 Aug 2020 19:03:08 +0200 Subject: [PATCH 643/828] Docking: Fix honoring payload filter with overlapping nodes. (we incorrectly over-relied on g.HoveredDockNode when making change for #3398) Essentially undo part of 85a661d (#3398) + ref cf31254 (#3420) --- imgui.cpp | 28 ++++++++++++++++++---------- imgui_internal.h | 2 +- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 110b73d2ee08..15749d3a9abf 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -14890,18 +14890,26 @@ void ImGui::BeginDockableDragDropTarget(ImGuiWindow* window) if (AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_WINDOW, ImGuiDragDropFlags_AcceptBeforeDelivery | ImGuiDragDropFlags_AcceptNoDrawDefaultRect)) { // Select target node - // (we should not assume that g.HoveredDockNode is != NULL when window is a host dock node: it depends on padding/spacing handled by DockNodeTreeFindVisibleNodeByPos) - ImGuiDockNode* node = g.HoveredDockNode; - const bool allow_null_target_node = window->DockNode == NULL && window->DockNodeAsHost == NULL; + // (Important: we cannot use g.HoveredDockNode here! Because each of our target node have filters based on payload, each candidate drop target will do its own evaluation) + bool dock_into_floating_window = false; + ImGuiDockNode* node = NULL; + if (window->DockNodeAsHost) + { + // Cannot assume that node will != NULL even though we passed the rectangle test: it depends on padding/spacing handled by DockNodeTreeFindVisibleNodeByPos(). + node = DockNodeTreeFindVisibleNodeByPos(window->DockNodeAsHost, g.IO.MousePos); - // There is an edge case when docking into a dockspace which only has inactive nodes (because none of the windows are active) - // In this case we need to fallback into any leaf mode, possibly the central node. - if (window->DockNodeAsHost && node && node->IsDockSpace() && node->IsRootNode()) + // There is an edge case when docking into a dockspace which only has _inactive_ nodes (because none of the windows are active) + // In this case we need to fallback into any leaf mode, possibly the central node. + // FIXME-20181220: We should not have to test for IsLeafNode() here but we have another bug to fix first. + if (node && node->IsDockSpace() && node->IsRootNode()) + node = (node->CentralNode && node->IsLeafNode()) ? node->CentralNode : DockNodeTreeFindFallbackLeafNode(node); + } + else { - if (node->CentralNode && node->IsLeafNode()) // FIXME-20181220: We should not have to test for IsLeafNode() here but we have another bug to fix first. - node = node->CentralNode; + if (window->DockNode) + node = window->DockNode; else - node = DockNodeTreeFindFallbackLeafNode(node); + dock_into_floating_window = true; // Dock into a regular window } const ImRect explicit_target_rect = (node && node->TabBar && !node->IsHiddenTabBar() && !node->IsNoTabBar()) ? node->TabBar->BarRect : ImRect(window->Pos, window->Pos + ImVec2(window->Size.x, GetFrameHeight())); @@ -14910,7 +14918,7 @@ void ImGui::BeginDockableDragDropTarget(ImGuiWindow* window) // Preview docking request and find out split direction/ratio //const bool do_preview = true; // Ignore testing for payload->IsPreview() which removes one frame of delay, but breaks overlapping drop targets within the same window. const bool do_preview = payload->IsPreview() || payload->IsDelivery(); - if (do_preview && (node != NULL || allow_null_target_node)) + if (do_preview && (node != NULL || dock_into_floating_window)) { ImGuiDockPreviewData split_inner; ImGuiDockPreviewData split_outer; diff --git a/imgui_internal.h b/imgui_internal.h index 0105add45093..5ccf05812792 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1296,7 +1296,7 @@ struct ImGuiContext ImGuiWindow* HoveredWindow; // Window the mouse is hovering. Will typically catch mouse inputs. ImGuiWindow* HoveredRootWindow; // == HoveredWindow ? HoveredWindow->RootWindow : NULL, merely a shortcut to avoid null test in some situation. ImGuiWindow* HoveredWindowUnderMovingWindow; // Hovered window ignoring MovingWindow. Only set if MovingWindow is set. - ImGuiDockNode* HoveredDockNode; + ImGuiDockNode* HoveredDockNode; // Hovered dock node. ImGuiWindow* MovingWindow; // Track the window we clicked on (in order to preserve focus). The actual window that is moved is generally MovingWindow->RootWindow. ImGuiWindow* WheelingWindow; // Track the window we started mouse-wheeling on. Until a timer elapse or mouse has moved, generally keep scrolling the same window even if during the course of scrolling the mouse ends up hovering a child window. ImVec2 WheelingWindowRefMousePos; From 600b8f60b48f8a038065753830e99e975412379e Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 28 Aug 2020 20:20:28 +0200 Subject: [PATCH 644/828] Docking: Fixed crash in metrics. --- imgui.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index c0279359bf6a..68ebd424c208 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -15687,7 +15687,8 @@ void ImGui::ShowMetricsWindow(bool* p_open) open = ImGui::TreeNode((void*)(intptr_t)node->ID, "%s 0x%04X%s: %s split (vis: '%s')", label, node->ID, node->IsVisible ? "" : " (hidden)", (node->SplitAxis == ImGuiAxis_X) ? "horizontal" : (node->SplitAxis == ImGuiAxis_Y) ? "vertical" : "n/a", node->VisibleWindow ? node->VisibleWindow->Name : "NULL"); if (!is_alive) { PopStyleColor(); } if (is_active && ImGui::IsItemHovered()) - GetForegroundDrawList(node->HostWindow ? node->HostWindow : node->VisibleWindow)->AddRect(node->Pos, node->Pos + node->Size, IM_COL32(255, 255, 0, 255)); + if (ImGuiWindow* window = node->HostWindow ? node->HostWindow : node->VisibleWindow) + GetForegroundDrawList(window)->AddRect(node->Pos, node->Pos + node->Size, IM_COL32(255, 255, 0, 255)); if (open) { IM_ASSERT(node->ChildNodes[0] == NULL || node->ChildNodes[0]->ParentNode == node); From fc625d249f4f31c42cb0d73cca954c97d120c0bb Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 1 Sep 2020 15:24:24 +0200 Subject: [PATCH 645/828] Internals: Begin: update ->Hidden flags only on first begin of the frame. (ignore whitespace to see simple diff) # Conflicts: # imgui.cpp --- imgui.cpp | 75 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 40 insertions(+), 35 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 3d6662a1affa..cdfa456ddc1f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5602,6 +5602,7 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar ImGuiWindowFlags flags = window->Flags; // Ensure that ScrollBar doesn't read last frame's SkipItems + IM_ASSERT(window->BeginCount == 0); window->SkipItems = false; // Draw window + handle manual resize @@ -6626,49 +6627,53 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->BeginCount++; g.NextWindowData.ClearFlags(); - // When we are about to select this tab (which will only be visible on the _next frame_), flag it with a non-zero HiddenFramesCannotSkipItems. - // This will have the important effect of actually returning true in Begin() and not setting SkipItems, allowing an earlier submission of the window contents. - // This is analogous to regular windows being hidden from one frame. - // It is especially important as e.g. nested TabBars would otherwise generate flicker in the form of one empty frame, or focus requests won't be processed. - if (window->DockIsActive && !window->DockTabIsVisible) + // Update visibility + if (first_begin_of_the_frame) { - if (window->LastFrameJustFocused == g.FrameCount) - window->HiddenFramesCannotSkipItems = 1; - else - window->HiddenFramesCanSkipItems = 1; - } + // When we are about to select this tab (which will only be visible on the _next frame_), flag it with a non-zero HiddenFramesCannotSkipItems. + // This will have the important effect of actually returning true in Begin() and not setting SkipItems, allowing an earlier submission of the window contents. + // This is analogous to regular windows being hidden from one frame. + // It is especially important as e.g. nested TabBars would otherwise generate flicker in the form of one empty frame, or focus requests won't be processed. + if (window->DockIsActive && !window->DockTabIsVisible) + { + if (window->LastFrameJustFocused == g.FrameCount) + window->HiddenFramesCannotSkipItems = 1; + else + window->HiddenFramesCanSkipItems = 1; + } - if (flags & ImGuiWindowFlags_ChildWindow) - { - // Child window can be out of sight and have "negative" clip windows. - // Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar). - IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0 || (window->DockIsActive)); - if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) - if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y) + if (flags & ImGuiWindowFlags_ChildWindow) + { + // Child window can be out of sight and have "negative" clip windows. + // Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar). + IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0 || (window->DockIsActive)); + if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) + if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y) + window->HiddenFramesCanSkipItems = 1; + + // Hide along with parent or if parent is collapsed + if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCanSkipItems > 0)) window->HiddenFramesCanSkipItems = 1; + if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCannotSkipItems > 0)) + window->HiddenFramesCannotSkipItems = 1; + } - // Hide along with parent or if parent is collapsed - if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCanSkipItems > 0)) + // Don't render if style alpha is 0.0 at the time of Begin(). This is arbitrary and inconsistent but has been there for a long while (may remove at some point) + if (style.Alpha <= 0.0f) window->HiddenFramesCanSkipItems = 1; - if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCannotSkipItems > 0)) - window->HiddenFramesCannotSkipItems = 1; - } - // Don't render if style alpha is 0.0 at the time of Begin(). This is arbitrary and inconsistent but has been there for a long while (may remove at some point) - if (style.Alpha <= 0.0f) - window->HiddenFramesCanSkipItems = 1; + // Update the Hidden flag + window->Hidden = (window->HiddenFramesCanSkipItems > 0) || (window->HiddenFramesCannotSkipItems > 0); - // Update the Hidden flag - window->Hidden = (window->HiddenFramesCanSkipItems > 0) || (window->HiddenFramesCannotSkipItems > 0); - - // Update the SkipItems flag, used to early out of all items functions (no layout required) - bool skip_items = false; - if (window->Collapsed || !window->Active || window->Hidden) - if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && window->HiddenFramesCannotSkipItems <= 0) - skip_items = true; - window->SkipItems = skip_items; + // Update the SkipItems flag, used to early out of all items functions (no layout required) + bool skip_items = false; + if (window->Collapsed || !window->Active || window->Hidden) + if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && window->HiddenFramesCannotSkipItems <= 0) + skip_items = true; + window->SkipItems = skip_items; + } - return !skip_items; + return !window->SkipItems; } void ImGui::End() From 8dacb4da20021f4289ac56a83738672604a0db61 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 1 Sep 2020 19:43:46 +0200 Subject: [PATCH 646/828] Docking: Fixed DockNode tab bar initial order broken by 8c80d533d --- imgui.cpp | 5 +++-- imgui.h | 2 +- imgui_widgets.cpp | 3 ++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index cdfa456ddc1f..3611c424b8a5 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -13256,8 +13256,8 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w IMGUI_DEBUG_LOG_DOCKING("In node 0x%08X: %d new appearing tabs:%s\n", node->ID, tab_bar->Tabs.Size - tabs_unsorted_start, (tab_bar->Tabs.Size > tabs_unsorted_start + 1) ? " (will sort)" : ""); for (int tab_n = tabs_unsorted_start; tab_n < tab_bar->Tabs.Size; tab_n++) IMGUI_DEBUG_LOG_DOCKING(" - Tab '%s' Order %d\n", tab_bar->Tabs[tab_n].Window->Name, tab_bar->Tabs[tab_n].Window->DockOrder); - if (tab_bar->Tabs.Size > tabs_unsorted_start + 1) - ImQsort(tab_bar->Tabs.Data + tabs_unsorted_start, tab_bar->Tabs.Size - tabs_unsorted_start, sizeof(ImGuiTabItem), TabItemComparerByDockOrder); + if (tab_bar->Tabs.Size > tabs_unsorted_start + 1) + ImQsort(tab_bar->Tabs.Data + tabs_unsorted_start, tab_bar->Tabs.Size - tabs_unsorted_start, sizeof(ImGuiTabItem), TabItemComparerByDockOrder); } // Selected newly added tabs, or persistent tab ID if the tab bar was just recreated @@ -14144,6 +14144,7 @@ void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags fla flags |= ImGuiDockNodeFlags_KeepAliveOnly; IM_ASSERT((flags & ImGuiDockNodeFlags_DockSpace) == 0); + IM_ASSERT(id != 0); ImGuiDockNode* node = DockContextFindNodeByID(ctx, id); if (!node) { diff --git a/imgui.h b/imgui.h index 12cc980e1282..ae5b40ca5afe 100644 --- a/imgui.h +++ b/imgui.h @@ -1180,7 +1180,7 @@ enum ImGuiConfigFlags_ // [BETA] Viewports // When using viewports it is recommended that your default value for ImGuiCol_WindowBg is opaque (Alpha=1.0) so transition to a viewport won't be noticeable. - ImGuiConfigFlags_ViewportsEnable = 1 << 10, // Viewport enable flags (require both ImGuiConfigFlags_PlatformHasViewports + ImGuiConfigFlags_RendererHasViewports set by the respective back-ends) + ImGuiConfigFlags_ViewportsEnable = 1 << 10, // Viewport enable flags (require both ImGuiBackendFlags_PlatformHasViewports + ImGuiBackendFlags_RendererHasViewports set by the respective back-ends) ImGuiConfigFlags_DpiEnableScaleViewports= 1 << 14, // [BETA: Don't use] FIXME-DPI: Reposition and resize imgui windows when the DpiScale of a viewport changed (mostly useful for the main viewport hosting other window). Note that resizing the main window itself is up to your application. ImGuiConfigFlags_DpiEnableScaleFonts = 1 << 15, // [BETA: Don't use] FIXME-DPI: Request bitmap-scaled fonts to match DpiScale. This is a very low-quality workaround. The correct way to handle DPI is _currently_ to replace the atlas and/or fonts in the Platform_OnChangedViewport callback, but this is all early work in progress. diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 9dc515ae3100..2d516bc11117 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6885,7 +6885,8 @@ bool ImGui::BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& tab_bar_bb, ImG // When toggling ImGuiTabBarFlags_Reorderable flag, ensure tabs are ordered based on their submission order. if ((flags & ImGuiTabBarFlags_Reorderable) != (tab_bar->Flags & ImGuiTabBarFlags_Reorderable) && tab_bar->Tabs.Size > 1) - ImQsort(tab_bar->Tabs.Data, tab_bar->Tabs.Size, sizeof(ImGuiTabItem), TabItemComparerByBeginOrder); + if ((flags & ImGuiTabBarFlags_DockNode) == 0) + ImQsort(tab_bar->Tabs.Data, tab_bar->Tabs.Size, sizeof(ImGuiTabItem), TabItemComparerByBeginOrder); // Flags if ((flags & ImGuiTabBarFlags_FittingPolicyMask_) == 0) From aa8e09d7f1481641792e3bb6419acda896af62ae Mon Sep 17 00:00:00 2001 From: Doug Binks Date: Fri, 4 Sep 2020 16:26:31 +0100 Subject: [PATCH 647/828] Backends: GLFW: workaround for cases where glfwGetMonitorWorkarea fails (#3457) --- examples/imgui_impl_glfw.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index e7dc10ca0297..caded5c471be 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -442,16 +442,16 @@ static void ImGui_ImplGlfw_UpdateMonitors() int x, y; glfwGetMonitorPos(glfw_monitors[n], &x, &y); const GLFWvidmode* vid_mode = glfwGetVideoMode(glfw_monitors[n]); + monitor.MainPos = monitor.WorkPos = ImVec2((float)x, (float)y); + monitor.MainSize = monitor.WorkSize = ImVec2((float)vid_mode->width, (float)vid_mode->height); #if GLFW_HAS_MONITOR_WORK_AREA - monitor.MainPos = ImVec2((float)x, (float)y); - monitor.MainSize = ImVec2((float)vid_mode->width, (float)vid_mode->height); int w, h; glfwGetMonitorWorkarea(glfw_monitors[n], &x, &y, &w, &h); - monitor.WorkPos = ImVec2((float)x, (float)y);; - monitor.WorkSize = ImVec2((float)w, (float)h); -#else - monitor.MainPos = monitor.WorkPos = ImVec2((float)x, (float)y); - monitor.MainSize = monitor.WorkSize = ImVec2((float)vid_mode->width, (float)vid_mode->height); + if (w > 0 && h > 0) // Workaround a small GLFW issue reporting zero on monitor changes: https://github.com/glfw/glfw/pull/1761 + { + monitor.WorkPos = ImVec2((float)x, (float)y); + monitor.WorkSize = ImVec2((float)w, (float)h); + } #endif #if GLFW_HAS_PER_MONITOR_DPI // Warning: the validity of monitor DPI information on Windows depends on the application DPI awareness settings, which generally needs to be set in the manifest or at runtime. From 770c99536533923d2cf07fc5004c752ef2c63f5c Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 8 Sep 2020 22:39:53 +0200 Subject: [PATCH 648/828] Backends: Vulkan: Removed unused shader code. Fix leaks. Avoid unnecessary pipeline creation for main viewport. Amend 41e2aa2. (#3459) + Add ImGui_ImplVulkanH_CreateWindowSwapChain in ImGui_ImplVulkanH_CreateOrResizeWindow(). --- examples/imgui_impl_vulkan.cpp | 32 +++++++++----------------------- examples/imgui_impl_vulkan.h | 2 +- 2 files changed, 10 insertions(+), 24 deletions(-) diff --git a/examples/imgui_impl_vulkan.cpp b/examples/imgui_impl_vulkan.cpp index 52238ffba0ac..1cf5c789f174 100644 --- a/examples/imgui_impl_vulkan.cpp +++ b/examples/imgui_impl_vulkan.cpp @@ -693,7 +693,7 @@ static void ImGui_ImplVulkan_CreatePipelineLayout(VkDevice device, const VkAlloc check_vk_result(err); } -static void ImGui_ImplVulkan_CreatePipeline(VkDevice device, const VkAllocationCallbacks* allocator, VkPipelineCache pipelineCache, VkRenderPass renderPass, VkSampleCountFlagBits MSAASamples, VkPipeline *pipeline) +static void ImGui_ImplVulkan_CreatePipeline(VkDevice device, const VkAllocationCallbacks* allocator, VkPipelineCache pipelineCache, VkRenderPass renderPass, VkSampleCountFlagBits MSAASamples, VkPipeline* pipeline) { ImGui_ImplVulkan_CreateShaderModules(device, allocator); @@ -801,24 +801,6 @@ bool ImGui_ImplVulkan_CreateDeviceObjects() { ImGui_ImplVulkan_InitInfo* v = &g_VulkanInitInfo; VkResult err; - VkShaderModule vert_module; - VkShaderModule frag_module; - - // Create The Shader Modules: - { - VkShaderModuleCreateInfo vert_info = {}; - vert_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; - vert_info.codeSize = sizeof(__glsl_shader_vert_spv); - vert_info.pCode = (uint32_t*)__glsl_shader_vert_spv; - err = vkCreateShaderModule(v->Device, &vert_info, v->Allocator, &vert_module); - check_vk_result(err); - VkShaderModuleCreateInfo frag_info = {}; - frag_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; - frag_info.codeSize = sizeof(__glsl_shader_frag_spv); - frag_info.pCode = (uint32_t*)__glsl_shader_frag_spv; - err = vkCreateShaderModule(v->Device, &frag_info, v->Allocator, &frag_module); - check_vk_result(err); - } if (!g_FontSampler) { @@ -884,9 +866,6 @@ bool ImGui_ImplVulkan_CreateDeviceObjects() ImGui_ImplVulkan_CreatePipeline(v->Device, v->Allocator, v->PipelineCache, g_RenderPass, v->MSAASamples, &g_Pipeline); - vkDestroyShaderModule(v->Device, vert_module, v->Allocator); - vkDestroyShaderModule(v->Device, frag_module, v->Allocator); - return true; } @@ -911,6 +890,8 @@ void ImGui_ImplVulkan_DestroyDeviceObjects() ImGui_ImplVulkanH_DestroyAllViewportsRenderBuffers(v->Device, v->Allocator); ImGui_ImplVulkan_DestroyFontUploadObjects(); + if (g_ShaderModuleVert) { vkDestroyShaderModule(v->Device, g_ShaderModuleVert, v->Allocator); g_ShaderModuleVert = VK_NULL_HANDLE; } + if (g_ShaderModuleFrag) { vkDestroyShaderModule(v->Device, g_ShaderModuleFrag, v->Allocator); g_ShaderModuleFrag = VK_NULL_HANDLE; } if (g_FontView) { vkDestroyImageView(v->Device, g_FontView, v->Allocator); g_FontView = VK_NULL_HANDLE; } if (g_FontImage) { vkDestroyImage(v->Device, g_FontImage, v->Allocator); g_FontImage = VK_NULL_HANDLE; } if (g_FontMemory) { vkFreeMemory(v->Device, g_FontMemory, v->Allocator); g_FontMemory = VK_NULL_HANDLE; } @@ -1247,7 +1228,10 @@ void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, V info.pDependencies = &dependency; err = vkCreateRenderPass(device, &info, allocator, &wd->RenderPass); check_vk_result(err); - ImGui_ImplVulkan_CreatePipeline(device, allocator, VK_NULL_HANDLE, wd->RenderPass, VK_SAMPLE_COUNT_1_BIT, &wd->Pipeline); + + // We do not create a pipeline by default as this is also used by examples' main.cpp, + // but secondary viewport in multi-viewport mode may want to create one with: + //ImGui_ImplVulkan_CreatePipeline(device, allocator, VK_NULL_HANDLE, wd->RenderPass, VK_SAMPLE_COUNT_1_BIT, &wd->Pipeline); } // Create The Image Views @@ -1297,6 +1281,7 @@ void ImGui_ImplVulkanH_CreateOrResizeWindow(VkInstance instance, VkPhysicalDevic { (void)instance; ImGui_ImplVulkanH_CreateWindowSwapChain(physical_device, device, wd, allocator, width, height, min_image_count); + ImGui_ImplVulkan_CreatePipeline(device, allocator, VK_NULL_HANDLE, wd->RenderPass, VK_SAMPLE_COUNT_1_BIT, &wd->Pipeline); ImGui_ImplVulkanH_CreateWindowCommandBuffers(physical_device, device, wd, queue_family, allocator); } @@ -1314,6 +1299,7 @@ void ImGui_ImplVulkanH_DestroyWindow(VkInstance instance, VkDevice device, ImGui IM_FREE(wd->FrameSemaphores); wd->Frames = NULL; wd->FrameSemaphores = NULL; + vkDestroyPipeline(device, wd->Pipeline, allocator); vkDestroyRenderPass(device, wd->RenderPass, allocator); vkDestroySwapchainKHR(device, wd->Swapchain, allocator); vkDestroySurfaceKHR(instance, wd->Surface, allocator); diff --git a/examples/imgui_impl_vulkan.h b/examples/imgui_impl_vulkan.h index f20a72f41c64..31dbd01fd91e 100644 --- a/examples/imgui_impl_vulkan.h +++ b/examples/imgui_impl_vulkan.h @@ -109,7 +109,7 @@ struct ImGui_ImplVulkanH_Window VkSurfaceFormatKHR SurfaceFormat; VkPresentModeKHR PresentMode; VkRenderPass RenderPass; - VkPipeline Pipeline; // The window pipeline uses a different VkRenderPass than the user's + VkPipeline Pipeline; // The window pipeline may uses a different VkRenderPass than the one passed in ImGui_ImplVulkan_InitInfo bool ClearEnable; VkClearValue ClearValue; uint32_t FrameIndex; // Current frame being rendered to (0 <= FrameIndex < FrameInFlightCount) From e230ec5a01d7c8da5a61ba0a19bbb8a0441c717b Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 16 Sep 2020 11:05:02 +0200 Subject: [PATCH 649/828] Viewports, Backends: DX12: Make secondary viewport format match main viewport one (#3462) {@BeastLe9enD] --- examples/imgui_impl_dx12.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/imgui_impl_dx12.cpp b/examples/imgui_impl_dx12.cpp index 690f3a8bcfe5..fcda0d5f12bb 100644 --- a/examples/imgui_impl_dx12.cpp +++ b/examples/imgui_impl_dx12.cpp @@ -806,7 +806,7 @@ static void ImGui_ImplDX12_CreateWindow(ImGuiViewport* viewport) sd1.BufferCount = g_numFramesInFlight; sd1.Width = (UINT)viewport->Size.x; sd1.Height = (UINT)viewport->Size.y; - sd1.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + sd1.Format = g_RTVFormat; sd1.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; sd1.SampleDesc.Count = 1; sd1.SampleDesc.Quality = 0; From 6bc526676c45186ff8420fac3ecca71a3c3e87df Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 10 Sep 2020 11:02:40 +0200 Subject: [PATCH 650/828] Viewports: Comments, removed unnecessary use of ViewportFrontMostStampCount (the LastFrontMostStampCount is enough) --- imgui.cpp | 13 ++++++------- imgui_internal.h | 2 -- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index cb26785d1ad4..b8de6caf4e7a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3493,7 +3493,7 @@ void ImGui::UpdateMouseMovingWindowNewFrame() g.MouseViewport = moving_window->Viewport; // Clear the NoInput window flag set by the Viewport system - moving_window->Viewport->Flags &= ~ImGuiViewportFlags_NoInputs; + moving_window->Viewport->Flags &= ~ImGuiViewportFlags_NoInputs; // FIXME-VIEWPORT: Test engine managed to crash here because Viewport was NULL. ClearActiveID(); g.MovingWindow = NULL; @@ -11523,6 +11523,7 @@ void ImGui::UpdatePlatformWindows() // Update our implicit z-order knowledge of platform windows, which is used when the back-end cannot provide io.MouseHoveredViewport. // When setting Platform_GetWindowFocus, it is expected that the platform back-end can handle calls without crashing if it doesn't have data stored. + // FIXME-VIEWPORT: We should use this information to also set dear imgui-side focus, allowing us to handle os-level alt+tab. if (g.PlatformIO.Platform_GetWindowFocus != NULL) { ImGuiViewportP* focused_viewport = NULL; @@ -11533,12 +11534,10 @@ void ImGui::UpdatePlatformWindows() if (g.PlatformIO.Platform_GetWindowFocus(viewport)) focused_viewport = viewport; } - if (focused_viewport && g.PlatformLastFocusedViewport != focused_viewport->ID) - { - if (focused_viewport->LastFrontMostStampCount != g.ViewportFrontMostStampCount) - focused_viewport->LastFrontMostStampCount = ++g.ViewportFrontMostStampCount; - g.PlatformLastFocusedViewport = focused_viewport->ID; - } + + // Store a tag so we can infer z-order easily from all our windows + if (focused_viewport && focused_viewport->LastFrontMostStampCount != g.ViewportFrontMostStampCount) + focused_viewport->LastFrontMostStampCount = ++g.ViewportFrontMostStampCount; } } diff --git a/imgui_internal.h b/imgui_internal.h index 607ae3783d70..98c231ac48db 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1353,7 +1353,6 @@ struct ImGuiContext ImGuiViewportP* CurrentViewport; // We track changes of viewport (happening in Begin) so we can call Platform_OnChangedViewport() ImGuiViewportP* MouseViewport; ImGuiViewportP* MouseLastHoveredViewport; // Last known viewport that was hovered by mouse (even if we are not hovering any viewport any more) + honoring the _NoInputs flag. - ImGuiID PlatformLastFocusedViewport; // Record of last focused platform window/viewport, when this changes we stamp the viewport as front-most int ViewportFrontMostStampCount; // Every time the front-most window changes, we stamp its viewport with an incrementing counter // Gamepad/keyboard Navigation @@ -1558,7 +1557,6 @@ struct ImGuiContext CurrentDpiScale = 0.0f; CurrentViewport = NULL; MouseViewport = MouseLastHoveredViewport = NULL; - PlatformLastFocusedViewport = 0; ViewportFrontMostStampCount = 0; NavWindow = NULL; From 42575d4a9979b0c880c00670f0165beed680175c Mon Sep 17 00:00:00 2001 From: Sven Balzer <4653051+Kodokuna@users.noreply.github.com> Date: Thu, 17 Sep 2020 02:58:23 +0200 Subject: [PATCH 651/828] Viewports, Backends: Win32: Fix toggling of ImGuiViewportFlags_TopMost (#3477) --- examples/imgui_impl_win32.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index 878efeeb950c..07a0c5603505 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -660,13 +660,19 @@ static void ImGui_ImplWin32_UpdateWindow(ImGuiViewport* viewport) // Only reapply the flags that have been changed from our point of view (as other flags are being modified by Windows) if (data->DwStyle != new_style || data->DwExStyle != new_ex_style) { + // (Optional) Update TopMost state if it changed _after_ creation + bool top_most_changed = (data->DwExStyle & WS_EX_TOPMOST) != (new_ex_style & WS_EX_TOPMOST); + HWND insert_after = top_most_changed ? ((viewport->Flags & ImGuiViewportFlags_TopMost) ? HWND_TOPMOST : HWND_NOTOPMOST) : 0; + UINT swp_flag = top_most_changed ? 0 : SWP_NOZORDER; + + // Apply flags and position (since it is affected by flags) data->DwStyle = new_style; data->DwExStyle = new_ex_style; ::SetWindowLong(data->Hwnd, GWL_STYLE, data->DwStyle); ::SetWindowLong(data->Hwnd, GWL_EXSTYLE, data->DwExStyle); RECT rect = { (LONG)viewport->Pos.x, (LONG)viewport->Pos.y, (LONG)(viewport->Pos.x + viewport->Size.x), (LONG)(viewport->Pos.y + viewport->Size.y) }; ::AdjustWindowRectEx(&rect, data->DwStyle, FALSE, data->DwExStyle); // Client to Screen - ::SetWindowPos(data->Hwnd, NULL, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED); + ::SetWindowPos(data->Hwnd, insert_after, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, swp_flag | SWP_NOACTIVATE | SWP_FRAMECHANGED); ::ShowWindow(data->Hwnd, SW_SHOWNA); // This is necessary when we alter the style viewport->PlatformRequestMove = viewport->PlatformRequestResize = true; } From c49330fc521134651d3a7ea4c6524474e9b0861c Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 30 Sep 2020 14:11:22 +0200 Subject: [PATCH 652/828] Docking: Fix handling of WindowMenuButtonPosition == ImGuiDir_None in Docking Nodes. (#3499) --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 05969a5f443d..a83a947a869c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -13254,7 +13254,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w node->IsFocused = is_focused; const ImGuiDockNodeFlags node_flags = node->GetMergedFlags(); - const bool has_window_menu_button = (node_flags & ImGuiDockNodeFlags_NoWindowMenuButton) == 0; + const bool has_window_menu_button = (node_flags & ImGuiDockNodeFlags_NoWindowMenuButton) == 0 && (style.WindowMenuButtonPosition != ImGuiDir_None); const bool has_close_button = (node_flags & ImGuiDockNodeFlags_NoCloseButton) == 0; // In a dock node, the Collapse Button turns into the Window Menu button. From bae2240edad9e6517c1586951973338c9affcf50 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 14 Oct 2020 18:34:33 +0200 Subject: [PATCH 653/828] Tab Bar: Made it possible to append to an existing tab bar by calling BeginTabBar()/EndTabBar() again. # Conflicts: # imgui_widgets.cpp --- docs/CHANGELOG.txt | 3 +-- imgui_internal.h | 6 ++++-- imgui_widgets.cpp | 26 ++++++++++++++++++-------- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 4ccb5fa1688b..5eebda8481d4 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -120,12 +120,11 @@ Breaking Changes: IMGUI_DISABLE_OBSOLETE_FUNCTIONS in imconfig.h even temporarily to have a pass at finding and removing up old API calls, if any remaining. - Other Changes: +- Tab Bar: Made it possible to append to an existing tab bar by calling BeginTabBar()/EndTabBar() again. - Docs: Split examples/README.txt into docs/BACKENDS.md and docs/EXAMPLES.md improved them. - Docs: Consistently renamed all occurences of "binding" and "back-end" to "backend" in comments and docs. ->>>>>>> master ----------------------------------------------------------------------- diff --git a/imgui_internal.h b/imgui_internal.h index 5a9e9fc2893a..0d04d70e2581 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1960,7 +1960,8 @@ struct ImGuiTabBar int CurrFrameVisible; int PrevFrameVisible; ImRect BarRect; - float LastTabContentHeight; // Record the height of contents submitted below the tab bar + float CurrTabsContentsHeight; + float PrevTabsContentsHeight; // Record the height of contents submitted below the tab bar float WidthAllTabs; // Actual width of all tabs (locked during layout) float WidthAllTabsIdeal; // Ideal width if all tabs were visible and not clipped float ScrollingAnim; @@ -1976,8 +1977,9 @@ struct ImGuiTabBar bool WantLayout; bool VisibleTabWasSubmitted; bool TabsAddedNew; // Set to true when a new tab item or button has been added to the tab bar during last frame - short LastTabItemIdx; // Index of last BeginTabItem() tab for use by EndTabItem() + short LastTabItemIdx; // Index of last BeginTabItem() tab for use by EndTabItem() ImVec2 FramePadding; // style.FramePadding locked at the time of BeginTabBar() + ImVec2 TabsContentsMin; ImGuiTextBuffer TabsNames; // For non-docking tab bar we re-append names in a contiguous buffer. ImGuiTabBar(); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index f6813593b23b..d2f52a108932 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6837,7 +6837,7 @@ ImGuiTabBar::ImGuiTabBar() ID = 0; SelectedTabId = NextSelectedTabId = VisibleTabId = 0; CurrFrameVisible = PrevFrameVisible = -1; - LastTabContentHeight = 0.0f; + CurrTabsContentsHeight = PrevTabsContentsHeight = 0.0f; WidthAllTabs = WidthAllTabsIdeal = 0.0f; ScrollingAnim = ScrollingTarget = ScrollingTargetDistToVisibility = ScrollingSpeed = 0.0f; ScrollingRectMinX = ScrollingRectMaxX = 0.0f; @@ -6909,10 +6909,10 @@ bool ImGui::BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& tab_bar_bb, ImG g.CurrentTabBarStack.push_back(GetTabBarRefFromTabBar(tab_bar)); g.CurrentTabBar = tab_bar; + // Append with multiple BeginTabBar()/EndTabBar() pairs. if (tab_bar->CurrFrameVisible == g.FrameCount) { - //IMGUI_DEBUG_LOG("BeginTabBarEx already called this frame\n", g.FrameCount); - //IM_ASSERT(0); + window->DC.CursorPos = tab_bar->TabsContentsMin; return true; } @@ -6931,12 +6931,15 @@ bool ImGui::BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& tab_bar_bb, ImG tab_bar->WantLayout = true; // Layout will be done on the first call to ItemTab() tab_bar->PrevFrameVisible = tab_bar->CurrFrameVisible; tab_bar->CurrFrameVisible = g.FrameCount; + tab_bar->PrevTabsContentsHeight = tab_bar->CurrTabsContentsHeight; + tab_bar->CurrTabsContentsHeight = 0.0f; tab_bar->FramePadding = g.Style.FramePadding; tab_bar->TabsActiveCount = 0; // Set cursor pos in a way which only be used in the off-chance the user erroneously submits item before BeginTabItem(): items will overlap - window->DC.CursorPos.x = tab_bar->BarRect.Min.x; - window->DC.CursorPos.y = tab_bar->BarRect.Max.y + g.Style.ItemSpacing.y; + tab_bar->TabsContentsMin.x = tab_bar->BarRect.Min.x; + tab_bar->TabsContentsMin.y = tab_bar->BarRect.Max.y + g.Style.ItemSpacing.y; + window->DC.CursorPos = tab_bar->TabsContentsMin; // Draw separator const ImU32 col = GetColorU32((flags & ImGuiTabBarFlags_IsFocused) ? ImGuiCol_TabActive : ImGuiCol_TabUnfocusedActive); @@ -6969,15 +6972,22 @@ void ImGui::EndTabBar() IM_ASSERT_USER_ERROR(tab_bar != NULL, "Mismatched BeginTabBar()/EndTabBar()!"); return; } - if (tab_bar->WantLayout) // Fallback in case no TabItem have been submitted + + // Fallback in case no TabItem have been submitted + if (tab_bar->WantLayout) TabBarLayout(tab_bar); // Restore the last visible height if no tab is visible, this reduce vertical flicker/movement when a tabs gets removed without calling SetTabItemClosed(). const bool tab_bar_appearing = (tab_bar->PrevFrameVisible + 1 < g.FrameCount); if (tab_bar->VisibleTabWasSubmitted || tab_bar->VisibleTabId == 0 || tab_bar_appearing) - tab_bar->LastTabContentHeight = ImMax(window->DC.CursorPos.y - tab_bar->BarRect.Max.y, 0.0f); + { + tab_bar->CurrTabsContentsHeight = ImMax(window->DC.CursorPos.y - tab_bar->BarRect.Max.y, tab_bar->CurrTabsContentsHeight); + window->DC.CursorPos.y = tab_bar->BarRect.Max.y + tab_bar->CurrTabsContentsHeight; + } else - window->DC.CursorPos.y = tab_bar->BarRect.Max.y + tab_bar->LastTabContentHeight; + { + window->DC.CursorPos.y = tab_bar->BarRect.Max.y + tab_bar->PrevTabsContentsHeight; + } if ((tab_bar->Flags & ImGuiTabBarFlags_DockNode) == 0) PopID(); From b26f1530b753745d37072078900f2e1d10c68e67 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 14 Oct 2020 19:11:00 +0200 Subject: [PATCH 654/828] Internals: Docking, Tab Bar: Add DockNodeBeginAmendTabBar() and work toward making hybrid dock node with windows tab bars somehow work (not done). --- imgui.cpp | 46 ++++++++++++++++++++++++++++++++++++++-------- imgui_internal.h | 2 ++ imgui_widgets.cpp | 30 ++++++++++++++++-------------- 3 files changed, 56 insertions(+), 22 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index b9ebd0d5c18d..e519b6d53cc2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -12510,6 +12510,8 @@ bool ImGui::DockContextCalcDropPosForDocking(ImGuiWindow* target, ImGuiDockNode* // - DockNodeStartMouseMovingWindow() // - DockNodeUpdate() // - DockNodeUpdateWindowMenu() +// - DockNodeBeginAmendTabBar() +// - DockNodeEndAmendTabBar() // - DockNodeUpdateTabBar() // - DockNodeAddTabBar() // - DockNodeRemoveTabBar() @@ -13231,7 +13233,8 @@ static ImGuiID ImGui::DockNodeUpdateWindowMenu(ImGuiDockNode* node, ImGuiTabBar* for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++) { ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; - IM_ASSERT(tab->Window != NULL); + if (tab->Window == NULL) + continue; if (Selectable(tab->Window->Name, tab->ID == tab_bar->SelectedTabId)) ret_tab_id = tab->ID; SameLine(); @@ -13243,6 +13246,26 @@ static ImGuiID ImGui::DockNodeUpdateWindowMenu(ImGuiDockNode* node, ImGuiTabBar* return ret_tab_id; } +// User helper to append/amend into a dock node tab bar. Most commonly used to add e.g. a "+" button. +bool ImGui::DockNodeBeginAmendTabBar(ImGuiDockNode* node) +{ + if (node->TabBar == NULL || node->HostWindow == NULL) + return false; + Begin(node->HostWindow->Name); + PushOverrideID(node->ID); + bool ret = BeginTabBarEx(node->TabBar, node->TabBar->BarRect, node->TabBar->Flags, node); + IM_ASSERT(ret); + return true; +} + +void ImGui::DockNodeEndAmendTabBar() +{ + EndTabBar(); + PopID(); + End(); +} + +// Submit the tab bar corresponding to a dock node and various housekeeping details. static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_window) { ImGuiContext& g = *GImGui; @@ -13446,7 +13469,14 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w if (g.HoveredId == 0 || g.HoveredId == title_bar_id || g.ActiveId == title_bar_id) { bool held; - ButtonBehavior(title_bar_rect, title_bar_id, NULL, &held); + ButtonBehavior(title_bar_rect, title_bar_id, NULL, &held, ImGuiButtonFlags_AllowItemOverlap); + if (g.HoveredId == title_bar_id) + { + // ImGuiButtonFlags_AllowItemOverlap + SetItemAllowOverlap() required for appending into dock node tab bar, + // otherwise dragging window will steal HoveredId and amended tabs cannot get them. + host_window->DC.LastItemId = title_bar_id; + SetItemAllowOverlap(); + } if (held) { if (IsMouseClicked(0)) @@ -15847,13 +15877,13 @@ void ImGui::ShowMetricsWindow(bool* p_open) const char* buf_end = buf + IM_ARRAYSIZE(buf); const bool is_active = (tab_bar->PrevFrameVisible >= ImGui::GetFrameCount() - 2); p += ImFormatString(p, buf_end - p, "Tab Bar 0x%08X (%d tabs)%s", tab_bar->ID, tab_bar->Tabs.Size, is_active ? "" : " *Inactive*"); - if (tab_bar->Flags & ImGuiTabBarFlags_DockNode) + p += ImFormatString(p, buf_end - p, " { "); + for (int tab_n = 0; tab_n < ImMin(tab_bar->Tabs.Size, 3); tab_n++) { - p += ImFormatString(p, buf_end - p, " { "); - for (int tab_n = 0; tab_n < ImMin(tab_bar->Tabs.Size, 3); tab_n++) - p += ImFormatString(p, buf_end - p, "%s'%s'", tab_n > 0 ? ", " : "", tab_bar->Tabs[tab_n].Window->Name); - p += ImFormatString(p, buf_end - p, (tab_bar->Tabs.Size > 3) ? " ... }" : " } "); + ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; + p += ImFormatString(p, buf_end - p, "%s'%s'", tab_n > 0 ? ", " : "", (tab->Window || tab->NameOffset != -1) ? tab_bar->GetTabName(tab) : "???"); } + p += ImFormatString(p, buf_end - p, (tab_bar->Tabs.Size > 3) ? " ... }" : " } "); if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); } bool open = ImGui::TreeNode(tab_bar, "%s", buf); if (!is_active) { PopStyleColor(); } @@ -15872,7 +15902,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::PushID(tab); if (ImGui::SmallButton("<")) { TabBarQueueReorder(tab_bar, tab, -1); } ImGui::SameLine(0, 2); if (ImGui::SmallButton(">")) { TabBarQueueReorder(tab_bar, tab, +1); } ImGui::SameLine(); - ImGui::Text("%02d%c Tab 0x%08X '%s' Offset: %.1f, Width: %.1f/%.1f", tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID, (tab->Window || tab->NameOffset != -1) ? tab_bar->GetTabName(tab) : "", tab->Offset, tab->Width, tab->ContentWidth); + ImGui::Text("%02d%c Tab 0x%08X '%s' Offset: %.1f, Width: %.1f/%.1f", tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID, (tab->Window || tab->NameOffset != -1) ? tab_bar->GetTabName(tab) : "???", tab->Offset, tab->Width, tab->ContentWidth); ImGui::PopID(); } ImGui::TreePop(); diff --git a/imgui_internal.h b/imgui_internal.h index 0d04d70e2581..e9398f58ecd6 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2170,6 +2170,8 @@ namespace ImGui IMGUI_API void DockContextQueueUndockWindow(ImGuiContext* ctx, ImGuiWindow* window); IMGUI_API void DockContextQueueUndockNode(ImGuiContext* ctx, ImGuiDockNode* node); IMGUI_API bool DockContextCalcDropPosForDocking(ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, bool split_outer, ImVec2* out_pos); + IMGUI_API bool DockNodeBeginAmendTabBar(ImGuiDockNode* node); + IMGUI_API void DockNodeEndAmendTabBar(); inline ImGuiDockNode* DockNodeGetRootNode(ImGuiDockNode* node) { while (node->ParentNode) node = node->ParentNode; return node; } inline int DockNodeGetDepth(const ImGuiDockNode* node) { int depth = 0; while (node->ParentNode) { node = node->ParentNode; depth++; } return depth; } inline ImGuiDockNode* GetWindowDockNode() { ImGuiContext& g = *GImGui; return g.CurrentWindow->DockNode; } diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index d2f52a108932..cc87c718155e 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6824,7 +6824,7 @@ struct ImGuiTabBarSection namespace ImGui { static void TabBarLayout(ImGuiTabBar* tab_bar); - static ImU32 TabBarCalcTabID(ImGuiTabBar* tab_bar, const char* label); + static ImU32 TabBarCalcTabID(ImGuiTabBar* tab_bar, const char* label, ImGuiWindow* docked_window); static float TabBarCalcMaxTabWidth(); static float TabBarScrollClamp(ImGuiTabBar* tab_bar, float scrolling); static void TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab, ImGuiTabBarSection* sections); @@ -6918,7 +6918,7 @@ bool ImGui::BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& tab_bar_bb, ImG // Ensure correct ordering when toggling ImGuiTabBarFlags_Reorderable flag, or when a new tab was added while being not reorderable if ((flags & ImGuiTabBarFlags_Reorderable) != (tab_bar->Flags & ImGuiTabBarFlags_Reorderable) || (tab_bar->TabsAddedNew && !(flags & ImGuiTabBarFlags_Reorderable))) - if (tab_bar->Tabs.Size > 1 && (flags & ImGuiTabBarFlags_DockNode) == 0) + if (tab_bar->Tabs.Size > 1 && (flags & ImGuiTabBarFlags_DockNode) == 0) // FIXME: TabBar with DockNode can now be hybrid ImQsort(tab_bar->Tabs.Data, tab_bar->Tabs.Size, sizeof(ImGuiTabItem), TabItemComparerByBeginOrder); tab_bar->TabsAddedNew = false; @@ -7177,6 +7177,7 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) { ImGuiTabItem* tab = &tab_bar->Tabs[section_tab_index + tab_n]; tab->Offset = tab_offset; + tab->NameOffset = -1; tab_offset += tab->Width + (tab_n < section->TabCount - 1 ? g.Style.ItemInnerSpacing.x : 0.0f); } tab_bar->WidthAllTabs += ImMax(section->Width + section->Spacing, 0.0f); @@ -7184,6 +7185,9 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) section_tab_index += section->TabCount; } + // Clear name buffers + tab_bar->TabsNames.Buf.resize(0); + // If we have lost the selected tab, select the next most recently active one if (found_selected_tab_id == false) tab_bar->SelectedTabId = 0; @@ -7220,21 +7224,18 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) tab_bar->ScrollingRectMinX = tab_bar->BarRect.Min.x + sections[0].Width + sections[0].Spacing; tab_bar->ScrollingRectMaxX = tab_bar->BarRect.Max.x - sections[2].Width - sections[1].Spacing; - // Clear name buffers - if ((tab_bar->Flags & ImGuiTabBarFlags_DockNode) == 0) - tab_bar->TabsNames.Buf.resize(0); - // Actual layout in host window (we don't do it in BeginTabBar() so as not to waste an extra frame) ImGuiWindow* window = g.CurrentWindow; window->DC.CursorPos = tab_bar->BarRect.Min; ItemSize(ImVec2(tab_bar->WidthAllTabsIdeal, tab_bar->BarRect.GetHeight()), tab_bar->FramePadding.y); } -// Dockables uses Name/ID in the global namespace. Non-dockable items use the ID stack. -static ImU32 ImGui::TabBarCalcTabID(ImGuiTabBar* tab_bar, const char* label) +// Dockable uses Name/ID in the global namespace. Non-dockable items use the ID stack. +static ImU32 ImGui::TabBarCalcTabID(ImGuiTabBar* tab_bar, const char* label, ImGuiWindow* docked_window) { - if (tab_bar->Flags & ImGuiTabBarFlags_DockNode) + if (docked_window != NULL) { + IM_ASSERT(tab_bar->Flags & ImGuiTabBarFlags_DockNode); ImGuiID id = ImHashStr(label); KeepAliveID(id); return id; @@ -7581,7 +7582,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, return false; const ImGuiStyle& style = g.Style; - const ImGuiID id = TabBarCalcTabID(tab_bar, label); + const ImGuiID id = TabBarCalcTabID(tab_bar, label, docked_window); // If the user called us with *p_open == false, we early out and don't render. // We make a call to ItemAdd() so that attempts to use a contextual popup menu with an implicit ID won't use an older ID. @@ -7631,9 +7632,10 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, tab->Window = docked_window; // Append name with zero-terminator - if (tab_bar->Flags & ImGuiTabBarFlags_DockNode) + // (regular tabs are permitted in a DockNode tab bar, but window tabs not permitted in a non-DockNode tab bar) + if (tab->Window != NULL) { - IM_ASSERT(tab->Window != NULL); + IM_ASSERT(tab_bar->Flags & ImGuiTabBarFlags_DockNode); tab->NameOffset = -1; } else @@ -7851,7 +7853,7 @@ void ImGui::SetTabItemClosed(const char* label) if (is_within_manual_tab_bar) { ImGuiTabBar* tab_bar = g.CurrentTabBar; - ImGuiID tab_id = TabBarCalcTabID(tab_bar, label); + ImGuiID tab_id = TabBarCalcTabID(tab_bar, label, NULL); if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_id)) tab->WantClose = true; // Will be processed by next call to TabBarLayout() } @@ -7860,7 +7862,7 @@ void ImGui::SetTabItemClosed(const char* label) if (window->DockIsActive) if (ImGuiDockNode* node = window->DockNode) { - ImGuiID tab_id = TabBarCalcTabID(node->TabBar, label); + ImGuiID tab_id = TabBarCalcTabID(node->TabBar, label, window); TabBarRemoveTab(node->TabBar, tab_id); window->DockTabWantClose = true; } From d3a80d9f1b543aa89d35546ae6585d696e2eadfd Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 15 Oct 2020 14:54:41 +0200 Subject: [PATCH 655/828] Internals: Docking: More fixes to make DockNodeBeginAmendTabBar() viable (probably some issues left) --- imgui.cpp | 60 +++++++++++++++++++++++++++-------------------- imgui_widgets.cpp | 4 ++-- 2 files changed, 37 insertions(+), 27 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index e519b6d53cc2..dfd3f289f962 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6747,7 +6747,7 @@ void ImGui::End() IM_ASSERT(g.CurrentWindowStack.Size > 0); // Error checking: verify that user doesn't directly call End() on a child window. - if ((window->Flags & ImGuiWindowFlags_ChildWindow) && !window->DockIsActive) + if ((window->Flags & ImGuiWindowFlags_ChildWindow) && !(window->Flags & ImGuiWindowFlags_DockNodeHost) && !window->DockIsActive) IM_ASSERT_USER_ERROR(g.WithinEndChild, "Must call EndChild() and not End()!"); // Close anything that is open @@ -12705,7 +12705,7 @@ static void ImGui::DockNodeMoveWindows(ImGuiDockNode* dst_node, ImGuiDockNode* s IM_ASSERT(src_node && dst_node && dst_node != src_node); ImGuiTabBar* src_tab_bar = src_node->TabBar; if (src_tab_bar != NULL) - IM_ASSERT(src_node->Windows.Size == src_node->TabBar->Tabs.Size); + IM_ASSERT(src_node->Windows.Size <= src_node->TabBar->Tabs.Size); // If the dst_node is empty we can just move the entire tab bar (to preserve selection, scrolling, etc.) bool move_tab_bar = (src_tab_bar != NULL) && (dst_node->TabBar == NULL); @@ -12717,10 +12717,13 @@ static void ImGui::DockNodeMoveWindows(ImGuiDockNode* dst_node, ImGuiDockNode* s for (int n = 0; n < src_node->Windows.Size; n++) { - ImGuiWindow* window = src_tab_bar ? src_tab_bar->Tabs[n].Window : src_node->Windows[n]; - window->DockNode = NULL; - window->DockIsActive = false; - DockNodeAddWindow(dst_node, window, move_tab_bar ? false : true); + // DockNode's TabBar may have non-window Tabs manually appended by user + if (ImGuiWindow* window = src_tab_bar ? src_tab_bar->Tabs[n].Window : src_node->Windows[n]) + { + window->DockNode = NULL; + window->DockIsActive = false; + DockNodeAddWindow(dst_node, window, move_tab_bar ? false : true); + } } src_node->Windows.clear(); @@ -13233,9 +13236,9 @@ static ImGuiID ImGui::DockNodeUpdateWindowMenu(ImGuiDockNode* node, ImGuiTabBar* for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++) { ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; - if (tab->Window == NULL) + if (tab->Flags & ImGuiTabItemFlags_Button) continue; - if (Selectable(tab->Window->Name, tab->ID == tab_bar->SelectedTabId)) + if (Selectable(tab_bar->GetTabName(tab), tab->ID == tab_bar->SelectedTabId)) ret_tab_id = tab->ID; SameLine(); Text(" "); @@ -13251,6 +13254,8 @@ bool ImGui::DockNodeBeginAmendTabBar(ImGuiDockNode* node) { if (node->TabBar == NULL || node->HostWindow == NULL) return false; + if (node->SharedFlags & ImGuiDockNodeFlags_KeepAliveOnly) + return false; Begin(node->HostWindow->Name); PushOverrideID(node->ID); bool ret = BeginTabBarEx(node->TabBar, node->TabBar->BarRect, node->TabBar->Flags, node); @@ -13484,7 +13489,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w // Forward moving request to selected window if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_bar->SelectedTabId)) - StartMouseMovingWindowOrNode(tab->Window, node, false); + StartMouseMovingWindowOrNode(tab->Window ? tab->Window : node->HostWindow, node, false); } } @@ -13500,10 +13505,11 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w // Apply navigation focus if (focus_tab_id != 0) if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, focus_tab_id)) - { - FocusWindow(tab->Window); - NavInitWindow(tab->Window, false); - } + if (tab->Window) + { + FocusWindow(tab->Window); + NavInitWindow(tab->Window, false); + } EndTabBar(); PopID(); @@ -13819,25 +13825,29 @@ static void ImGui::DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDock // Draw tab shape/label preview (payload may be a loose window or a host window carrying multiple tabbed windows) if (root_payload->DockNodeAsHost) - IM_ASSERT(root_payload->DockNodeAsHost->Windows.Size == root_payload->DockNodeAsHost->TabBar->Tabs.Size); - const int payload_count = root_payload->DockNodeAsHost ? root_payload->DockNodeAsHost->TabBar->Tabs.Size : 1; + IM_ASSERT(root_payload->DockNodeAsHost->Windows.Size <= root_payload->DockNodeAsHost->TabBar->Tabs.Size); + ImGuiTabBar* tab_bar_with_payload = root_payload->DockNodeAsHost ? root_payload->DockNodeAsHost->TabBar : NULL; + const int payload_count = tab_bar_with_payload ? tab_bar_with_payload->Tabs.Size : 1; for (int payload_n = 0; payload_n < payload_count; payload_n++) { - // Calculate the tab bounding box for each payload window - ImGuiWindow* payload = root_payload->DockNodeAsHost ? root_payload->DockNodeAsHost->TabBar->Tabs[payload_n].Window : root_payload; - if (!DockNodeIsDropAllowedOne(payload, host_window)) + // DockNode's TabBar may have non-window Tabs manually appended by user + ImGuiWindow* payload_window = tab_bar_with_payload ? tab_bar_with_payload->Tabs[payload_n].Window : root_payload; + if (tab_bar_with_payload && payload_window == NULL) + continue; + if (!DockNodeIsDropAllowedOne(payload_window, host_window)) continue; - ImVec2 tab_size = TabItemCalcSize(payload->Name, payload->HasCloseButton); + // Calculate the tab bounding box for each payload window + ImVec2 tab_size = TabItemCalcSize(payload_window->Name, payload_window->HasCloseButton); ImRect tab_bb(tab_pos.x, tab_pos.y, tab_pos.x + tab_size.x, tab_pos.y + tab_size.y); tab_pos.x += tab_size.x + g.Style.ItemInnerSpacing.x; for (int overlay_n = 0; overlay_n < overlay_draw_lists_count; overlay_n++) { - ImGuiTabItemFlags tab_flags = ImGuiTabItemFlags_Preview | ((payload->Flags & ImGuiWindowFlags_UnsavedDocument) ? ImGuiTabItemFlags_UnsavedDocument : 0); + ImGuiTabItemFlags tab_flags = ImGuiTabItemFlags_Preview | ((payload_window->Flags & ImGuiWindowFlags_UnsavedDocument) ? ImGuiTabItemFlags_UnsavedDocument : 0); if (!tab_bar_rect.Contains(tab_bb)) overlay_draw_lists[overlay_n]->PushClipRect(tab_bar_rect.Min, tab_bar_rect.Max); TabItemBackground(overlay_draw_lists[overlay_n], tab_bb, tab_flags, overlay_col_tabs); - TabItemLabelAndCloseButton(overlay_draw_lists[overlay_n], tab_bb, tab_flags, g.Style.FramePadding, payload->Name, 0, 0, false); + TabItemLabelAndCloseButton(overlay_draw_lists[overlay_n], tab_bb, tab_flags, g.Style.FramePadding, payload_window->Name, 0, 0, false); if (!tab_bar_rect.Contains(tab_bb)) overlay_draw_lists[overlay_n]->PopClipRect(); } @@ -14358,10 +14368,8 @@ void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags fla // Update the node DockNodeUpdate(node); - g.WithinEndChild = true; End(); ItemSize(size); - g.WithinEndChild = false; } // Tips: Use with ImGuiDockNodeFlags_PassthruCentralNode! @@ -15881,7 +15889,8 @@ void ImGui::ShowMetricsWindow(bool* p_open) for (int tab_n = 0; tab_n < ImMin(tab_bar->Tabs.Size, 3); tab_n++) { ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; - p += ImFormatString(p, buf_end - p, "%s'%s'", tab_n > 0 ? ", " : "", (tab->Window || tab->NameOffset != -1) ? tab_bar->GetTabName(tab) : "???"); + p += ImFormatString(p, buf_end - p, "%s'%s'", + tab_n > 0 ? ", " : "", (tab->Window || tab->NameOffset != -1) ? tab_bar->GetTabName(tab) : "???"); } p += ImFormatString(p, buf_end - p, (tab_bar->Tabs.Size > 3) ? " ... }" : " } "); if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); } @@ -15902,7 +15911,8 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::PushID(tab); if (ImGui::SmallButton("<")) { TabBarQueueReorder(tab_bar, tab, -1); } ImGui::SameLine(0, 2); if (ImGui::SmallButton(">")) { TabBarQueueReorder(tab_bar, tab, +1); } ImGui::SameLine(); - ImGui::Text("%02d%c Tab 0x%08X '%s' Offset: %.1f, Width: %.1f/%.1f", tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID, (tab->Window || tab->NameOffset != -1) ? tab_bar->GetTabName(tab) : "???", tab->Offset, tab->Width, tab->ContentWidth); + ImGui::Text("%02d%c Tab 0x%08X '%s' Offset: %.1f, Width: %.1f/%.1f", + tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID, (tab->Window || tab->NameOffset != -1) ? tab_bar->GetTabName(tab) : "???", tab->Offset, tab->Width, tab->ContentWidth); ImGui::PopID(); } ImGui::TreePop(); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index cc87c718155e..5d5d31eed955 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7099,7 +7099,7 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) // Additionally, when using TabBarAddTab() to manipulate tab bar order we occasionally insert new tabs that don't have a width yet, // and we cannot wait for the next BeginTabItem() call. We cannot compute this width within TabBarAddTab() because font size depends on the active window. const char* tab_name = tab_bar->GetTabName(tab); - const bool has_close_button = tab->Window ? tab->Window->HasCloseButton : ((tab->Flags & ImGuiTabItemFlags_NoCloseButton) == 0); + const bool has_close_button = (tab->Flags & ImGuiTabItemFlags_NoCloseButton) == 0; tab->ContentWidth = TabItemCalcSize(tab_name, has_close_button).x; int section_n = (tab->Flags & ImGuiTabItemFlags_Leading) ? 0 : (tab->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1; @@ -7524,7 +7524,7 @@ bool ImGui::BeginTabItem(const char* label, bool* p_open, ImGuiTabItemFlags f IM_ASSERT_USER_ERROR(tab_bar, "Needs to be called between BeginTabBar() and EndTabBar()!"); return false; } - IM_ASSERT(!(flags & ImGuiTabItemFlags_Button)); // BeginTabItem() Can't be used with button flags, use TabItemButton() instead! + IM_ASSERT((flags & ImGuiTabItemFlags_Button) == 0); // BeginTabItem() Can't be used with button flags, use TabItemButton() instead! bool ret = TabItemEx(tab_bar, label, p_open, flags, NULL); if (ret && !(flags & ImGuiTabItemFlags_NoPushId)) From cecf6b4209b4494ade5c54eba8f645030e9a5baa Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 19 Nov 2020 16:07:07 +0100 Subject: [PATCH 656/828] Viewports: made standalone modals appear in taskbar + new window perform z-check before merging in main host viewport. (#3511, #1542) This should fix a good amount of "lost modal" problems, however it is still possible to loose a modal in a host viewport if secondary viewports are configured as children above the host. --- imgui.cpp | 16 ++++++++++------ imgui.h | 2 +- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 7165637abe5c..e86a4968b57b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6227,10 +6227,13 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Update common viewport flags const ImGuiViewportFlags viewport_flags_to_clear = ImGuiViewportFlags_TopMost | ImGuiViewportFlags_NoTaskBarIcon | ImGuiViewportFlags_NoDecoration | ImGuiViewportFlags_NoRendererClear; ImGuiViewportFlags viewport_flags = window->Viewport->Flags & ~viewport_flags_to_clear; - const bool is_short_lived_floating_window = (flags & (ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) != 0; + const bool is_modal = (flags & ImGuiWindowFlags_Modal) != 0; + const bool is_short_lived_floating_window = (flags & (ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) != 0 && !is_modal; if (flags & ImGuiWindowFlags_Tooltip) viewport_flags |= ImGuiViewportFlags_TopMost; - if (g.IO.ConfigViewportsNoTaskBarIcon || is_short_lived_floating_window) + //if (flags & ImGuiWindowFlags_Modal) + // viewport_flags |= ImGuiViewportFlags_TopMost; // Not correct because other popups can be stack above a modal? + if ((g.IO.ConfigViewportsNoTaskBarIcon || is_short_lived_floating_window) && !is_modal) viewport_flags |= ImGuiViewportFlags_NoTaskBarIcon; if (g.IO.ConfigViewportsNoDecoration || is_short_lived_floating_window) viewport_flags |= ImGuiViewportFlags_NoDecoration; @@ -6239,7 +6242,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // won't steal the OS focus away from their parent window (which may be reflected in OS the title bar decoration). // Setting _NoFocusOnClick would technically prevent us from bringing back to front in case they are being covered by an OS window from a different app, // but it shouldn't be much of a problem considering those are already popups that are closed when clicking elsewhere. - if (is_short_lived_floating_window && (flags & ImGuiWindowFlags_Modal) == 0) + if (is_short_lived_floating_window && !is_modal) viewport_flags |= ImGuiViewportFlags_NoFocusOnAppearing | ImGuiViewportFlags_NoFocusOnClick; // We can overwrite viewport flags using ImGuiWindowClass (advanced users) @@ -11426,10 +11429,11 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) UpdateTryMergeWindowIntoHostViewports(window); } - // Fallback to default viewport + // Fallback: merge in default viewport if z-order matches, otherwise create a new viewport if (window->Viewport == NULL) - window->Viewport = main_viewport; - + if (!UpdateTryMergeWindowIntoHostViewport(window, main_viewport)) + window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_None); + // Mark window as allowed to protrude outside of its viewport and into the current monitor if (!lock_viewport) { diff --git a/imgui.h b/imgui.h index b40a2c1495f6..6a54d30f6077 100644 --- a/imgui.h +++ b/imgui.h @@ -1609,7 +1609,7 @@ struct ImGuiIO // Viewport options (when ImGuiConfigFlags_ViewportsEnable is set) bool ConfigViewportsNoAutoMerge; // = false; // Set to make all floating imgui windows always create their own viewport. Otherwise, they are merged into the main host viewports when overlapping it. May also set ImGuiViewportFlags_NoAutoMerge on individual viewport. bool ConfigViewportsNoTaskBarIcon; // = false // Disable default OS task bar icon flag for secondary viewports. When a viewport doesn't want a task bar icon, ImGuiViewportFlags_NoTaskBarIcon will be set on it. - bool ConfigViewportsNoDecoration; // = true // [BETA] Disable default OS window decoration flag for secondary viewports. When a viewport doesn't want window decorations, ImGuiViewportFlags_NoDecoration will be set on it. Enabling decoration can create subsequent issues at OS levels (e.g. minimum window size). + bool ConfigViewportsNoDecoration; // = true // Disable default OS window decoration flag for secondary viewports. When a viewport doesn't want window decorations, ImGuiViewportFlags_NoDecoration will be set on it. Enabling decoration can create subsequent issues at OS levels (e.g. minimum window size). bool ConfigViewportsNoDefaultParent; // = false // Disable default OS parenting to main viewport for secondary viewports. By default, viewports are marked with ParentViewportId = , expecting the platform backend to setup a parent/child relationship between the OS windows (some backend may ignore this). Set to true if you want the default to be 0, then all viewports will be top-level OS windows. // Miscellaneous options From 3dcbcd8bf0d0331a7bc870bbf725145e6beed069 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 19 Nov 2020 16:58:14 +0100 Subject: [PATCH 657/828] Internals: added IsWindowAbove() for use for modal/viewport bugfix. --- imgui.cpp | 26 +++++++++++++++----------- imgui_internal.h | 1 + 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index e86a4968b57b..83ed42f7f218 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3571,17 +3571,7 @@ void ImGui::UpdateMouseMovingWindowEndFrame() // Find the top-most window between HoveredWindow and the top-most Modal Window. // This is where we can trim the popup stack. ImGuiWindow* modal = GetTopMostPopupModal(); - bool hovered_window_above_modal = false; - if (modal == NULL) - hovered_window_above_modal = true; - for (int i = g.Windows.Size - 1; i >= 0 && hovered_window_above_modal == false; i--) - { - ImGuiWindow* window = g.Windows[i]; - if (window == modal) - break; - if (window == g.HoveredWindow) - hovered_window_above_modal = true; - } + bool hovered_window_above_modal = g.HoveredWindow && IsWindowAbove(g.HoveredWindow, modal); ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal, true); } } @@ -6956,6 +6946,20 @@ bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent) return false; } +bool ImGui::IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below) +{ + ImGuiContext& g = *GImGui; + for (int i = g.Windows.Size - 1; i >= 0; i--) + { + ImGuiWindow* candidate_window = g.Windows[i]; + if (candidate_window == potential_above) + return true; + if (candidate_window == potential_below) + return false; + } + return false; +} + bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags) { IM_ASSERT((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0); // Flags not supported by this function diff --git a/imgui_internal.h b/imgui_internal.h index f9ff80981130..490fc2f3cdc4 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2026,6 +2026,7 @@ namespace ImGui IMGUI_API void UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window); IMGUI_API ImVec2 CalcWindowExpectedSize(ImGuiWindow* window); IMGUI_API bool IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent); + IMGUI_API bool IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below); IMGUI_API bool IsWindowNavFocusable(ImGuiWindow* window); IMGUI_API ImRect GetWindowAllowedExtentRect(ImGuiWindow* window); IMGUI_API void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond = 0); From 4da92b89ed5861d4630d4ed2c3c702ad962b2202 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 19 Nov 2020 17:03:56 +0100 Subject: [PATCH 658/828] Viewports: fix incorrect whitening of popups above a modal if both use their own viewport + fix pvs warning. --- imgui.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 83ed42f7f218..11cb361a3467 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4385,6 +4385,8 @@ static void ImGui::EndFrameDrawDimmedBackgrounds() continue; if (g.NavWindowingTargetAnim && viewport == g.NavWindowingTargetAnim->Viewport) continue; + if (viewport->Window && modal_window && IsWindowAbove(viewport->Window, modal_window)) + continue; ImDrawList* draw_list = GetForegroundDrawList(viewport); const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio); draw_list->AddRectFilled(viewport->Pos, viewport->Pos + viewport->Size, dim_bg_col); @@ -6218,16 +6220,20 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) const ImGuiViewportFlags viewport_flags_to_clear = ImGuiViewportFlags_TopMost | ImGuiViewportFlags_NoTaskBarIcon | ImGuiViewportFlags_NoDecoration | ImGuiViewportFlags_NoRendererClear; ImGuiViewportFlags viewport_flags = window->Viewport->Flags & ~viewport_flags_to_clear; const bool is_modal = (flags & ImGuiWindowFlags_Modal) != 0; - const bool is_short_lived_floating_window = (flags & (ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) != 0 && !is_modal; + const bool is_short_lived_floating_window = (flags & (ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) != 0; if (flags & ImGuiWindowFlags_Tooltip) viewport_flags |= ImGuiViewportFlags_TopMost; - //if (flags & ImGuiWindowFlags_Modal) - // viewport_flags |= ImGuiViewportFlags_TopMost; // Not correct because other popups can be stack above a modal? if ((g.IO.ConfigViewportsNoTaskBarIcon || is_short_lived_floating_window) && !is_modal) viewport_flags |= ImGuiViewportFlags_NoTaskBarIcon; if (g.IO.ConfigViewportsNoDecoration || is_short_lived_floating_window) viewport_flags |= ImGuiViewportFlags_NoDecoration; + // Not correct to set modal as topmost because: + // - Because other popups can be stacked above a modal (e.g. combo box in a modal) + // - ImGuiViewportFlags_TopMost is currently handled different in backends: in Win32 it is "appear top most" whereas in GLFW and SDL it is "stay topmost" + //if (flags & ImGuiWindowFlags_Modal) + // viewport_flags |= ImGuiViewportFlags_TopMost; + // For popups and menus that may be protruding out of their parent viewport, we enable _NoFocusOnClick so that clicking on them // won't steal the OS focus away from their parent window (which may be reflected in OS the title bar decoration). // Setting _NoFocusOnClick would technically prevent us from bringing back to front in case they are being covered by an OS window from a different app, From 657589ab47b31e66804c71fbcdaf0f72e8e4f146 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 4 Dec 2020 11:29:53 +0100 Subject: [PATCH 659/828] Backends: Vulkan+Viewports: fixed build, removed extraneous pipeline creation (770c9953, e8447dea, 6a0e85c5) (#3459, #3579) --- backends/imgui_impl_vulkan.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp index e0e4cc4c750d..64c762f9c7ca 100644 --- a/backends/imgui_impl_vulkan.cpp +++ b/backends/imgui_impl_vulkan.cpp @@ -1285,7 +1285,7 @@ void ImGui_ImplVulkanH_CreateOrResizeWindow(VkInstance instance, VkPhysicalDevic { (void)instance; ImGui_ImplVulkanH_CreateWindowSwapChain(physical_device, device, wd, allocator, width, height, min_image_count); - ImGui_ImplVulkan_CreatePipeline(device, allocator, VK_NULL_HANDLE, wd->RenderPass, VK_SAMPLE_COUNT_1_BIT, &wd->Pipeline); + //ImGui_ImplVulkan_CreatePipeline(device, allocator, VK_NULL_HANDLE, wd->RenderPass, VK_SAMPLE_COUNT_1_BIT, &wd->Pipeline, g_VulkanInitInfo.Subpass); ImGui_ImplVulkanH_CreateWindowCommandBuffers(physical_device, device, wd, queue_family, allocator); } From 9e4956d86bf6cbb8a10df210cba2e70857cd3eb0 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 18 Jan 2021 12:32:26 +0100 Subject: [PATCH 660/828] Docking: added comments. added experimental TabItemFlagsOverrideSet to ImGuiWindowClass. (Could probably do something similar with TabBarFlagsOverrideSet+Clear for #2700 later.) --- imgui.cpp | 49 +++++++++++++++++++++++++++++++++++++++++++++++- imgui.h | 3 ++- imgui_internal.h | 2 +- 3 files changed, 51 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 13969aef2157..ade0f17a7f67 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11930,6 +11930,50 @@ void ImGui::DestroyPlatformWindows() // Docking: Settings //----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// Typical Docking call flow: (root level is generally public API): +//----------------------------------------------------------------------------- +// - NewFrame() new dear imgui frame +// | DockContextUpdateUndocking() - process queued undocking requests +// | - DockContextProcessUndockWindow() - process one window undocking request +// | - DockContextProcessUndockNode() - process one whole node undocking request +// | DockContextUpdateDocking() - process queue docking requests, create floating dock nodes +// | - update g.HoveredDockNode - [debug] update node hovered by mouse +// | - DockContextProcessDock() - process one docking request +// | - DockNodeUpdate() +// | - DockNodeUpdateForRootNode() +// | - DockNodeUpdateVisibleFlagAndInactiveChilds() +// | - DockNodeFindInfo() +// | - destroy unused node or tab bar +// | - create dock node host window +// | - Begin() etc. +// | - DockNodeStartMouseMovingWindow() +// | - DockNodeTreeUpdatePosSize() +// | - DockNodeTreeUpdateSplitter() +// | - draw node background +// | - DockNodeUpdateTabBar() - create/update tab bar for a docking node +// | - DockNodeAddTabBar() +// | - DockNodeUpdateWindowMenu() +// | - DockNodeCalcTabBarLayout() +// | - BeginTabBarEx() +// | - TabItemEx() calls +// | - EndTabBar() +// | - BeginDockableDragDropTarget() +// | - DockNodeUpdate() - recurse into child nodes... +//----------------------------------------------------------------------------- +// - DockSpace() user submit a dockspace into a window +// | Begin(Child) - create a child window +// | DockNodeUpdate() - call main dock node update function +// | End(Child) +// | ItemSize() +//----------------------------------------------------------------------------- +// - Begin() +// | BeginDocked() +// | BeginDockableDragDropSource() +// | BeginDockableDragDropTarget() +// | - DockNodePreviewDockRender() +//----------------------------------------------------------------------------- + //----------------------------------------------------------------------------- // Docking: Internal Types //----------------------------------------------------------------------------- @@ -12191,7 +12235,9 @@ void ImGui::DockContextUpdateDocking(ImGuiContext* ctx) if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable)) return; - // Store hovered dock node. We could in theory use DockNodeTreeFindVisibleNodeByPos() on the root host dock node, but using ->DockNode is a good shortcut. + // [DEBUG] Store hovered dock node. + // We could in theory use DockNodeTreeFindVisibleNodeByPos() on the root host dock node, but using ->DockNode is a good shortcut. + // Note this is mostly a debug thing and isn't actually used for docking target, because docking involve more detailed filtering. g.HoveredDockNode = NULL; if (ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow) { @@ -13582,6 +13628,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w if (window->LastFrameActive + 1 >= g.FrameCount || !node_was_active) { ImGuiTabItemFlags tab_item_flags = 0; + tab_item_flags |= window->WindowClass.TabItemFlagsOverrideSet; if (window->Flags & ImGuiWindowFlags_UnsavedDocument) tab_item_flags |= ImGuiTabItemFlags_UnsavedDocument; if (tab_bar->Flags & ImGuiTabBarFlags_NoCloseWithMiddleMouseButton) diff --git a/imgui.h b/imgui.h index 61e9613d617c..0b8964415f52 100644 --- a/imgui.h +++ b/imgui.h @@ -1995,12 +1995,13 @@ struct ImGuiWindowClass ImGuiID ParentViewportId; // Hint for the platform backend. If non-zero, the platform backend can create a parent<>child relationship between the platform windows. Not conforming backends are free to e.g. parent every viewport to the main viewport or not. ImGuiViewportFlags ViewportFlagsOverrideSet; // Viewport flags to set when a window of this class owns a viewport. This allows you to enforce OS decoration or task bar icon, override the defaults on a per-window basis. ImGuiViewportFlags ViewportFlagsOverrideClear; // Viewport flags to clear when a window of this class owns a viewport. This allows you to enforce OS decoration or task bar icon, override the defaults on a per-window basis. + ImGuiTabItemFlags TabItemFlagsOverrideSet; // [EXPERIMENTAL] TabItem flags to set when a window of this class gets submitted into a dock node tab bar. May use with ImGuiTabItemFlags_Leading or ImGuiTabItemFlags_Trailing. ImGuiDockNodeFlags DockNodeFlagsOverrideSet; // [EXPERIMENTAL] Dock node flags to set when a window of this class is hosted by a dock node (it doesn't have to be selected!) ImGuiDockNodeFlags DockNodeFlagsOverrideClear; // [EXPERIMENTAL] bool DockingAlwaysTabBar; // Set to true to enforce single floating windows of this class always having their own docking node (equivalent of setting the global io.ConfigDockingAlwaysTabBar) bool DockingAllowUnclassed; // Set to true to allow windows of this class to be docked/merged with an unclassed window. // FIXME-DOCK: Move to DockNodeFlags override? - ImGuiWindowClass() { ClassId = 0; ParentViewportId = 0; ViewportFlagsOverrideSet = ViewportFlagsOverrideClear = 0x00; DockNodeFlagsOverrideSet = DockNodeFlagsOverrideClear = 0x00; DockingAlwaysTabBar = false; DockingAllowUnclassed = true; } + ImGuiWindowClass() { memset(this, 0, sizeof(*this)); DockingAllowUnclassed = true; } }; // Data payload for Drag and Drop operations: AcceptDragDropPayload(), GetDragDropPayload() diff --git a/imgui_internal.h b/imgui_internal.h index e76d805613b0..861d92379cd9 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1253,7 +1253,7 @@ struct ImGuiDockContext ImVector Requests; ImVector NodesSettings; bool WantFullRebuild; - ImGuiDockContext() { WantFullRebuild = false; } + ImGuiDockContext() { memset(this, 0, sizeof(*this)); } }; #endif // #ifdef IMGUI_HAS_DOCK From ebbb98d5199cb9e37ea873bf12e3ebc5cf1367e5 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 18 Jan 2021 16:10:58 +0100 Subject: [PATCH 661/828] Docking: docked window honor tab and text colors by storing them. (#2771) Later to lead into #2700 and #2539 Move tab submission block above in DockNodeUpdateTabBar(), not strictly necessary for this change as is, but useful if needing to apply override for TitleBg* as we'd need a value for node->VisibleWindow earlier than currently set. --- imgui.cpp | 75 +++++++++++++++++++++++++++++++++--------------- imgui_internal.h | 33 +++++++++++++++++---- 2 files changed, 79 insertions(+), 29 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index ade0f17a7f67..dac35b53df7a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2488,6 +2488,11 @@ struct ImGuiStyleVarInfo void* GetVarPtr(ImGuiStyle* style) const { return (void*)((unsigned char*)style + Offset); } }; +static const ImGuiCol GWindowDockStyleColors[ImGuiWindowDockStyleCol_COUNT] = +{ + ImGuiCol_Text, ImGuiCol_Tab, ImGuiCol_TabHovered, ImGuiCol_TabActive, ImGuiCol_TabUnfocused, ImGuiCol_TabUnfocusedActive +}; + static const ImGuiStyleVarInfo GStyleVarInfo[] = { { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha) }, // ImGuiStyleVar_Alpha @@ -4059,7 +4064,7 @@ void ImGui::NewFrame() // Undocking // (needs to be before UpdateMouseMovingWindowNewFrame so the window is already offset and following the mouse on the detaching frame) - DockContextUpdateUndocking(&g); + DockContextNewFrameUpdateUndocking(&g); // Find hovered window // (needs to be before UpdateMouseMovingWindowNewFrame so we fill g.HoveredWindowUnderMovingWindow on the mouse release frame) @@ -4123,7 +4128,7 @@ void ImGui::NewFrame() ClosePopupsOverWindow(g.NavWindow, false); // Docking - DockContextUpdateDocking(&g); + DockContextNewFrameUpdateDocking(&g); // [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack. UpdateDebugToolItemPicker(); @@ -11934,10 +11939,10 @@ void ImGui::DestroyPlatformWindows() // Typical Docking call flow: (root level is generally public API): //----------------------------------------------------------------------------- // - NewFrame() new dear imgui frame -// | DockContextUpdateUndocking() - process queued undocking requests +// | DockContextNewFrameUpdateUndocking() - process queued undocking requests // | - DockContextProcessUndockWindow() - process one window undocking request // | - DockContextProcessUndockNode() - process one whole node undocking request -// | DockContextUpdateDocking() - process queue docking requests, create floating dock nodes +// | DockContextNewFrameUpdateUndocking() - process queue docking requests, create floating dock nodes // | - update g.HoveredDockNode - [debug] update node hovered by mouse // | - DockContextProcessDock() - process one docking request // | - DockNodeUpdate() @@ -12124,8 +12129,8 @@ namespace ImGui // - DockContextShutdown() // - DockContextClearNodes() // - DockContextRebuildNodes() -// - DockContextUpdateUndocking() -// - DockContextUpdateDocking() +// - DockContextNewFrameUpdateUndocking() +// - DockContextNewFrameUpdateDocking() // - DockContextFindNodeByID() // - DockContextBindNodeToWindow() // - DockContextGenNodeID() @@ -12184,7 +12189,7 @@ void ImGui::DockContextRebuildNodes(ImGuiContext* ctx) } // Docking context update function, called by NewFrame() -void ImGui::DockContextUpdateUndocking(ImGuiContext* ctx) +void ImGui::DockContextNewFrameUpdateUndocking(ImGuiContext* ctx) { ImGuiContext& g = *ctx; ImGuiDockContext* dc = &ctx->DockContext; @@ -12228,7 +12233,7 @@ void ImGui::DockContextUpdateUndocking(ImGuiContext* ctx) } // Docking context update function, called by NewFrame() -void ImGui::DockContextUpdateDocking(ImGuiContext* ctx) +void ImGui::DockContextNewFrameUpdateDocking(ImGuiContext* ctx) { ImGuiContext& g = *ctx; ImGuiDockContext* dc = &ctx->DockContext; @@ -13321,8 +13326,8 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) if (g.NavWindow && g.NavWindow->RootWindowDockStop->DockNode && g.NavWindow->RootWindowDockStop->ParentWindow == host_window) node->LastFocusedNodeId = g.NavWindow->RootWindowDockStop->DockNode->ID; - // We need to draw a background at the root level if requested by ImGuiDockNodeFlags_PassthruCentralNode, but we will only know the correct pos/size after - // processing the resizing splitters. So we are using the DrawList channel splitting facility to submit drawing primitives out of order! + // We need to draw a background at the root level if requested by ImGuiDockNodeFlags_PassthruCentralNode, but we will only know the correct pos/size + // _after_ processing the resizing splitters. So we are using the DrawList channel splitting facility to submit drawing primitives out of order! const bool render_dockspace_bg = node->IsRootNode() && host_window && (node_flags & ImGuiDockNodeFlags_PassthruCentralNode) != 0; if (render_dockspace_bg) { @@ -13561,6 +13566,17 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w ImVec2 window_menu_button_pos; DockNodeCalcTabBarLayout(node, &title_bar_rect, &tab_bar_rect, &window_menu_button_pos); + // Submit new tabs and apply NavWindow focus back to the tab bar. They will be added as Unsorted and sorted below based on relative DockOrder value. + const int tabs_count_old = tab_bar->Tabs.Size; + for (int window_n = 0; window_n < node->Windows.Size; window_n++) + { + ImGuiWindow* window = node->Windows[window_n]; + if (g.NavWindow && g.NavWindow->RootWindowDockStop == window) + tab_bar->SelectedTabId = window->ID; + if (TabBarFindTabByID(tab_bar, window->ID) == NULL) + TabBarAddTab(tab_bar, ImGuiTabItemFlags_Unsorted, window); + } + // Title bar if (is_focused) node->LastFrameFocused = g.FrameCount; @@ -13576,17 +13592,6 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w focus_tab_id = tab_bar->SelectedTabId; } - // Submit new tabs and apply NavWindow focus back to the tab bar. They will be added as Unsorted and sorted below based on relative DockOrder value. - const int tabs_count_old = tab_bar->Tabs.Size; - for (int window_n = 0; window_n < node->Windows.Size; window_n++) - { - ImGuiWindow* window = node->Windows[window_n]; - if (g.NavWindow && g.NavWindow->RootWindowDockStop == window) - tab_bar->SelectedTabId = window->ID; - if (TabBarFindTabByID(tab_bar, window->ID) == NULL) - TabBarAddTab(tab_bar, ImGuiTabItemFlags_Unsorted, window); - } - // If multiple tabs are appearing on the same frame, sort them based on their persistent DockOrder value int tabs_unsorted_start = tab_bar->Tabs.Size; for (int tab_n = tab_bar->Tabs.Size - 1; tab_n >= 0 && (tab_bar->Tabs[tab_n].Flags & ImGuiTabItemFlags_Unsorted); tab_n--) @@ -13618,6 +13623,11 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w BeginTabBarEx(tab_bar, tab_bar_rect, tab_bar_flags, node); //host_window->DrawList->AddRect(tab_bar_rect.Min, tab_bar_rect.Max, IM_COL32(255,0,255,255)); + // Backup style colors + ImVec4 backup_style_cols[ImGuiWindowDockStyleCol_COUNT]; + for (int color_n = 0; color_n < ImGuiWindowDockStyleCol_COUNT; color_n++) + backup_style_cols[color_n] = g.Style.Colors[GWindowDockStyleColors[color_n]]; + // Submit actual tabs node->VisibleWindow = NULL; for (int window_n = 0; window_n < node->Windows.Size; window_n++) @@ -13634,6 +13644,10 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w if (tab_bar->Flags & ImGuiTabBarFlags_NoCloseWithMiddleMouseButton) tab_item_flags |= ImGuiTabItemFlags_NoCloseWithMiddleMouseButton; + // Apply stored style overrides for the window + for (int color_n = 0; color_n < ImGuiWindowDockStyleCol_COUNT; color_n++) + g.Style.Colors[GWindowDockStyleColors[color_n]] = ColorConvertU32ToFloat4(window->DockStyle.Colors[color_n]); + bool tab_open = true; TabItemEx(tab_bar, window->Name, window->HasCloseButton ? &tab_open : NULL, tab_item_flags, window); if (!tab_open) @@ -13651,6 +13665,10 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w } } + // Restore style colors + for (int color_n = 0; color_n < ImGuiWindowDockStyleCol_COUNT; color_n++) + g.Style.Colors[GWindowDockStyleColors[color_n]] = backup_style_cols[color_n]; + // Notify root of visible window (used to display title in OS task bar) if (node->VisibleWindow) if (is_focused || root_node->VisibleWindow == NULL) @@ -13999,7 +14017,6 @@ static void ImGui::DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDock overlay_draw_lists[overlay_draw_lists_count++] = GetForegroundDrawList(root_payload->Viewport); // Draw main preview rectangle - const ImU32 overlay_col_tabs = GetColorU32(ImGuiCol_TabActive); const ImU32 overlay_col_main = GetColorU32(ImGuiCol_DockingPreview, is_transparent_payload ? 0.60f : 0.40f); const ImU32 overlay_col_drop = GetColorU32(ImGuiCol_DockingPreview, is_transparent_payload ? 0.90f : 0.70f); const ImU32 overlay_col_drop_hovered = GetColorU32(ImGuiCol_DockingPreview, is_transparent_payload ? 1.20f : 1.00f); @@ -14054,6 +14071,9 @@ static void ImGui::DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDock ImVec2 tab_size = TabItemCalcSize(payload_window->Name, payload_window->HasCloseButton); ImRect tab_bb(tab_pos.x, tab_pos.y, tab_pos.x + tab_size.x, tab_pos.y + tab_size.y); tab_pos.x += tab_size.x + g.Style.ItemInnerSpacing.x; + const ImU32 overlay_col_text = GetColorU32(payload_window->DockStyle.Colors[ImGuiWindowDockStyleCol_Text]); + const ImU32 overlay_col_tabs = GetColorU32(payload_window->DockStyle.Colors[ImGuiWindowDockStyleCol_TabActive]); + PushStyleColor(ImGuiCol_Text, overlay_col_text); for (int overlay_n = 0; overlay_n < overlay_draw_lists_count; overlay_n++) { ImGuiTabItemFlags tab_flags = ImGuiTabItemFlags_Preview | ((payload_window->Flags & ImGuiWindowFlags_UnsavedDocument) ? ImGuiTabItemFlags_UnsavedDocument : 0); @@ -14064,6 +14084,7 @@ static void ImGui::DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDock if (!tab_bar_rect.Contains(tab_bb)) overlay_draw_lists[overlay_n]->PopClipRect(); } + PopStyleColor(); } } @@ -15142,7 +15163,7 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) // Note how we are testing for LastFrameAlive and NOT LastFrameActive. A DockSpace node can be maintained alive while being inactive with ImGuiDockNodeFlags_KeepAliveOnly. if (node->LastFrameAlive < g.FrameCount) { - // If the window has been orphaned, transition the docknode to an implicit node processed in DockContextUpdateDocking() + // If the window has been orphaned, transition the docknode to an implicit node processed in DockContextNewFrameUpdateDocking() ImGuiDockNode* root_node = DockNodeGetRootNode(node); if (root_node->LastFrameAlive < g.FrameCount) { @@ -15156,6 +15177,10 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) return; } + // Store style overrides + for (int color_n = 0; color_n < ImGuiWindowDockStyleCol_COUNT; color_n++) + window->DockStyle.Colors[color_n] = ColorConvertFloat4ToU32(g.Style.Colors[GWindowDockStyleColors[color_n]]); + // Fast path return. It is common for windows to hold on a persistent DockId but be the only visible window, // and never create neither a host window neither a tab bar. // FIXME-DOCK: replace ->HostWindow NULL compare with something more explicit (~was initially intended as a first frame test) @@ -15227,6 +15252,10 @@ void ImGui::BeginDockableDragDropSource(ImGuiWindow* window) { SetDragDropPayload(IMGUI_PAYLOAD_TYPE_WINDOW, &window, sizeof(window)); EndDragDropSource(); + + // Store style overrides + for (int color_n = 0; color_n < ImGuiWindowDockStyleCol_COUNT; color_n++) + window->DockStyle.Colors[color_n] = ColorConvertFloat4ToU32(g.Style.Colors[GWindowDockStyleColors[color_n]]); } } diff --git a/imgui_internal.h b/imgui_internal.h index 861d92379cd9..6710b3124fa4 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1247,6 +1247,26 @@ struct ImGuiDockNode ImRect Rect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } }; +// List of colors that are stored at the time of Begin() into Docked Windows. +// We currently store the packed colors in a simple array window->DockStyle.Colors[]. +// A better solution may involve appending into a log of colors in ImGuiContext + store offsets into those arrays in ImGuiWindow, +// but it would be more complex as we'd need to double-buffer both as e.g. drop target may refer to window from last frame. +enum ImGuiWindowDockStyleCol +{ + ImGuiWindowDockStyleCol_Text, + ImGuiWindowDockStyleCol_Tab, + ImGuiWindowDockStyleCol_TabHovered, + ImGuiWindowDockStyleCol_TabActive, + ImGuiWindowDockStyleCol_TabUnfocused, + ImGuiWindowDockStyleCol_TabUnfocusedActive, + ImGuiWindowDockStyleCol_COUNT +}; + +struct ImGuiWindowDockStyle +{ + ImU32 Colors[ImGuiWindowDockStyleCol_COUNT]; +}; + struct ImGuiDockContext { ImGuiStorage Nodes; // Map ID -> ImGuiDockNode*: Active nodes @@ -1970,15 +1990,16 @@ struct IMGUI_API ImGuiWindow bool MemoryCompacted; // Set when window extraneous data have been garbage collected // Docking + bool DockIsActive :1; // When docking artifacts are actually visible. When this is set, DockNode is guaranteed to be != NULL. ~~ (DockNode != NULL) && (DockNode->Windows.Size > 1). + bool DockTabIsVisible :1; // Is our window visible this frame? ~~ is the corresponding tab selected? + bool DockTabWantClose :1; + short DockOrder; // Order of the last time the window was visible within its DockNode. This is used to reorder windows that are reappearing on the same frame. Same value between windows that were active and windows that were none are possible. + ImGuiWindowDockStyle DockStyle; ImGuiDockNode* DockNode; // Which node are we docked into. Important: Prefer testing DockIsActive in many cases as this will still be set when the dock node is hidden. ImGuiDockNode* DockNodeAsHost; // Which node are we owning (for parent windows) ImGuiID DockId; // Backup of last valid DockNode->ID, so single window remember their dock node id even when they are not bound any more ImGuiItemStatusFlags DockTabItemStatusFlags; ImRect DockTabItemRect; - short DockOrder; // Order of the last time the window was visible within its DockNode. This is used to reorder windows that are reappearing on the same frame. Same value between windows that were active and windows that were none are possible. - bool DockIsActive :1; // When docking artifacts are actually visible. When this is set, DockNode is guaranteed to be != NULL. ~~ (DockNode != NULL) && (DockNode->Windows.Size > 1). - bool DockTabIsVisible :1; // Is our window visible this frame? ~~ is the corresponding tab selected? - bool DockTabWantClose :1; public: ImGuiWindow(ImGuiContext* context, const char* name); @@ -2504,8 +2525,8 @@ namespace ImGui IMGUI_API void DockContextShutdown(ImGuiContext* ctx); IMGUI_API void DockContextClearNodes(ImGuiContext* ctx, ImGuiID root_id, bool clear_settings_refs); // Use root_id==0 to clear all IMGUI_API void DockContextRebuildNodes(ImGuiContext* ctx); - IMGUI_API void DockContextUpdateUndocking(ImGuiContext* ctx); - IMGUI_API void DockContextUpdateDocking(ImGuiContext* ctx); + IMGUI_API void DockContextNewFrameUpdateUndocking(ImGuiContext* ctx); + IMGUI_API void DockContextNewFrameUpdateDocking(ImGuiContext* ctx); IMGUI_API ImGuiID DockContextGenNodeID(ImGuiContext* ctx); IMGUI_API void DockContextQueueDock(ImGuiContext* ctx, ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, float split_ratio, bool split_outer); IMGUI_API void DockContextQueueUndockWindow(ImGuiContext* ctx, ImGuiWindow* window); From 376c88a864f8933463003a08f94574e17bd9eef3 Mon Sep 17 00:00:00 2001 From: Sammy Fatnassi Date: Tue, 26 Jan 2021 14:09:18 +0100 Subject: [PATCH 662/828] Fixed some compile warnings with Clang on Windows (#3754) --- backends/imgui_impl_win32.cpp | 2 +- imgui.cpp | 4 +++- imgui_widgets.cpp | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index 4562e896938f..ee128843f0b2 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -272,7 +272,7 @@ static void ImGui_ImplWin32_UpdateGamepads() static BOOL CALLBACK ImGui_ImplWin32_UpdateMonitors_EnumFunc(HMONITOR monitor, HDC, LPRECT, LPARAM) { - MONITORINFO info = { 0 }; + MONITORINFO info = {}; info.cbSize = sizeof(MONITORINFO); if (!::GetMonitorInfo(monitor, &info)) return TRUE; diff --git a/imgui.cpp b/imgui.cpp index e97dce139ab7..f993d6f3eddb 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7696,6 +7696,7 @@ static void ImGui::ErrorCheckNewFrameSanityChecks() for (int monitor_n = 0; monitor_n < g.PlatformIO.Monitors.Size; monitor_n++) { ImGuiPlatformMonitor& mon = g.PlatformIO.Monitors[monitor_n]; + IM_UNUSED(mon); IM_ASSERT(mon.MainSize.x > 0.0f && mon.MainSize.y > 0.0f && "Monitor main bounds not setup properly."); IM_ASSERT(ImRect(mon.MainPos, mon.MainPos + mon.MainSize).Contains(ImRect(mon.WorkPos, mon.WorkPos + mon.WorkSize)) && "Monitor work bounds not setup properly. If you don't have work area information, just copy MainPos/MainSize into them."); IM_ASSERT(mon.DpiScale != 0.0f); @@ -13484,7 +13485,8 @@ bool ImGui::DockNodeBeginAmendTabBar(ImGuiDockNode* node) Begin(node->HostWindow->Name); PushOverrideID(node->ID); bool ret = BeginTabBarEx(node->TabBar, node->TabBar->BarRect, node->TabBar->Flags, node); - IM_ASSERT(ret); + IM_UNUSED(ret); + IM_ASSERT(ret); return true; } diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 92964bd43689..950b21fa4530 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7305,6 +7305,7 @@ static ImU32 ImGui::TabBarCalcTabID(ImGuiTabBar* tab_bar, const char* label, I { if (docked_window != NULL) { + IM_UNUSED(tab_bar); IM_ASSERT(tab_bar->Flags & ImGuiTabBarFlags_DockNode); ImGuiID id = ImHashStr(label); KeepAliveID(id); From cff816245471954a477935862161eee445755534 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 28 Jan 2021 16:03:44 +0100 Subject: [PATCH 663/828] Viewports, Backends: Vulkan: handle VK_ERROR_OUT_OF_DATE_KHR when resizing secondary viewport (#3766, #3758) Cannot repro here but appears to a user on Linux. Fix may be not super solid. --- backends/imgui_impl_vulkan.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp index 64c762f9c7ca..ee5acb0cc107 100644 --- a/backends/imgui_impl_vulkan.cpp +++ b/backends/imgui_impl_vulkan.cpp @@ -1518,7 +1518,10 @@ static void ImGui_ImplVulkan_SwapBuffers(ImGuiViewport* viewport, void*) info.pSwapchains = &wd->Swapchain; info.pImageIndices = &present_index; err = vkQueuePresentKHR(v->Queue, &info); - check_vk_result(err); + if (err == VK_ERROR_OUT_OF_DATE_KHR) + ImGui_ImplVulkanH_CreateOrResizeWindow(v->Instance, v->PhysicalDevice, v->Device, &data->Window, v->QueueFamily, v->Allocator, (int)viewport->Size.x, (int)viewport->Size.y, v->MinImageCount); + else + check_vk_result(err); wd->FrameIndex = (wd->FrameIndex + 1) % wd->ImageCount; // This is for the next vkWaitForFences() wd->SemaphoreIndex = (wd->SemaphoreIndex + 1) % wd->ImageCount; // Now we can use the next set of semaphores From 84e8802891f4e05dc0e006019cd99bfb00ea6911 Mon Sep 17 00:00:00 2001 From: GamingMinds-DanielC Date: Thu, 14 Jan 2021 17:42:59 +0100 Subject: [PATCH 664/828] Docking: on node split, update memorized DockId for currently closed windows (#3716) Amended by @ocornut with same fix in DockBuilderRemoveNodeChildNodes(). --- imgui.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/imgui.cpp b/imgui.cpp index 483202a21c44..2c81272616dc 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -14190,6 +14190,7 @@ void ImGui::DockNodeTreeSplit(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImG child_1->SizeRef[split_axis] = ImFloor(size_avail - child_0->SizeRef[split_axis]); DockNodeMoveWindows(parent_node->ChildNodes[split_inheritor_child_idx], parent_node); + DockSettingsRenameNodeReferences(parent_node->ID, parent_node->ChildNodes[split_inheritor_child_idx]->ID); DockNodeTreeUpdatePosSize(parent_node, parent_node->Pos, parent_node->Size); // Flags transfer (e.g. this is where we transfer the ImGuiDockNodeFlags_CentralNode property) @@ -14815,7 +14816,10 @@ void ImGui::DockBuilderRemoveNodeChildNodes(ImGuiID root_id) if (root_id != 0) DockContextQueueNotifyRemovedNode(ctx, node); if (root_node) + { DockNodeMoveWindows(root_node, node); + DockSettingsRenameNodeReferences(node->ID, root_node->ID); + } nodes_to_remove.push_back(node); } } From 22d9a61b3349be48844dd17a2da619c5ea6c05eb Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 2 Feb 2021 15:14:37 +0100 Subject: [PATCH 665/828] Docking: fix gap in hit test hold when using ImGuiDockNodeFlags_PassthruCentralNode touching the edge of a viewport. (#3733) --- imgui.cpp | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 2c81272616dc..164aec7aae2d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -13369,7 +13369,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) } // Register a hit-test hole in the window unless we are currently dragging a window that is compatible with our dockspace - const ImGuiDockNode* central_node = node->CentralNode; + ImGuiDockNode* central_node = node->CentralNode; const bool central_node_hole = node->IsRootNode() && host_window && (node_flags & ImGuiDockNodeFlags_PassthruCentralNode) != 0 && central_node != NULL && central_node->IsEmpty(); bool central_node_hole_register_hit_test_hole = central_node_hole; if (central_node_hole) @@ -13378,14 +13378,22 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) central_node_hole_register_hit_test_hole = false; if (central_node_hole_register_hit_test_hole) { - // Add a little padding to match the "resize from edges" behavior and allow grabbing the splitter easily. + // We add a little padding to match the "resize from edges" behavior and allow grabbing the splitter easily. + // (But we only add it if there's something else on the other side of the hole, otherwise for e.g. fullscreen + // covering passthru node we'd have a gap on the edge not covered by the hole) IM_ASSERT(node->IsDockSpace()); // We cannot pass this flag without the DockSpace() api. Testing this because we also setup the hole in host_window->ParentNode - ImRect central_hole(central_node->Pos, central_node->Pos + central_node->Size); - central_hole.Expand(ImVec2(-WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS, -WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS)); - if (central_node_hole && !central_hole.IsInverted()) + ImGuiDockNode* root_node = DockNodeGetRootNode(central_node); + ImRect root_rect(root_node->Pos, root_node->Pos + root_node->Size); + ImRect hole_rect(central_node->Pos, central_node->Pos + central_node->Size); + if (hole_rect.Min.x > root_rect.Min.x) { hole_rect.Min.x += WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS; } + if (hole_rect.Max.x < root_rect.Max.x) { hole_rect.Max.x -= WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS; } + if (hole_rect.Min.y > root_rect.Min.y) { hole_rect.Min.y += WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS; } + if (hole_rect.Max.y < root_rect.Max.y) { hole_rect.Max.y -= WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS; } + //GetForegroundDrawList()->AddRect(hole_rect.Min, hole_rect.Max, IM_COL32(255, 0, 0, 255)); + if (central_node_hole && !hole_rect.IsInverted()) { - SetWindowHitTestHole(host_window, central_hole.Min, central_hole.Max - central_hole.Min); - SetWindowHitTestHole(host_window->ParentWindow, central_hole.Min, central_hole.Max - central_hole.Min); + SetWindowHitTestHole(host_window, hole_rect.Min, hole_rect.Max - hole_rect.Min); + SetWindowHitTestHole(host_window->ParentWindow, hole_rect.Min, hole_rect.Max - hole_rect.Min); } } From fa55b0cb6022eb26dc50729cf8293b749f8bd17c Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 5 Feb 2021 16:00:17 +0100 Subject: [PATCH 666/828] Viewports: (breaking) removed ImGuiPlatformIO::MainViewport which is now pretty much unused and duplicate (and misleading as we will evolve the concept) Use GetMainViewport() if stuck. --- docs/CHANGELOG.txt | 7 +++++-- imgui.cpp | 10 +++++----- imgui.h | 7 +++---- imgui_widgets.cpp | 2 +- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 1161f8dfe581..c1ba26356c88 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -76,7 +76,10 @@ Other changes: (FIXME: This need a fuller explanation!) - Added ImGuiPlatformIO structure and GetPlatformIO(). - Similarly to ImGuiIO and GetIO(), this structure is the main point of communication for backends supporting multi-viewports. + - Similarly to ImGuiIO and GetIO(), this structure is the main point of communication for backends supporting multi-viewports. + - Backend sets functions in ImGuiPlatformIO to manipulate platform windows. + - ImGuiPlatformIO::Monitors is a list of platform monitors (input from backend) + - ImGuiPlatformIO::Viewports is a list of viewports (output from dear imgui) - Added ImGuiPlatformMonitor to feed OS monitor information in the ImGuiPlatformIO::Monitors. - Added GetMainViewport(). - Added GetWindowViewport(), SetNextWindowViewport(). @@ -88,7 +91,7 @@ Other changes: - Added ImGuiConfigFlags_ViewportsEnable configuration flag and other viewport options. - Added io.ConfigViewportsNoAutoMerge, io.ConfigViewportsNoTaskBarIcon, io.ConfigViewportsNoDecoration, io.ConfigViewportsNoDefaultParent options. - Added ImGuiBackendFlags_PlatformHasViewports, ImGuiBackendFlags_RendererHasViewports, ImGuiBackendFlags_HasMouseHoveredViewport backend flags. -- Added io.MainViewport, io.Viewports, io.MouseHoveredViewport (MouseHoveredViewport is optional _even_ for multi-viewport support). +- Added io.MouseHoveredViewport (optional _even_ for multi-viewport support, tied to ImGuiBackendFlags_HasMouseHoveredViewport flag). - Added ImGuiViewport structure, ImGuiViewportFlags flags. - Added ImGuiWindowClass and SetNextWindowClass() for passing viewport related hints to the OS/platform back-end. - Examples: Renderer: OpenGL2, OpenGL3, DirectX11, DirectX12, Vulkan: Added support for multi-viewports. diff --git a/imgui.cpp b/imgui.cpp index 164aec7aae2d..6f3e949439cf 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4220,7 +4220,6 @@ void ImGui::Initialize(ImGuiContext* context) viewport->Idx = 0; viewport->PlatformWindowCreated = true; g.Viewports.push_back(viewport); - g.PlatformIO.MainViewport = g.Viewports[0]; // Make it accessible in public-facing GetPlatformIO() immediately (before the first call to EndFrame) g.PlatformIO.Viewports.push_back(g.Viewports[0]); // Extensions @@ -6413,7 +6412,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if (window->Viewport->PlatformMonitor == -1) { // Fallback for "lost" window (e.g. a monitor disconnected): we move the window back over the main viewport - SetWindowPos(window, g.Viewports[0]->Pos + style.DisplayWindowPadding, ImGuiCond_Always); + ImGuiViewport* main_viewport = GetMainViewport(); + SetWindowPos(window, main_viewport->Pos + style.DisplayWindowPadding, ImGuiCond_Always); } else { @@ -11260,13 +11260,14 @@ static bool ImGui::UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImG return true; } +// FIXME: handle 0 to N host viewports static bool ImGui::UpdateTryMergeWindowIntoHostViewports(ImGuiWindow* window) { ImGuiContext& g = *GImGui; return UpdateTryMergeWindowIntoHostViewport(window, g.Viewports[0]); } -// Translate imgui windows when a Host Viewport has been moved +// Translate Dear ImGui windows when a Host Viewport has been moved // (This additionally keeps windows at the same place when ImGuiConfigFlags_ViewportsEnable is toggled!) void ImGui::TranslateWindowsInViewport(ImGuiViewportP* viewport, const ImVec2& old_pos, const ImVec2& new_pos) { @@ -11502,7 +11503,6 @@ static void ImGui::UpdateViewportsNewFrame() static void ImGui::UpdateViewportsEndFrame() { ImGuiContext& g = *GImGui; - g.PlatformIO.MainViewport = g.Viewports[0]; g.PlatformIO.Viewports.resize(0); for (int i = 0; i < g.Viewports.Size; i++) { @@ -11589,7 +11589,7 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) window->ViewportAllowPlatformMonitorExtend = -1; // Restore main viewport if multi-viewport is not supported by the backend - ImGuiViewportP* main_viewport = g.Viewports[0]; + ImGuiViewportP* main_viewport = (ImGuiViewportP*)GetMainViewport(); if (!(g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable)) { SetWindowViewport(window, main_viewport); diff --git a/imgui.h b/imgui.h index 155202619388..79d593cffac6 100644 --- a/imgui.h +++ b/imgui.h @@ -894,7 +894,7 @@ namespace ImGui // Read comments around the ImGuiPlatformIO structure for more details. // Note: You may use GetWindowViewport() to get the current viewport of the current window. IMGUI_API ImGuiPlatformIO& GetPlatformIO(); // platform/renderer functions, for backend to setup + viewports list. - IMGUI_API ImGuiViewport* GetMainViewport(); // main viewport. same as GetPlatformIO().MainViewport == GetPlatformIO().Viewports[0]. + IMGUI_API ImGuiViewport* GetMainViewport(); // return primary/default viewport. In the future in "no main platform window" mode we will direct this to primary monitor. IMGUI_API void UpdatePlatformWindows(); // call in main loop. will call CreateWindow/ResizeWindow/etc. platform functions for each secondary viewport, and DestroyWindow for each inactive viewport. IMGUI_API void RenderPlatformWindowsDefault(void* platform_render_arg = NULL, void* renderer_render_arg = NULL); // call in main loop. will call RenderWindow/SwapBuffers platform functions for each secondary viewport which doesn't have the ImGuiViewportFlags_Minimized flag set. May be reimplemented by user for custom rendering needs. IMGUI_API void DestroyPlatformWindows(); // call DestroyWindow platform functions for all viewports. call from backend Shutdown() if you need to close platform windows before imgui shutdown. otherwise will be called by DestroyContext(). @@ -2930,7 +2930,6 @@ struct ImGuiPlatformIO // Viewports list (the list is updated by calling ImGui::EndFrame or ImGui::Render) // (in the future we will attempt to organize this feature to remove the need for a "main viewport") - ImGuiViewport* MainViewport; // Guaranteed to be == Viewports[0] ImVector Viewports; // Main viewports, followed by all secondary viewports. ImGuiPlatformIO() { memset(this, 0, sizeof(*this)); } // Zero clear }; @@ -2973,8 +2972,8 @@ struct ImGuiViewport ImVec2 WorkOffsetMin; // Work Area: Offset from Pos to top-left corner of Work Area. Generally (0,0) or (0,+main_menu_bar_height). Work Area is Full Area but without menu-bars/status-bars (so WorkArea always fit inside Pos/Size!) ImVec2 WorkOffsetMax; // Work Area: Offset from Pos+Size to bottom-right corner of Work Area. Generally (0,0) or (0,-status_bar_height). float DpiScale; // 1.0f = 96 DPI = No extra scale. - ImDrawData* DrawData; // The ImDrawData corresponding to this viewport. Valid after Render() and until the next call to NewFrame(). ImGuiID ParentViewportId; // (Advanced) 0: no parent. Instruct the platform backend to setup a parent/child relationship between platform windows. + ImDrawData* DrawData; // The ImDrawData corresponding to this viewport. Valid after Render() and until the next call to NewFrame(). // Our design separate the Renderer and Platform backends to facilitate combining default backends with each others. // When our create your own backend for a custom engine, it is possible that both Renderer and Platform will be handled @@ -2988,7 +2987,7 @@ struct ImGuiViewport bool PlatformRequestResize; // Platform window requested resize (e.g. window was resized by the OS / host window manager, authoritative size will be OS window size) bool PlatformRequestClose; // Platform window requested closure (e.g. window was moved by the OS / host window manager, e.g. pressing ALT-F4) - ImGuiViewport() { ID = 0; Flags = 0; DpiScale = 0.0f; DrawData = NULL; ParentViewportId = 0; RendererUserData = PlatformUserData = PlatformHandle = PlatformHandleRaw = NULL; PlatformRequestMove = PlatformRequestResize = PlatformRequestClose = false; } + ImGuiViewport() { memset(this, 0, sizeof(*this)); } ~ImGuiViewport() { IM_ASSERT(PlatformUserData == NULL && RendererUserData == NULL); } // Access work-area rectangle with GetWorkXXX functions (see comments above) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 2f33118c8219..d0539412bb07 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6575,7 +6575,7 @@ void ImGui::EndMenuBar() bool ImGui::BeginMainMenuBar() { ImGuiContext& g = *GImGui; - ImGuiViewportP* viewport = g.Viewports[0]; + ImGuiViewportP* viewport = (ImGuiViewportP*)GetMainViewport(); ImGuiWindow* menu_bar_window = FindWindowByName("##MainMenuBar"); // For the main menu bar, which cannot be moved, we honor g.Style.DisplaySafeAreaPadding to ensure text can be visible on a TV set. From 862bfc53d3aea7be062bcfbc0deda37f5770159e Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 9 Feb 2021 11:47:55 +0100 Subject: [PATCH 667/828] Viewports: Moved in own section of imgui.h ahead of merging a small part of viewport interface to master. --- imgui.h | 113 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 59 insertions(+), 54 deletions(-) diff --git a/imgui.h b/imgui.h index 79d593cffac6..dec2fb217e0c 100644 --- a/imgui.h +++ b/imgui.h @@ -31,7 +31,8 @@ Index of this file: // [SECTION] Helpers (ImGuiOnceUponAFrame, ImGuiTextFilter, ImGuiTextBuffer, ImGuiStorage, ImGuiListClipper, ImColor) // [SECTION] Drawing API (ImDrawCallback, ImDrawCmd, ImDrawIdx, ImDrawVert, ImDrawChannel, ImDrawListSplitter, ImDrawListFlags, ImDrawList, ImDrawData) // [SECTION] Font API (ImFontConfig, ImFontGlyph, ImFontGlyphRangesBuilder, ImFontAtlasFlags, ImFontAtlas, ImFont) -// [SECTION] Platform interface for multi-viewport support (ImGuiPlatformIO, ImGuiPlatformMonitor, ImGuiViewportFlags, ImGuiViewport) +// [SECTION] Viewports (ImGuiViewportFlags, ImGuiViewport) +// [SECTION] Platform interface for multi-viewport support (ImGuiPlatformIO, ImGuiPlatformMonitor) */ @@ -326,11 +327,11 @@ namespace ImGui IMGUI_API bool IsWindowHovered(ImGuiHoveredFlags flags=0); // is current window hovered (and typically: not blocked by a popup/modal)? see flags for options. NB: If you are trying to check whether your mouse should be dispatched to imgui or to your app, you should use the 'io.WantCaptureMouse' boolean for that! Please read the FAQ! IMGUI_API ImDrawList* GetWindowDrawList(); // get draw list associated to the current window, to append your own drawing primitives IMGUI_API float GetWindowDpiScale(); // get DPI scale currently associated to the current window's viewport. - IMGUI_API ImGuiViewport*GetWindowViewport(); // get viewport currently associated to the current window. IMGUI_API ImVec2 GetWindowPos(); // get current window position in screen space (useful if you want to do your own drawing via the DrawList API) IMGUI_API ImVec2 GetWindowSize(); // get current window size IMGUI_API float GetWindowWidth(); // get current window width (shortcut for GetWindowSize().x) IMGUI_API float GetWindowHeight(); // get current window height (shortcut for GetWindowSize().y) + IMGUI_API ImGuiViewport*GetWindowViewport(); // get viewport currently associated to the current window. // Prefer using SetNextXXX functions (before Begin) rather that SetXXX functions (after Begin). IMGUI_API void SetNextWindowPos(const ImVec2& pos, ImGuiCond cond = 0, const ImVec2& pivot = ImVec2(0, 0)); // set next window position. call before Begin(). use pivot=(0.5f,0.5f) to center on given point, etc. @@ -2822,6 +2823,62 @@ struct ImFont IMGUI_API bool IsGlyphRangeUnused(unsigned int c_begin, unsigned int c_last); }; +//----------------------------------------------------------------------------- +// [SECTION] Viewports +//----------------------------------------------------------------------------- + +// Flags stored in ImGuiViewport::Flags, giving indications to the platform backends. +enum ImGuiViewportFlags_ +{ + ImGuiViewportFlags_None = 0, + ImGuiViewportFlags_NoDecoration = 1 << 0, // Platform Window: Disable platform decorations: title bar, borders, etc. (generally set all windows, but if ImGuiConfigFlags_ViewportsDecoration is set we only set this on popups/tooltips) + ImGuiViewportFlags_NoTaskBarIcon = 1 << 1, // Platform Window: Disable platform task bar icon (generally set on popups/tooltips, or all windows if ImGuiConfigFlags_ViewportsNoTaskBarIcon is set) + ImGuiViewportFlags_NoFocusOnAppearing = 1 << 2, // Platform Window: Don't take focus when created. + ImGuiViewportFlags_NoFocusOnClick = 1 << 3, // Platform Window: Don't take focus when clicked on. + ImGuiViewportFlags_NoInputs = 1 << 4, // Platform Window: Make mouse pass through so we can drag this window while peaking behind it. + ImGuiViewportFlags_NoRendererClear = 1 << 5, // Platform Window: Renderer doesn't need to clear the framebuffer ahead (because we will fill it entirely). + ImGuiViewportFlags_TopMost = 1 << 6, // Platform Window: Display on top (for tooltips only). + ImGuiViewportFlags_Minimized = 1 << 7, // Platform Window: Window is minimized, can skip render. When minimized we tend to avoid using the viewport pos/size for clipping window or testing if they are contained in the viewport. + ImGuiViewportFlags_NoAutoMerge = 1 << 8, // Platform Window: Avoid merging this window into another host window. This can only be set via ImGuiWindowClass viewport flags override (because we need to now ahead if we are going to create a viewport in the first place!). + ImGuiViewportFlags_CanHostOtherWindows = 1 << 9 // Main viewport: can host multiple imgui windows (secondary viewports are associated to a single window). +}; + +// The viewports created and managed by Dear ImGui. The role of the platform backend is to create the platform/OS windows corresponding to each viewport. +// - Main Area = entire viewport. +// - Work Area = entire viewport minus sections optionally used by menu bars, status bars. Windows are generally trying to stay within this area. +struct ImGuiViewport +{ + ImGuiID ID; // Unique identifier for the viewport + ImGuiViewportFlags Flags; // See ImGuiViewportFlags_ + ImVec2 Pos; // Main Area: Position of the viewport (the imgui coordinates are the same as OS desktop/native coordinates) + ImVec2 Size; // Main Area: Size of the viewport. + ImVec2 WorkOffsetMin; // Work Area: Offset from Pos to top-left corner of Work Area. Generally (0,0) or (0,+main_menu_bar_height). Work Area is Full Area but without menu-bars/status-bars (so WorkArea always fit inside Pos/Size!) + ImVec2 WorkOffsetMax; // Work Area: Offset from Pos+Size to bottom-right corner of Work Area. Generally (0,0) or (0,-status_bar_height). + float DpiScale; // 1.0f = 96 DPI = No extra scale. + ImGuiID ParentViewportId; // (Advanced) 0: no parent. Instruct the platform backend to setup a parent/child relationship between platform windows. + ImDrawData* DrawData; // The ImDrawData corresponding to this viewport. Valid after Render() and until the next call to NewFrame(). + + // Our design separate the Renderer and Platform backends to facilitate combining default backends with each others. + // When our create your own backend for a custom engine, it is possible that both Renderer and Platform will be handled + // by the same system and you may not need to use all the UserData/Handle fields. + // The library never uses those fields, they are merely storage to facilitate backend implementation. + void* RendererUserData; // void* to hold custom data structure for the renderer (e.g. swap chain, framebuffers etc.). generally set by your Renderer_CreateWindow function. + void* PlatformUserData; // void* to hold custom data structure for the OS / platform (e.g. windowing info, render context). generally set by your Platform_CreateWindow function. + void* PlatformHandle; // void* for FindViewportByPlatformHandle(). (e.g. suggested to use natural platform handle such as HWND, GLFWWindow*, SDL_Window*) + void* PlatformHandleRaw; // void* to hold lower-level, platform-native window handle (e.g. the HWND) when using an abstraction layer like GLFW or SDL (where PlatformHandle would be a SDL_Window*) + bool PlatformRequestMove; // Platform window requested move (e.g. window was moved by the OS / host window manager, authoritative position will be OS window position) + bool PlatformRequestResize; // Platform window requested resize (e.g. window was resized by the OS / host window manager, authoritative size will be OS window size) + bool PlatformRequestClose; // Platform window requested closure (e.g. window was moved by the OS / host window manager, e.g. pressing ALT-F4) + + ImGuiViewport() { memset(this, 0, sizeof(*this)); } + ~ImGuiViewport() { IM_ASSERT(PlatformUserData == NULL && RendererUserData == NULL); } + + // Access work-area rectangle with GetWorkXXX functions (see comments above) + ImVec2 GetCenter() { return ImVec2(Pos.x + Size.x * 0.5f, Pos.y + Size.y * 0.5f); } + ImVec2 GetWorkPos() { return ImVec2(Pos.x + WorkOffsetMin.x, Pos.y + WorkOffsetMin.y); } + ImVec2 GetWorkSize() { return ImVec2(Size.x - WorkOffsetMin.x + WorkOffsetMax.x, Size.y - WorkOffsetMin.y + WorkOffsetMax.y); } // This not clamped +}; + //----------------------------------------------------------------------------- // [SECTION] Platform interface for multi-viewport support //----------------------------------------------------------------------------- @@ -2944,58 +3001,6 @@ struct ImGuiPlatformMonitor ImGuiPlatformMonitor() { MainPos = MainSize = WorkPos = WorkSize = ImVec2(0, 0); DpiScale = 1.0f; } }; -// Flags stored in ImGuiViewport::Flags, giving indications to the platform backends. -enum ImGuiViewportFlags_ -{ - ImGuiViewportFlags_None = 0, - ImGuiViewportFlags_NoDecoration = 1 << 0, // Platform Window: Disable platform decorations: title bar, borders, etc. (generally set all windows, but if ImGuiConfigFlags_ViewportsDecoration is set we only set this on popups/tooltips) - ImGuiViewportFlags_NoTaskBarIcon = 1 << 1, // Platform Window: Disable platform task bar icon (generally set on popups/tooltips, or all windows if ImGuiConfigFlags_ViewportsNoTaskBarIcon is set) - ImGuiViewportFlags_NoFocusOnAppearing = 1 << 2, // Platform Window: Don't take focus when created. - ImGuiViewportFlags_NoFocusOnClick = 1 << 3, // Platform Window: Don't take focus when clicked on. - ImGuiViewportFlags_NoInputs = 1 << 4, // Platform Window: Make mouse pass through so we can drag this window while peaking behind it. - ImGuiViewportFlags_NoRendererClear = 1 << 5, // Platform Window: Renderer doesn't need to clear the framebuffer ahead (because we will fill it entirely). - ImGuiViewportFlags_TopMost = 1 << 6, // Platform Window: Display on top (for tooltips only). - ImGuiViewportFlags_Minimized = 1 << 7, // Platform Window: Window is minimized, can skip render. When minimized we tend to avoid using the viewport pos/size for clipping window or testing if they are contained in the viewport. - ImGuiViewportFlags_NoAutoMerge = 1 << 8, // Platform Window: Avoid merging this window into another host window. This can only be set via ImGuiWindowClass viewport flags override (because we need to now ahead if we are going to create a viewport in the first place!). - ImGuiViewportFlags_CanHostOtherWindows = 1 << 9 // Main viewport: can host multiple imgui windows (secondary viewports are associated to a single window). -}; - -// The viewports created and managed by Dear ImGui. The role of the platform backend is to create the platform/OS windows corresponding to each viewport. -// - Main Area = entire viewport. -// - Work Area = entire viewport minus sections optionally used by menu bars, status bars. Some positioning code will prefer to use this. Window are also trying to stay within this area. -struct ImGuiViewport -{ - ImGuiID ID; // Unique identifier for the viewport - ImGuiViewportFlags Flags; // See ImGuiViewportFlags_ - ImVec2 Pos; // Main Area: Position of the viewport (the imgui coordinates are the same as OS desktop/native coordinates) - ImVec2 Size; // Main Area: Size of the viewport. - ImVec2 WorkOffsetMin; // Work Area: Offset from Pos to top-left corner of Work Area. Generally (0,0) or (0,+main_menu_bar_height). Work Area is Full Area but without menu-bars/status-bars (so WorkArea always fit inside Pos/Size!) - ImVec2 WorkOffsetMax; // Work Area: Offset from Pos+Size to bottom-right corner of Work Area. Generally (0,0) or (0,-status_bar_height). - float DpiScale; // 1.0f = 96 DPI = No extra scale. - ImGuiID ParentViewportId; // (Advanced) 0: no parent. Instruct the platform backend to setup a parent/child relationship between platform windows. - ImDrawData* DrawData; // The ImDrawData corresponding to this viewport. Valid after Render() and until the next call to NewFrame(). - - // Our design separate the Renderer and Platform backends to facilitate combining default backends with each others. - // When our create your own backend for a custom engine, it is possible that both Renderer and Platform will be handled - // by the same system and you may not need to use all the UserData/Handle fields. - // The library never uses those fields, they are merely storage to facilitate backend implementation. - void* RendererUserData; // void* to hold custom data structure for the renderer (e.g. swap chain, framebuffers etc.). generally set by your Renderer_CreateWindow function. - void* PlatformUserData; // void* to hold custom data structure for the OS / platform (e.g. windowing info, render context). generally set by your Platform_CreateWindow function. - void* PlatformHandle; // void* for FindViewportByPlatformHandle(). (e.g. suggested to use natural platform handle such as HWND, GLFWWindow*, SDL_Window*) - void* PlatformHandleRaw; // void* to hold lower-level, platform-native window handle (e.g. the HWND) when using an abstraction layer like GLFW or SDL (where PlatformHandle would be a SDL_Window*) - bool PlatformRequestMove; // Platform window requested move (e.g. window was moved by the OS / host window manager, authoritative position will be OS window position) - bool PlatformRequestResize; // Platform window requested resize (e.g. window was resized by the OS / host window manager, authoritative size will be OS window size) - bool PlatformRequestClose; // Platform window requested closure (e.g. window was moved by the OS / host window manager, e.g. pressing ALT-F4) - - ImGuiViewport() { memset(this, 0, sizeof(*this)); } - ~ImGuiViewport() { IM_ASSERT(PlatformUserData == NULL && RendererUserData == NULL); } - - // Access work-area rectangle with GetWorkXXX functions (see comments above) - ImVec2 GetCenter() { return ImVec2(Pos.x + Size.x * 0.5f, Pos.y + Size.y * 0.5f); } - ImVec2 GetWorkPos() { return ImVec2(Pos.x + WorkOffsetMin.x, Pos.y + WorkOffsetMin.y); } - ImVec2 GetWorkSize() { return ImVec2(Size.x - WorkOffsetMin.x + WorkOffsetMax.x, Size.y - WorkOffsetMin.y + WorkOffsetMax.y); } // This not clamped -}; - #if defined(__clang__) #pragma clang diagnostic pop #elif defined(__GNUC__) From 1a3af8cb4cc1d58f3b7a755e491943efe9b8feab Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 9 Feb 2021 12:02:55 +0100 Subject: [PATCH 668/828] Viewports: trying to treat GetMainViewport() as const. Reducing unnecessary casts of ImGuiViewportP* Metrics: readded root Drawlists node in metrics to match master. The (void*) casts are implying const-casst but currently left GetMainViewport() as returning non-const. --- imgui.cpp | 48 +++++++++++++++++++++++++++++++++++------------ imgui.h | 8 ++++---- imgui_demo.cpp | 6 +++--- imgui_internal.h | 5 +++-- imgui_widgets.cpp | 2 +- 5 files changed, 47 insertions(+), 22 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 6f3e949439cf..2b48b27bfea4 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5295,7 +5295,7 @@ ImGuiWindow* ImGui::FindWindowByName(const char* name) static void ApplyWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settings) { - ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + const ImGuiViewport* main_viewport = ImGui::GetMainViewport(); window->ViewportPos = main_viewport->Pos; if (settings->ViewportId) { @@ -5321,7 +5321,7 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags) g.WindowsById.SetVoidPtr(window->ID, window); // Default/arbitrary window position. Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window. - ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + const ImGuiViewport* main_viewport = ImGui::GetMainViewport(); window->Pos = main_viewport->Pos + ImVec2(60, 60); window->ViewportPos = main_viewport->Pos; @@ -6412,7 +6412,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if (window->Viewport->PlatformMonitor == -1) { // Fallback for "lost" window (e.g. a monitor disconnected): we move the window back over the main viewport - ImGuiViewport* main_viewport = GetMainViewport(); + const ImGuiViewport* main_viewport = GetMainViewport(); SetWindowPos(window, main_viewport->Pos + style.DisplayWindowPadding, ImGuiCond_Always); } else @@ -8878,8 +8878,8 @@ bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags fla // FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window. if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) == 0) { - ImGuiViewportP* viewport = window->WasActive ? window->Viewport : (ImGuiViewportP*)GetMainViewport(); // FIXME-VIEWPORT: What may be our reference viewport? - SetNextWindowPos(viewport->GetMainRect().GetCenter(), ImGuiCond_FirstUseEver, ImVec2(0.5f, 0.5f)); + const ImGuiViewport* viewport = window->WasActive ? window->Viewport : GetMainViewport(); // FIXME-VIEWPORT: What may be our reference viewport? + SetNextWindowPos(viewport->GetCenter(), ImGuiCond_FirstUseEver, ImVec2(0.5f, 0.5f)); } flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoDocking; @@ -10259,7 +10259,7 @@ void ImGui::NavUpdateWindowingOverlay() if (g.NavWindowingListWindow == NULL) g.NavWindowingListWindow = FindWindowByName("###NavWindowingList"); - ImGuiViewportP* viewport = /*g.NavWindow ? g.NavWindow->Viewport :*/ (ImGuiViewportP*)GetMainViewport(); + const ImGuiViewport* viewport = /*g.NavWindow ? g.NavWindow->Viewport :*/ GetMainViewport(); SetNextWindowSizeConstraints(ImVec2(viewport->Size.x * 0.20f, viewport->Size.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX)); SetNextWindowPos(viewport->Pos + viewport->Size * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f)); PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f); @@ -11589,7 +11589,7 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) window->ViewportAllowPlatformMonitorExtend = -1; // Restore main viewport if multi-viewport is not supported by the backend - ImGuiViewportP* main_viewport = (ImGuiViewportP*)GetMainViewport(); + ImGuiViewportP* main_viewport = (ImGuiViewportP*)(void*)GetMainViewport(); if (!(g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable)) { SetWindowViewport(window, main_viewport); @@ -14409,7 +14409,7 @@ void ImGui::DockNodeTreeUpdateSplitter(ImGuiDockNode* node) /* // [DEBUG] Render limits - ImDrawList* draw_list = node->HostWindow ? GetForegroundDrawList(node->HostWindow) : GetForegroundDrawList((ImGuiViewportP*)GetMainViewport()); + ImDrawList* draw_list = node->HostWindow ? GetForegroundDrawList(node->HostWindow) : GetForegroundDrawList(GetMainViewport()); for (int n = 0; n < 2; n++) if (axis == ImGuiAxis_X) draw_list->AddLine(ImVec2(resize_limits[n], node->ChildNodes[n]->Pos.y), ImVec2(resize_limits[n], node->ChildNodes[n]->Pos.y + node->ChildNodes[n]->Size.y), IM_COL32(255, 0, 255, 255), 3.0f); @@ -14437,7 +14437,7 @@ void ImGui::DockNodeTreeUpdateSplitter(ImGuiDockNode* node) for (int touching_node_n = 0; touching_node_n < touching_nodes[side_n].Size; touching_node_n++) { ImGuiDockNode* touching_node = touching_nodes[side_n][touching_node_n]; - //ImDrawList* draw_list = node->HostWindow ? GetForegroundDrawList(node->HostWindow) : GetForegroundDrawList((ImGuiViewportP*)GetMainViewport()); + //ImDrawList* draw_list = node->HostWindow ? GetForegroundDrawList(node->HostWindow) : GetForegroundDrawList(GetMainViewport()); //draw_list->AddRect(touching_node->Pos, touching_node->Pos + touching_node->Size, IM_COL32(255, 128, 0, 255)); while (touching_node->ParentNode != node) { @@ -14653,7 +14653,7 @@ void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags fla // The limitation with this call is that your window won't have a menu bar. // Even though we could pass window flags, it would also require the user to be able to call BeginMenuBar() somehow meaning we can't Begin/End in a single function. // But you can also use BeginMainMenuBar(). If you really want a menu bar inside the same window as the one hosting the dockspace, you will need to copy this code somewhere and tweak it. -ImGuiID ImGui::DockSpaceOverViewport(ImGuiViewport* viewport, ImGuiDockNodeFlags dockspace_flags, const ImGuiWindowClass* window_class) +ImGuiID ImGui::DockSpaceOverViewport(const ImGuiViewport* viewport, ImGuiDockNodeFlags dockspace_flags, const ImGuiWindowClass* window_class) { if (viewport == NULL) viewport = GetMainViewport(); @@ -15980,9 +15980,33 @@ void ImGui::ShowMetricsWindow(bool* p_open) TreePop(); } - // Contents + // Windows DebugNodeWindowsList(&g.Windows, "Windows"); //DebugNodeWindowsList(&g.WindowsFocusOrder, "WindowsFocusOrder"); + + // Drawlists + int drawlist_count = 0; + for (int viewport_i = 0; viewport_i < g.Viewports.Size; viewport_i++) + drawlist_count += g.Viewports[viewport_i]->DrawDataBuilder.GetDrawListCount(); + if (TreeNode("DrawLists", "DrawLists (%d)", drawlist_count)) + { + for (int viewport_i = 0; viewport_i < g.Viewports.Size; viewport_i++) + { + ImGuiViewportP* viewport = g.Viewports[viewport_i]; + bool viewport_has_drawlist = false; + for (int layer_i = 0; layer_i < IM_ARRAYSIZE(viewport->DrawDataBuilder.Layers); layer_i++) + for (int draw_list_i = 0; draw_list_i < viewport->DrawDataBuilder.Layers[layer_i].Size; draw_list_i++) + { + if (!viewport_has_drawlist) + ImGui::Text("Active DrawLists in Viewport #%d, ID: 0x%08X", viewport->Idx, viewport->ID); + viewport_has_drawlist = true; + DebugNodeDrawList(NULL, viewport, viewport->DrawDataBuilder.Layers[layer_i][draw_list_i], "DrawList"); + } + } + TreePop(); + } + + // Viewports if (TreeNode("Viewports", "Viewports (%d)", g.Viewports.Size)) { Indent(GetTreeNodeToLabelSpacing()); @@ -16228,7 +16252,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) char buf[64] = ""; char* p = buf; ImGuiDockNode* node = g.HoveredDockNode; - ImDrawList* overlay_draw_list = node->HostWindow ? GetForegroundDrawList(node->HostWindow) : GetForegroundDrawList((ImGuiViewportP*)GetMainViewport()); + ImDrawList* overlay_draw_list = node->HostWindow ? GetForegroundDrawList(node->HostWindow) : GetForegroundDrawList(GetMainViewport()); p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "DockId: %X%s\n", node->ID, node->IsCentralNode() ? " *CentralNode*" : ""); p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "WindowClass: %08X\n", node->WindowClass.ClassId); p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "Size: (%.0f, %.0f)\n", node->Size.x, node->Size.y); diff --git a/imgui.h b/imgui.h index dec2fb217e0c..898bc30010a4 100644 --- a/imgui.h +++ b/imgui.h @@ -756,7 +756,7 @@ namespace ImGui // - Use DockSpace() to create an explicit dock node _within_ an existing window. See Docking demo for details. // - DockSpace() needs to be submitted _before_ any window they can host. If you use a dockspace, submit it early in your app. IMGUI_API void DockSpace(ImGuiID id, const ImVec2& size = ImVec2(0, 0), ImGuiDockNodeFlags flags = 0, const ImGuiWindowClass* window_class = NULL); - IMGUI_API ImGuiID DockSpaceOverViewport(ImGuiViewport* viewport = NULL, ImGuiDockNodeFlags flags = 0, const ImGuiWindowClass* window_class = NULL); + IMGUI_API ImGuiID DockSpaceOverViewport(const ImGuiViewport* viewport = NULL, ImGuiDockNodeFlags flags = 0, const ImGuiWindowClass* window_class = NULL); IMGUI_API void SetNextWindowDockID(ImGuiID dock_id, ImGuiCond cond = 0); // set next window dock id (FIXME-DOCK) IMGUI_API void SetNextWindowClass(const ImGuiWindowClass* window_class); // set next window class (rare/advanced uses: provide hints to the platform backend via altered viewport flags and parent/child info) IMGUI_API ImGuiID GetWindowDockID(); @@ -2874,9 +2874,9 @@ struct ImGuiViewport ~ImGuiViewport() { IM_ASSERT(PlatformUserData == NULL && RendererUserData == NULL); } // Access work-area rectangle with GetWorkXXX functions (see comments above) - ImVec2 GetCenter() { return ImVec2(Pos.x + Size.x * 0.5f, Pos.y + Size.y * 0.5f); } - ImVec2 GetWorkPos() { return ImVec2(Pos.x + WorkOffsetMin.x, Pos.y + WorkOffsetMin.y); } - ImVec2 GetWorkSize() { return ImVec2(Size.x - WorkOffsetMin.x + WorkOffsetMax.x, Size.y - WorkOffsetMin.y + WorkOffsetMax.y); } // This not clamped + ImVec2 GetCenter() const { return ImVec2(Pos.x + Size.x * 0.5f, Pos.y + Size.y * 0.5f); } + ImVec2 GetWorkPos() const { return ImVec2(Pos.x + WorkOffsetMin.x, Pos.y + WorkOffsetMin.y); } + ImVec2 GetWorkSize() const { return ImVec2(Size.x - WorkOffsetMin.x + WorkOffsetMax.x, Size.y - WorkOffsetMin.y + WorkOffsetMax.y); } // This not clamped }; //----------------------------------------------------------------------------- diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 09ddb47691dc..6beaafc48eaf 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -333,7 +333,7 @@ void ImGui::ShowDemoWindow(bool* p_open) // We specify a default position/size in case there's no data in the .ini file. // We only do it to make the demo applications a little more welcoming, but typically this isn't required. - ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + const ImGuiViewport* main_viewport = ImGui::GetMainViewport(); ImGui::SetNextWindowPos(ImVec2(main_viewport->GetWorkPos().x + 650, main_viewport->GetWorkPos().y + 20), ImGuiCond_FirstUseEver); ImGui::SetNextWindowSize(ImVec2(550, 680), ImGuiCond_FirstUseEver); @@ -7031,7 +7031,7 @@ static void ShowExampleAppSimpleOverlay(bool* p_open) if (corner != -1) { window_flags |= ImGuiWindowFlags_NoMove; - ImGuiViewport* viewport = ImGui::GetMainViewport(); + const ImGuiViewport* viewport = ImGui::GetMainViewport(); ImVec2 work_area_pos = viewport->GetWorkPos(); // Instead of using viewport->Pos we use GetWorkPos() to avoid menu bars, if any! ImVec2 work_area_size = viewport->GetWorkSize(); ImVec2 window_pos = ImVec2((corner & 1) ? (work_area_pos.x + work_area_size.x - DISTANCE) : (work_area_pos.x + DISTANCE), (corner & 2) ? (work_area_pos.y + work_area_size.y - DISTANCE) : (work_area_pos.y + DISTANCE)); @@ -7368,7 +7368,7 @@ void ShowExampleAppDockSpace(bool* p_open) ImGuiWindowFlags window_flags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking; if (opt_fullscreen) { - ImGuiViewport* viewport = ImGui::GetMainViewport(); + const ImGuiViewport* viewport = ImGui::GetMainViewport(); ImGui::SetNextWindowPos(viewport->GetWorkPos()); ImGui::SetNextWindowSize(viewport->GetWorkSize()); ImGui::SetNextWindowViewport(viewport->ID); diff --git a/imgui_internal.h b/imgui_internal.h index 3ff8c521f295..1a6bc2898ae8 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -664,8 +664,9 @@ struct ImDrawDataBuilder { ImVector Layers[2]; // Global layers for: regular, tooltip - void Clear() { for (int n = 0; n < IM_ARRAYSIZE(Layers); n++) Layers[n].resize(0); } - void ClearFreeMemory() { for (int n = 0; n < IM_ARRAYSIZE(Layers); n++) Layers[n].clear(); } + void Clear() { for (int n = 0; n < IM_ARRAYSIZE(Layers); n++) Layers[n].resize(0); } + void ClearFreeMemory() { for (int n = 0; n < IM_ARRAYSIZE(Layers); n++) Layers[n].clear(); } + int GetDrawListCount() const { int count = 0; for (int n = 0; n < IM_ARRAYSIZE(Layers); n++) count += Layers[n].Size; return count; } IMGUI_API void FlattenIntoSingleLayer(); }; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index d0539412bb07..17761630fdfb 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6575,7 +6575,7 @@ void ImGui::EndMenuBar() bool ImGui::BeginMainMenuBar() { ImGuiContext& g = *GImGui; - ImGuiViewportP* viewport = (ImGuiViewportP*)GetMainViewport(); + ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)GetMainViewport(); ImGuiWindow* menu_bar_window = FindWindowByName("##MainMenuBar"); // For the main menu bar, which cannot be moved, we honor g.Style.DisplaySafeAreaPadding to ensure text can be visible on a TV set. From 73ccb7e4b8c27af6c7756025f29c495b4effd359 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 9 Feb 2021 12:57:00 +0100 Subject: [PATCH 669/828] Viewports: (Breaking) turned GetWorkPos(), GetWorkSize() into straight fields -> WorkPos, WorkSize before exposing in master branch. --- imgui.cpp | 14 +++++++++++--- imgui.h | 11 +++++------ imgui_demo.cpp | 10 +++++----- imgui_internal.h | 7 +++++-- 4 files changed, 26 insertions(+), 16 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 2b48b27bfea4..8d1ff87dddb6 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3583,7 +3583,10 @@ void ImGui::UpdateMouseMovingWindowNewFrame() MarkIniSettingsDirty(moving_window); SetWindowPos(moving_window, pos, ImGuiCond_Always); if (moving_window->ViewportOwned) // Synchronize viewport immediately because some overlays may relies on clipping rectangle before we Begin() into the window. + { moving_window->Viewport->Pos = pos; + moving_window->Viewport->UpdateWorkRect(); + } } FocusWindow(g.MovingWindow); } @@ -6340,6 +6343,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) viewport_rect_changed = true; window->Viewport->Size = window->Size; } + window->Viewport->UpdateWorkRect(); // The viewport may have changed monitor since the global update in UpdateViewportsNewFrame() // Either a SetNextWindowPos() call in the current frame or a SetWindowPos() call in the previous frame may have this effect. @@ -6467,6 +6471,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->Viewport->Pos = window->Pos; if (!window->Viewport->PlatformRequestResize) window->Viewport->Size = window->Size; + window->Viewport->UpdateWorkRect(); viewport_rect = window->Viewport->GetMainRect(); } @@ -11398,6 +11403,7 @@ static void ImGui::UpdateViewportsNewFrame() // We do it early in the frame instead of waiting for UpdatePlatformWindows() to avoid a frame of lag when moving/resizing using OS facilities. if (!(viewport->Flags & ImGuiViewportFlags_Minimized) && platform_funcs_available) { + // Viewport->WorkPos and WorkSize will be updated below if (viewport->PlatformRequestMove) viewport->Pos = viewport->LastPlatformPos = g.PlatformIO.Platform_GetWindowPos(viewport); if (viewport->PlatformRequestResize) @@ -11412,6 +11418,7 @@ static void ImGui::UpdateViewportsNewFrame() viewport->WorkOffsetMin = viewport->CurrWorkOffsetMin; viewport->WorkOffsetMax = viewport->CurrWorkOffsetMax; viewport->CurrWorkOffsetMin = viewport->CurrWorkOffsetMax = ImVec2(0.0f, 0.0f); + viewport->UpdateWorkRect(); // Reset alpha every frame. Users of transparency (docking) needs to request a lower alpha back. viewport->Alpha = 1.0f; @@ -11573,6 +11580,7 @@ ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const viewport->Window = window; viewport->LastFrameActive = g.FrameCount; + viewport->UpdateWorkRect(); IM_ASSERT(window == NULL || viewport->ID == window->ID); if (window != NULL) @@ -14658,8 +14666,8 @@ ImGuiID ImGui::DockSpaceOverViewport(const ImGuiViewport* viewport, ImGuiDockNod if (viewport == NULL) viewport = GetMainViewport(); - SetNextWindowPos(viewport->GetWorkPos()); - SetNextWindowSize(viewport->GetWorkSize()); + SetNextWindowPos(viewport->WorkPos); + SetNextWindowSize(viewport->WorkSize); SetNextWindowViewport(viewport->ID); ImGuiWindowFlags host_window_flags = 0; @@ -16524,7 +16532,7 @@ void ImGui::DebugNodeViewport(ImGuiViewportP* viewport) viewport->Pos.x, viewport->Pos.y, viewport->Size.x, viewport->Size.y, viewport->WorkOffsetMin.x, viewport->WorkOffsetMin.y, viewport->WorkOffsetMax.x, viewport->WorkOffsetMax.y, viewport->PlatformMonitor, viewport->DpiScale * 100.0f); - if (viewport->Idx > 0) { SameLine(); if (SmallButton("Reset Pos")) { viewport->Pos = ImVec2(200,200); if (viewport->Window) viewport->Window->Pos = ImVec2(200,200); } } + if (viewport->Idx > 0) { SameLine(); if (SmallButton("Reset Pos")) { viewport->Pos = ImVec2(200, 200); viewport->UpdateWorkRect(); if (viewport->Window) viewport->Window->Pos = viewport->Pos; } } BulletText("Flags: 0x%04X =%s%s%s%s%s%s%s", viewport->Flags, (flags & ImGuiViewportFlags_CanHostOtherWindows) ? " CanHostOtherWindows" : "", (flags & ImGuiViewportFlags_NoDecoration) ? " NoDecoration" : "", (flags & ImGuiViewportFlags_NoFocusOnAppearing) ? " NoFocusOnAppearing" : "", (flags & ImGuiViewportFlags_NoInputs) ? " NoInputs" : "", diff --git a/imgui.h b/imgui.h index 898bc30010a4..522e38762341 100644 --- a/imgui.h +++ b/imgui.h @@ -2850,10 +2850,10 @@ struct ImGuiViewport { ImGuiID ID; // Unique identifier for the viewport ImGuiViewportFlags Flags; // See ImGuiViewportFlags_ - ImVec2 Pos; // Main Area: Position of the viewport (the imgui coordinates are the same as OS desktop/native coordinates) + ImVec2 Pos; // Main Area: Position of the viewport (Dear Imgui coordinates are the same as OS desktop/native coordinates) ImVec2 Size; // Main Area: Size of the viewport. - ImVec2 WorkOffsetMin; // Work Area: Offset from Pos to top-left corner of Work Area. Generally (0,0) or (0,+main_menu_bar_height). Work Area is Full Area but without menu-bars/status-bars (so WorkArea always fit inside Pos/Size!) - ImVec2 WorkOffsetMax; // Work Area: Offset from Pos+Size to bottom-right corner of Work Area. Generally (0,0) or (0,-status_bar_height). + ImVec2 WorkPos; // Work Area: Position of the viewport minus task bars, menus bars, status bars (>= Pos) + ImVec2 WorkSize; // Work Area: Size of the viewport minus task bars, menu bars, status bars (<= Size) float DpiScale; // 1.0f = 96 DPI = No extra scale. ImGuiID ParentViewportId; // (Advanced) 0: no parent. Instruct the platform backend to setup a parent/child relationship between platform windows. ImDrawData* DrawData; // The ImDrawData corresponding to this viewport. Valid after Render() and until the next call to NewFrame(). @@ -2873,10 +2873,9 @@ struct ImGuiViewport ImGuiViewport() { memset(this, 0, sizeof(*this)); } ~ImGuiViewport() { IM_ASSERT(PlatformUserData == NULL && RendererUserData == NULL); } - // Access work-area rectangle with GetWorkXXX functions (see comments above) + // Helpers ImVec2 GetCenter() const { return ImVec2(Pos.x + Size.x * 0.5f, Pos.y + Size.y * 0.5f); } - ImVec2 GetWorkPos() const { return ImVec2(Pos.x + WorkOffsetMin.x, Pos.y + WorkOffsetMin.y); } - ImVec2 GetWorkSize() const { return ImVec2(Size.x - WorkOffsetMin.x + WorkOffsetMax.x, Size.y - WorkOffsetMin.y + WorkOffsetMax.y); } // This not clamped + ImVec2 GetWorkCenter() const { return ImVec2(WorkPos.x + WorkSize.x * 0.5f, WorkPos.y + WorkSize.y * 0.5f); } }; //----------------------------------------------------------------------------- diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 6beaafc48eaf..47a6f9d77da8 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -334,7 +334,7 @@ void ImGui::ShowDemoWindow(bool* p_open) // We specify a default position/size in case there's no data in the .ini file. // We only do it to make the demo applications a little more welcoming, but typically this isn't required. const ImGuiViewport* main_viewport = ImGui::GetMainViewport(); - ImGui::SetNextWindowPos(ImVec2(main_viewport->GetWorkPos().x + 650, main_viewport->GetWorkPos().y + 20), ImGuiCond_FirstUseEver); + ImGui::SetNextWindowPos(ImVec2(main_viewport->WorkPos.x + 650, main_viewport->WorkPos.y + 20), ImGuiCond_FirstUseEver); ImGui::SetNextWindowSize(ImVec2(550, 680), ImGuiCond_FirstUseEver); // Main body of the Demo window starts here. @@ -7032,8 +7032,8 @@ static void ShowExampleAppSimpleOverlay(bool* p_open) { window_flags |= ImGuiWindowFlags_NoMove; const ImGuiViewport* viewport = ImGui::GetMainViewport(); - ImVec2 work_area_pos = viewport->GetWorkPos(); // Instead of using viewport->Pos we use GetWorkPos() to avoid menu bars, if any! - ImVec2 work_area_size = viewport->GetWorkSize(); + ImVec2 work_area_pos = viewport->WorkPos; // Use work area to avoid menu-bar/task-bar, if any! + ImVec2 work_area_size = viewport->WorkSize; ImVec2 window_pos = ImVec2((corner & 1) ? (work_area_pos.x + work_area_size.x - DISTANCE) : (work_area_pos.x + DISTANCE), (corner & 2) ? (work_area_pos.y + work_area_size.y - DISTANCE) : (work_area_pos.y + DISTANCE)); ImVec2 window_pos_pivot = ImVec2((corner & 1) ? 1.0f : 0.0f, (corner & 2) ? 1.0f : 0.0f); ImGui::SetNextWindowPos(window_pos, ImGuiCond_Always, window_pos_pivot); @@ -7369,8 +7369,8 @@ void ShowExampleAppDockSpace(bool* p_open) if (opt_fullscreen) { const ImGuiViewport* viewport = ImGui::GetMainViewport(); - ImGui::SetNextWindowPos(viewport->GetWorkPos()); - ImGui::SetNextWindowSize(viewport->GetWorkSize()); + ImGui::SetNextWindowPos(viewport->WorkPos); + ImGui::SetNextWindowSize(viewport->WorkSize); ImGui::SetNextWindowViewport(viewport->ID); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); diff --git a/imgui_internal.h b/imgui_internal.h index 1a6bc2898ae8..f5b4da83b2ce 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1316,13 +1316,16 @@ struct ImGuiViewportP : public ImGuiViewport ImVec2 LastPlatformPos; ImVec2 LastPlatformSize; ImVec2 LastRendererSize; - ImVec2 CurrWorkOffsetMin; // Work area top-left offset being increased during the frame - ImVec2 CurrWorkOffsetMax; // Work area bottom-right offset being decreased during the frame + ImVec2 WorkOffsetMin; // Work Area: Offset from Pos to top-left corner of Work Area. Generally (0,0) or (0,+main_menu_bar_height). Work Area is Full Area but without menu-bars/status-bars (so WorkArea always fit inside Pos/Size!) + ImVec2 WorkOffsetMax; // Work Area: Offset from Pos+Size to bottom-right corner of Work Area. Generally (0,0) or (0,-status_bar_height). + ImVec2 CurrWorkOffsetMin; // Work Area: Offset being built/increased during current frame + ImVec2 CurrWorkOffsetMax; // Work Area: Offset being built/decreased during current frame ImGuiViewportP() { Idx = -1; LastFrameActive = LastFrameDrawLists[0] = LastFrameDrawLists[1] = LastFrontMostStampCount = -1; LastNameHash = 0; Alpha = LastAlpha = 1.0f; PlatformMonitor = -1; PlatformWindowCreated = false; Window = NULL; DrawLists[0] = DrawLists[1] = NULL; LastPlatformPos = LastPlatformSize = LastRendererSize = ImVec2(FLT_MAX, FLT_MAX); } ~ImGuiViewportP() { if (DrawLists[0]) IM_DELETE(DrawLists[0]); if (DrawLists[1]) IM_DELETE(DrawLists[1]); } ImRect GetMainRect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } ImRect GetWorkRect() const { return ImRect(Pos.x + WorkOffsetMin.x, Pos.y + WorkOffsetMin.y, Pos.x + Size.x + WorkOffsetMax.x, Pos.y + Size.y + WorkOffsetMax.y); } + void UpdateWorkRect() { WorkPos = Pos + WorkOffsetMin; WorkSize = ImVec2(ImMax(0.0f, Size.x - WorkOffsetMin.x + WorkOffsetMax.x), ImMax(0.0f, Size.y - WorkOffsetMin.y + WorkOffsetMax.y)); } void ClearRequestFlags() { PlatformRequestClose = PlatformRequestMove = PlatformRequestResize = false; } }; From 6f5f80a544f1542e20995359a3646416961264bd Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 9 Feb 2021 14:06:30 +0100 Subject: [PATCH 670/828] Fix for compiling imgui_internal.h without operators + made GetWorkRect() consistent with clamped WorkSize. --- imgui_internal.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/imgui_internal.h b/imgui_internal.h index 0d4c517ae9ed..e669929964cd 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1324,8 +1324,8 @@ struct ImGuiViewportP : public ImGuiViewport ImGuiViewportP() { Idx = -1; LastFrameActive = LastFrameDrawLists[0] = LastFrameDrawLists[1] = LastFrontMostStampCount = -1; LastNameHash = 0; Alpha = LastAlpha = 1.0f; PlatformMonitor = -1; PlatformWindowCreated = false; Window = NULL; DrawLists[0] = DrawLists[1] = NULL; LastPlatformPos = LastPlatformSize = LastRendererSize = ImVec2(FLT_MAX, FLT_MAX); } ~ImGuiViewportP() { if (DrawLists[0]) IM_DELETE(DrawLists[0]); if (DrawLists[1]) IM_DELETE(DrawLists[1]); } ImRect GetMainRect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } - ImRect GetWorkRect() const { return ImRect(Pos.x + WorkOffsetMin.x, Pos.y + WorkOffsetMin.y, Pos.x + Size.x + WorkOffsetMax.x, Pos.y + Size.y + WorkOffsetMax.y); } - void UpdateWorkRect() { WorkPos = Pos + WorkOffsetMin; WorkSize = ImVec2(ImMax(0.0f, Size.x - WorkOffsetMin.x + WorkOffsetMax.x), ImMax(0.0f, Size.y - WorkOffsetMin.y + WorkOffsetMax.y)); } + ImRect GetWorkRect() const { return ImRect(WorkPos.x, WorkPos.y, WorkPos.x + WorkSize.x, WorkPos.y + WorkSize.y); } + void UpdateWorkRect() { WorkPos = ImVec2(Pos.x + WorkOffsetMin.x, Pos.y + WorkOffsetMin.y); WorkSize = ImVec2(ImMax(0.0f, Size.x - WorkOffsetMin.x + WorkOffsetMax.x), ImMax(0.0f, Size.y - WorkOffsetMin.y + WorkOffsetMax.y)); } void ClearRequestFlags() { PlatformRequestClose = PlatformRequestMove = PlatformRequestResize = false; } }; From 2a5eaf239fc6cbfdf855fe6a6b06d0610c167e38 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 10 Feb 2021 14:24:19 +0100 Subject: [PATCH 671/828] Misc tweaks - mostly toward minimizing diff in upcoming backport merge of a few viewport structures in master --- imgui.cpp | 69 +++++++++++++++++++++++------------------------ imgui.h | 4 +-- imgui_demo.cpp | 36 ++++++++++++++----------- imgui_internal.h | 22 +++++++-------- imgui_widgets.cpp | 5 ++-- 5 files changed, 69 insertions(+), 67 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 516b03d29a31..bce64ce3cd13 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3451,7 +3451,8 @@ ImGuiPlatformIO& ImGui::GetPlatformIO() ImDrawData* ImGui::GetDrawData() { ImGuiContext& g = *GImGui; - return g.Viewports[0]->DrawDataP.Valid ? &g.Viewports[0]->DrawDataP : NULL; + ImGuiViewportP* viewport = g.Viewports[0]; + return viewport->DrawDataP.Valid ? &viewport->DrawDataP : NULL; } double ImGui::GetTime() @@ -3478,12 +3479,12 @@ static ImDrawList* GetViewportDrawList(ImGuiViewportP* viewport, size_t drawlist } // Our ImDrawList system requires that there is always a command - if (viewport->LastFrameDrawLists[drawlist_no] != g.FrameCount) + if (viewport->DrawListsLastFrame[drawlist_no] != g.FrameCount) { draw_list->_ResetForNewFrame(); draw_list->PushTextureID(g.IO.Fonts->TexID); draw_list->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size, false); - viewport->LastFrameDrawLists[drawlist_no] = g.FrameCount; + viewport->DrawListsLastFrame[drawlist_no] = g.FrameCount; } return draw_list; } @@ -3495,8 +3496,8 @@ ImDrawList* ImGui::GetBackgroundDrawList(ImGuiViewport* viewport) ImDrawList* ImGui::GetBackgroundDrawList() { - ImGuiWindow* window = GImGui->CurrentWindow; - return GetBackgroundDrawList(window->Viewport); + ImGuiContext& g = *GImGui; + return GetBackgroundDrawList(g.CurrentWindow->Viewport); } ImDrawList* ImGui::GetForegroundDrawList(ImGuiViewport* viewport) @@ -3506,8 +3507,8 @@ ImDrawList* ImGui::GetForegroundDrawList(ImGuiViewport* viewport) ImDrawList* ImGui::GetForegroundDrawList() { - ImGuiWindow* window = GImGui->CurrentWindow; - return GetForegroundDrawList(window->Viewport); + ImGuiContext& g = *GImGui; + return GetForegroundDrawList(g.CurrentWindow->Viewport); } ImDrawListSharedData* ImGui::GetDrawListSharedData() @@ -3635,7 +3636,7 @@ void ImGui::UpdateMouseMovingWindowEndFrame() if (g.NavWindow && g.NavWindow->Appearing) return; - // Click on void to focus window and start moving + // Click on empty space to focus window and start moving // (after we're done with all our widgets, so e.g. clicking on docking tab-bar which have set HoveredId already and not get us here!) if (g.IO.MouseClicked[0]) { @@ -4005,7 +4006,7 @@ void ImGui::NewFrame() ImRect virtual_space(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX); for (int n = 0; n < g.Viewports.Size; n++) virtual_space.Add(g.Viewports[n]->GetMainRect()); - g.DrawListSharedData.ClipRectFullscreen = ImVec4(virtual_space.Min.x, virtual_space.Min.y, virtual_space.Max.x, virtual_space.Max.y); + g.DrawListSharedData.ClipRectFullscreen = virtual_space.ToVec4(); g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol; g.DrawListSharedData.SetCircleSegmentMaxError(g.Style.CircleSegmentMaxError); g.DrawListSharedData.InitialFlags = ImDrawListFlags_None; @@ -4391,8 +4392,9 @@ static void AddDrawListToDrawData(ImVector* out_list, ImDrawList* d static void AddWindowToDrawData(ImGuiWindow* window, int layer) { ImGuiContext& g = *GImGui; + ImGuiViewportP* viewport = window->Viewport; g.IO.MetricsRenderWindows++; - AddDrawListToDrawData(&window->Viewport->DrawDataBuilder.Layers[layer], window->DrawList); + AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[layer], window->DrawList); for (int i = 0; i < window->DC.ChildWindows.Size; i++) { ImGuiWindow* child = window->DC.ChildWindows[i]; @@ -4435,6 +4437,7 @@ static void SetupViewportDrawData(ImGuiViewportP* viewport, ImVectorFlags & ImGuiViewportFlags_Minimized) != 0; + ImGuiIO& io = ImGui::GetIO(); ImDrawData* draw_data = &viewport->DrawDataP; viewport->DrawData = draw_data; // Make publicly accessible draw_data->Valid = true; @@ -4443,7 +4446,7 @@ static void SetupViewportDrawData(ImGuiViewportP* viewport, ImVectorTotalVtxCount = draw_data->TotalIdxCount = 0; draw_data->DisplayPos = viewport->Pos; draw_data->DisplaySize = is_minimized ? ImVec2(0.0f, 0.0f) : viewport->Size; - draw_data->FramebufferScale = ImGui::GetIO().DisplayFramebufferScale; // FIXME-VIEWPORT: This may vary on a per-monitor/viewport basis? + draw_data->FramebufferScale = io.DisplayFramebufferScale; // FIXME-VIEWPORT: This may vary on a per-monitor/viewport basis? draw_data->OwnerViewport = viewport; for (int n = 0; n < draw_lists->Size; n++) { @@ -5460,7 +5463,7 @@ static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_cont const int monitor_idx = window->ViewportAllowPlatformMonitorExtend; if (monitor_idx >= 0 && monitor_idx < g.PlatformIO.Monitors.Size) avail_size = g.PlatformIO.Monitors[monitor_idx].WorkSize; - ImVec2 size_auto_fit = ImClamp(size_desired, size_min, ImMax(size_min, avail_size - g.Style.DisplaySafeAreaPadding * 2.0f)); + ImVec2 size_auto_fit = ImClamp(size_desired, size_min, ImMax(size_min, avail_size - style.DisplaySafeAreaPadding * 2.0f)); // When the window cannot fit all contents (either because of constraints, either because screen is too small), // we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than ViewportSize-WindowPadding. @@ -9079,8 +9082,7 @@ ImRect ImGui::GetWindowAllowedExtentRect(ImGuiWindow* window) else { // Use the full viewport area (not work area) for popups - r_screen.Min = window->Viewport->Pos; - r_screen.Max = window->Viewport->Pos + window->Viewport->Size; + r_screen = window->Viewport->GetMainRect(); } ImVec2 padding = g.Style.DisplaySafeAreaPadding; r_screen.Expand(ImVec2((r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f)); @@ -9090,13 +9092,14 @@ ImRect ImGui::GetWindowAllowedExtentRect(ImGuiWindow* window) ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window) { ImGuiContext& g = *GImGui; + + ImRect r_outer = GetWindowAllowedExtentRect(window); if (window->Flags & ImGuiWindowFlags_ChildMenu) { // Child menus typically request _any_ position within the parent menu item, and then we move the new menu outside the parent bounds. // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu. ImGuiWindow* parent_window = window->ParentWindow; float horizontal_overlap = g.Style.ItemInnerSpacing.x; // We want some overlap to convey the relative depth of each menu (currently the amount of overlap is hard-coded to style.ItemSpacing.x). - ImRect r_outer = GetWindowAllowedExtentRect(window); ImRect r_avoid; if (parent_window->DC.MenuBarAppending) r_avoid = ImRect(-FLT_MAX, parent_window->ClipRect.Min.y, FLT_MAX, parent_window->ClipRect.Max.y); // Avoid parent menu-bar. If we wanted multi-line menu-bar, we may instead want to have the calling window setup e.g. a NextWindowData.PosConstraintAvoidRect field @@ -9106,7 +9109,6 @@ ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window) } if (window->Flags & ImGuiWindowFlags_Popup) { - ImRect r_outer = GetWindowAllowedExtentRect(window); ImRect r_avoid = ImRect(window->Pos.x - 1, window->Pos.y - 1, window->Pos.x + 1, window->Pos.y + 1); return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Default); } @@ -9115,7 +9117,6 @@ ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window) // Position tooltip (always follows mouse) float sc = g.Style.MouseCursorScale; ImVec2 ref_pos = NavCalcPreferredRefPos(); - ImRect r_outer = GetWindowAllowedExtentRect(window); ImRect r_avoid; if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos)) r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8); @@ -9529,8 +9530,8 @@ static ImVec2 ImGui::NavCalcPreferredRefPos() // When navigation is active and mouse is disabled, decide on an arbitrary position around the bottom left of the currently navigated item. const ImRect& rect_rel = g.NavWindow->NavRectRel[g.NavLayer]; ImVec2 pos = g.NavWindow->Pos + ImVec2(rect_rel.Min.x + ImMin(g.Style.FramePadding.x * 4, rect_rel.GetWidth()), rect_rel.Max.y - ImMin(g.Style.FramePadding.y, rect_rel.GetHeight())); - ImRect visible_rect = g.NavWindow->Viewport->GetMainRect(); - return ImFloor(ImClamp(pos, visible_rect.Min, visible_rect.Max)); // ImFloor() is important because non-integer mouse position application in backend might be lossy and result in undesirable non-zero delta. + ImGuiViewport* viewport = g.NavWindow->Viewport; + return ImFloor(ImClamp(pos, viewport->Pos, viewport->Pos + viewport->Size)); // ImFloor() is important because non-integer mouse position application in backend might be lossy and result in undesirable non-zero delta. } } @@ -10275,7 +10276,7 @@ void ImGui::NavUpdateWindowingOverlay() g.NavWindowingListWindow = FindWindowByName("###NavWindowingList"); const ImGuiViewport* viewport = /*g.NavWindow ? g.NavWindow->Viewport :*/ GetMainViewport(); SetNextWindowSizeConstraints(ImVec2(viewport->Size.x * 0.20f, viewport->Size.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX)); - SetNextWindowPos(viewport->Pos + viewport->Size * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f)); + SetNextWindowPos(viewport->GetCenter(), ImGuiCond_Always, ImVec2(0.5f, 0.5f)); PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f); Begin("###NavWindowingList", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings); for (int n = g.WindowsFocusOrder.Size - 1; n >= 0; n--) @@ -15809,9 +15810,7 @@ static void RenderViewportThumbnail(ImDrawList* draw_list, ImGuiViewportP* viewp for (int i = 0; i != g.Windows.Size; i++) { ImGuiWindow* thumb_window = g.Windows[i]; - if (!thumb_window->WasActive || ((thumb_window->Flags & ImGuiWindowFlags_ChildWindow))) - continue; - if (thumb_window->SkipItems && (thumb_window->Flags & ImGuiWindowFlags_ChildWindow)) // FIXME-DOCK: Skip hidden docked windows. Identify those betters. + if (!thumb_window->WasActive || (thumb_window->Flags & ImGuiWindowFlags_ChildWindow)) continue; if (thumb_window->Viewport != viewport) continue; @@ -15840,14 +15839,10 @@ static void RenderViewportsThumbnails() // We don't display full monitor bounds (we could, but it often looks awkward), instead we display just enough to cover all of our viewports. float SCALE = 1.0f / 8.0f; ImRect bb_full; - //for (int n = 0; n < g.PlatformIO.Monitors.Size; n++) - // bb_full.Add(GetPlatformMonitorMainRect(g.PlatformIO.Monitors[n])); for (int n = 0; n < g.Viewports.Size; n++) bb_full.Add(g.Viewports[n]->GetMainRect()); ImVec2 p = window->DC.CursorPos; ImVec2 off = p - bb_full.Min * SCALE; - //for (int n = 0; n < g.PlatformIO.Monitors.Size; n++) - // window->DrawList->AddRect(off + g.PlatformIO.Monitors[n].MainPos * SCALE, off + (g.PlatformIO.Monitors[n].MainPos + g.PlatformIO.Monitors[n].MainSize) * SCALE, ImGui::GetColorU32(ImGuiCol_Border)); for (int n = 0; n < g.Viewports.Size; n++) { ImGuiViewportP* viewport = g.Viewports[n]; @@ -16019,7 +16014,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) DebugNodeWindowsList(&g.Windows, "Windows"); //DebugNodeWindowsList(&g.WindowsFocusOrder, "WindowsFocusOrder"); - // Drawlists + // DrawLists int drawlist_count = 0; for (int viewport_i = 0; viewport_i < g.Viewports.Size; viewport_i++) drawlist_count += g.Viewports[viewport_i]->DrawDataBuilder.GetDrawListCount(); @@ -16033,7 +16028,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) for (int draw_list_i = 0; draw_list_i < viewport->DrawDataBuilder.Layers[layer_i].Size; draw_list_i++) { if (!viewport_has_drawlist) - ImGui::Text("Active DrawLists in Viewport #%d, ID: 0x%08X", viewport->Idx, viewport->ID); + Text("Active DrawLists in Viewport #%d, ID: 0x%08X", viewport->Idx, viewport->ID); viewport_has_drawlist = true; DebugNodeDrawList(NULL, viewport, viewport->DrawDataBuilder.Layers[layer_i][draw_list_i], "DrawList"); } @@ -16047,8 +16042,9 @@ void ImGui::ShowMetricsWindow(bool* p_open) Indent(GetTreeNodeToLabelSpacing()); RenderViewportsThumbnails(); Unindent(GetTreeNodeToLabelSpacing()); + bool open = TreeNode("Monitors", "Monitors (%d)", g.PlatformIO.Monitors.Size); - ImGui::SameLine(); + SameLine(); MetricsHelpMarker("Dear ImGui uses monitor data:\n- to query DPI settings on a per monitor basis\n- to position popup/tooltips so they don't straddle monitors."); if (open) { @@ -16062,6 +16058,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) } TreePop(); } + for (int i = 0; i < g.Viewports.Size; i++) DebugNodeViewport(g.Viewports[i]); TreePop(); @@ -16155,14 +16152,14 @@ void ImGui::ShowMetricsWindow(bool* p_open) #endif // #ifdef IMGUI_HAS_TABLE #ifdef IMGUI_HAS_DOCK - if (ImGui::TreeNode("SettingsDocking", "Settings packed data: Docking")) + if (TreeNode("SettingsDocking", "Settings packed data: Docking")) { ImGuiDockContext* dc = &g.DockContext; - ImGui::Text("In SettingsWindows:"); + Text("In SettingsWindows:"); for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings)) if (settings->DockId != 0) - ImGui::BulletText("Window '%s' -> DockId %08X", settings->GetName(), settings->DockId); - ImGui::Text("In SettingsNodes:"); + BulletText("Window '%s' -> DockId %08X", settings->GetName(), settings->DockId); + Text("In SettingsNodes:"); for (int n = 0; n < dc->NodesSettings.Size; n++) { ImGuiDockNodeSettings* settings = &dc->NodesSettings[n]; @@ -16174,9 +16171,9 @@ void ImGui::ShowMetricsWindow(bool* p_open) else if (ImGuiWindowSettings* window_settings = FindWindowSettings(settings->SelectedWindowId)) selected_tab_name = window_settings->GetName(); } - ImGui::BulletText("Node %08X, Parent %08X, SelectedTab %08X ('%s')", settings->ID, settings->ParentNodeId, settings->SelectedWindowId, selected_tab_name ? selected_tab_name : settings->SelectedWindowId ? "N/A" : ""); + BulletText("Node %08X, Parent %08X, SelectedTab %08X ('%s')", settings->ID, settings->ParentNodeId, settings->SelectedWindowId, selected_tab_name ? selected_tab_name : settings->SelectedWindowId ? "N/A" : ""); } - ImGui::TreePop(); + TreePop(); } #endif // #ifdef IMGUI_HAS_DOCK diff --git a/imgui.h b/imgui.h index 28b18bc9994a..3345249bccd0 100644 --- a/imgui.h +++ b/imgui.h @@ -427,8 +427,8 @@ namespace ImGui IMGUI_API void SetCursorPosX(float local_x); // GetWindowPos() + GetCursorPos() == GetCursorScreenPos() etc.) IMGUI_API void SetCursorPosY(float local_y); // IMGUI_API ImVec2 GetCursorStartPos(); // initial cursor position in window coordinates - IMGUI_API ImVec2 GetCursorScreenPos(); // cursor position in absolute screen coordinates (0..io.DisplaySize) or natural OS coordinates when using multiple viewport. Useful to work with ImDrawList API. - IMGUI_API void SetCursorScreenPos(const ImVec2& pos); // cursor position in absolute screen coordinates (0..io.DisplaySize) or natural OS coordinates when using multiple viewport. + IMGUI_API ImVec2 GetCursorScreenPos(); // cursor position in absolute coordinates (useful to work with ImDrawList API). generally top-left == GetMainViewport()->Pos == (0,0) in single viewport mode, and bottom-right == GetMainViewport()->Pos+Size == io.DisplaySize in single-viewport mode. + IMGUI_API void SetCursorScreenPos(const ImVec2& pos); // cursor position in absolute coordinates IMGUI_API void AlignTextToFramePadding(); // vertically align upcoming text baseline to FramePadding.y so that it will align properly to regularly framed items (call if you have text on a line before a framed item) IMGUI_API float GetTextLineHeight(); // ~ FontSize IMGUI_API float GetTextLineHeightWithSpacing(); // ~ FontSize + style.ItemSpacing.y (distance in pixels between 2 consecutive lines of text) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 45a6dd0526bf..54a4e8803fd7 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3291,9 +3291,6 @@ static void ShowDemoWindowPopups() // Always center this window when appearing ImVec2 center = ImGui::GetMainViewport()->GetCenter(); - //ImVec2 parent_pos = ImGui::GetWindowPos(); - //ImVec2 parent_size = ImGui::GetWindowSize(); - //ImVec2 center(parent_pos.x + parent_size.x * 0.5f, parent_pos.y + parent_size.y * 0.5f); ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); if (ImGui::BeginPopupModal("Delete?", NULL, ImGuiWindowFlags_AlwaysAutoResize)) @@ -7068,21 +7065,23 @@ static void ShowExampleAppConstrainedResize(bool* p_open) // + a context-menu to choose which corner of the screen to use. static void ShowExampleAppSimpleOverlay(bool* p_open) { - // FIXME-VIEWPORT: Select a default viewport - const float DISTANCE = 10.0f; static int corner = 0; ImGuiIO& io = ImGui::GetIO(); ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav; if (corner != -1) { - window_flags |= ImGuiWindowFlags_NoMove; + const float PAD = 10.0f; const ImGuiViewport* viewport = ImGui::GetMainViewport(); - ImVec2 work_area_pos = viewport->WorkPos; // Use work area to avoid menu-bar/task-bar, if any! - ImVec2 work_area_size = viewport->WorkSize; - ImVec2 window_pos = ImVec2((corner & 1) ? (work_area_pos.x + work_area_size.x - DISTANCE) : (work_area_pos.x + DISTANCE), (corner & 2) ? (work_area_pos.y + work_area_size.y - DISTANCE) : (work_area_pos.y + DISTANCE)); - ImVec2 window_pos_pivot = ImVec2((corner & 1) ? 1.0f : 0.0f, (corner & 2) ? 1.0f : 0.0f); + ImVec2 work_pos = viewport->WorkPos; // Use work area to avoid menu-bar/task-bar, if any! + ImVec2 work_size = viewport->WorkSize; + ImVec2 window_pos, window_pos_pivot; + window_pos.x = (corner & 1) ? (work_pos.x + work_size.x - PAD) : (work_pos.x + PAD); + window_pos.y = (corner & 2) ? (work_pos.y + work_size.y - PAD) : (work_pos.y + PAD); + window_pos_pivot.x = (corner & 1) ? 1.0f : 0.0f; + window_pos_pivot.y = (corner & 2) ? 1.0f : 0.0f; ImGui::SetNextWindowPos(window_pos, ImGuiCond_Always, window_pos_pivot); ImGui::SetNextWindowViewport(viewport->ID); + window_flags |= ImGuiWindowFlags_NoMove; } ImGui::SetNextWindowBgAlpha(0.35f); // Transparent background if (ImGui::Begin("Example: Simple overlay", p_open, window_flags)) @@ -7114,10 +7113,12 @@ static void ShowExampleAppSimpleOverlay(bool* p_open) // Demonstrate creating a window covering the entire screen/viewport static void ShowExampleAppFullscreen(bool* p_open) { - ImGuiIO& io = ImGui::GetIO(); + // May use viewport->WorkPos and viewport->WorkSize to avoid menu-bar/task-bar + const ImGuiViewport* viewport = ImGui::GetMainViewport(); + ImGui::SetNextWindowPos(viewport->Pos); + ImGui::SetNextWindowSize(viewport->Size); + static ImGuiWindowFlags flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings; - ImGui::SetNextWindowPos(ImVec2(0, 0)); - ImGui::SetNextWindowSize(io.DisplaySize); if (ImGui::Begin("Example: Fullscreen window", p_open, flags)) { ImGui::CheckboxFlags("ImGuiWindowFlags_NoBackground", &flags, ImGuiWindowFlags_NoBackground); @@ -7140,16 +7141,19 @@ static void ShowExampleAppFullscreen(bool* p_open) // Read FAQ section "How can I have multiple widgets with the same label?" for details. static void ShowExampleAppWindowTitles(bool*) { + ImGuiViewport* viewport = ImGui::GetMainViewport(); + ImVec2 base_pos = viewport->WorkPos; + // By default, Windows are uniquely identified by their title. // You can use the "##" and "###" markers to manipulate the display/ID. // Using "##" to display same title but have unique identifier. - ImGui::SetNextWindowPos(ImVec2(100, 100), ImGuiCond_FirstUseEver); + ImGui::SetNextWindowPos(ImVec2(base_pos.x + 100, base_pos.y + 100), ImGuiCond_FirstUseEver); ImGui::Begin("Same title as another window##1"); ImGui::Text("This is window 1.\nMy title is the same as window 2, but my identifier is unique."); ImGui::End(); - ImGui::SetNextWindowPos(ImVec2(100, 200), ImGuiCond_FirstUseEver); + ImGui::SetNextWindowPos(ImVec2(base_pos.x + 100, base_pos.y + 200), ImGuiCond_FirstUseEver); ImGui::Begin("Same title as another window##2"); ImGui::Text("This is window 2.\nMy title is the same as window 1, but my identifier is unique."); ImGui::End(); @@ -7157,7 +7161,7 @@ static void ShowExampleAppWindowTitles(bool*) // Using "###" to display a changing title but keep a static identifier "AnimatedTitle" char buf[128]; sprintf(buf, "Animated title %c %d###AnimatedTitle", "|/-\\"[(int)(ImGui::GetTime() / 0.25f) & 3], ImGui::GetFrameCount()); - ImGui::SetNextWindowPos(ImVec2(100, 300), ImGuiCond_FirstUseEver); + ImGui::SetNextWindowPos(ImVec2(base_pos.x + 100, base_pos.y + 300), ImGuiCond_FirstUseEver); ImGui::Begin(buf); ImGui::Text("This window has a changing title."); ImGui::End(); diff --git a/imgui_internal.h b/imgui_internal.h index e669929964cd..0ea3020f776e 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1296,21 +1296,21 @@ struct ImGuiDockContext #ifdef IMGUI_HAS_VIEWPORT // ImGuiViewport Private/Internals fields (cardinal sin: we are using inheritance!) -// Note that every instance of ImGuiViewport is in fact a ImGuiViewportP. +// Every instance of ImGuiViewport is in fact a ImGuiViewportP. struct ImGuiViewportP : public ImGuiViewport { int Idx; - int LastFrameActive; // Last frame number this viewport was activated by a window - int LastFrameDrawLists[2]; // Last frame number the background (0) and foreground (1) draw lists were used - int LastFrontMostStampCount; // Last stamp number from when a window hosted by this viewport was made front-most (by comparing this value between two viewport we have an implicit viewport z-order + int LastFrameActive; // Last frame number this viewport was activated by a window + int LastFrontMostStampCount;// Last stamp number from when a window hosted by this viewport was made front-most (by comparing this value between two viewport we have an implicit viewport z-order ImGuiID LastNameHash; ImVec2 LastPos; - float Alpha; // Window opacity (when dragging dockable windows/viewports we make them transparent) + float Alpha; // Window opacity (when dragging dockable windows/viewports we make them transparent) float LastAlpha; short PlatformMonitor; bool PlatformWindowCreated; - ImGuiWindow* Window; // Set when the viewport is owned by a window (and ImGuiViewportFlags_CanHostOtherWindows is NOT set) - ImDrawList* DrawLists[2]; // Convenience background (0) and foreground (1) draw lists. We use them to draw software mouser cursor when io.MouseDrawCursor is set and to draw most debug overlays. + ImGuiWindow* Window; // Set when the viewport is owned by a window (and ImGuiViewportFlags_CanHostOtherWindows is NOT set) + int DrawListsLastFrame[2]; // Last frame number the background (0) and foreground (1) draw lists were used + ImDrawList* DrawLists[2]; // Convenience background (0) and foreground (1) draw lists. We use them to draw software mouser cursor when io.MouseDrawCursor is set and to draw most debug overlays. ImDrawData DrawDataP; ImDrawDataBuilder DrawDataBuilder; ImVec2 LastPlatformPos; @@ -1321,7 +1321,7 @@ struct ImGuiViewportP : public ImGuiViewport ImVec2 CurrWorkOffsetMin; // Work Area: Offset being built/increased during current frame ImVec2 CurrWorkOffsetMax; // Work Area: Offset being built/decreased during current frame - ImGuiViewportP() { Idx = -1; LastFrameActive = LastFrameDrawLists[0] = LastFrameDrawLists[1] = LastFrontMostStampCount = -1; LastNameHash = 0; Alpha = LastAlpha = 1.0f; PlatformMonitor = -1; PlatformWindowCreated = false; Window = NULL; DrawLists[0] = DrawLists[1] = NULL; LastPlatformPos = LastPlatformSize = LastRendererSize = ImVec2(FLT_MAX, FLT_MAX); } + ImGuiViewportP() { Idx = -1; LastFrameActive = DrawListsLastFrame[0] = DrawListsLastFrame[1] = LastFrontMostStampCount = -1; LastNameHash = 0; Alpha = LastAlpha = 1.0f; PlatformMonitor = -1; PlatformWindowCreated = false; Window = NULL; DrawLists[0] = DrawLists[1] = NULL; LastPlatformPos = LastPlatformSize = LastRendererSize = ImVec2(FLT_MAX, FLT_MAX); } ~ImGuiViewportP() { if (DrawLists[0]) IM_DELETE(DrawLists[0]); if (DrawLists[1]) IM_DELETE(DrawLists[1]); } ImRect GetMainRect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } ImRect GetWorkRect() const { return ImRect(WorkPos.x, WorkPos.y, WorkPos.x + WorkSize.x, WorkPos.y + WorkSize.y); } @@ -2439,9 +2439,9 @@ namespace ImGui IMGUI_API void CallContextHooks(ImGuiContext* context, ImGuiContextHookType type); // Viewports - IMGUI_API void TranslateWindowsInViewport(ImGuiViewportP* viewport, const ImVec2& old_pos, const ImVec2& new_pos); - IMGUI_API void ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale); - IMGUI_API void DestroyPlatformWindow(ImGuiViewportP* viewport); + IMGUI_API void TranslateWindowsInViewport(ImGuiViewportP* viewport, const ImVec2& old_pos, const ImVec2& new_pos); + IMGUI_API void ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale); + IMGUI_API void DestroyPlatformWindow(ImGuiViewportP* viewport); // Settings IMGUI_API void MarkIniSettingsDirty(); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 0fb150b36a8a..b28a92371f63 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6592,7 +6592,8 @@ bool ImGui::BeginMainMenuBar() if (menu_bar_window == NULL || menu_bar_window->BeginCount == 0) { // Set window position - // We don't attempt to calculate our height ahead, as it depends on the per-viewport font size. However menu-bar will affect the minimum window size so we'll get the right height. + // We don't attempt to calculate our height ahead, as it depends on the per-viewport font size. + // However menu-bar will affect the minimum window size so we'll get the right height. ImVec2 menu_bar_pos = viewport->Pos + viewport->CurrWorkOffsetMin; ImVec2 menu_bar_size = ImVec2(viewport->Size.x - viewport->CurrWorkOffsetMin.x + viewport->CurrWorkOffsetMax.x, 1.0f); SetNextWindowPos(menu_bar_pos); @@ -6602,7 +6603,7 @@ bool ImGui::BeginMainMenuBar() // Create window SetNextWindowViewport(viewport->ID); // Enforce viewport so we don't create our own viewport when ImGuiConfigFlags_ViewportsNoMerge is set. PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); - PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0, 0)); // Lift normal size constraint, however the presence of a menu-bar will give us the minimum height we want. + PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0, 0)); // Lift normal size constraint, however the presence of a menu-bar will give us the minimum height we want. ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_MenuBar; bool is_open = Begin("##MainMenuBar", NULL, window_flags) && BeginMenuBar(); PopStyleVar(2); From 3607c42bec99c9c9f5cfe17d5658de8179cd5355 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 10 Feb 2021 14:30:18 +0100 Subject: [PATCH 672/828] Viewports: Fix issue inferring viewport z-order when new popups gets created. (#3734) + Metrics updates. Revert 6bc52667 Showing inferred order and missing flags in metrics. --- imgui.cpp | 47 +++++++++++++++++++++++++++++++++++++++-------- imgui_internal.h | 2 ++ 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index bce64ce3cd13..6355fc2c02c1 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11839,7 +11839,7 @@ void ImGui::UpdatePlatformWindows() // This is useful for our platform z-order heuristic when io.MouseHoveredViewport is not available. if (viewport->LastFrontMostStampCount != g.ViewportFrontMostStampCount) viewport->LastFrontMostStampCount = ++g.ViewportFrontMostStampCount; - } + } // Clear request flags viewport->ClearRequestFlags(); @@ -11860,8 +11860,14 @@ void ImGui::UpdatePlatformWindows() } // Store a tag so we can infer z-order easily from all our windows - if (focused_viewport && focused_viewport->LastFrontMostStampCount != g.ViewportFrontMostStampCount) - focused_viewport->LastFrontMostStampCount = ++g.ViewportFrontMostStampCount; + // We compare PlatformLastFocusedViewportId so newly created viewports with _NoFocusOnAppearing flag + // will keep the front most stamp instead of losing it back to their parent viewport. + if (focused_viewport && g.PlatformLastFocusedViewportId != focused_viewport->ID) + { + if (focused_viewport->LastFrontMostStampCount != g.ViewportFrontMostStampCount) + focused_viewport->LastFrontMostStampCount = ++g.ViewportFrontMostStampCount; + g.PlatformLastFocusedViewportId = focused_viewport->ID; + } } } @@ -15852,6 +15858,13 @@ static void RenderViewportsThumbnails() ImGui::Dummy(bb_full.GetSize() * SCALE); } +static int IMGUI_CDECL ViewportComparerByFrontMostStampCount(const void* lhs, const void* rhs) +{ + const ImGuiViewportP* a = *(const ImGuiViewportP* const *)lhs; + const ImGuiViewportP* b = *(const ImGuiViewportP* const *)rhs; + return b->LastFrontMostStampCount - a->LastFrontMostStampCount; +} + // Avoid naming collision with imgui_demo.cpp's HelpMarker() for unity builds. static void MetricsHelpMarker(const char* desc) { @@ -16059,6 +16072,18 @@ void ImGui::ShowMetricsWindow(bool* p_open) TreePop(); } + if (TreeNode("Inferred order (front-to-back)")) + { + static ImVector viewports; + viewports.resize(g.Viewports.Size); + memcpy(viewports.Data, g.Viewports.Data, g.Viewports.size_in_bytes()); + if (viewports.Size > 1) + ImQsort(viewports.Data, viewports.Size, sizeof(ImGuiViewport*), ViewportComparerByFrontMostStampCount); + for (int i = 0; i < viewports.Size; i++) + BulletText("Viewport #%d, ID: 0x%08X, FrontMostStampCount = %08d, Window: \"%s\"", viewports[i]->Idx, viewports[i]->ID, viewports[i]->LastFrontMostStampCount, viewports[i]->Window ? viewports[i]->Window->Name : "N/A"); + TreePop(); + } + for (int i = 0; i < g.Viewports.Size; i++) DebugNodeViewport(g.Viewports[i]); TreePop(); @@ -16557,11 +16582,17 @@ void ImGui::DebugNodeViewport(ImGuiViewportP* viewport) viewport->WorkOffsetMin.x, viewport->WorkOffsetMin.y, viewport->WorkOffsetMax.x, viewport->WorkOffsetMax.y, viewport->PlatformMonitor, viewport->DpiScale * 100.0f); if (viewport->Idx > 0) { SameLine(); if (SmallButton("Reset Pos")) { viewport->Pos = ImVec2(200, 200); viewport->UpdateWorkRect(); if (viewport->Window) viewport->Window->Pos = viewport->Pos; } } - BulletText("Flags: 0x%04X =%s%s%s%s%s%s%s", viewport->Flags, - (flags & ImGuiViewportFlags_CanHostOtherWindows) ? " CanHostOtherWindows" : "", (flags & ImGuiViewportFlags_NoDecoration) ? " NoDecoration" : "", - (flags & ImGuiViewportFlags_NoFocusOnAppearing) ? " NoFocusOnAppearing" : "", (flags & ImGuiViewportFlags_NoInputs) ? " NoInputs" : "", - (flags & ImGuiViewportFlags_NoRendererClear) ? " NoRendererClear" : "", (flags & ImGuiViewportFlags_Minimized) ? " Minimized" : "", - (flags & ImGuiViewportFlags_NoAutoMerge) ? " NoAutoMerge" : ""); + BulletText("Flags: 0x%04X =%s%s%s%s%s%s%s%s%s%s", viewport->Flags, + (flags & ImGuiViewportFlags_NoDecoration) ? " NoDecoration" : "", + (flags & ImGuiViewportFlags_NoTaskBarIcon) ? " NoTaskBarIcon" : "", + (flags & ImGuiViewportFlags_NoFocusOnAppearing) ? " NoFocusOnAppearing" : "", + (flags & ImGuiViewportFlags_NoFocusOnClick) ? " NoFocusOnClick" : "", + (flags & ImGuiViewportFlags_NoInputs) ? " NoInputs" : "", + (flags & ImGuiViewportFlags_NoRendererClear) ? " NoRendererClear" : "", + (flags & ImGuiViewportFlags_TopMost) ? " TopMost" : "", + (flags & ImGuiViewportFlags_Minimized) ? " Minimized" : "", + (flags & ImGuiViewportFlags_NoAutoMerge) ? " NoAutoMerge" : "", + (flags & ImGuiViewportFlags_CanHostOtherWindows) ? " CanHostOtherWindows" : ""); for (int layer_i = 0; layer_i < IM_ARRAYSIZE(viewport->DrawDataBuilder.Layers); layer_i++) for (int draw_list_i = 0; draw_list_i < viewport->DrawDataBuilder.Layers[layer_i].Size; draw_list_i++) DebugNodeDrawList(NULL, viewport, viewport->DrawDataBuilder.Layers[layer_i][draw_list_i], "DrawList"); diff --git a/imgui_internal.h b/imgui_internal.h index 0ea3020f776e..2100b1f622a1 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1531,6 +1531,7 @@ struct ImGuiContext ImGuiViewportP* CurrentViewport; // We track changes of viewport (happening in Begin) so we can call Platform_OnChangedViewport() ImGuiViewportP* MouseViewport; ImGuiViewportP* MouseLastHoveredViewport; // Last known viewport that was hovered by mouse (even if we are not hovering any viewport any more) + honoring the _NoInputs flag. + ImGuiID PlatformLastFocusedViewportId; int ViewportFrontMostStampCount; // Every time the front-most window changes, we stamp its viewport with an incrementing counter // Gamepad/keyboard Navigation @@ -1752,6 +1753,7 @@ struct ImGuiContext CurrentDpiScale = 0.0f; CurrentViewport = NULL; MouseViewport = MouseLastHoveredViewport = NULL; + PlatformLastFocusedViewportId = 0; ViewportFrontMostStampCount = 0; NavWindow = NULL; From 04f7ea818d33942b352c98a4a44cc56ce57183ec Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 10 Feb 2021 16:42:22 +0100 Subject: [PATCH 673/828] Viewports: Setting the new (currently dummy) flags on viewports. (#3789, #1542, #3680, #3350, #3012, #2471) Amend the merging of f14042ca786324dcdf54a4921438c1e4d8320898 (merge ee59d7a2667352e0f39d826f5c5cbf947d4cf284) --- imgui.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 8f43e9bf2191..4609b82c024d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4226,6 +4226,7 @@ void ImGui::Initialize(ImGuiContext* context) viewport->ID = IMGUI_VIEWPORT_DEFAULT_ID; viewport->Idx = 0; viewport->PlatformWindowCreated = true; + viewport->Flags = ImGuiViewportFlags_OwnedByApp; g.Viewports.push_back(viewport); g.PlatformIO.Viewports.push_back(g.Viewports[0]); @@ -11393,7 +11394,7 @@ static void ImGui::UpdateViewportsNewFrame() main_viewport_pos = main_viewport->Pos; // Preserve last pos/size when minimized (FIXME: We don't do the same for Size outside of the viewport path) main_viewport_size = main_viewport->Size; } - AddUpdateViewport(NULL, IMGUI_VIEWPORT_DEFAULT_ID, main_viewport_pos, main_viewport_size, ImGuiViewportFlags_CanHostOtherWindows); + AddUpdateViewport(NULL, IMGUI_VIEWPORT_DEFAULT_ID, main_viewport_pos, main_viewport_size, ImGuiViewportFlags_OwnedByApp | ImGuiViewportFlags_CanHostOtherWindows); g.CurrentDpiScale = 0.0f; g.CurrentViewport = NULL; @@ -11563,6 +11564,7 @@ ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const ImGuiContext& g = *GImGui; IM_ASSERT(id != 0); + flags |= ImGuiViewportFlags_IsPlatformWindow; if (window != NULL) { if (g.MovingWindow && g.MovingWindow->RootWindow == window) @@ -16583,8 +16585,8 @@ void ImGui::DebugNodeViewport(ImGuiViewportP* viewport) viewport->WorkOffsetMin.x, viewport->WorkOffsetMin.y, viewport->WorkOffsetMax.x, viewport->WorkOffsetMax.y, viewport->PlatformMonitor, viewport->DpiScale * 100.0f); if (viewport->Idx > 0) { SameLine(); if (SmallButton("Reset Pos")) { viewport->Pos = ImVec2(200, 200); viewport->UpdateWorkRect(); if (viewport->Window) viewport->Window->Pos = viewport->Pos; } } - BulletText("Flags: 0x%04X =%s%s%s%s%s%s%s%s%s%s%s%s%s", viewport->Flags, - (flags & ImGuiViewportFlags_IsPlatformWindow) ? " IsPlatformWindow" : "", + BulletText("Flags: 0x%04X =%s%s%s%s%s%s%s%s%s%s%s%s", viewport->Flags, + //(flags & ImGuiViewportFlags_IsPlatformWindow) ? " IsPlatformWindow" : "", // Omitting because it is the standard (flags & ImGuiViewportFlags_IsPlatformMonitor) ? " IsPlatformMonitor" : "", (flags & ImGuiViewportFlags_OwnedByApp) ? " OwnedByApp" : "", (flags & ImGuiViewportFlags_NoDecoration) ? " NoDecoration" : "", From 7180e9ac661ea1c7b9dca6c89319049bb651c404 Mon Sep 17 00:00:00 2001 From: Adam Kewley Date: Thu, 11 Feb 2021 10:03:14 +0100 Subject: [PATCH 674/828] Remove redundant GetMainViewport decl in imgui.h (#3801, #3800) --- imgui.h | 1 - 1 file changed, 1 deletion(-) diff --git a/imgui.h b/imgui.h index 48b7175664b1..e17a8200ec98 100644 --- a/imgui.h +++ b/imgui.h @@ -905,7 +905,6 @@ namespace ImGui // Read comments around the ImGuiPlatformIO structure for more details. // Note: You may use GetWindowViewport() to get the current viewport of the current window. IMGUI_API ImGuiPlatformIO& GetPlatformIO(); // platform/renderer functions, for backend to setup + viewports list. - IMGUI_API ImGuiViewport* GetMainViewport(); // return primary/default viewport. In the future in "no main platform window" mode we will direct this to primary monitor. IMGUI_API void UpdatePlatformWindows(); // call in main loop. will call CreateWindow/ResizeWindow/etc. platform functions for each secondary viewport, and DestroyWindow for each inactive viewport. IMGUI_API void RenderPlatformWindowsDefault(void* platform_render_arg = NULL, void* renderer_render_arg = NULL); // call in main loop. will call RenderWindow/SwapBuffers platform functions for each secondary viewport which doesn't have the ImGuiViewportFlags_Minimized flag set. May be reimplemented by user for custom rendering needs. IMGUI_API void DestroyPlatformWindows(); // call DestroyWindow platform functions for all viewports. call from backend Shutdown() if you need to close platform windows before imgui shutdown. otherwise will be called by DestroyContext(). From 3d75f438bcc0228a720206f44674987d5ccd2a03 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 12 Feb 2021 21:17:31 +0100 Subject: [PATCH 675/828] Docking: Made close button enable logic consistent on dockspace. When no docked window have a close button or it is disabled on the node, the space is given to tabs. Clarified close_button_is_visible vs close_button_is_enabled behavior in DockNodeUpdateTabBar().. (#3633, #3521) Reduced alpha of disabled close button a little bit further. Removed 'EnableCloseButton' which was actually unused (can't be infered early, need ->VisibleWindow anyway, which is not == ->ActiveWindow) --- imgui.cpp | 44 +++++++++++++++++++++++--------------------- imgui_internal.h | 3 +-- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 1d5c88a197f1..3b6b9ab452e0 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -12838,7 +12838,7 @@ ImGuiDockNode::ImGuiDockNode(ImGuiID id) AuthorityForPos = AuthorityForSize = ImGuiDataAuthority_DockNode; AuthorityForViewport = ImGuiDataAuthority_Auto; IsVisible = true; - IsFocused = HasCloseButton = HasWindowMenuButton = EnableCloseButton = false; + IsFocused = HasCloseButton = HasWindowMenuButton = false; WantCloseAll = WantLockSizeOnce = WantMouseMove = WantHiddenTabBarUpdate = WantHiddenTabBarToggle = false; MarkedForPosSizeWrite = false; } @@ -13148,6 +13148,7 @@ static void ImGui::DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* nod } else { + // FIXME-DOCKING: Missing policies for conflict resolution, hence the "Experimental" tag on this. node->LocalFlags &= ~window->WindowClass.DockNodeFlagsOverrideClear; node->LocalFlags |= window->WindowClass.DockNodeFlagsOverrideSet; } @@ -13272,7 +13273,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) node->State = ImGuiDockNodeState_HostWindowHiddenBecauseSingleWindow; node->WantCloseAll = false; node->WantCloseTabId = 0; - node->HasCloseButton = node->HasWindowMenuButton = node->EnableCloseButton = false; + node->HasCloseButton = node->HasWindowMenuButton = false; node->LastFrameActive = g.FrameCount; if (node->WantMouseMove && node->Windows.Size == 1) @@ -13307,6 +13308,19 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) const ImGuiDockNodeFlags node_flags = node->GetMergedFlags(); + // Decide if the node will have a close button and a window menu button + node->HasWindowMenuButton = (node->Windows.Size > 0) && (node_flags & ImGuiDockNodeFlags_NoWindowMenuButton) == 0; + node->HasCloseButton = false; + for (int window_n = 0; window_n < node->Windows.Size; window_n++) + { + // FIXME-DOCK: Setting DockIsActive here means that for single active window in a leaf node, DockIsActive will be cleared until the next Begin() call. + ImGuiWindow* window = node->Windows[window_n]; + node->HasCloseButton |= window->HasCloseButton; + window->DockIsActive = (node->Windows.Size > 1); + } + if (node_flags & ImGuiDockNodeFlags_NoCloseButton) + node->HasCloseButton = false; + // Bind or create host window ImGuiWindow* host_window = NULL; bool beginned_into_host_window = false; @@ -13314,25 +13328,11 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) { // [Explicit root dockspace node] IM_ASSERT(node->HostWindow); - node->EnableCloseButton = false; - node->HasCloseButton = (node_flags & ImGuiDockNodeFlags_NoCloseButton) == 0; - node->HasWindowMenuButton = (node_flags & ImGuiDockNodeFlags_NoWindowMenuButton) == 0; host_window = node->HostWindow; } else { // [Automatic root or child nodes] - node->EnableCloseButton = false; - node->HasCloseButton = (node->Windows.Size > 0) && (node_flags & ImGuiDockNodeFlags_NoCloseButton) == 0; - node->HasWindowMenuButton = (node->Windows.Size > 0) && (node_flags & ImGuiDockNodeFlags_NoWindowMenuButton) == 0; - for (int window_n = 0; window_n < node->Windows.Size; window_n++) - { - // FIXME-DOCK: Setting DockIsActive here means that for single active window in a leaf node, DockIsActive will be cleared until the next Begin() call. - ImGuiWindow* window = node->Windows[window_n]; - window->DockIsActive = (node->Windows.Size > 1); - node->EnableCloseButton |= window->HasCloseButton; - } - if (node->IsRootNode() && node->IsVisible) { ImGuiWindow* ref_window = (node->Windows.Size > 0) ? node->Windows[0] : NULL; @@ -13638,7 +13638,6 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w const ImGuiDockNodeFlags node_flags = node->GetMergedFlags(); const bool has_window_menu_button = (node_flags & ImGuiDockNodeFlags_NoWindowMenuButton) == 0 && (style.WindowMenuButtonPosition != ImGuiDir_None); - const bool has_close_button = (node_flags & ImGuiDockNodeFlags_NoCloseButton) == 0; // In a dock node, the Collapse Button turns into the Window Menu button. // FIXME-DOCK FIXME-OPT: Could we recycle popups id across multiple dock nodes? @@ -13764,12 +13763,15 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w // Close button (after VisibleWindow was updated) // Note that VisibleWindow may have been overrided by CTRL+Tabbing, so VisibleWindow->ID may be != from tab_bar->SelectedTabId - if (has_close_button && node->VisibleWindow) + const bool close_button_is_enabled = node->HasCloseButton && node->VisibleWindow && node->VisibleWindow->HasCloseButton; + const bool close_button_is_visible = node->HasCloseButton; + //const bool close_button_is_visible = close_button_is_enabled; // Most people would expect this behavior of not even showing the button (leaving a hole since we can't claim that space as other windows in the tba bar have one) + if (close_button_is_visible) { - if (!node->VisibleWindow->HasCloseButton) + if (!close_button_is_enabled) { PushItemFlag(ImGuiItemFlags_Disabled, true); - PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_Text] * ImVec4(1.0f,1.0f,1.0f,0.5f)); + PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_Text] * ImVec4(1.0f,1.0f,1.0f,0.4f)); } const float button_sz = g.FontSize; if (CloseButton(host_window->GetID("#CLOSE"), title_bar_rect.GetTR() + ImVec2(-style.FramePadding.x * 2.0f - button_sz, 0.0f))) @@ -13780,7 +13782,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w } //if (IsItemActive()) // focus_tab_id = tab_bar->SelectedTabId; - if (!node->VisibleWindow->HasCloseButton) + if (!close_button_is_enabled) { PopStyleColor(); PopItemFlag(); diff --git a/imgui_internal.h b/imgui_internal.h index de24933591d2..b77f8db881a5 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1233,9 +1233,8 @@ struct ImGuiDockNode ImGuiDataAuthority AuthorityForViewport :3; bool IsVisible :1; // Set to false when the node is hidden (usually disabled as it has no active window) bool IsFocused :1; - bool HasCloseButton :1; + bool HasCloseButton :1; // Provide space for a close button (if any of the docked window has one). Note that button may be hidden on window without one. bool HasWindowMenuButton :1; - bool EnableCloseButton :1; bool WantCloseAll :1; // Set when closing all tabs at once. bool WantLockSizeOnce :1; bool WantMouseMove :1; // After a node extraction we need to transition toward moving the newly created host window From a4e4f57cb0a71547e0407f84a21d1150c40a3866 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 18 Feb 2021 12:53:15 +0100 Subject: [PATCH 676/828] Examples: DX9-DX11: Removed half-assed DPI awareness enable. Updated Docking/Viewports part of Changelog (e.g. removed bits that are now already in master, clarified some added bits) --- backends/imgui_impl_glfw.cpp | 2 +- docs/CHANGELOG.txt | 24 ++++++++++------------- examples/example_win32_directx10/main.cpp | 2 -- examples/example_win32_directx11/main.cpp | 8 ++------ examples/example_win32_directx9/main.cpp | 2 -- imgui.h | 2 +- 6 files changed, 14 insertions(+), 26 deletions(-) diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index 3344b7bdf4e1..91872ecf4ae6 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -567,7 +567,7 @@ static void ImGui_ImplGlfw_CreateWindow(ImGuiViewport* viewport) glfwWindowHint(GLFW_VISIBLE, false); glfwWindowHint(GLFW_FOCUSED, false); #if GLFW_HAS_FOCUS_ON_SHOW - glfwWindowHint(GLFW_FOCUS_ON_SHOW, false); + glfwWindowHint(GLFW_FOCUS_ON_SHOW, false); #endif glfwWindowHint(GLFW_DECORATED, (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? false : true); #if GLFW_HAS_WINDOW_TOPMOST diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index e1ef0359f9be..ecab76f2c2b0 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -36,6 +36,7 @@ HOW TO UPDATE? ----------------------------------------------------------------------- DOCKING FEATURES +(see https://github.com/ocornut/imgui/wiki/Docking for quick intro) - Added Docking system: [BETA] (#2109, #351) - Added ImGuiConfigFlags_DockingEnable flag to enable Docking. @@ -54,7 +55,8 @@ DOCKING FEATURES - Style: Added ImGuiCol_DockingPreview, ImGuiCol_DockingEmptyBg colors. - Demo: Added "DockSpace" example app showcasing use of explicit dockspace nodes. -MULTI-VIEWPORT FEATURES (was previously 'viewport' branch, merged into 'docking') +MULTI-VIEWPORT FEATURES +(see https://github.com/ocornut/imgui/wiki/Multi-Viewports for quick intro) Breaking Changes: @@ -65,10 +67,6 @@ Breaking Changes: - Likewise io.MousePos and GetMousePos() will use OS coordinates. If you query mouse positions to interact with non-imgui coordinates you will need to offset them. e.g. subtract GetWindowViewport()->Pos. -- Render function: the ImDrawData structure now contains 'DisplayPos' and 'DisplaySize' fields. - To support multi-viewport, you need to use those values when creating your orthographic projection matrix. - Use 'draw_data->DisplaySize' instead of 'io.DisplaySize', and 'draw_data->DisplayPos' instead of (0,0) as the upper-left point. - You need to subtract 'draw_data->DisplayPos' from your scissor rectangles to convert them from global coordinates to frame-buffer coordinates. - IO: Moved IME support functions from io.ImeSetInputScreenPosFn, io.ImeWindowHandle to the PlatformIO api. - IO: Removed io.DisplayVisibleMin, io.DisplayVisibleMax settings (they were marked obsoleted, used to clip within the (0,0)..(DisplaySize) range). @@ -81,26 +79,24 @@ Other changes: - ImGuiPlatformIO::Monitors is a list of platform monitors (input from backend) - ImGuiPlatformIO::Viewports is a list of viewports (output from dear imgui) - Added ImGuiPlatformMonitor to feed OS monitor information in the ImGuiPlatformIO::Monitors. -- Added GetMainViewport(). - Added GetWindowViewport(), SetNextWindowViewport(). - Added GetWindowDpiScale(). - Added GetOverlayDrawList(ImGuiViewport* viewport). The no-parameter version of GetOverlayDrawList() return the overlay for the current window's viewport. -- Added UpdatePlatformWindows(), RenderPlatformWindows(), DestroyPlatformWindows() for usage for application core. +- Added UpdatePlatformWindows(), RenderPlatformWindowsDefault(), DestroyPlatformWindows() for usage in application setup. - Added FindViewportByID(), FindViewportByPlatformHandle() for usage by backends. - Added ImGuiConfigFlags_ViewportsEnable configuration flag and other viewport options. -- Added io.ConfigViewportsNoAutoMerge, io.ConfigViewportsNoTaskBarIcon, io.ConfigViewportsNoDecoration, io.ConfigViewportsNoDefaultParent options. +- Added io.ConfigViewportsNoAutoMerge option. +- Added io.ConfigViewportsNoTaskBarIcon option. +- Added io.ConfigViewportsNoDecoration option. +- Added io.ConfigViewportsNoDefaultParent option. - Added ImGuiBackendFlags_PlatformHasViewports, ImGuiBackendFlags_RendererHasViewports, ImGuiBackendFlags_HasMouseHoveredViewport backend flags. - Added io.MouseHoveredViewport (optional _even_ for multi-viewport support, tied to ImGuiBackendFlags_HasMouseHoveredViewport flag). -- Added ImGuiViewport structure, ImGuiViewportFlags flags. +- Expanded ImGuiViewport structure, ImGuiViewportFlags flags. - Added ImGuiWindowClass and SetNextWindowClass() for passing viewport related hints to the OS/platform back-end. -- Examples: Renderer: OpenGL2, OpenGL3, DirectX11, DirectX12, Vulkan: Added support for multi-viewports. +- Examples: Renderer: OpenGL2, OpenGL3, DirectX9, DirectX10, DirectX11, DirectX12, Vulkan: Added support for multi-viewports. - Examples: Platforms: Win32, GLFW, SDL2: Added support for multi-viewports. Note that Linux/Mac still have inconsistent support for multi-viewports. If you want to help see https://github.com/ocornut/imgui/issues/2117. -- Examples: Win32: Added DPI-related helpers to access DPI features without requiring the latest Windows SDK at compile time, - and without requiring Windows 10 at runtime. -- Examples: Vulkan: Added various optional helpers in imgui_impl_vulkan.h (they are used for multi-viewport support) - to make the examples main.cpp easier to read. ----------------------------------------------------------------------- diff --git a/examples/example_win32_directx10/main.cpp b/examples/example_win32_directx10/main.cpp index 34247b4ad553..9dfd8faf43f5 100644 --- a/examples/example_win32_directx10/main.cpp +++ b/examples/example_win32_directx10/main.cpp @@ -26,8 +26,6 @@ LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); // Main code int main(int, char**) { - ImGui_ImplWin32_EnableDpiAwareness(); - // Create application window //ImGui_ImplWin32_EnableDpiAwareness(); WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, WndProc, 0L, 0L, GetModuleHandle(NULL), NULL, NULL, NULL, NULL, _T("ImGui Example"), NULL }; diff --git a/examples/example_win32_directx11/main.cpp b/examples/example_win32_directx11/main.cpp index 9b371e602778..4450dc368f0b 100644 --- a/examples/example_win32_directx11/main.cpp +++ b/examples/example_win32_directx11/main.cpp @@ -26,8 +26,6 @@ LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); // Main code int main(int, char**) { - ImGui_ImplWin32_EnableDpiAwareness(); - // Create application window //ImGui_ImplWin32_EnableDpiAwareness(); WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, WndProc, 0L, 0L, GetModuleHandle(NULL), NULL, NULL, NULL, NULL, _T("ImGui Example"), NULL }; @@ -59,10 +57,8 @@ int main(int, char**) //io.ConfigViewportsNoDefaultParent = true; //io.ConfigDockingAlwaysTabBar = true; //io.ConfigDockingTransparentPayload = true; -#if 1 - io.ConfigFlags |= ImGuiConfigFlags_DpiEnableScaleFonts; // FIXME-DPI: THIS CURRENTLY DOESN'T WORK AS EXPECTED. DON'T USE IN USER APP! - io.ConfigFlags |= ImGuiConfigFlags_DpiEnableScaleViewports; // FIXME-DPI -#endif + //io.ConfigFlags |= ImGuiConfigFlags_DpiEnableScaleFonts; // FIXME-DPI: Experimental. THIS CURRENTLY DOESN'T WORK AS EXPECTED. DON'T USE IN USER APP! + //io.ConfigFlags |= ImGuiConfigFlags_DpiEnableScaleViewports; // FIXME-DPI: Experimental. // Setup Dear ImGui style ImGui::StyleColorsDark(); diff --git a/examples/example_win32_directx9/main.cpp b/examples/example_win32_directx9/main.cpp index 1c1a040c2fd9..0ee9c2135c2f 100644 --- a/examples/example_win32_directx9/main.cpp +++ b/examples/example_win32_directx9/main.cpp @@ -24,8 +24,6 @@ LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); // Main code int main(int, char**) { - ImGui_ImplWin32_EnableDpiAwareness(); - // Create application window //ImGui_ImplWin32_EnableDpiAwareness(); WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, WndProc, 0L, 0L, GetModuleHandle(NULL), NULL, NULL, NULL, NULL, _T("ImGui Example"), NULL }; diff --git a/imgui.h b/imgui.h index e6033f28330e..f8d6b6f01254 100644 --- a/imgui.h +++ b/imgui.h @@ -2981,7 +2981,7 @@ struct ImGuiPlatformIO bool (*Platform_GetWindowFocus)(ImGuiViewport* vp); // . . U . . // bool (*Platform_GetWindowMinimized)(ImGuiViewport* vp); // N . . . . // Get platform window minimized state. When minimized, we generally won't attempt to get/set size and contents will be culled more easily void (*Platform_SetWindowTitle)(ImGuiViewport* vp, const char* str); // . . U . . // Set platform window title (given an UTF-8 string) - void (*Platform_SetWindowAlpha)(ImGuiViewport* vp, float alpha); // . . U . . // (Optional) Setup window transparency + void (*Platform_SetWindowAlpha)(ImGuiViewport* vp, float alpha); // . . U . . // (Optional) Setup global transparency (not per-pixel transparency) void (*Platform_UpdateWindow)(ImGuiViewport* vp); // . . U . . // (Optional) Called by UpdatePlatformWindows(). Optional hook to allow the platform backend from doing general book-keeping every frame. void (*Platform_RenderWindow)(ImGuiViewport* vp, void* render_arg); // . . . R . // (Optional) Main rendering (platform side! This is often unused, or just setting a "current" context for OpenGL bindings). 'render_arg' is the value passed to RenderPlatformWindowsDefault(). void (*Platform_SwapBuffers)(ImGuiViewport* vp, void* render_arg); // . . . R . // (Optional) Call Present/SwapBuffers (platform side! This is often unused!). 'render_arg' is the value passed to RenderPlatformWindowsDefault(). From 3ec14186cbc09f0a4a5e6709c850919d71138362 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 18 Feb 2021 14:08:50 +0100 Subject: [PATCH 677/828] Viewports: Fix setting of ImGuiViewportFlags_NoRendererClear. (#3213) --- imgui.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 29071b2cb96f..63d23f1ae74d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6404,9 +6404,11 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if (window->WindowClass.ViewportFlagsOverrideClear) viewport_flags &= ~window->WindowClass.ViewportFlagsOverrideClear; - // We also tell the backend that clearing the platform window won't be necessary, as our window is filling the viewport and we have disabled BgAlpha + // We can also tell the backend that clearing the platform window won't be necessary, + // as our window background is filling the viewport and we have disabled BgAlpha. + // FIXME: Work on support for per-viewport transparency (#2766) if (!(flags & ImGuiWindowFlags_NoBackground)) - viewport_flags &= ~ImGuiViewportFlags_NoRendererClear; + viewport_flags |= ImGuiViewportFlags_NoRendererClear; window->Viewport->Flags = viewport_flags; } From 839ecce57154a33ede985eeab746828f8102c8d9 Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Thu, 25 Feb 2021 14:04:07 +0200 Subject: [PATCH 678/828] Internals: Add a way to request window to not process any interactions for specified number of frames. --- imgui.cpp | 7 +++++++ imgui_internal.h | 1 + 2 files changed, 8 insertions(+) diff --git a/imgui.cpp b/imgui.cpp index 63d23f1ae74d..9ac6d20ad1da 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6827,6 +6827,13 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Update the Hidden flag window->Hidden = (window->HiddenFramesCanSkipItems > 0) || (window->HiddenFramesCannotSkipItems > 0) || (window->HiddenFramesForRenderOnly > 0); + // Disable inputs for requested number of frames + if (window->DisableInputsFrames > 0) + { + window->DisableInputsFrames--; + window->Flags |= ImGuiWindowFlags_NoInputs; + } + // Update the SkipItems flag, used to early out of all items functions (no layout required) bool skip_items = false; if (window->Collapsed || !window->Active || window->Hidden) diff --git a/imgui_internal.h b/imgui_internal.h index 96236360a290..92173ef08238 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1970,6 +1970,7 @@ struct IMGUI_API ImGuiWindow ImS8 HiddenFramesCanSkipItems; // Hide the window for N frames ImS8 HiddenFramesCannotSkipItems; // Hide the window for N frames while allowing items to be submitted so we can measure their size ImS8 HiddenFramesForRenderOnly; // Hide the window until frame N at Render() time only + ImS8 DisableInputsFrames; // Disable window interactions for N frames ImGuiCond SetWindowPosAllowFlags : 8; // store acceptable condition flags for SetNextWindowPos() use. ImGuiCond SetWindowSizeAllowFlags : 8; // store acceptable condition flags for SetNextWindowSize() use. ImGuiCond SetWindowCollapsedAllowFlags : 8; // store acceptable condition flags for SetNextWindowCollapsed() use. From 732cd837a9ed8ecb2ce4035e7d40a1c9a04ae240 Mon Sep 17 00:00:00 2001 From: David Maas Date: Sat, 27 Feb 2021 14:15:38 -0600 Subject: [PATCH 679/828] Added missing IMGUI_API to internal docking-related structs. (#3850) --- imgui_internal.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/imgui_internal.h b/imgui_internal.h index 92173ef08238..5fae5ef70760 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1212,7 +1212,7 @@ enum ImGuiDockNodeState }; // sizeof() 116~160 -struct ImGuiDockNode +struct IMGUI_API ImGuiDockNode { ImGuiID ID; ImGuiDockNodeFlags SharedFlags; // Flags shared by all nodes of a same dockspace hierarchy (inherited from the root node) @@ -2106,7 +2106,7 @@ struct ImGuiTabItem }; // Storage for a tab bar (sizeof() 152 bytes) -struct ImGuiTabBar +struct IMGUI_API ImGuiTabBar { ImVector Tabs; ImGuiTabBarFlags Flags; From b794ecc079f27472f8657dd619d2b2e5bf203dc7 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 25 Feb 2021 15:36:52 +0100 Subject: [PATCH 680/828] Internals: Docking: some renaming. --- imgui.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 9ac6d20ad1da..1a1d48fd06fa 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -12131,14 +12131,14 @@ struct ImGuiDockNodeSettings ImGuiID ID; ImGuiID ParentNodeId; ImGuiID ParentWindowId; - ImGuiID SelectedWindowId; + ImGuiID SelectedTabId; signed char SplitAxis; char Depth; ImGuiDockNodeFlags Flags; // NB: We save individual flags one by one in ascii format (ImGuiDockNodeFlags_SavedFlagsMask_) ImVec2ih Pos; ImVec2ih Size; ImVec2ih SizeRef; - ImGuiDockNodeSettings() { ID = ParentNodeId = ParentWindowId = SelectedWindowId = 0; SplitAxis = ImGuiAxis_None; Depth = 0; Flags = ImGuiDockNodeFlags_None; } + ImGuiDockNodeSettings() { memset(this, 0, sizeof(*this)); SplitAxis = ImGuiAxis_None; } }; //----------------------------------------------------------------------------- @@ -12515,7 +12515,7 @@ static void ImGui::DockContextBuildNodesFromSettings(ImGuiContext* ctx, ImGuiDoc node->ParentNode->ChildNodes[0] = node; else if (node->ParentNode && node->ParentNode->ChildNodes[1] == NULL) node->ParentNode->ChildNodes[1] = node; - node->SelectedTabId = settings->SelectedWindowId; + node->SelectedTabId = settings->SelectedTabId; node->SplitAxis = (ImGuiAxis)settings->SplitAxis; node->LocalFlags |= (settings->Flags & ImGuiDockNodeFlags_SavedFlagsMask_); @@ -15563,7 +15563,7 @@ static void ImGui::DockSettingsHandler_ReadLine(ImGuiContext* ctx, ImGuiSettings if (sscanf(line, " HiddenTabBar=%d%n", &x, &r) == 1) { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_HiddenTabBar; } if (sscanf(line, " NoWindowMenuButton=%d%n", &x, &r) == 1) { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoWindowMenuButton; } if (sscanf(line, " NoCloseButton=%d%n", &x, &r) == 1) { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoCloseButton; } - if (sscanf(line, " Selected=0x%08X%n", &node.SelectedWindowId,&r) == 1) { line += r; } + if (sscanf(line, " Selected=0x%08X%n", &node.SelectedTabId,&r) == 1) { line += r; } if (node.ParentNodeId != 0) if (ImGuiDockNodeSettings* parent_settings = DockSettingsFindNodeSettings(ctx, node.ParentNodeId)) node.Depth = parent_settings->Depth + 1; @@ -15577,7 +15577,7 @@ static void DockSettingsHandler_DockNodeToSettings(ImGuiDockContext* dc, ImGuiDo node_settings.ID = node->ID; node_settings.ParentNodeId = node->ParentNode ? node->ParentNode->ID : 0; node_settings.ParentWindowId = (node->IsDockSpace() && node->HostWindow && node->HostWindow->ParentWindow) ? node->HostWindow->ParentWindow->ID : 0; - node_settings.SelectedWindowId = node->SelectedTabId; + node_settings.SelectedTabId = node->SelectedTabId; node_settings.SplitAxis = (signed char)(node->IsSplitNode() ? node->SplitAxis : ImGuiAxis_None); node_settings.Depth = (char)depth; node_settings.Flags = (node->LocalFlags & ImGuiDockNodeFlags_SavedFlagsMask_); @@ -15643,8 +15643,8 @@ static void ImGui::DockSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettings buf->appendf(" NoWindowMenuButton=1"); if (node_settings->Flags & ImGuiDockNodeFlags_NoCloseButton) buf->appendf(" NoCloseButton=1"); - if (node_settings->SelectedWindowId) - buf->appendf(" Selected=0x%08X", node_settings->SelectedWindowId); + if (node_settings->SelectedTabId) + buf->appendf(" Selected=0x%08X", node_settings->SelectedTabId); #if IMGUI_DEBUG_INI_SETTINGS // [DEBUG] Include comments in the .ini file to ease debugging @@ -16205,14 +16205,14 @@ void ImGui::ShowMetricsWindow(bool* p_open) { ImGuiDockNodeSettings* settings = &dc->NodesSettings[n]; const char* selected_tab_name = NULL; - if (settings->SelectedWindowId) + if (settings->SelectedTabId) { - if (ImGuiWindow* window = FindWindowByID(settings->SelectedWindowId)) + if (ImGuiWindow* window = FindWindowByID(settings->SelectedTabId)) selected_tab_name = window->Name; - else if (ImGuiWindowSettings* window_settings = FindWindowSettings(settings->SelectedWindowId)) + else if (ImGuiWindowSettings* window_settings = FindWindowSettings(settings->SelectedTabId)) selected_tab_name = window_settings->GetName(); } - BulletText("Node %08X, Parent %08X, SelectedTab %08X ('%s')", settings->ID, settings->ParentNodeId, settings->SelectedWindowId, selected_tab_name ? selected_tab_name : settings->SelectedWindowId ? "N/A" : ""); + BulletText("Node %08X, Parent %08X, SelectedTab %08X ('%s')", settings->ID, settings->ParentNodeId, settings->SelectedTabId, selected_tab_name ? selected_tab_name : settings->SelectedTabId ? "N/A" : ""); } TreePop(); } From 705f082674bf4ce5673f2a9f8cc29cc3ba5d1d10 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 5 Mar 2021 16:21:39 +0100 Subject: [PATCH 681/828] Internals: rename RootWindow->RootWindowDockTree, RootWindowDockStop->RootWindow. Why? So by default RootWindow matches user expectation on both branches, and RootWindowDockTree is more intentful. (Actually should reduce diff between master<>docking) --- imgui.cpp | 124 +++++++++++++++++++++++----------------------- imgui_internal.h | 8 +-- imgui_tables.cpp | 7 +-- imgui_widgets.cpp | 2 +- 4 files changed, 68 insertions(+), 73 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index b50dba16fe37..06de4ea57e16 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3144,8 +3144,8 @@ static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFla // FIXME-OPT: This could be cached/stored within the window. ImGuiContext& g = *GImGui; if (g.NavWindow) - if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindow) - if (focused_root_window->WasActive && focused_root_window != window->RootWindow) + if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindowDockTree) + if (focused_root_window->WasActive && focused_root_window != window->RootWindowDockTree) { // For the purpose of those flags we differentiate "standard popup" from "modal popup" // NB: The order of those two tests is important because Modal windows are also Popups. @@ -3157,7 +3157,7 @@ static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFla // Filter by viewport if (window->Viewport != g.MouseViewport) - if (g.MovingWindow == NULL || window->RootWindow != g.MovingWindow->RootWindow) + if (g.MovingWindow == NULL || window->RootWindowDockTree != g.MovingWindow->RootWindowDockTree) return false; return true; @@ -3545,10 +3545,10 @@ void ImGui::StartMouseMovingWindow(ImGuiWindow* window) SetActiveID(window->MoveId, window); g.NavDisableHighlight = true; g.ActiveIdNoClearOnFocusLoss = true; - g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - window->RootWindow->Pos; + g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - window->RootWindowDockTree->Pos; bool can_move_window = true; - if ((window->Flags & ImGuiWindowFlags_NoMove) || (window->RootWindow->Flags & ImGuiWindowFlags_NoMove)) + if ((window->Flags & ImGuiWindowFlags_NoMove) || (window->RootWindowDockTree->Flags & ImGuiWindowFlags_NoMove)) can_move_window = false; if (ImGuiDockNode* node = window->DockNodeAsHost) if (node->VisibleWindow && (node->VisibleWindow->Flags & ImGuiWindowFlags_NoMove)) @@ -3596,8 +3596,8 @@ void ImGui::UpdateMouseMovingWindowNewFrame() // We actually want to move the root window. g.MovingWindow == window we clicked on (could be a child window). // We track it to preserve Focus and so that generally ActiveIdWindow == MovingWindow and ActiveId == MovingWindow->MoveId for consistency. KeepAliveID(g.ActiveId); - IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindow); - ImGuiWindow* moving_window = g.MovingWindow->RootWindow; + IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindowDockTree); + ImGuiWindow* moving_window = g.MovingWindow->RootWindowDockTree; if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos)) { ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset; @@ -3661,7 +3661,7 @@ void ImGui::UpdateMouseMovingWindowEndFrame() { // Handle the edge case of a popup being closed while clicking in its empty space. // If we try to focus it, FocusWindow() > ClosePopupsOverWindow() will accidentally close any parent popups because they are not linked together any more. - ImGuiWindow* root_window = g.HoveredWindow ? g.HoveredWindow->RootWindowDockStop : NULL; + ImGuiWindow* root_window = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL; const bool is_closed_popup = root_window && (root_window->Flags & ImGuiWindowFlags_Popup) && !IsPopupOpen(root_window->PopupId, ImGuiPopupFlags_AnyPopupLevel); if (root_window != NULL && !is_closed_popup) @@ -3922,7 +3922,7 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags() // Modal windows prevents mouse from hovering behind them. ImGuiWindow* modal_window = GetTopMostPopupModal(); - if (modal_window && g.HoveredWindow && !IsWindowChildOf(g.HoveredWindow->RootWindow, modal_window)) + if (modal_window && g.HoveredWindow && !IsWindowChildOf(g.HoveredWindow->RootWindowDockTree, modal_window)) clear_hovered_windows = true; // Disabled mouse? @@ -4534,15 +4534,15 @@ static void ImGui::EndFrameDrawDimmedBackgrounds() // Choose a draw list that will be front-most across all our children // In the unlikely case that the window wasn't made active we can't rely on its drawlist and skip rendering all-together. ImGuiWindow* window = g.NavWindowingTargetAnim; - ImDrawList* draw_list = FindFrontMostVisibleChildWindow(window->RootWindow)->DrawList; + ImDrawList* draw_list = FindFrontMostVisibleChildWindow(window->RootWindowDockTree)->DrawList; draw_list->PushClipRectFullScreen(); // Docking: draw modal whitening background on other nodes of a same dock tree // For CTRL+TAB within a docking node we need to render the dimming background in 8 steps // (Because the root node renders the background in one shot, in order to avoid flickering when a child dock node is not submitted) - if (window->RootWindowDockStop->DockIsActive) - if (window->RootWindow != window->RootWindowDockStop) - RenderRectFilledWithHole(draw_list, window->RootWindow->Rect(), window->RootWindowDockStop->Rect(), GetColorU32(ImGuiCol_NavWindowingDimBg, g.DimBgRatio), g.Style.WindowRounding); + if (window->RootWindow->DockIsActive) + if (window->RootWindowDockTree != window->RootWindow) + RenderRectFilledWithHole(draw_list, window->RootWindowDockTree->Rect(), window->RootWindow->Rect(), GetColorU32(ImGuiCol_NavWindowingDimBg, g.DimBgRatio), g.Style.WindowRounding); // Draw navigation selection/windowing rectangle border float rounding = ImMax(window->WindowRounding, g.Style.WindowRounding); @@ -4674,7 +4674,7 @@ void ImGui::Render() // Add ImDrawList to render ImGuiWindow* windows_to_render_top_most[2]; - windows_to_render_top_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindow : NULL; + windows_to_render_top_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindowDockTree : NULL; windows_to_render_top_most[1] = (g.NavWindowingTarget ? g.NavWindowingListWindow : NULL); for (int n = 0; n != g.Windows.Size; n++) { @@ -4799,7 +4799,7 @@ static void FindHoveredWindow() if (hovered_window == NULL) hovered_window = window; - if (hovered_window_ignoring_moving_window == NULL && (!g.MovingWindow || window->RootWindow != g.MovingWindow->RootWindow)) + if (hovered_window_ignoring_moving_window == NULL && (!g.MovingWindow || window->RootWindowDockTree != g.MovingWindow->RootWindowDockTree)) hovered_window_ignoring_moving_window = window; if (hovered_window && hovered_window_ignoring_moving_window) break; @@ -5690,7 +5690,7 @@ static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& s window->DC.NavLayerCurrent = ImGuiNavLayer_Main; // Navigation resize (keyboard/gamepad) - if (g.NavWindowingTarget && g.NavWindowingTarget->RootWindow == window) + if (g.NavWindowingTarget && g.NavWindowingTarget->RootWindowDockTree == window) { ImVec2 nav_resize_delta; if (g.NavInputSource == ImGuiInputSource_NavKeyboard && g.IO.KeyShift) @@ -5975,12 +5975,12 @@ void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& titl void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window) { window->ParentWindow = parent_window; - window->RootWindow = window->RootWindowDockStop = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window; + window->RootWindow = window->RootWindowDockTree = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window; if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip)) { - window->RootWindow = parent_window->RootWindow; + window->RootWindowDockTree = parent_window->RootWindowDockTree; if (!window->DockIsActive && !(parent_window->Flags & ImGuiWindowFlags_DockNodeHost)) - window->RootWindowDockStop = parent_window->RootWindowDockStop; + window->RootWindow = parent_window->RootWindow; } if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup))) window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight; @@ -6604,7 +6604,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Draw modal or window list full viewport dimming background (for other viewports we'll render them in EndFrame) ImGuiWindow* window_window_list = g.NavWindowingListWindow; const bool dim_bg_for_modal = (flags & ImGuiWindowFlags_Modal) && window == GetTopMostPopupModal() && window->HiddenFramesCannotSkipItems <= 0; - const bool dim_bg_for_window_list = g.NavWindowingTargetAnim && ((window == g.NavWindowingTargetAnim->RootWindow) || (window == window_window_list && window_window_list->Viewport != g.NavWindowingTargetAnim->Viewport)); + const bool dim_bg_for_window_list = g.NavWindowingTargetAnim && ((window == g.NavWindowingTargetAnim->RootWindowDockTree) || (window == window_window_list && window_window_list->Viewport != g.NavWindowingTargetAnim->Viewport)); if (dim_bg_for_modal || dim_bg_for_window_list) { const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio); @@ -6765,13 +6765,13 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Docking: Dragging a dockable window (or any of its child) turns it into a drag and drop source. // We need to do this _before_ we overwrite window->DC.LastItemId below because BeginDockableDragDropSource() also overwrites it. if ((g.MovingWindow == window) && (g.IO.ConfigDockingWithShift == g.IO.KeyShift)) - if ((window->RootWindow->Flags & ImGuiWindowFlags_NoDocking) == 0) + if ((window->RootWindowDockTree->Flags & ImGuiWindowFlags_NoDocking) == 0) BeginDockableDragDropSource(window); // Docking: Any dockable window can act as a target. For dock node hosts we call BeginDockableDragDropTarget() in DockNodeUpdate() instead. if (g.DragDropActive && !(flags & ImGuiWindowFlags_NoDocking)) - if (g.MovingWindow == NULL || g.MovingWindow->RootWindow != window) - if ((window == window->RootWindow) && !(window->Flags & ImGuiWindowFlags_DockNodeHost)) + if (g.MovingWindow == NULL || g.MovingWindow->RootWindowDockTree != window) + if ((window == window->RootWindowDockTree) && !(window->Flags & ImGuiWindowFlags_DockNodeHost)) BeginDockableDragDropTarget(window); } @@ -6923,7 +6923,7 @@ void ImGui::BringWindowToDisplayFront(ImGuiWindow* window) { ImGuiContext& g = *GImGui; ImGuiWindow* current_front_window = g.Windows.back(); - if (current_front_window == window || current_front_window->RootWindow == window) // Cheap early out (could be better) + if (current_front_window == window || current_front_window->RootWindowDockTree == window) // Cheap early out (could be better) return; for (int i = g.Windows.Size - 2; i >= 0; i--) // We can ignore the top-most window if (g.Windows[i] == window) @@ -6971,9 +6971,9 @@ void ImGui::FocusWindow(ImGuiWindow* window) ClosePopupsOverWindow(window, false); // Move the root window to the top of the pile - IM_ASSERT(window == NULL || window->RootWindow != NULL); - ImGuiWindow* focus_front_window = window ? window->RootWindowDockStop : NULL; - ImGuiWindow* display_front_window = window ? window->RootWindow : NULL; + IM_ASSERT(window == NULL || window->RootWindowDockTree != NULL); + ImGuiWindow* focus_front_window = window ? window->RootWindow : NULL; + ImGuiWindow* display_front_window = window ? window->RootWindowDockTree : NULL; ImGuiDockNode* dock_node = window ? window->DockNode : NULL; bool active_id_window_is_dock_node_host = (g.ActiveIdWindow && dock_node && dock_node->HostWindow == g.ActiveIdWindow); @@ -6981,7 +6981,7 @@ void ImGui::FocusWindow(ImGuiWindow* window) // - Focus a window while an InputText in another window is active, if focus happens before the old InputText can run. // - When using Nav to activate menu items (due to timing of activating on press->new window appears->losing ActiveId) // - Using dock host items (tab, collapse button) can trigger this before we redirect the ActiveIdWindow toward the child window. - if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindowDockStop != focus_front_window) + if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != focus_front_window) if (!g.ActiveIdNoClearOnFocusLoss && !active_id_window_is_dock_node_host) ClearActiveID(); @@ -7015,7 +7015,7 @@ void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWind { // We may later decide to test for different NoXXXInputs based on the active navigation input (mouse vs nav) but that may feel more confusing to the user. ImGuiWindow* window = g.WindowsFocusOrder[i]; - if (window != ignore_window && window->WasActive && window->RootWindowDockStop == window) + if (window != ignore_window && window->WasActive && window->RootWindow == window) if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) { // FIXME-DOCK: This is failing (lagging by one frame) for docked windows. @@ -7125,7 +7125,7 @@ void ImGui::PopTextWrapPos() bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent) { - if (window->RootWindow == potential_parent) + if (window->RootWindowDockTree == potential_parent) return true; while (window != NULL) { @@ -7163,11 +7163,11 @@ bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags) switch (flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) { case ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows: - if (g.HoveredWindow->RootWindowDockStop != window->RootWindowDockStop) + if (g.HoveredWindow->RootWindow != window->RootWindow) return false; break; case ImGuiHoveredFlags_RootWindow: - if (g.HoveredWindow != window->RootWindowDockStop) + if (g.HoveredWindow != window->RootWindow) return false; break; case ImGuiHoveredFlags_ChildWindows: @@ -7200,9 +7200,9 @@ bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags) switch (flags & (ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows)) { case ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows: - return g.NavWindow && g.NavWindow->RootWindowDockStop == g.CurrentWindow->RootWindowDockStop; + return g.NavWindow && g.NavWindow->RootWindow == g.CurrentWindow->RootWindow; case ImGuiFocusedFlags_RootWindow: - return g.NavWindow == g.CurrentWindow->RootWindowDockStop; + return g.NavWindow == g.CurrentWindow->RootWindow; case ImGuiFocusedFlags_ChildWindows: return g.NavWindow && IsWindowChildOf(g.NavWindow, g.CurrentWindow); default: @@ -7227,7 +7227,7 @@ bool ImGui::IsWindowDocked() // If you want a window to never be focused, you may use the e.g. NoInputs flag. bool ImGui::IsWindowNavFocusable(ImGuiWindow* window) { - return window->WasActive && window == window->RootWindowDockStop && !(window->Flags & ImGuiWindowFlags_NoNavFocus); + return window->WasActive && window == window->RootWindow && !(window->Flags & ImGuiWindowFlags_NoNavFocus); } float ImGui::GetWindowWidth() @@ -8800,12 +8800,12 @@ void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to // Trim the stack unless the popup is a direct parent of the reference window (the reference window is often the NavWindow) // - With this stack of window, clicking/focusing Popup1 will close Popup2 and Popup3: // Window -> Popup1 -> Popup2 -> Popup3 - // - Each popups may contain child windows, which is why we compare ->RootWindow! + // - Each popups may contain child windows, which is why we compare ->RootWindowDockTree! // Window -> Popup1 -> Popup1_Child -> Popup2 -> Popup2_Child bool ref_window_is_descendent_of_popup = false; for (int n = popup_count_to_keep; n < g.OpenPopupStack.Size; n++) if (ImGuiWindow* popup_window = g.OpenPopupStack[n].Window) - if (popup_window->RootWindow == ref_window->RootWindow) + if (popup_window->RootWindowDockTree == ref_window->RootWindowDockTree) { ref_window_is_descendent_of_popup = true; break; @@ -9488,7 +9488,7 @@ void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags mov static void ImGui::NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window) { ImGuiWindow* parent = nav_window; - while (parent && parent->RootWindowDockStop != parent && (parent->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (parent->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0) + while (parent && parent->RootWindow != parent && (parent->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (parent->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0) parent = parent->ParentWindow; if (parent && parent != nav_window) parent->NavLastChildNavWindow = nav_window; @@ -9718,7 +9718,7 @@ static void ImGui::NavUpdate() if (!IsActiveIdUsingNavInput(ImGuiNavInput_Cancel)) ClearActiveID(); } - else if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow) && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow && g.NavWindow != g.NavWindow->RootWindowDockStop) + else if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow) && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow && g.NavWindow != g.NavWindow->RootWindow) { // Exit child window ImGuiWindow* child_window = g.NavWindow; @@ -10166,7 +10166,7 @@ static void ImGui::NavUpdateWindowing() if (start_windowing_with_gamepad || start_windowing_with_keyboard) if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1)) { - g.NavWindowingTarget = g.NavWindowingTargetAnim = window->RootWindowDockStop; + g.NavWindowingTarget = g.NavWindowingTargetAnim = window->RootWindow; g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f; g.NavWindowingToggleLayer = start_windowing_with_keyboard ? false : true; g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_NavKeyboard : ImGuiInputSource_NavGamepad; @@ -10230,7 +10230,7 @@ static void ImGui::NavUpdateWindowing() { const float NAV_MOVE_SPEED = 800.0f; const float move_speed = ImFloor(NAV_MOVE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y)); // FIXME: Doesn't handle variable framerate very well - ImGuiWindow* moving_window = g.NavWindowingTarget->RootWindow; + ImGuiWindow* moving_window = g.NavWindowingTarget->RootWindowDockTree; SetWindowPos(moving_window, moving_window->Pos + move_delta * move_speed, ImGuiCond_Always); MarkIniSettingsDirty(moving_window); g.NavDisableMouseHover = true; @@ -10238,7 +10238,7 @@ static void ImGui::NavUpdateWindowing() } // Apply final focus - if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindowDockStop)) + if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindow)) { ImGuiViewport* previous_viewport = g.NavWindow ? g.NavWindow->Viewport : NULL; ClearActiveID(); @@ -10524,7 +10524,7 @@ bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id) ImGuiWindow* window = g.CurrentWindow; ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow; - if (hovered_window == NULL || window->RootWindow != hovered_window->RootWindow) + if (hovered_window == NULL || window->RootWindowDockTree != hovered_window->RootWindowDockTree) return false; IM_ASSERT(id != 0); if (!IsMouseHoveringRect(bb.Min, bb.Max) || (id == g.DragDropPayload.SourceId)) @@ -10553,7 +10553,7 @@ bool ImGui::BeginDragDropTarget() if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect)) return false; ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow; - if (hovered_window == NULL || window->RootWindow != hovered_window->RootWindow) + if (hovered_window == NULL || window->RootWindowDockTree != hovered_window->RootWindowDockTree) return false; const ImRect& display_rect = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? window->DC.LastItemDisplayRect : window->DC.LastItemRect; @@ -11616,7 +11616,7 @@ ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const flags |= ImGuiViewportFlags_IsPlatformWindow; if (window != NULL) { - if (g.MovingWindow && g.MovingWindow->RootWindow == window) + if (g.MovingWindow && g.MovingWindow->RootWindowDockTree == window) flags |= ImGuiViewportFlags_NoInputs | ImGuiViewportFlags_NoFocusOnAppearing; if ((window->Flags & ImGuiWindowFlags_NoMouseInputs) && (window->Flags & ImGuiWindowFlags_NoNavInputs)) flags |= ImGuiViewportFlags_NoInputs; @@ -11729,7 +11729,7 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) { window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_None); } - else if (g.MovingWindow && g.MovingWindow->RootWindow == window && IsMousePosValid()) + else if (g.MovingWindow && g.MovingWindow->RootWindowDockTree == window && IsMousePosValid()) { if (window->Viewport != NULL && window->Viewport->Window == window) window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_None); @@ -12376,8 +12376,8 @@ void ImGui::DockContextNewFrameUpdateDocking(ImGuiContext* ctx) { if (hovered_window->DockNodeAsHost) g.HoveredDockNode = DockNodeTreeFindVisibleNodeByPos(hovered_window->DockNodeAsHost, g.IO.MousePos); - else if (hovered_window->RootWindowDockStop->DockNode) - g.HoveredDockNode = hovered_window->RootWindowDockStop->DockNode; + else if (hovered_window->RootWindow->DockNode) + g.HoveredDockNode = hovered_window->RootWindow->DockNode; } // Process Docking requests @@ -12973,7 +12973,7 @@ static void ImGui::DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window { ImGuiContext& g = *GImGui; IM_ASSERT(window->DockNode == node); - //IM_ASSERT(window->RootWindow == node->HostWindow); + //IM_ASSERT(window->RootWindowDockTree == node->HostWindow); //IM_ASSERT(window->LastFrameActive < g.FrameCount); // We may call this from Begin() IM_ASSERT(save_dock_id == 0 || save_dock_id == node->ID); IMGUI_DEBUG_LOG_DOCKING("DockNodeRemoveWindow node 0x%08X window '%s'\n", node->ID, window->Name); @@ -13451,8 +13451,8 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) if (node->IsSplitNode()) IM_ASSERT(node->TabBar == NULL); if (node->IsRootNode()) - if (g.NavWindow && g.NavWindow->RootWindowDockStop->DockNode && g.NavWindow->RootWindowDockStop->ParentWindow == host_window) - node->LastFocusedNodeId = g.NavWindow->RootWindowDockStop->DockNode->ID; + if (g.NavWindow && g.NavWindow->RootWindow->DockNode && g.NavWindow->RootWindow->ParentWindow == host_window) + node->LastFocusedNodeId = g.NavWindow->RootWindow->DockNode->ID; // We need to draw a background at the root level if requested by ImGuiDockNodeFlags_PassthruCentralNode, but we will only know the correct pos/size // _after_ processing the resizing splitters. So we are using the DrawList channel splitting facility to submit drawing primitives out of order! @@ -13532,7 +13532,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) // Draw payload drop target if (host_window && node->IsVisible) - if (node->IsRootNode() && (g.MovingWindow == NULL || g.MovingWindow->RootWindow != host_window)) + if (node->IsRootNode() && (g.MovingWindow == NULL || g.MovingWindow->RootWindowDockTree != host_window)) BeginDockableDragDropTarget(host_window); // We update this after DockNodeUpdateTabBar() @@ -13641,7 +13641,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w ImGuiDockNode* root_node = DockNodeGetRootNode(node); if (g.NavWindowingTarget) is_focused = (g.NavWindowingTarget->DockNode == node); - else if (g.NavWindow && g.NavWindow->RootWindowForTitleBarHighlight == host_window->RootWindow && root_node->LastFocusedNodeId == node->ID) + else if (g.NavWindow && g.NavWindow->RootWindowForTitleBarHighlight == host_window->RootWindowDockTree && root_node->LastFocusedNodeId == node->ID) is_focused = true; // Hidden tab bar will show a triangle on the upper-left (in Begin) @@ -13707,7 +13707,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w for (int window_n = 0; window_n < node->Windows.Size; window_n++) { ImGuiWindow* window = node->Windows[window_n]; - if (g.NavWindow && g.NavWindow->RootWindowDockStop == window) + if (g.NavWindow && g.NavWindow->RootWindow == window) tab_bar->SelectedTabId = window->ID; if (TabBarFindTabByID(tab_bar, window->ID) == NULL) TabBarAddTab(tab_bar, ImGuiTabItemFlags_Unsorted, window); @@ -13796,7 +13796,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w window->DockTabItemRect = host_window->DC.LastItemRect; // Update navigation ID on menu layer - if (g.NavWindow && g.NavWindow->RootWindowDockStop == window && (window->DC.NavLayerActiveMask & (1 << ImGuiNavLayer_Menu)) == 0) + if (g.NavWindow && g.NavWindow->RootWindow == window && (window->DC.NavLayerActiveMask & (1 << ImGuiNavLayer_Menu)) == 0) host_window->NavLastIds[1] = window->ID; } } @@ -15388,7 +15388,7 @@ void ImGui::BeginDockableDragDropSource(ImGuiWindow* window) IM_ASSERT(g.MovingWindow == window); window->DC.LastItemId = window->MoveId; - window = window->RootWindow; + window = window->RootWindowDockTree; IM_ASSERT((window->Flags & ImGuiWindowFlags_NoDocking) == 0); bool is_drag_docking = (g.IO.ConfigDockingWithShift) || ImRect(0, 0, window->SizeFull.x, GetFrameHeight()).Contains(g.ActiveIdClickOffset); if (is_drag_docking && BeginDragDropSource(ImGuiDragDropFlags_SourceNoPreviewTooltip | ImGuiDragDropFlags_SourceNoHoldToOpenOthers | ImGuiDragDropFlags_SourceAutoExpirePayload)) @@ -15407,7 +15407,7 @@ void ImGui::BeginDockableDragDropTarget(ImGuiWindow* window) ImGuiContext* ctx = GImGui; ImGuiContext& g = *ctx; - //IM_ASSERT(window->RootWindow == window); // May also be a DockSpace + //IM_ASSERT(window->RootWindowDockTree == window); // May also be a DockSpace IM_ASSERT((window->Flags & ImGuiWindowFlags_NoDocking) == 0); if (!g.DragDropActive) return; @@ -16272,7 +16272,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) Text("WINDOWING"); Indent(); Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL"); - Text("HoveredWindow->Root: '%s'", g.HoveredWindow ? g.HoveredWindow->RootWindow->Name : "NULL"); + Text("HoveredWindow->Root: '%s'", g.HoveredWindow ? g.HoveredWindow->RootWindowDockTree->Name : "NULL"); Text("HoveredWindowUnderMovingWindow: '%s'", g.HoveredWindowUnderMovingWindow ? g.HoveredWindowUnderMovingWindow->Name : "NULL"); Text("HoveredDockNode: 0x%08X", g.HoveredDockNode ? g.HoveredDockNode->ID : 0); Text("MovingWindow: '%s'", g.MovingWindow ? g.MovingWindow->Name : "NULL"); @@ -16701,10 +16701,10 @@ void ImGui::DebugNodeWindow(ImGuiWindow* window, const char* label) BulletText("DockId: 0x%04X, DockOrder: %d, Act: %d, Vis: %d", window->DockId, window->DockOrder, window->DockIsActive, window->DockTabIsVisible); if (window->DockNode || window->DockNodeAsHost) DebugNodeDockNode(window->DockNodeAsHost ? window->DockNodeAsHost : window->DockNode, window->DockNodeAsHost ? "DockNodeAsHost" : "DockNode"); - if (window->RootWindow != window) { DebugNodeWindow(window->RootWindow, "RootWindow"); } - if (window->RootWindowDockStop != window->RootWindow) { DebugNodeWindow(window->RootWindowDockStop, "RootWindowDockStop"); } - if (window->ParentWindow != NULL) { DebugNodeWindow(window->ParentWindow, "ParentWindow"); } - if (window->DC.ChildWindows.Size > 0) { DebugNodeWindowsList(&window->DC.ChildWindows, "ChildWindows"); } + if (window->RootWindow != window) { DebugNodeWindow(window->RootWindow, "RootWindow"); } + if (window->RootWindowDockTree != window->RootWindow) { DebugNodeWindow(window->RootWindowDockTree, "RootWindowDockTree"); } + if (window->ParentWindow != NULL) { DebugNodeWindow(window->ParentWindow, "ParentWindow"); } + if (window->DC.ChildWindows.Size > 0) { DebugNodeWindowsList(&window->DC.ChildWindows, "ChildWindows"); } if (window->ColumnsStorage.Size > 0 && TreeNode("Columns", "Columns sets (%d)", window->ColumnsStorage.Size)) { for (int n = 0; n < window->ColumnsStorage.Size; n++) diff --git a/imgui_internal.h b/imgui_internal.h index 2008d6710ea6..7e14085f05e9 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1474,7 +1474,7 @@ struct ImGuiContext // Windows state ImVector Windows; // Windows, sorted in display order, back to front - ImVector WindowsFocusOrder; // Windows, sorted in focus order, back to front. (FIXME: We could only store root windows here! Need to sort out the Docking equivalent which is RootWindowDockStop and is unfortunately a little more dynamic) + ImVector WindowsFocusOrder; // Windows, sorted in focus order, back to front. (FIXME: We could only store root windows here!) ImVector WindowsTempSortBuffer; // Temporary buffer used in EndFrame() to reorder windows so parents are kept before their child ImVector CurrentWindowStack; ImGuiStorage WindowsById; // Map window's ImGuiID to ImGuiWindow* @@ -1483,7 +1483,7 @@ struct ImGuiContext ImGuiWindow* HoveredWindow; // Window the mouse is hovering. Will typically catch mouse inputs. ImGuiWindow* HoveredWindowUnderMovingWindow; // Hovered window ignoring MovingWindow. Only set if MovingWindow is set. ImGuiDockNode* HoveredDockNode; // Hovered dock node. - ImGuiWindow* MovingWindow; // Track the window we clicked on (in order to preserve focus). The actual window that is moved is generally MovingWindow->RootWindow. + ImGuiWindow* MovingWindow; // Track the window we clicked on (in order to preserve focus). The actual window that is moved is generally MovingWindow->RootWindowDockTree. ImGuiWindow* WheelingWindow; // Track the window we started mouse-wheeling on. Until a timer elapse or mouse has moved, generally keep scrolling the same window even if during the course of scrolling the mouse ends up hovering a child window. ImVec2 WheelingWindowRefMousePos; float WheelingWindowTimer; @@ -2010,8 +2010,8 @@ struct IMGUI_API ImGuiWindow ImDrawList* DrawList; // == &DrawListInst (for backward compatibility reason with code using imgui_internal.h we keep this a pointer) ImDrawList DrawListInst; ImGuiWindow* ParentWindow; // If we are a child _or_ popup window, this is pointing to our parent. Otherwise NULL. - ImGuiWindow* RootWindow; // Point to ourself or first ancestor that is not a child window == Top-level window. - ImGuiWindow* RootWindowDockStop; // Point to ourself or first ancestor that is not a child window. Doesn't cross through dock nodes. We use this so IsWindowFocused() can behave consistently regardless of docking state. + ImGuiWindow* RootWindow; // Point to ourself or first ancestor that is not a child window. Doesn't cross through dock nodes. We use this so IsWindowFocused() can behave consistently regardless of docking state. + ImGuiWindow* RootWindowDockTree; // Point to ourself or first ancestor that is not a child window. Cross through dock nodes. ImGuiWindow* RootWindowForTitleBarHighlight; // Point to ourself or first ancestor which will display TitleBgActive color when this window is active. ImGuiWindow* RootWindowForNav; // Point to ourself or first ancestor which doesn't have the NavFlattened flag. diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 8c7f0682a44f..26dab2182c97 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -272,12 +272,7 @@ inline ImGuiTableFlags TableFixFlags(ImGuiTableFlags flags, ImGuiWindow* outer_w flags |= ImGuiTableFlags_NoSavedSettings; // Inherit _NoSavedSettings from top-level window (child windows always have _NoSavedSettings set) -#ifdef IMGUI_HAS_DOCK - ImGuiWindow* window_for_settings = outer_window->RootWindowDockStop; -#else - ImGuiWindow* window_for_settings = outer_window->RootWindow; -#endif - if (window_for_settings->Flags & ImGuiWindowFlags_NoSavedSettings) + if (outer_window->RootWindow->Flags & ImGuiWindowFlags_NoSavedSettings) flags |= ImGuiTableFlags_NoSavedSettings; return flags; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 24b9cc4c742c..09a18ba4c26a 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -500,7 +500,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool flags |= ImGuiButtonFlags_PressedOnDefault_; ImGuiWindow* backup_hovered_window = g.HoveredWindow; - const bool flatten_hovered_children = (flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredWindow && g.HoveredWindow->RootWindow == window->RootWindow; + const bool flatten_hovered_children = (flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredWindow && g.HoveredWindow->RootWindowDockTree == window->RootWindowDockTree; if (flatten_hovered_children) g.HoveredWindow = window; From d5a4d5300055c1222585a5f6758a232bb9d22d3f Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 5 Mar 2021 16:35:05 +0100 Subject: [PATCH 682/828] Viewports, Backend: SDL: Fix missing ImGuiBackendFlags_HasSetMousePos flag in docking branch (ok in master), GLFW: Fix application of WantSetMousePos. (#1542, #787) Shows how little this feature is used with nav (was designed for small devices and frankly may be dropped) - but the backend support itself we will make use of for other features. --- backends/imgui_impl_sdl.cpp | 1 + backends/imgui_impl_win32.cpp | 2 +- imgui.cpp | 2 +- imgui_internal.h | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/backends/imgui_impl_sdl.cpp b/backends/imgui_impl_sdl.cpp index f5568c864a12..9bfe1600eb15 100644 --- a/backends/imgui_impl_sdl.cpp +++ b/backends/imgui_impl_sdl.cpp @@ -168,6 +168,7 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window, void* sdl_gl_context) // Setup backend capabilities flags ImGuiIO& io = ImGui::GetIO(); io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) + io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) #if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional) #endif diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index 45504bac51fe..22d9eba02766 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -218,7 +218,7 @@ static void ImGui_ImplWin32_UpdateMousePos() if (io.WantSetMousePos) { POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y }; - if ((io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) != 0 || ::ClientToScreen(g_hWnd, &pos)) + if ((io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) == 0) ::ClientToScreen(g_hWnd, &pos); ::SetCursorPos(pos.x, pos.y); } diff --git a/imgui.cpp b/imgui.cpp index 06de4ea57e16..92f38bd6126b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -9530,6 +9530,7 @@ static inline void ImGui::NavUpdateAnyRequestFlag() // This needs to be called before we submit any widget (aka in or before Begin) void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit) { + // FIXME: ChildWindow test here is wrong for docking ImGuiContext& g = *GImGui; IM_ASSERT(window == g.NavWindow); bool init_for_nav = false; @@ -11206,7 +11207,6 @@ static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandl buf->appendf("Collapsed=%d\n", settings->Collapsed); if (settings->DockId != 0) { - // Write DockId as 4 digits if possible. Automatic DockId are small numbers, but full explicit DockSpace() are full ImGuiID range. if (settings->DockOrder == -1) buf->appendf("DockId=0x%08X\n", settings->DockId); else diff --git a/imgui_internal.h b/imgui_internal.h index 7e14085f05e9..72dc3fb50d0f 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -188,9 +188,9 @@ namespace ImStb // Debug Logging for selected systems. Remove the '((void)0) //' to enable. //#define IMGUI_DEBUG_LOG_POPUP IMGUI_DEBUG_LOG // Enable log +//#define IMGUI_DEBUG_LOG_NAV IMGUI_DEBUG_LOG // Enable log //#define IMGUI_DEBUG_LOG_VIEWPORT IMGUI_DEBUG_LOG // Enable log //#define IMGUI_DEBUG_LOG_DOCKING IMGUI_DEBUG_LOG // Enable log -//#define IMGUI_DEBUG_LOG_NAV IMGUI_DEBUG_LOG // Enable log #define IMGUI_DEBUG_LOG_POPUP(...) ((void)0) // Disable log #define IMGUI_DEBUG_LOG_NAV(...) ((void)0) // Disable log #define IMGUI_DEBUG_LOG_VIEWPORT(...) ((void)0) // Disable log From 4b9bc4902084a4a7171d6d4339717c9ecb121ccf Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 10 Mar 2021 17:01:00 +0100 Subject: [PATCH 683/828] Viewports, Internals: added GetViewportPlatformMonitor() will a safety net to keep code portable + simplified handling of disconnected monitor in Begin(). --- imgui.cpp | 46 ++++++++++++++++++++++++++++++---------------- imgui_internal.h | 8 +++++--- 2 files changed, 35 insertions(+), 19 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 92f38bd6126b..1972240954a1 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3985,7 +3985,7 @@ void ImGui::NewFrame() { IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?"); ImGuiContext& g = *GImGui; - + // Remove pending delete hooks before frame start. // This deferred removal avoid issues of removal while iterating the hook vector for (int n = g.Hooks.Size - 1; n >= 0; n--) @@ -6449,19 +6449,11 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) } else if (window->ViewportOwned && g.PlatformIO.Monitors.Size > 0) { - if (window->Viewport->PlatformMonitor == -1) - { - // Fallback for "lost" window (e.g. a monitor disconnected): we move the window back over the main viewport - const ImGuiViewport* main_viewport = GetMainViewport(); - SetWindowPos(window, main_viewport->Pos + style.DisplayWindowPadding, ImGuiCond_Always); - } - else - { - ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[window->Viewport->PlatformMonitor]; - visibility_rect.Min = monitor.WorkPos + visibility_padding; - visibility_rect.Max = monitor.WorkPos + monitor.WorkSize - visibility_padding; - ClampWindowRect(window, visibility_rect); - } + // Lost windows (e.g. a monitor disconnected) will naturally moved to the fallback/dummy monitor aka the main viewport. + const ImGuiPlatformMonitor* monitor = GetViewportPlatformMonitor(window->Viewport); + visibility_rect.Min = monitor->WorkPos + visibility_padding; + visibility_rect.Max = monitor->WorkPos + monitor->WorkSize - visibility_padding; + ClampWindowRect(window, visibility_rect); } } window->Pos = ImFloor(window->Pos); @@ -11534,6 +11526,17 @@ static void ImGui::UpdateViewportsNewFrame() viewport->DpiScale = new_dpi_scale; } + // Update fallback monitor + if (g.PlatformIO.Monitors.Size == 0) + { + ImGuiPlatformMonitor* monitor = &g.FallbackMonitor; + monitor->MainPos = main_viewport->Pos; + monitor->MainSize = main_viewport->Size; + monitor->WorkPos = main_viewport->WorkPos; + monitor->WorkSize = main_viewport->WorkSize; + monitor->DpiScale = main_viewport->DpiScale; + } + if (!viewports_enabled) { g.MouseViewport = main_viewport; @@ -11747,7 +11750,7 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) if (window->Viewport == NULL) if (!UpdateTryMergeWindowIntoHostViewport(window, main_viewport)) window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_None); - + // Mark window as allowed to protrude outside of its viewport and into the current monitor if (!lock_viewport) { @@ -12010,6 +12013,17 @@ static void ImGui::UpdateViewportPlatformMonitor(ImGuiViewportP* viewport) viewport->PlatformMonitor = (short)FindPlatformMonitorForRect(viewport->GetMainRect()); } +// Return value is always != NULL, but don't hold on it across frames. +const ImGuiPlatformMonitor* ImGui::GetViewportPlatformMonitor(ImGuiViewport* viewport_p) +{ + ImGuiContext& g = *GImGui; + ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)viewport_p; + int monitor_idx = viewport->PlatformMonitor; + if (monitor_idx >= 0 || monitor_idx < g.PlatformIO.Monitors.Size) + return &g.PlatformIO.Monitors[monitor_idx]; + return &g.FallbackMonitor; +} + void ImGui::DestroyPlatformWindow(ImGuiViewportP* viewport) { ImGuiContext& g = *GImGui; @@ -13613,7 +13627,7 @@ bool ImGui::DockNodeBeginAmendTabBar(ImGuiDockNode* node) PushOverrideID(node->ID); bool ret = BeginTabBarEx(node->TabBar, node->TabBar->BarRect, node->TabBar->Flags, node); IM_UNUSED(ret); - IM_ASSERT(ret); + IM_ASSERT(ret); return true; } diff --git a/imgui_internal.h b/imgui_internal.h index 72dc3fb50d0f..fb379c85ef66 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1542,6 +1542,7 @@ struct ImGuiContext ImGuiViewportP* MouseViewport; ImGuiViewportP* MouseLastHoveredViewport; // Last known viewport that was hovered by mouse (even if we are not hovering any viewport any more) + honoring the _NoInputs flag. ImGuiID PlatformLastFocusedViewportId; + ImGuiPlatformMonitor FallbackMonitor; // Virtual monitor used as fallback if backend doesn't provide monitor information. int ViewportFrontMostStampCount; // Every time the front-most window changes, we stamp its viewport with an incrementing counter // Gamepad/keyboard Navigation @@ -2452,9 +2453,10 @@ namespace ImGui IMGUI_API void CallContextHooks(ImGuiContext* context, ImGuiContextHookType type); // Viewports - IMGUI_API void TranslateWindowsInViewport(ImGuiViewportP* viewport, const ImVec2& old_pos, const ImVec2& new_pos); - IMGUI_API void ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale); - IMGUI_API void DestroyPlatformWindow(ImGuiViewportP* viewport); + IMGUI_API void TranslateWindowsInViewport(ImGuiViewportP* viewport, const ImVec2& old_pos, const ImVec2& new_pos); + IMGUI_API void ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale); + IMGUI_API void DestroyPlatformWindow(ImGuiViewportP* viewport); + const ImGuiPlatformMonitor* GetViewportPlatformMonitor(ImGuiViewport* viewport); // Settings IMGUI_API void MarkIniSettingsDirty(); From 0157502eab7c43dfd9241d7ab1ac42d0161df1a2 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 12 Mar 2021 14:04:40 +0100 Subject: [PATCH 684/828] ImDrawFlags: rework/revert c2d6d26 + 39432bf in a way that is closer to old version and back to opt-in but with default 0 = all corners. --- docs/CHANGELOG.txt | 39 ++++++++-------- imgui.cpp | 33 +++++++------- imgui.h | 39 ++++++++-------- imgui_demo.cpp | 2 +- imgui_draw.cpp | 110 +++++++++++++++++++++++++-------------------- imgui_widgets.cpp | 18 ++++---- 6 files changed, 127 insertions(+), 114 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 9b9ec7155899..db308631ff78 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -106,28 +106,29 @@ Other changes: Breaking Changes: - Removed redirecting functions/enums names that were marked obsolete in 1.66 (September 2018): - - ImGui::SetScrollHere() --> use ImGui::SetScrollHereY() + - ImGui::SetScrollHere() --> use ImGui::SetScrollHereY() - ImDrawList: upgraded AddPolyline()/PathStroke()'s "bool closed" parameter to use "ImDrawFlags flags". + - bool closed = false --> use ImDrawFlags_None, or 0 + - bool closed = true --> use ImDrawFlags_Closed The matching ImDrawFlags_Closed value is guaranteed to always stay == 1 in the future. - - bool closed = false --> use ImDrawFlags_None, or 0 - - bool closed = true --> use ImDrawFlags_Closed Difference may not be noticeable for most but zealous type-checking tools may report a need to change. -- ImDrawList: upgraded AddRect(), AddRectFilled(), PathRect() to use general ImDrawFlags instead of ImDrawCornersFlags. - The old ImDrawCornersFlags used an awkward default value of ~0 or 0xF (4 lower bits set) to signify "round all - corners". We still support old flags, but note that the new named flags are opt-out instead of opt-in. [@rokups] - - AddRect(..., rounding, ImDrawCornerFlags_TopLeft) --> AddRect(..., ImDrawFlags_NoRoundCorners ^ ImDrawFlags_NoRoundCornerTL) - - Flags now sanely default to 0 instead of defaulting to ~0 or ImDrawCornerFlags_All, consistent with everywhere else. - - In practice, this shouldn't have an effect as those flags were rarely used. - - All meaningful old uses are supported: - - Default flags will behave the same. - - Use of old named flags will behave the same (but will be obsoleted later) - - Use of hardcoded ~0 or 0xF, previously suggested as a convenience, will behave the same (but will be obsoleted later). - - Not supported: - - Use of hardcoded values between 0x01 and 0x0E not using named flags are NOT supported (will assert). - - Use of new ImDrawFlags together with ImDrawCornerFlags in the same call (will assert). - - Use of rounding > 0.0f along old flags explicitely set as hardcoded 0 would have previously overidden the - rounding value back into "no rounding", whereas it will now behave as "round all corners". - This is technically the only real breaking change which we can't solve automatically. +- ImDrawList: upgraded AddRect(), AddRectFilled(), PathRect() to use ImDrawFlags instead of ImDrawCornersFlags. + - ImDrawCornerFlags_TopLeft --> use ImDrawFlags_RoundCornersTopLeft + - ImDrawCornerFlags_BotRight --> use ImDrawFlags_RoundCornersBottomRight + - ImDrawCornerFlags_None --> use ImDrawFlags_RoundCornersNone etc. + Flags now sanely defaults to 0 instead of 0x0F, consistent with all other flags in the API. + IMPORTANT: The default with rounding > 0.0f is now "round all corners" vs old implicit "round no corners": + - rounding == 0.0f + flags == 0 --> meant no rounding --> unchanged (common use) + - rounding > 0.0f + flags != 0 --> meant rounding --> unchanged (common use) + - rounding == 0.0f + flags != 0 --> meant no rounding --> unchanged (unlikely use) + - rounding > 0.0f + flags == 0 --> meant no rounding --> BREAKING (unlikely use)! + - this ONLY matters for hardcoded use of 0 with rounding > 0.0f. + - fix by using named ImDrawFlags_RoundCornersNone or rounding == 0.0f! + - this is technically the only real breaking change which we can't solve automatically (it's also uncommon). + The old ImDrawCornersFlags used awkward default values of ~0 or 0xF (4 lower bits set) to signify "round all corners" + and we sometimes encouraged using them as shortcuts. As a result the legacy path still support use of hardcoded ~0 + or any value from 0x1 or 0xF. They will behave the same with legacy paths enabled (will assert otherwise). + Courtesy of legacy untangling commity: [@rokups, @ocornut, @thedmd] - ImDrawList: clarified that PathArcTo()/PathArcToFast() won't render with radius < 0.0f. Previously it sorts of accidentally worked but would lead to counter-clockwise paths which and have an effect on anti-aliasing. - Moved 'misc/natvis/imgui.natvis' to 'misc/debuggers/imgui.natvis' as we will provide scripts for other debuggers. diff --git a/imgui.cpp b/imgui.cpp index 62dafae8af66..00400b0994c7 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -386,23 +386,24 @@ CODE - 2021/XX/XX (1.XX) - Moved IME support functions from io.ImeSetInputScreenPosFn, io.ImeWindowHandle to the PlatformIO api. - - 2021/03/11 (1.82) - upgraded ImDrawList::AddRect(), AddRectFilled(), PathRect() to use general ImDrawFlags instead of ImDrawCornersFlags. - the old ImDrawCornersFlags used an awkward default value of ~0 or 0xF (4 lower bits set) to signify "round all corners". We still support old flags, but note that the new named flags are opt-out instead of opt-in! - - AddRect(..., rounding, ImDrawCornerFlags_TopLeft) --> AddRect(..., ImDrawFlags_NoRoundCorners ^ ImDrawFlags_NoRoundCornerTL) - flags now sanely defaults to 0 instead of defaulting to ~0 or ImDrawCornerFlags_All, consistent with everywhere else in the codebase. - in practice, this shouldn't have an effect as those flags were rarely used. all meaningful old uses are supported: - - default flags will behave the same. - - use of old named flags will behave the same (but will be obsoleted later). - - use of hardcoded ~0 or 0xF, previously suggested as a convenience, will behave the same (but will be obsoleted later). - not supported: - - use of hardcoded values between 0x01 and 0x0E not using named flags are NOT supported (will assert). - - use of new ImDrawFlags together with ImDrawCornerFlags in the same call (will assert). - - use of rounding > 0.0f along old flags explicitely set as hard coded 0 would have previously overidden the rounding value back into "no rounding", whereas it will now behave as "round all corners". This is technically the only real breaking change which we can't solve automatically. + - 2021/03/12 (1.82) - upgraded ImDrawList::AddRect(), AddRectFilled(), PathRect() to use ImDrawFlags instead of ImDrawCornersFlags. + - ImDrawCornerFlags_TopLeft -> use ImDrawFlags_RoundCornersTopLeft + - ImDrawCornerFlags_BotRight -> use ImDrawFlags_RoundCornersBottomRight + - ImDrawCornerFlags_None -> use ImDrawFlags_RoundCornersNone etc. + flags now sanely defaults to 0 instead of 0x0F, consistent with all other flags in the API. + breaking: the default with rounding > 0.0f is now "round all corners" vs old implicit "round no corners": + - rounding == 0.0f + flags == 0 --> meant no rounding --> unchanged (common use) + - rounding > 0.0f + flags != 0 --> meant rounding --> unchanged (common use) + - rounding == 0.0f + flags != 0 --> meant no rounding --> unchanged (unlikely use) + - rounding > 0.0f + flags == 0 --> meant no rounding --> BREAKING (unlikely use): will now round all corners --> use ImDrawFlags_RoundCornersNone or rounding == 0.0f. + this ONLY matters for hard coded use of 0 + rounding > 0.0f. Use of named ImDrawFlags_RoundCornersNone (new) or ImDrawCornerFlags_None (old) are ok. + the old ImDrawCornersFlags used awkward default values of ~0 or 0xF (4 lower bits set) to signify "round all corners" and we sometimes encouraged using them as shortcuts. + legacy path still support use of hard coded ~0 or any value from 0x1 or 0xF. They will behave the same with legacy paths enabled (will assert otherwise). - 2021/03/11 (1.82) - removed redirecting functions/enums names that were marked obsolete in 1.66 (September 2018): - ImGui::SetScrollHere() -> use ImGui::SetScrollHereY() - 2021/03/11 (1.82) - clarified that ImDrawList::PathArcTo(), ImDrawList::PathArcToFast() won't render with radius < 0.0f. Previously it sorts of accidentally worked but would generally lead to counter-clockwise paths and have an effect on anti-aliasing. - 2021/03/10 (1.82) - upgraded ImDrawList::AddPolyline() and PathStroke() "bool closed" parameter to "ImDrawFlags flags". The matching ImDrawFlags_Closed value is guaranteed to always stay == 1 in the future. ->- 2021/02/22 (1.82) - win32+mingw: Re-enabled IME functions by default even under MinGW. In July 2016, issue #738 had me incorrectly disable those default functions for MinGW. MinGW users should: either link with -limm32, either set their imconfig file with '#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS'. + - 2021/02/22 (1.82) - win32+mingw: Re-enabled IME functions by default even under MinGW. In July 2016, issue #738 had me incorrectly disable those default functions for MinGW. MinGW users should: either link with -limm32, either set their imconfig file with '#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS'. - 2021/02/17 (1.82) - renamed rarely used style.CircleSegmentMaxError (old default = 1.60f) to style.CircleTessellationMaxError (new default = 0.30f) as the meaning of the value changed. - 2021/02/03 (1.81) - renamed ListBoxHeader(const char* label, ImVec2 size) to BeginListBox(). Kept inline redirection function (will obsolete). - removed ListBoxHeader(const char* label, int items_count, int height_in_items = -1) in favor of specifying size. Kept inline redirection function (will obsolete). @@ -5840,7 +5841,7 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar if (override_alpha) bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(alpha) << IM_COL32_A_SHIFT); } - window->DrawList->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight()), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? 0 : ImDrawFlags_NoRoundCornerT); + window->DrawList->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight()), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? 0 : ImDrawFlags_RoundCornersBottom); } // Title bar @@ -5849,7 +5850,7 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar if (!(flags & ImGuiWindowFlags_NoTitleBar) && !window->DockIsActive) { ImU32 title_bar_col = GetColorU32(title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg); - window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawFlags_NoRoundCornerB); + window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawFlags_RoundCornersTop); } // Menu bar @@ -5857,7 +5858,7 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar { ImRect menu_bar_rect = window->MenuBarRect(); menu_bar_rect.ClipWith(window->Rect()); // Soft clipping, in particular child window don't have minimum size covering the menu bar so this is useful for them. - window->DrawList->AddRectFilled(menu_bar_rect.Min + ImVec2(window_border_size, 0), menu_bar_rect.Max - ImVec2(window_border_size, 0), GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, ImDrawFlags_NoRoundCornerB); + window->DrawList->AddRectFilled(menu_bar_rect.Min + ImVec2(window_border_size, 0), menu_bar_rect.Max - ImVec2(window_border_size, 0), GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, ImDrawFlags_RoundCornersTop); if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y) window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border), style.FrameBorderSize); } diff --git a/imgui.h b/imgui.h index 2c392f61c2ad..4ee4cb952989 100644 --- a/imgui.h +++ b/imgui.h @@ -2380,24 +2380,23 @@ struct ImDrawListSplitter }; // Flags for ImDrawList functions -// (Before version 1.82: functions like AddRect(), AddRectFilled(), AddImageRounded(), PathRect() used ImDrawCornerFlags, -// we have moved those in 1.82 to ImDrawList but using opposite "opt-out" logic instead of "opt-in" logic: -// Old: ImDrawCornerFlags_BotLeft New: ImDrawFlags_NoRoundCornerBL -// The old enums are defined under ImDrawCornerFlags_ in the "Obsolete functions and types" section of this header) -// (Legacy: bit 0 must always correspond to ImDrawFlags_Closed to be backward compatible with old API using a bool. Bits 1..3 must b unused) +// (Legacy: bit 0 must always correspond to ImDrawFlags_Closed to be backward compatible with old API using a bool. Bits 1..3 must be unused) enum ImDrawFlags_ { ImDrawFlags_None = 0, ImDrawFlags_Closed = 1 << 0, // PathStroke(), AddPolyline(): specify that shape should be closed (Important: this is always == 1 for legacy reason) - ImDrawFlags_NoRoundCornerTL = 1 << 4, // AddRect(), AddRectFilled(), PathRect(): disable rounding top-left corner when rounding > 0.0f - ImDrawFlags_NoRoundCornerTR = 1 << 5, // AddRect(), AddRectFilled(), PathRect(): disable rounding top-right corner when rounding > 0.0f - ImDrawFlags_NoRoundCornerBL = 1 << 6, // AddRect(), AddRectFilled(), PathRect(): disable rounding bottom-left corner when rounding > 0.0f - ImDrawFlags_NoRoundCornerBR = 1 << 7, // AddRect(), AddRectFilled(), PathRect(): disable rounding bottom-right corner when rounding > 0.0f - ImDrawFlags_NoRoundCornerT = ImDrawFlags_NoRoundCornerTL | ImDrawFlags_NoRoundCornerTR, - ImDrawFlags_NoRoundCornerR = ImDrawFlags_NoRoundCornerTR | ImDrawFlags_NoRoundCornerBR, - ImDrawFlags_NoRoundCornerL = ImDrawFlags_NoRoundCornerTL | ImDrawFlags_NoRoundCornerBL, - ImDrawFlags_NoRoundCornerB = ImDrawFlags_NoRoundCornerBL | ImDrawFlags_NoRoundCornerBR, - ImDrawFlags_NoRoundCorners = ImDrawFlags_NoRoundCornerTL | ImDrawFlags_NoRoundCornerTR | ImDrawFlags_NoRoundCornerBL | ImDrawFlags_NoRoundCornerBR + ImDrawFlags_RoundCornersTopLeft = 1 << 4, // AddRect(), AddRectFilled(), PathRect(): enable rounding top-left corner only (when rounding > 0.0f, we default to all corners). Was 0x01. + ImDrawFlags_RoundCornersTopRight = 1 << 5, // AddRect(), AddRectFilled(), PathRect(): enable rounding top-right corner only (when rounding > 0.0f, we default to all corners). Was 0x02. + ImDrawFlags_RoundCornersBottomLeft = 1 << 6, // AddRect(), AddRectFilled(), PathRect(): enable rounding bottom-left corner only (when rounding > 0.0f, we default to all corners). Was 0x04. + ImDrawFlags_RoundCornersBottomRight = 1 << 7, // AddRect(), AddRectFilled(), PathRect(): enable rounding bottom-right corner only (when rounding > 0.0f, we default to all corners). Wax 0x08. + ImDrawFlags_RoundCornersNone = 1 << 8, // AddRect(), AddRectFilled(), PathRect(): disable rounding on all corners (when rounding > 0.0f). This is NOT zero, NOT an implicit flag! + ImDrawFlags_RoundCornersTop = ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersTopRight, + ImDrawFlags_RoundCornersBottom = ImDrawFlags_RoundCornersBottomLeft | ImDrawFlags_RoundCornersBottomRight, + ImDrawFlags_RoundCornersLeft = ImDrawFlags_RoundCornersBottomLeft | ImDrawFlags_RoundCornersTopLeft, + ImDrawFlags_RoundCornersRight = ImDrawFlags_RoundCornersBottomRight | ImDrawFlags_RoundCornersTopRight, + ImDrawFlags_RoundCornersAll = ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersTopRight | ImDrawFlags_RoundCornersBottomLeft | ImDrawFlags_RoundCornersBottomRight, + ImDrawFlags_RoundCornersDefault_ = ImDrawFlags_RoundCornersAll, // Default to ALL corners if none of the _RoundCornersXX flags are specified. + ImDrawFlags_RoundCornersMask_ = ImDrawFlags_RoundCornersAll | ImDrawFlags_RoundCornersNone }; // Flags for ImDrawList instance. Those are set automatically by ImGui:: functions from ImGuiIO settings, and generally not manipulated directly. @@ -3044,12 +3043,12 @@ namespace ImGui typedef ImDrawFlags ImDrawCornerFlags; enum ImDrawCornerFlags_ { - ImDrawCornerFlags_None = ImDrawFlags_NoRoundCorners, - ImDrawCornerFlags_TopLeft = 1 << 24, // Was (1 << 0) prior to 1.82. Order matches ImDrawFlags_NoRoundCorner* flag (we exploit this internally). - ImDrawCornerFlags_TopRight = 1 << 25, // Was (1 << 1) prior to 1.82. - ImDrawCornerFlags_BotLeft = 1 << 26, // Was (1 << 2) prior to 1.82. - ImDrawCornerFlags_BotRight = 1 << 27, // Was (1 << 3) prior to 1.82. - ImDrawCornerFlags_All = ImDrawCornerFlags_TopLeft | ImDrawCornerFlags_TopRight | ImDrawCornerFlags_BotLeft | ImDrawCornerFlags_BotRight, // Was (0x0F) prior to 1.82 + ImDrawCornerFlags_None = ImDrawFlags_RoundCornersNone, // Was == 0 prior to 1.82, this is now == ImDrawFlags_RoundCornersNone which is != 0 and not implicit + ImDrawCornerFlags_TopLeft = ImDrawFlags_RoundCornersTopLeft, // Was == 0x01 (1 << 0) prior to 1.82. Order matches ImDrawFlags_NoRoundCorner* flag (we exploit this internally). + ImDrawCornerFlags_TopRight = ImDrawFlags_RoundCornersTopRight, // Was == 0x02 (1 << 1) prior to 1.82. + ImDrawCornerFlags_BotLeft = ImDrawFlags_RoundCornersBottomLeft, // Was == 0x04 (1 << 2) prior to 1.82. + ImDrawCornerFlags_BotRight = ImDrawFlags_RoundCornersBottomRight, // Was == 0x08 (1 << 3) prior to 1.82. + ImDrawCornerFlags_All = ImDrawFlags_RoundCornersAll, // Was == 0x0F prior to 1.82 ImDrawCornerFlags_Top = ImDrawCornerFlags_TopLeft | ImDrawCornerFlags_TopRight, ImDrawCornerFlags_Bot = ImDrawCornerFlags_BotLeft | ImDrawCornerFlags_BotRight, ImDrawCornerFlags_Left = ImDrawCornerFlags_TopLeft | ImDrawCornerFlags_BotLeft, diff --git a/imgui_demo.cpp b/imgui_demo.cpp index d23c9bf9235e..ef021f45381b 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -7286,7 +7286,7 @@ static void ShowExampleAppCustomRendering(bool* p_open) const ImVec2 p = ImGui::GetCursorScreenPos(); const ImU32 col = ImColor(colf); const float spacing = 10.0f; - const ImDrawFlags corners_tl_br = ImDrawFlags_NoRoundCornerTR | ImDrawFlags_NoRoundCornerBL; + const ImDrawFlags corners_tl_br = ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersBottomRight; const float rounding = sz / 5.0f; const int circle_segments = circle_segments_override ? circle_segments_override_v : 0; const int curve_segments = curve_segments_override ? curve_segments_override_v : 0; diff --git a/imgui_draw.cpp b/imgui_draw.cpp index db09c1210e9c..a78c365b651f 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1285,39 +1285,47 @@ void ImDrawList::PathBezierQuadraticCurveTo(const ImVec2& p2, const ImVec2& p3, } } -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS -static inline ImDrawFlags FixDrawCornerFlags(ImDrawFlags flags) +IM_STATIC_ASSERT(ImDrawFlags_RoundCornersTopLeft == (1 << 4)); +static inline ImDrawFlags FixRectCornerFlags(ImDrawFlags flags) { - // If any of following asserts triggers, please update your code replacing all uses of ImDrawCornerFlags_* with ImDrawFlags_*. - // Legacy Rule 1: Support for hard coded 0x0F or ~0 (used to be equivalent to ImDrawCornerFlags_All) - if (flags == ~0 || flags == 0x0F) - return 0; +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + // Legacy Support for hard coded ~0 (used to be a suggested equivalent to ImDrawCornerFlags_All) + // ~0 --> ImDrawFlags_RoundCornersAll or 0 + if (flags == ~0) + return ImDrawFlags_RoundCornersAll; + + // Legacy Support for hard coded 0x01 to 0x0F (matching 15 out of 16 old flags combinations) + // 0x01 --> ImDrawFlags_RoundCornersTopLeft (VALUE 0x01 OVERLAPS ImDrawFlags_Closed but ImDrawFlags_Closed is never valid in this path!) + // 0x02 --> ImDrawFlags_RoundCornersTopRight + // 0x03 --> ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersTopRight + // 0x04 --> ImDrawFlags_RoundCornersBotLeft + // 0x05 --> ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersBotLeft + // ... + // 0x0F --> ImDrawFlags_RoundCornersAll or 0 + // (See all values in ImDrawCornerFlags_) + if (flags >= 0x01 && flags <= 0x0F) + return (flags << 4); + + // We cannot support hard coded 0x00 with 'float rounding > 0.0f' --> replace with ImDrawFlags_RoundCornersNone or use 'float rounding = 0.0f' +#endif - // Legacy Rule 2: if any of old ImDrawCornerFlags flags, move them to corresponding positions of non-legacy flags and invert them. - if ((flags & ImDrawCornerFlags_All) != 0) - { - IM_ASSERT((flags & ~ImDrawCornerFlags_All) == 0 && "Mixing legacy ImDrawCornerFlags and new ImDrawFlags is not allowed."); - IM_STATIC_ASSERT((ImDrawCornerFlags_TopLeft >> 20) == ImDrawFlags_NoRoundCornerTL); - return (flags >> 20) ^ ImDrawFlags_NoRoundCorners; - } + // If this triggers, please update your code replacing hardcoded values with new ImDrawFlags_RoundCorners* values. + // Note that ImDrawFlags_Closed (== 0x01) is an invalid flag for AddRect(), AddRectFilled(), PathRect() etc... + IM_ASSERT((flags & 0x0F) == 0 && "Misuse of legacy hardcoded ImDrawCornerFlags values!"); + + if ((flags & ImDrawFlags_RoundCornersMask_) == 0) + flags |= ImDrawFlags_RoundCornersAll; - // Bits 1..3 are unused, did you use old hard coded values?! In which case, check the old values in ImDrawCornerFlags_ definition. - // If you used ~0 or 0x0F you can now change them to 0 or ImDrawFlags_None. - IM_ASSERT((flags & 0x0E) == 0); return flags; } -#else -// Assert and return same value -#define FixDrawCornerFlags(FLAGS) (IM_ASSERT((FLAGS & 0x0E) == 0), FLAGS) -#endif void ImDrawList::PathRect(const ImVec2& a, const ImVec2& b, float rounding, ImDrawFlags flags) { - flags = FixDrawCornerFlags(flags); - rounding = ImMin(rounding, ImFabs(b.x - a.x) * ( ((flags & ImDrawFlags_NoRoundCornerT) == 0) || ((flags & ImDrawFlags_NoRoundCornerB) == 0) ? 0.5f : 1.0f ) - 1.0f); - rounding = ImMin(rounding, ImFabs(b.y - a.y) * ( ((flags & ImDrawFlags_NoRoundCornerL) == 0) || ((flags & ImDrawFlags_NoRoundCornerR) == 0) ? 0.5f : 1.0f ) - 1.0f); + flags = FixRectCornerFlags(flags); + rounding = ImMin(rounding, ImFabs(b.x - a.x) * ( ((flags & ImDrawFlags_RoundCornersTop) == ImDrawFlags_RoundCornersTop) || ((flags & ImDrawFlags_RoundCornersBottom) == ImDrawFlags_RoundCornersBottom) ? 0.5f : 1.0f ) - 1.0f); + rounding = ImMin(rounding, ImFabs(b.y - a.y) * ( ((flags & ImDrawFlags_RoundCornersLeft) == ImDrawFlags_RoundCornersLeft) || ((flags & ImDrawFlags_RoundCornersRight) == ImDrawFlags_RoundCornersRight) ? 0.5f : 1.0f ) - 1.0f); - if (rounding <= 0.0f || (flags & ImDrawFlags_NoRoundCorners) == ImDrawFlags_NoRoundCorners) + if (rounding <= 0.0f || (flags & ImDrawFlags_RoundCornersMask_) == ImDrawFlags_RoundCornersNone) { PathLineTo(a); PathLineTo(ImVec2(b.x, a.y)); @@ -1326,10 +1334,10 @@ void ImDrawList::PathRect(const ImVec2& a, const ImVec2& b, float rounding, ImDr } else { - const float rounding_tl = (flags & ImDrawFlags_NoRoundCornerTL) ? 0.0f : rounding; - const float rounding_tr = (flags & ImDrawFlags_NoRoundCornerTR) ? 0.0f : rounding; - const float rounding_br = (flags & ImDrawFlags_NoRoundCornerBR) ? 0.0f : rounding; - const float rounding_bl = (flags & ImDrawFlags_NoRoundCornerBL) ? 0.0f : rounding; + const float rounding_tl = (flags & ImDrawFlags_RoundCornersTopLeft) ? rounding : 0.0f; + const float rounding_tr = (flags & ImDrawFlags_RoundCornersTopRight) ? rounding : 0.0f; + const float rounding_br = (flags & ImDrawFlags_RoundCornersBottomRight) ? rounding : 0.0f; + const float rounding_bl = (flags & ImDrawFlags_RoundCornersBottomLeft) ? rounding : 0.0f; PathArcToFast(ImVec2(a.x + rounding_tl, a.y + rounding_tl), rounding_tl, 6, 9); PathArcToFast(ImVec2(b.x - rounding_tr, a.y + rounding_tr), rounding_tr, 9, 12); PathArcToFast(ImVec2(b.x - rounding_br, b.y - rounding_br), rounding_br, 0, 3); @@ -1363,15 +1371,15 @@ void ImDrawList::AddRectFilled(const ImVec2& p_min, const ImVec2& p_max, ImU32 c { if ((col & IM_COL32_A_MASK) == 0) return; - if (rounding > 0.0f && (flags & ImDrawFlags_NoRoundCorners) != ImDrawFlags_NoRoundCorners) + if (rounding <= 0.0f || (flags & ImDrawFlags_RoundCornersMask_) == ImDrawFlags_RoundCornersNone) { - PathRect(p_min, p_max, rounding, flags); - PathFillConvex(col); + PrimReserve(6, 4); + PrimRect(p_min, p_max, col); } else { - PrimReserve(6, 4); - PrimRect(p_min, p_max, col); + PathRect(p_min, p_max, rounding, flags); + PathFillConvex(col); } } @@ -1606,8 +1614,8 @@ void ImDrawList::AddImageRounded(ImTextureID user_texture_id, const ImVec2& p_mi if ((col & IM_COL32_A_MASK) == 0) return; - flags = FixDrawCornerFlags(flags); - if (rounding <= 0.0f || (flags & ImDrawFlags_NoRoundCorners) == ImDrawFlags_NoRoundCorners) + flags = FixRectCornerFlags(flags); + if (rounding <= 0.0f || (flags & ImDrawFlags_RoundCornersMask_) == ImDrawFlags_RoundCornersNone) { AddImage(user_texture_id, p_min, p_max, uv_min, uv_max, col); return; @@ -3858,14 +3866,14 @@ void ImGui::RenderRectFilledWithHole(ImDrawList* draw_list, ImRect outer, ImRect const bool fill_R = (inner.Max.x < outer.Max.x); const bool fill_U = (inner.Min.y > outer.Min.y); const bool fill_D = (inner.Max.y < outer.Max.y); - if (fill_L) draw_list->AddRectFilled(ImVec2(outer.Min.x, inner.Min.y), ImVec2(inner.Min.x, inner.Max.y), col, rounding, (fill_U ? ImDrawFlags_NoRoundCornerTL : 0) | (fill_D ? ImDrawFlags_NoRoundCornerBL : 0)); - if (fill_R) draw_list->AddRectFilled(ImVec2(inner.Max.x, inner.Min.y), ImVec2(outer.Max.x, inner.Max.y), col, rounding, (fill_U ? ImDrawFlags_NoRoundCornerTR : 0) | (fill_D ? ImDrawFlags_NoRoundCornerBR : 0)); - if (fill_U) draw_list->AddRectFilled(ImVec2(inner.Min.x, outer.Min.y), ImVec2(inner.Max.x, inner.Min.y), col, rounding, (fill_L ? ImDrawFlags_NoRoundCornerTL : 0) | (fill_R ? ImDrawFlags_NoRoundCornerTR : 0)); - if (fill_D) draw_list->AddRectFilled(ImVec2(inner.Min.x, inner.Max.y), ImVec2(inner.Max.x, outer.Max.y), col, rounding, (fill_L ? ImDrawFlags_NoRoundCornerBL : 0) | (fill_R ? ImDrawFlags_NoRoundCornerBR : 0)); - if (fill_L && fill_U) draw_list->AddRectFilled(ImVec2(outer.Min.x, outer.Min.y), ImVec2(inner.Min.x, inner.Min.y), col, rounding, ImDrawFlags_NoRoundCornerB | ImDrawFlags_NoRoundCornerTR); - if (fill_R && fill_U) draw_list->AddRectFilled(ImVec2(inner.Max.x, outer.Min.y), ImVec2(outer.Max.x, inner.Min.y), col, rounding, ImDrawFlags_NoRoundCornerB | ImDrawFlags_NoRoundCornerTL); - if (fill_L && fill_D) draw_list->AddRectFilled(ImVec2(outer.Min.x, inner.Max.y), ImVec2(inner.Min.x, outer.Max.y), col, rounding, ImDrawFlags_NoRoundCornerT | ImDrawFlags_NoRoundCornerBR); - if (fill_R && fill_D) draw_list->AddRectFilled(ImVec2(inner.Max.x, inner.Max.y), ImVec2(outer.Max.x, outer.Max.y), col, rounding, ImDrawFlags_NoRoundCornerT | ImDrawFlags_NoRoundCornerBL); + if (fill_L) draw_list->AddRectFilled(ImVec2(outer.Min.x, inner.Min.y), ImVec2(inner.Min.x, inner.Max.y), col, rounding, (fill_U ? 0 : ImDrawFlags_RoundCornersTopLeft) | (fill_D ? 0 : ImDrawFlags_RoundCornersBottomLeft)); + if (fill_R) draw_list->AddRectFilled(ImVec2(inner.Max.x, inner.Min.y), ImVec2(outer.Max.x, inner.Max.y), col, rounding, (fill_U ? 0 : ImDrawFlags_RoundCornersTopRight) | (fill_D ? 0 : ImDrawFlags_RoundCornersBottomRight)); + if (fill_U) draw_list->AddRectFilled(ImVec2(inner.Min.x, outer.Min.y), ImVec2(inner.Max.x, inner.Min.y), col, rounding, (fill_L ? 0 : ImDrawFlags_RoundCornersTopLeft) | (fill_R ? 0 : ImDrawFlags_RoundCornersTopRight)); + if (fill_D) draw_list->AddRectFilled(ImVec2(inner.Min.x, inner.Max.y), ImVec2(inner.Max.x, outer.Max.y), col, rounding, (fill_L ? 0 : ImDrawFlags_RoundCornersBottomLeft) | (fill_R ? 0 : ImDrawFlags_RoundCornersBottomRight)); + if (fill_L && fill_U) draw_list->AddRectFilled(ImVec2(outer.Min.x, outer.Min.y), ImVec2(inner.Min.x, inner.Min.y), col, rounding, ImDrawFlags_RoundCornersTopLeft); + if (fill_R && fill_U) draw_list->AddRectFilled(ImVec2(inner.Max.x, outer.Min.y), ImVec2(outer.Max.x, inner.Min.y), col, rounding, ImDrawFlags_RoundCornersTopRight); + if (fill_L && fill_D) draw_list->AddRectFilled(ImVec2(outer.Min.x, inner.Max.y), ImVec2(inner.Min.x, outer.Max.y), col, rounding, ImDrawFlags_RoundCornersBottomLeft); + if (fill_R && fill_D) draw_list->AddRectFilled(ImVec2(inner.Max.x, inner.Max.y), ImVec2(outer.Max.x, outer.Max.y), col, rounding, ImDrawFlags_RoundCornersBottomRight); } // Helper for ColorPicker4() @@ -3874,10 +3882,12 @@ void ImGui::RenderRectFilledWithHole(ImDrawList* draw_list, ImRect outer, ImRect // FIXME: uses ImGui::GetColorU32 void ImGui::RenderColorRectWithAlphaCheckerboard(ImDrawList* draw_list, ImVec2 p_min, ImVec2 p_max, ImU32 col, float grid_step, ImVec2 grid_off, float rounding, ImDrawFlags flags) { + if ((flags & ImDrawFlags_RoundCornersMask_) == 0) + flags = ImDrawFlags_RoundCornersDefault_; if (((col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT) < 0xFF) { - ImU32 col_bg1 = ImGui::GetColorU32(ImAlphaBlendColors(IM_COL32(204, 204, 204, 255), col)); - ImU32 col_bg2 = ImGui::GetColorU32(ImAlphaBlendColors(IM_COL32(128, 128, 128, 255), col)); + ImU32 col_bg1 = GetColorU32(ImAlphaBlendColors(IM_COL32(204, 204, 204, 255), col)); + ImU32 col_bg2 = GetColorU32(ImAlphaBlendColors(IM_COL32(128, 128, 128, 255), col)); draw_list->AddRectFilled(p_min, p_max, col_bg1, rounding, flags); int yi = 0; @@ -3891,10 +3901,12 @@ void ImGui::RenderColorRectWithAlphaCheckerboard(ImDrawList* draw_list, ImVec2 p float x1 = ImClamp(x, p_min.x, p_max.x), x2 = ImMin(x + grid_step, p_max.x); if (x2 <= x1) continue; - ImDrawFlags cell_flags = ImDrawFlags_NoRoundCorners; - if (y1 <= p_min.y) { if (x1 <= p_min.x) cell_flags &= ~ImDrawFlags_NoRoundCornerTL; if (x2 >= p_max.x) cell_flags &= ~ImDrawFlags_NoRoundCornerTR; } - if (y2 >= p_max.y) { if (x1 <= p_min.x) cell_flags &= ~ImDrawFlags_NoRoundCornerBL; if (x2 >= p_max.x) cell_flags &= ~ImDrawFlags_NoRoundCornerBR; } - cell_flags |= flags; + ImDrawFlags cell_flags = ImDrawFlags_RoundCornersNone; + if (y1 <= p_min.y) { if (x1 <= p_min.x) cell_flags |= ImDrawFlags_RoundCornersTopLeft; if (x2 >= p_max.x) cell_flags |= ImDrawFlags_RoundCornersTopRight; } + if (y2 >= p_max.y) { if (x1 <= p_min.x) cell_flags |= ImDrawFlags_RoundCornersBottomLeft; if (x2 >= p_max.x) cell_flags |= ImDrawFlags_RoundCornersBottomRight; } + + // Combine flags + cell_flags = (flags == ImDrawFlags_RoundCornersNone || cell_flags == ImDrawFlags_RoundCornersNone) ? ImDrawFlags_RoundCornersNone : (cell_flags & flags); draw_list->AddRectFilled(ImVec2(x1, y1), ImVec2(x2, y2), col_bg2, rounding, cell_flags); } } diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 9c1c9f04091d..34f89fbde5e5 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -881,19 +881,19 @@ void ImGui::Scrollbar(ImGuiAxis axis) // Calculate scrollbar bounding box ImRect bb = GetWindowScrollbarRect(window, axis); - ImDrawFlags rounding_corners = ImDrawFlags_NoRoundCorners; + ImDrawFlags rounding_corners = ImDrawFlags_RoundCornersNone; if (axis == ImGuiAxis_X) { - rounding_corners &= ~ImDrawFlags_NoRoundCornerBL; + rounding_corners |= ImDrawFlags_RoundCornersBottomLeft; if (!window->ScrollbarY) - rounding_corners &= ~ImDrawFlags_NoRoundCornerBR; + rounding_corners |= ImDrawFlags_RoundCornersBottomRight; } else { if ((window->Flags & ImGuiWindowFlags_NoTitleBar) && !(window->Flags & ImGuiWindowFlags_MenuBar)) - rounding_corners &= ~ImDrawFlags_NoRoundCornerTR; + rounding_corners |= ImDrawFlags_RoundCornersTopRight; if (!window->ScrollbarX) - rounding_corners &= ~ImDrawFlags_NoRoundCornerBR; + rounding_corners |= ImDrawFlags_RoundCornersBottomRight; } float size_avail = window->InnerRect.Max[axis] - window->InnerRect.Min[axis]; float size_contents = window->ContentSize[axis] + window->WindowPadding[axis] * 2.0f; @@ -1586,12 +1586,12 @@ bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboF const float value_x2 = ImMax(frame_bb.Min.x, frame_bb.Max.x - arrow_size); RenderNavHighlight(frame_bb, id); if (!(flags & ImGuiComboFlags_NoPreview)) - window->DrawList->AddRectFilled(frame_bb.Min, ImVec2(value_x2, frame_bb.Max.y), frame_col, style.FrameRounding, (flags & ImGuiComboFlags_NoArrowButton) ? 0 : ImDrawFlags_NoRoundCornerR); + window->DrawList->AddRectFilled(frame_bb.Min, ImVec2(value_x2, frame_bb.Max.y), frame_col, style.FrameRounding, (flags & ImGuiComboFlags_NoArrowButton) ? ImDrawFlags_RoundCornersAll : ImDrawFlags_RoundCornersLeft); if (!(flags & ImGuiComboFlags_NoArrowButton)) { ImU32 bg_col = GetColorU32((popup_open || hovered) ? ImGuiCol_ButtonHovered : ImGuiCol_Button); ImU32 text_col = GetColorU32(ImGuiCol_Text); - window->DrawList->AddRectFilled(ImVec2(value_x2, frame_bb.Min.y), frame_bb.Max, bg_col, style.FrameRounding, (w <= arrow_size) ? 0 : ImDrawFlags_NoRoundCornerL); + window->DrawList->AddRectFilled(ImVec2(value_x2, frame_bb.Min.y), frame_bb.Max, bg_col, style.FrameRounding, (w <= arrow_size) ? ImDrawFlags_RoundCornersAll : ImDrawFlags_RoundCornersRight); if (value_x2 + arrow_size - style.FramePadding.x <= frame_bb.Max.x) RenderArrow(window->DrawList, ImVec2(value_x2 + style.FramePadding.y, frame_bb.Min.y + style.FramePadding.y), text_col, ImGuiDir_Down, 1.0f); } @@ -5367,8 +5367,8 @@ bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFl if ((flags & ImGuiColorEditFlags_AlphaPreviewHalf) && col_rgb.w < 1.0f) { float mid_x = IM_ROUND((bb_inner.Min.x + bb_inner.Max.x) * 0.5f); - RenderColorRectWithAlphaCheckerboard(window->DrawList, ImVec2(bb_inner.Min.x + grid_step, bb_inner.Min.y), bb_inner.Max, GetColorU32(col_rgb), grid_step, ImVec2(-grid_step + off, off), rounding, ImDrawFlags_NoRoundCornerL); - window->DrawList->AddRectFilled(bb_inner.Min, ImVec2(mid_x, bb_inner.Max.y), GetColorU32(col_rgb_without_alpha), rounding, ImDrawFlags_NoRoundCornerR); + RenderColorRectWithAlphaCheckerboard(window->DrawList, ImVec2(bb_inner.Min.x + grid_step, bb_inner.Min.y), bb_inner.Max, GetColorU32(col_rgb), grid_step, ImVec2(-grid_step + off, off), rounding, ImDrawFlags_RoundCornersRight); + window->DrawList->AddRectFilled(bb_inner.Min, ImVec2(mid_x, bb_inner.Max.y), GetColorU32(col_rgb_without_alpha), rounding, ImDrawFlags_RoundCornersLeft); } else { From cfe83c4b4410050094829579dc496bf6eab88993 Mon Sep 17 00:00:00 2001 From: CheckmateAt7 <66566308+CheckmateAt7@users.noreply.github.com> Date: Fri, 12 Mar 2021 18:02:46 +0100 Subject: [PATCH 685/828] Removed deprecated flag stopping compilation (#3902) --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 00400b0994c7..c74e9861c743 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -13760,7 +13760,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w if (is_focused) node->LastFrameFocused = g.FrameCount; ImU32 title_bar_col = GetColorU32(host_window->Collapsed ? ImGuiCol_TitleBgCollapsed : is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg); - host_window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, host_window->WindowRounding, ImDrawFlags_NoRoundCornerB); + host_window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, host_window->WindowRounding, ImDrawFlags_RoundCornersTop); // Docking/Collapse button if (has_window_menu_button) From 2231e1a369cf870f20ca23f187c05ff3190336c6 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 16 Mar 2021 14:30:36 +0100 Subject: [PATCH 686/828] Docking: Dockspace() never draws a background. (#3924) --- imgui.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 1bf605ad81e0..6da9b3ab551b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -14747,22 +14747,19 @@ void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags fla SetNextWindowSize(node->Size); g.NextWindowData.PosUndock = false; - // FIXME-DOCK Why do we need a child window to host a dockspace, could we host it in the existing window? + // FIXME-DOCK: Why do we need a child window to host a dockspace, could we host it in the existing window? + // FIXME-DOCK: What is the reason for not simply calling BeginChild()? (OK to have a reason but should be commented) ImGuiWindowFlags window_flags = ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_DockNodeHost; window_flags |= ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar; window_flags |= ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse; + window_flags |= ImGuiWindowFlags_NoBackground; char title[256]; ImFormatString(title, IM_ARRAYSIZE(title), "%s/DockSpace_%08X", window->Name, id); - // FIXME-DOCK: What is the reason for not simply calling BeginChild()? - if (node->Windows.Size > 0 || node->IsSplitNode()) - PushStyleColor(ImGuiCol_ChildBg, IM_COL32(0, 0, 0, 0)); PushStyleVar(ImGuiStyleVar_ChildBorderSize, 0.0f); Begin(title, NULL, window_flags); PopStyleVar(); - if (node->Windows.Size > 0 || node->IsSplitNode()) - PopStyleColor(); ImGuiWindow* host_window = g.CurrentWindow; host_window->DockNodeAsHost = node; From b202fa90639807e916fad861cd8a684e6f412a39 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 16 Mar 2021 16:01:12 +0100 Subject: [PATCH 687/828] Docking: undocking nodes/windows covering most of the monitor max their size down to 90% to ease further manipulations. Kind of a welcome hack. --- imgui.cpp | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 6da9b3ab551b..342f58d02b3f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -12815,6 +12815,29 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) MarkIniSettingsDirty(); } +// Problem: +// Undocking a large (~full screen) window would leave it so large that the bottom right sizing corner would more +// than likely be off the screen and the window would be hard to resize to fit on screen. This can be particularly problematic +// with 'ConfigWindowsMoveFromTitleBarOnly=true' and/or with 'ConfigWindowsResizeFromEdges=false' as well (the later can be +// due to missing ImGuiBackendFlags_HasMouseCursors backend flag). +// Solution: +// When undocking a window we currently force its maximum size to 90% of the host viewport or monitor. +// Reevaluate this when we implement preserving docked/undocked size ("docking_wip/undocked_size" branch). +static ImVec2 FixLargeWindowsWhenUndocking(const ImVec2& size, ImGuiViewport* ref_viewport) +{ + if (ref_viewport == NULL) + return size; + + ImGuiContext& g = *GImGui; + ImVec2 max_size = ImFloor(ref_viewport->WorkSize * 0.90f); + if (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable) + { + const ImGuiPlatformMonitor* monitor = ImGui::GetViewportPlatformMonitor(ref_viewport); + max_size = ImFloor(monitor->WorkSize * 0.90f); + } + return ImMin(size, max_size); +} + void ImGui::DockContextProcessUndockWindow(ImGuiContext* ctx, ImGuiWindow* window, bool clear_persistent_docking_ref) { IMGUI_DEBUG_LOG_DOCKING("DockContextProcessUndockWindow window '%s', clear_persistent_docking_ref = %d\n", window->Name, clear_persistent_docking_ref); @@ -12826,6 +12849,8 @@ void ImGui::DockContextProcessUndockWindow(ImGuiContext* ctx, ImGuiWindow* windo window->Collapsed = false; window->DockIsActive = false; window->DockTabIsVisible = false; + window->Size = window->SizeFull = FixLargeWindowsWhenUndocking(window->SizeFull, window->Viewport); + MarkIniSettingsDirty(); } @@ -12839,6 +12864,9 @@ void ImGui::DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node) { // In the case of a root node or central node, the node will have to stay in place. Create a new node to receive the payload. ImGuiDockNode* new_node = DockContextAddNode(ctx, 0); + new_node->Pos = node->Pos; + new_node->Size = node->Size; + new_node->SizeRef = node->SizeRef; DockNodeMoveWindows(new_node, node); DockSettingsRenameNodeReferences(node->ID, new_node->ID); for (int n = 0; n < new_node->Windows.Size; n++) @@ -12847,7 +12875,7 @@ void ImGui::DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node) } else { - // Otherwise extract our node and merging our sibling back into the parent node. + // Otherwise extract our node and merge our sibling back into the parent node. IM_ASSERT(node->ParentNode->ChildNodes[0] == node || node->ParentNode->ChildNodes[1] == node); int index_in_parent = (node->ParentNode->ChildNodes[0] == node) ? 0 : 1; node->ParentNode->ChildNodes[index_in_parent] = NULL; @@ -12855,7 +12883,8 @@ void ImGui::DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node) node->ParentNode->AuthorityForViewport = ImGuiDataAuthority_Window; // The node that stays in place keeps the viewport, so our newly dragged out node will create a new viewport node->ParentNode = NULL; } - node->AuthorityForPos = node->AuthorityForSize = ImGuiDataAuthority_Window; + node->AuthorityForPos = node->AuthorityForSize = ImGuiDataAuthority_DockNode; + node->Size = FixLargeWindowsWhenUndocking(node->Size, node->Windows[0]->Viewport); node->WantMouseMove = true; MarkIniSettingsDirty(); } From b17bfdd6f4adc728e0977bd2435294e38c706b86 Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Tue, 2 Feb 2021 17:20:30 +0200 Subject: [PATCH 688/828] Docking: Add support for split_outer in DockContextCalcDropPosForDocking(). Misc: Add FIXME regarding behavior of some window fields. --- imgui.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 892ac6b6e2ee..65b7811b794b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6207,6 +6207,10 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Update contents size from last frame for auto-fitting (or use explicit size) CalcWindowContentSizes(window, &window->ContentSize, &window->ContentSizeIdeal); + + // FIXME: These flags are decremented before they are used. This means that in order to have these fields produce their intended behaviors + // for one frame we must set them to at least 2, which is counter-intuitive. HiddenFramesCannotSkipItems is a more complicated case because + // it has a single usage before this code block and may be set below before it is finally checked. if (window->HiddenFramesCanSkipItems > 0) window->HiddenFramesCanSkipItems--; if (window->HiddenFramesCannotSkipItems > 0) @@ -12892,20 +12896,16 @@ void ImGui::DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node) // This is mostly used for automation. bool ImGui::DockContextCalcDropPosForDocking(ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, bool split_outer, ImVec2* out_pos) { - if (split_outer) - { - IM_ASSERT(0); - } - else - { - ImGuiDockPreviewData split_data; - DockNodePreviewDockSetup(target, target_node, payload, &split_data, false, split_outer); - if (split_data.DropRectsDraw[split_dir+1].IsInverted()) - return false; - *out_pos = split_data.DropRectsDraw[split_dir+1].GetCenter(); - return true; - } - return false; + // In DockNodePreviewDockSetup() for a root central node instead of showing both "inner" and "outer" drop rects + // (which would be functionally identical) we only show the outer one. Reflect this here. + if (target_node && target_node->ParentNode == NULL && target_node->IsCentralNode() && split_dir != ImGuiDir_None) + split_outer = true; + ImGuiDockPreviewData split_data; + DockNodePreviewDockSetup(target, target_node, payload, &split_data, false, split_outer); + if (split_data.DropRectsDraw[split_dir+1].IsInverted()) + return false; + *out_pos = split_data.DropRectsDraw[split_dir+1].GetCenter(); + return true; } //----------------------------------------------------------------------------- From 085cff2fe58077a4a0bf1f9e9284814769141801 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 18 Mar 2021 18:16:49 +0100 Subject: [PATCH 689/828] Viewports, Backends: Vulkan: Rebuild swapchain on VK_SUBOPTIMAL_KHR. (#3881) --- backends/imgui_impl_vulkan.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp index 2e0085a749b2..a7bb6a6b793a 100644 --- a/backends/imgui_impl_vulkan.cpp +++ b/backends/imgui_impl_vulkan.cpp @@ -1619,7 +1619,7 @@ static void ImGui_ImplVulkan_SwapBuffers(ImGuiViewport* viewport, void*) info.pSwapchains = &wd->Swapchain; info.pImageIndices = &present_index; err = vkQueuePresentKHR(v->Queue, &info); - if (err == VK_ERROR_OUT_OF_DATE_KHR) + if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) ImGui_ImplVulkanH_CreateOrResizeWindow(v->Instance, v->PhysicalDevice, v->Device, &data->Window, v->QueueFamily, v->Allocator, (int)viewport->Size.x, (int)viewport->Size.y, v->MinImageCount); else check_vk_result(err); From ca34c81ce9ac33bce2e1c87cee2b0e52b991161e Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 25 Mar 2021 15:21:19 +0100 Subject: [PATCH 690/828] BeginMainMenuBar(): remove expectation that we don't know menu bar height ahead, allowing up to generalize placement in any direction (will be done in master) Amend 75de34e281141f851f7e5f9f6a923573167bc0f0 --- imgui.cpp | 1 - imgui_internal.h | 1 + imgui_widgets.cpp | 14 ++++++-------- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 65b7811b794b..cf22e1b94392 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -943,7 +943,6 @@ static void UpdateViewportsEndFrame(); static void UpdateSelectWindowViewport(ImGuiWindow* window); static bool UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* host_viewport); static bool UpdateTryMergeWindowIntoHostViewports(ImGuiWindow* window); -static void SetCurrentViewport(ImGuiWindow* window, ImGuiViewportP* viewport); static bool GetWindowAlwaysWantOwnViewport(ImGuiWindow* window); static int FindPlatformMonitorForPos(const ImVec2& pos); static int FindPlatformMonitorForRect(const ImRect& r); diff --git a/imgui_internal.h b/imgui_internal.h index 04d729edc4f9..912d09409920 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2462,6 +2462,7 @@ namespace ImGui IMGUI_API void TranslateWindowsInViewport(ImGuiViewportP* viewport, const ImVec2& old_pos, const ImVec2& new_pos); IMGUI_API void ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale); IMGUI_API void DestroyPlatformWindow(ImGuiViewportP* viewport); + IMGUI_API void SetCurrentViewport(ImGuiWindow* window, ImGuiViewportP* viewport); const ImGuiPlatformMonitor* GetViewportPlatformMonitor(ImGuiViewport* viewport); // Settings diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 13fc27c7bec0..ecbfd4a247ad 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6625,12 +6625,15 @@ bool ImGui::BeginMainMenuBar() if (menu_bar_window == NULL || menu_bar_window->BeginCount == 0) { // Set window position - // We don't attempt to calculate our height ahead, as it depends on the per-viewport font size. - // However menu-bar will affect the minimum window size so we'll get the right height. + SetCurrentViewport(NULL, viewport); + float height = GetFrameHeight(); ImVec2 menu_bar_pos = viewport->Pos + viewport->CurrWorkOffsetMin; - ImVec2 menu_bar_size = ImVec2(viewport->Size.x - viewport->CurrWorkOffsetMin.x + viewport->CurrWorkOffsetMax.x, 1.0f); + ImVec2 menu_bar_size = ImVec2(viewport->Size.x - viewport->CurrWorkOffsetMin.x + viewport->CurrWorkOffsetMax.x, height); SetNextWindowPos(menu_bar_pos); SetNextWindowSize(menu_bar_size); + + // Report our size into work area + viewport->CurrWorkOffsetMin.y += height; } // Create window @@ -6641,11 +6644,6 @@ bool ImGui::BeginMainMenuBar() bool is_open = Begin("##MainMenuBar", NULL, window_flags) && BeginMenuBar(); PopStyleVar(2); - // Report our size into work area (for next frame) using actual window size - menu_bar_window = GetCurrentWindow(); - if (menu_bar_window->BeginCount == 1) - viewport->CurrWorkOffsetMin.y += menu_bar_window->Size.y; - g.NextWindowData.MenuBarOffsetMinVal = ImVec2(0.0f, 0.0f); if (!is_open) { From b305953bd114abf58c88f544209bb73448b57649 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 25 Mar 2021 17:13:25 +0100 Subject: [PATCH 691/828] Viewports: Hotfix for crash in monitor array access, caused by 4b9bc4902. (#3967) --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index e1631df33299..456bf78a998e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -12064,7 +12064,7 @@ const ImGuiPlatformMonitor* ImGui::GetViewportPlatformMonitor(ImGuiViewport* vie ImGuiContext& g = *GImGui; ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)viewport_p; int monitor_idx = viewport->PlatformMonitor; - if (monitor_idx >= 0 || monitor_idx < g.PlatformIO.Monitors.Size) + if (monitor_idx >= 0 && monitor_idx < g.PlatformIO.Monitors.Size) return &g.PlatformIO.Monitors[monitor_idx]; return &g.FallbackMonitor; } From d7051928d978edf6a32b8433c2c1bff1f8ff1bfd Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Mon, 22 Mar 2021 10:55:59 +0200 Subject: [PATCH 692/828] TabBar: Use mouse position instead of hardcoded +1/-1 offset when reordering tabs. Fixes tab reordering in test engine when using fast mode. # Conflicts: # imgui_widgets.cpp --- imgui_internal.h | 1 + imgui_widgets.cpp | 63 +++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 59 insertions(+), 5 deletions(-) diff --git a/imgui_internal.h b/imgui_internal.h index 63b78e4a731e..f513bf603b72 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2697,6 +2697,7 @@ namespace ImGui IMGUI_API void TabBarRemoveTab(ImGuiTabBar* tab_bar, ImGuiID tab_id); IMGUI_API void TabBarCloseTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab); IMGUI_API void TabBarQueueReorder(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, int dir); + IMGUI_API void TabBarQueueReorderFromMousePos(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, ImVec2 mouse_pos); IMGUI_API bool TabBarProcessReorder(ImGuiTabBar* tab_bar); IMGUI_API bool TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags, ImGuiWindow* docked_window); IMGUI_API ImVec2 TabItemCalcSize(const char* label, bool has_close_button); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 96da4f99342c..47925e8d9e9e 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7507,6 +7507,50 @@ void ImGui::TabBarQueueReorder(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, in tab_bar->ReorderRequestDir = (ImS8)dir; } +void ImGui::TabBarQueueReorderFromMousePos(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, ImVec2 mouse_pos) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(tab_bar->ReorderRequestTabId == 0); + + if ((tab_bar->Flags & ImGuiTabBarFlags_Reorderable) == 0) + return; + + int source_idx = tab_bar->Tabs.index_from_ptr(tab); + float bar_x = tab_bar->BarRect.Min.x; + int dir = bar_x + tab->Offset > mouse_pos.x ? -1 : +1; + int target_idx = source_idx; + + for (int i = source_idx; 0 <= i && i < tab_bar->Tabs.Size; i += dir) + { + const ImGuiTabItem* target_tab = &tab_bar->Tabs[i]; + + // Reorder only within tab groups with _Leading, _Trailing flag or without either of them. + if ((target_tab->Flags & ImGuiTabItemFlags_Leading) != (tab->Flags & ImGuiTabItemFlags_Leading)) + break; + if ((target_tab->Flags & ImGuiTabItemFlags_Trailing) != (tab->Flags & ImGuiTabItemFlags_Trailing)) + break; + + // Do not reorder past tabs with _NoReorder flag. + if (target_tab->Flags & ImGuiTabItemFlags_NoReorder) + break; + + target_idx = i; // target_tab can be swapped with dragged tab. + + // Current tab is destination tab under mouse position. Also include space after tab, so when mouse cursor is + // between tabs we would not continue checking further tabs that are not hovered. + if (dir > 0 && mouse_pos.x < bar_x + target_tab->Offset + target_tab->Width + g.Style.ItemInnerSpacing.x) // End of tab is past mouse_pos. + break; + if (dir < 0 && mouse_pos.x > bar_x + target_tab->Offset - g.Style.ItemInnerSpacing.x) // Mouse pos is past start of tab. + break; + } + + if (target_idx != source_idx) + { + tab_bar->ReorderRequestTabId = tab->ID; + tab_bar->ReorderRequestDir = (ImS8)(target_idx - source_idx); + } +} + bool ImGui::TabBarProcessReorder(ImGuiTabBar* tab_bar) { ImGuiTabItem* tab1 = TabBarFindTabByID(tab_bar, tab_bar->ReorderRequestTabId); @@ -7526,7 +7570,18 @@ bool ImGui::TabBarProcessReorder(ImGuiTabBar* tab_bar) return false; ImGuiTabItem item_tmp = *tab1; - *tab1 = *tab2; + ImGuiTabItem* src, *dst; + if (tab_bar->ReorderRequestDir > 0) + { + dst = tab1; + src = tab1 + 1; + } + else + { + dst = tab2 + 1; + src = tab2; + } + memmove(dst, src, abs(tab_bar->ReorderRequestDir) * sizeof(ImGuiTabItem)); *tab2 = item_tmp; if (tab_bar->Flags & ImGuiTabBarFlags_SaveSettings) @@ -7882,14 +7937,12 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, if (g.IO.MouseDelta.x < 0.0f && g.IO.MousePos.x < bb.Min.x) { drag_distance_from_edge_x = bb.Min.x - g.IO.MousePos.x; - if (tab_bar->Flags & ImGuiTabBarFlags_Reorderable) - TabBarQueueReorder(tab_bar, tab, -1); + TabBarQueueReorderFromMousePos(tab_bar, tab, g.IO.MousePos); } else if (g.IO.MouseDelta.x > 0.0f && g.IO.MousePos.x > bb.Max.x) { drag_distance_from_edge_x = g.IO.MousePos.x - bb.Max.x; - if (tab_bar->Flags & ImGuiTabBarFlags_Reorderable) - TabBarQueueReorder(tab_bar, tab, +1); + TabBarQueueReorderFromMousePos(tab_bar, tab, g.IO.MousePos); } } From b79b1cb9c073505e83471d73a5ec5b4fe1bb903e Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 25 Mar 2021 18:35:24 +0100 Subject: [PATCH 693/828] TabBar: Amend previous commit. Fix tab reordering when tab bar has scrolling. Some tidying up with helpers + honor 16-bit offsets as with other tab bar features (unlikely single reorder can reach that but consistent) --- docs/CHANGELOG.txt | 2 + imgui_internal.h | 5 ++- imgui_widgets.cpp | 100 ++++++++++++++++++++------------------------- 3 files changed, 50 insertions(+), 57 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index f371cd3a4523..5cbcb9018bd4 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -110,6 +110,8 @@ Other Changes: - Scrolling: Fix scroll tracking with e.g. SetScrollHereX/Y() when WindowPadding < ItemSpacing. - Scrolling: Fix scroll snapping on edge of scroll region when both scrollbars are enabled. - Tables: Expose TableSetColumnEnabled() in public api. (#3935) +- TabBar: Fixed mouse reordering with very fast movements (e.g. crossing multiple tabs in a single + frame and then immediately standling still (would only affect automation/bots). [@rokups] - Drags, Sliders, Inputs: Specifying a NULL format to Float functions default them to "%.3f" to be consistent with the compile-time default. (#3922) - DragScalar: Add default value for v_speed argument to match higher-level functions. (#3922) [@eliasdaler] diff --git a/imgui_internal.h b/imgui_internal.h index f513bf603b72..c68703f845e0 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2098,6 +2098,7 @@ enum ImGuiTabBarFlagsPrivate_ // Extend ImGuiTabItemFlags_ enum ImGuiTabItemFlagsPrivate_ { + ImGuiTabItemFlags_SectionMask_ = ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing, ImGuiTabItemFlags_NoCloseButton = 1 << 20, // Track whether p_open was set or not (we'll need this info on the next frame to recompute ContentWidth during layout) ImGuiTabItemFlags_Button = 1 << 21, // Used by TabItemButton, change the tab item behavior to mimic a button ImGuiTabItemFlags_Unsorted = 1 << 22, // [Docking] Trailing tabs with the _Unsorted flag will be sorted based on the DockOrder of their Window. @@ -2146,7 +2147,7 @@ struct IMGUI_API ImGuiTabBar float ScrollingRectMinX; float ScrollingRectMaxX; ImGuiID ReorderRequestTabId; - ImS8 ReorderRequestDir; + ImS16 ReorderRequestOffset; ImS8 BeginCount; bool WantLayout; bool VisibleTabWasSubmitted; @@ -2696,7 +2697,7 @@ namespace ImGui IMGUI_API void TabBarAddTab(ImGuiTabBar* tab_bar, ImGuiTabItemFlags tab_flags, ImGuiWindow* window); IMGUI_API void TabBarRemoveTab(ImGuiTabBar* tab_bar, ImGuiID tab_id); IMGUI_API void TabBarCloseTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab); - IMGUI_API void TabBarQueueReorder(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, int dir); + IMGUI_API void TabBarQueueReorder(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, int offset); IMGUI_API void TabBarQueueReorderFromMousePos(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, ImVec2 mouse_pos); IMGUI_API bool TabBarProcessReorder(ImGuiTabBar* tab_bar); IMGUI_API bool TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags, ImGuiWindow* docked_window); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 47925e8d9e9e..db075b5a978a 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6975,12 +6975,17 @@ ImGuiTabBar::ImGuiTabBar() LastTabItemIdx = -1; } +static inline int TabItemGetSectionIdx(const ImGuiTabItem* tab) +{ + return (tab->Flags & ImGuiTabItemFlags_Leading) ? 0 : (tab->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1; +} + static int IMGUI_CDECL TabItemComparerBySection(const void* lhs, const void* rhs) { const ImGuiTabItem* a = (const ImGuiTabItem*)lhs; const ImGuiTabItem* b = (const ImGuiTabItem*)rhs; - const int a_section = (a->Flags & ImGuiTabItemFlags_Leading) ? 0 : (a->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1; - const int b_section = (b->Flags & ImGuiTabItemFlags_Leading) ? 0 : (b->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1; + const int a_section = TabItemGetSectionIdx(a); + const int b_section = TabItemGetSectionIdx(b); if (a_section != b_section) return a_section - b_section; return (int)(a->IndexDuringLayout - b->IndexDuringLayout); @@ -7156,11 +7161,11 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) tab->IndexDuringLayout = (ImS16)tab_dst_n; // We will need sorting if tabs have changed section (e.g. moved from one of Leading/Central/Trailing to another) - int curr_tab_section_n = (tab->Flags & ImGuiTabItemFlags_Leading) ? 0 : (tab->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1; + int curr_tab_section_n = TabItemGetSectionIdx(tab); if (tab_dst_n > 0) { ImGuiTabItem* prev_tab = &tab_bar->Tabs[tab_dst_n - 1]; - int prev_tab_section_n = (prev_tab->Flags & ImGuiTabItemFlags_Leading) ? 0 : (prev_tab->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1; + int prev_tab_section_n = TabItemGetSectionIdx(prev_tab); if (curr_tab_section_n == 0 && prev_tab_section_n != 0) need_sort_by_section = true; if (prev_tab_section_n == 2 && curr_tab_section_n != 2) @@ -7232,7 +7237,7 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) const bool has_close_button = (tab->Flags & ImGuiTabItemFlags_NoCloseButton) == 0; tab->ContentWidth = TabItemCalcSize(tab_name, has_close_button).x; - int section_n = (tab->Flags & ImGuiTabItemFlags_Leading) ? 0 : (tab->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1; + int section_n = TabItemGetSectionIdx(tab); ImGuiTabBarSection* section = §ions[section_n]; section->Width += tab->ContentWidth + (section_n == curr_section_n ? g.Style.ItemInnerSpacing.x : 0.0f); curr_section_n = section_n; @@ -7287,7 +7292,7 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) if (shrinked_width < 0.0f) continue; - int section_n = (tab->Flags & ImGuiTabItemFlags_Leading) ? 0 : (tab->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1; + int section_n = TabItemGetSectionIdx(tab); sections[section_n].Width -= (tab->Width - shrinked_width); tab->Width = shrinked_width; } @@ -7470,7 +7475,7 @@ static void ImGui::TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiID tab_id, ImGui ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_id); if (tab == NULL) return; - if (tab->Flags & (ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing)) + if (tab->Flags & ImGuiTabItemFlags_SectionMask_) return; ImGuiContext& g = *GImGui; @@ -7499,56 +7504,48 @@ static void ImGui::TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiID tab_id, ImGui } } -void ImGui::TabBarQueueReorder(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, int dir) +void ImGui::TabBarQueueReorder(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, int offset) { - IM_ASSERT(dir == -1 || dir == +1); + IM_ASSERT(offset != 0); IM_ASSERT(tab_bar->ReorderRequestTabId == 0); tab_bar->ReorderRequestTabId = tab->ID; - tab_bar->ReorderRequestDir = (ImS8)dir; + tab_bar->ReorderRequestOffset = (ImS16)offset; } -void ImGui::TabBarQueueReorderFromMousePos(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, ImVec2 mouse_pos) +void ImGui::TabBarQueueReorderFromMousePos(ImGuiTabBar* tab_bar, const ImGuiTabItem* src_tab, ImVec2 mouse_pos) { ImGuiContext& g = *GImGui; IM_ASSERT(tab_bar->ReorderRequestTabId == 0); - if ((tab_bar->Flags & ImGuiTabBarFlags_Reorderable) == 0) return; - int source_idx = tab_bar->Tabs.index_from_ptr(tab); - float bar_x = tab_bar->BarRect.Min.x; - int dir = bar_x + tab->Offset > mouse_pos.x ? -1 : +1; - int target_idx = source_idx; + const bool is_central_section = (src_tab->Flags & ImGuiTabItemFlags_SectionMask_) == 0; + const float bar_offset = tab_bar->BarRect.Min.x - (is_central_section ? tab_bar->ScrollingTarget : 0); - for (int i = source_idx; 0 <= i && i < tab_bar->Tabs.Size; i += dir) + // Count number of contiguous tabs we are crossing over + const int dir = (bar_offset + src_tab->Offset) > mouse_pos.x ? -1 : +1; + const int src_idx = tab_bar->Tabs.index_from_ptr(src_tab); + int dst_idx = src_idx; + for (int i = src_idx; i >= 0 && i < tab_bar->Tabs.Size; i += dir) { - const ImGuiTabItem* target_tab = &tab_bar->Tabs[i]; - - // Reorder only within tab groups with _Leading, _Trailing flag or without either of them. - if ((target_tab->Flags & ImGuiTabItemFlags_Leading) != (tab->Flags & ImGuiTabItemFlags_Leading)) - break; - if ((target_tab->Flags & ImGuiTabItemFlags_Trailing) != (tab->Flags & ImGuiTabItemFlags_Trailing)) + // Reordered tabs must share the same section + const ImGuiTabItem* dst_tab = &tab_bar->Tabs[i]; + if (dst_tab->Flags & ImGuiTabItemFlags_NoReorder) break; - - // Do not reorder past tabs with _NoReorder flag. - if (target_tab->Flags & ImGuiTabItemFlags_NoReorder) + if ((dst_tab->Flags & ImGuiTabItemFlags_SectionMask_) != (src_tab->Flags & ImGuiTabItemFlags_SectionMask_)) break; + dst_idx = i; - target_idx = i; // target_tab can be swapped with dragged tab. - - // Current tab is destination tab under mouse position. Also include space after tab, so when mouse cursor is - // between tabs we would not continue checking further tabs that are not hovered. - if (dir > 0 && mouse_pos.x < bar_x + target_tab->Offset + target_tab->Width + g.Style.ItemInnerSpacing.x) // End of tab is past mouse_pos. - break; - if (dir < 0 && mouse_pos.x > bar_x + target_tab->Offset - g.Style.ItemInnerSpacing.x) // Mouse pos is past start of tab. + // Include spacing after tab, so when mouse cursor is between tabs we would not continue checking further tabs that are not hovered. + const float x1 = bar_offset + dst_tab->Offset - g.Style.ItemInnerSpacing.x; + const float x2 = bar_offset + dst_tab->Offset + dst_tab->Width + g.Style.ItemInnerSpacing.x; + //GetForegroundDrawList()->AddRect(ImVec2(x1, tab_bar->BarRect.Min.y), ImVec2(x2, tab_bar->BarRect.Max.y), IM_COL32(255, 0, 0, 255)); + if ((dir < 0 && mouse_pos.x > x1) || (dir > 0 && mouse_pos.x < x2)) break; } - if (target_idx != source_idx) - { - tab_bar->ReorderRequestTabId = tab->ID; - tab_bar->ReorderRequestDir = (ImS8)(target_idx - source_idx); - } + if (dst_idx != src_idx) + TabBarQueueReorder(tab_bar, src_tab, dst_idx - src_idx); } bool ImGui::TabBarProcessReorder(ImGuiTabBar* tab_bar) @@ -7558,30 +7555,23 @@ bool ImGui::TabBarProcessReorder(ImGuiTabBar* tab_bar) return false; //IM_ASSERT(tab_bar->Flags & ImGuiTabBarFlags_Reorderable); // <- this may happen when using debug tools - int tab2_order = tab_bar->GetTabOrder(tab1) + tab_bar->ReorderRequestDir; + int tab2_order = tab_bar->GetTabOrder(tab1) + tab_bar->ReorderRequestOffset; if (tab2_order < 0 || tab2_order >= tab_bar->Tabs.Size) return false; - // Reordered TabItem must share the same position flags than target + // Reordered tabs must share the same section + // (Note: TabBarQueueReorderFromMousePos() also has a similar test but since we allow direct calls to TabBarQueueReorder() we do it here too) ImGuiTabItem* tab2 = &tab_bar->Tabs[tab2_order]; if (tab2->Flags & ImGuiTabItemFlags_NoReorder) return false; - if ((tab1->Flags & (ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing)) != (tab2->Flags & (ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing))) + if ((tab1->Flags & ImGuiTabItemFlags_SectionMask_) != (tab2->Flags & ImGuiTabItemFlags_SectionMask_)) return false; ImGuiTabItem item_tmp = *tab1; - ImGuiTabItem* src, *dst; - if (tab_bar->ReorderRequestDir > 0) - { - dst = tab1; - src = tab1 + 1; - } - else - { - dst = tab2 + 1; - src = tab2; - } - memmove(dst, src, abs(tab_bar->ReorderRequestDir) * sizeof(ImGuiTabItem)); + ImGuiTabItem* src_tab = (tab_bar->ReorderRequestOffset > 0) ? tab1 + 1 : tab2; + ImGuiTabItem* dst_tab = (tab_bar->ReorderRequestOffset > 0) ? tab1 : tab2 + 1; + const int move_count = (tab_bar->ReorderRequestOffset > 0) ? tab_bar->ReorderRequestOffset : -tab_bar->ReorderRequestOffset; + memmove(dst_tab, src_tab, move_count * sizeof(ImGuiTabItem)); *tab2 = item_tmp; if (tab_bar->Flags & ImGuiTabBarFlags_SaveSettings) @@ -7874,7 +7864,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, const ImVec2 backup_main_cursor_pos = window->DC.CursorPos; // Layout - const bool is_central_section = (tab->Flags & (ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing)) == 0; + const bool is_central_section = (tab->Flags & ImGuiTabItemFlags_SectionMask_) == 0; size.x = tab->Width; if (is_central_section) window->DC.CursorPos = tab_bar->BarRect.Min + ImVec2(IM_FLOOR(tab->Offset - tab_bar->ScrollingAnim), 0.0f); @@ -7964,7 +7954,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, if (distance_from_edge_y >= threshold_y) undocking_tab = true; else if (drag_distance_from_edge_x > threshold_x) - if ((tab_bar->ReorderRequestDir < 0 && tab_bar->GetTabOrder(tab) == 0) || (tab_bar->ReorderRequestDir > 0 && tab_bar->GetTabOrder(tab) == tab_bar->Tabs.Size - 1)) + if ((tab_bar->ReorderRequestOffset < 0 && tab_bar->GetTabOrder(tab) == 0) || (tab_bar->ReorderRequestOffset > 0 && tab_bar->GetTabOrder(tab) == tab_bar->Tabs.Size - 1)) undocking_tab = true; } From a4adf6057677c780907b7fc38e2695199bccd446 Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Mon, 8 Mar 2021 10:25:07 +0200 Subject: [PATCH 694/828] Backends, Viewports: GLFW: Add a workaround for stuck keys after closing a GLFW window (#3837). --- backends/imgui_impl_glfw.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index b8b7f91227d8..2b6c34ca430a 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -84,6 +84,7 @@ static GlfwClientApi g_ClientApi = GlfwClientApi_Unknown; static double g_Time = 0.0; static bool g_MouseJustPressed[ImGuiMouseButton_COUNT] = {}; static GLFWcursor* g_MouseCursors[ImGuiMouseCursor_COUNT] = {}; +static GLFWwindow* g_KeyOwnerWindows[512] = {}; static bool g_InstalledCallbacks = false; static bool g_WantUpdateMonitors = true; @@ -135,9 +136,15 @@ void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int a ImGuiIO& io = ImGui::GetIO(); if (action == GLFW_PRESS) + { io.KeysDown[key] = true; + g_KeyOwnerWindows[key] = window; + } if (action == GLFW_RELEASE) + { io.KeysDown[key] = false; + g_KeyOwnerWindows[key] = NULL; + } // Modifiers are not reliable across systems io.KeyCtrl = io.KeysDown[GLFW_KEY_LEFT_CONTROL] || io.KeysDown[GLFW_KEY_RIGHT_CONTROL]; @@ -607,6 +614,13 @@ static void ImGui_ImplGlfw_DestroyWindow(ImGuiViewport* viewport) HWND hwnd = (HWND)viewport->PlatformHandleRaw; ::RemovePropA(hwnd, "IMGUI_VIEWPORT"); #endif + + // Release any keys that were pressed in the window being destroyed and are still held down, + // because we will not receive any release events after window is destroyed. + for (int i = 0; i < IM_ARRAYSIZE(g_KeyOwnerWindows); i++) + if (g_KeyOwnerWindows[i] == data->Window) + ImGui_ImplGlfw_KeyCallback(data->Window, i, 0, GLFW_RELEASE, 0); // Later params are only used for main viewport, on which this function is never called. + glfwDestroyWindow(data->Window); } data->Window = NULL; From 5991851eb9703fdbf4df996f1664abcc082c67de Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 26 Mar 2021 18:40:19 +0100 Subject: [PATCH 695/828] Docking: DockSpace() returns its node ID + adding branch changelog. --- docs/CHANGELOG.txt | 9 +++++++++ imgui.cpp | 9 +++++---- imgui.h | 2 +- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 5cbcb9018bd4..e98bbe324f9d 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -126,6 +126,15 @@ Other Changes: - Examples: Vulkan: Rebuild swapchain on VK_SUBOPTIMAL_KHR. (#3881) - Docs: Improvements to minor mistakes in documentation comments (#3923) [@ANF-Studios] +Docking Branch: + +- Docking: DockSpace() returns its node ID. +- Docking: Dockspace() never draws a background. (#3924) +- Docking: undocking nodes/windows covering most of the monitor max their size down to 90% to ease further manipulations. +- Viewports: Hotfix for crash in monitor array access, caused by 4b9bc4902. (#3967) +- Backends, Viewports: GLFW: Add a workaround for stuck keys after closing a GLFW window (#3837). +- Backends, Viewports: Vulkan: Rebuild swapchain on VK_SUBOPTIMAL_KHR. (#3881) + ----------------------------------------------------------------------- VERSION 1.82 (Released 2021-02-15) diff --git a/imgui.cpp b/imgui.cpp index 456bf78a998e..1610d96e2923 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -14730,13 +14730,13 @@ void ImGui::SetWindowDock(ImGuiWindow* window, ImGuiID dock_id, ImGuiCond cond) // Create an explicit dockspace node within an existing window. Also expose dock node flags and creates a CentralNode by default. // The Central Node is always displayed even when empty and shrink/extend according to the requested size of its neighbors. // DockSpace() needs to be submitted _before_ any window they can host. If you use a dockspace, submit it early in your app. -void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags flags, const ImGuiWindowClass* window_class) +ImGuiID ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags flags, const ImGuiWindowClass* window_class) { ImGuiContext* ctx = GImGui; ImGuiContext& g = *ctx; ImGuiWindow* window = GetCurrentWindow(); if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable)) - return; + return 0; // Early out if parent window is hidden/collapsed // This is faster but also DockNodeUpdateTabBar() relies on TabBarLayout() running (which won't if SkipItems=true) to set NextSelectedTabId = 0). See #2960. @@ -14764,7 +14764,7 @@ void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags fla { IM_ASSERT(node->IsDockSpace() == false && "Cannot call DockSpace() twice a frame with the same ID"); node->LocalFlags |= ImGuiDockNodeFlags_DockSpace; - return; + return id; } node->LocalFlags |= ImGuiDockNodeFlags_DockSpace; @@ -14772,7 +14772,7 @@ void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags fla if (flags & ImGuiDockNodeFlags_KeepAliveOnly) { node->LastFrameAlive = g.FrameCount; - return; + return id; } const ImVec2 content_avail = GetContentRegionAvail(); @@ -14825,6 +14825,7 @@ void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags fla End(); ItemSize(size); + return id; } // Tips: Use with ImGuiDockNodeFlags_PassthruCentralNode! diff --git a/imgui.h b/imgui.h index 48808aabec0e..5f15350e9a23 100644 --- a/imgui.h +++ b/imgui.h @@ -764,7 +764,7 @@ namespace ImGui // About DockSpace: // - Use DockSpace() to create an explicit dock node _within_ an existing window. See Docking demo for details. // - DockSpace() needs to be submitted _before_ any window they can host. If you use a dockspace, submit it early in your app. - IMGUI_API void DockSpace(ImGuiID id, const ImVec2& size = ImVec2(0, 0), ImGuiDockNodeFlags flags = 0, const ImGuiWindowClass* window_class = NULL); + IMGUI_API ImGuiID DockSpace(ImGuiID id, const ImVec2& size = ImVec2(0, 0), ImGuiDockNodeFlags flags = 0, const ImGuiWindowClass* window_class = NULL); IMGUI_API ImGuiID DockSpaceOverViewport(const ImGuiViewport* viewport = NULL, ImGuiDockNodeFlags flags = 0, const ImGuiWindowClass* window_class = NULL); IMGUI_API void SetNextWindowDockID(ImGuiID dock_id, ImGuiCond cond = 0); // set next window dock id (FIXME-DOCK) IMGUI_API void SetNextWindowClass(const ImGuiWindowClass* window_class); // set next window class (rare/advanced uses: provide hints to the platform backend via altered viewport flags and parent/child info) From 0e0a783b8c74aac2fba99d3d509df1e4cf03ec12 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 30 Mar 2021 14:52:10 +0200 Subject: [PATCH 696/828] Docking: fix undocking from tab-bar by moving mouse horizontally, broken by d705192. --- imgui_widgets.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index db075b5a978a..4539dfb6e53b 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7920,17 +7920,20 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, else if (held && !tab_appearing && IsMouseDragging(0)) { // Drag and drop: re-order tabs + int drag_dir = 0; float drag_distance_from_edge_x = 0.0f; if (!g.DragDropActive && ((tab_bar->Flags & ImGuiTabBarFlags_Reorderable) || (docked_window != NULL))) { // While moving a tab it will jump on the other side of the mouse, so we also test for MouseDelta.x if (g.IO.MouseDelta.x < 0.0f && g.IO.MousePos.x < bb.Min.x) { + drag_dir = -1; drag_distance_from_edge_x = bb.Min.x - g.IO.MousePos.x; TabBarQueueReorderFromMousePos(tab_bar, tab, g.IO.MousePos); } else if (g.IO.MouseDelta.x > 0.0f && g.IO.MousePos.x > bb.Max.x) { + drag_dir = +1; drag_distance_from_edge_x = g.IO.MousePos.x - bb.Max.x; TabBarQueueReorderFromMousePos(tab_bar, tab, g.IO.MousePos); } @@ -7941,11 +7944,9 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, { // We use a variable threshold to distinguish dragging tabs within a tab bar and extracting them out of the tab bar bool undocking_tab = (g.DragDropActive && g.DragDropPayload.SourceId == id); - if (!undocking_tab) //&& (!g.IO.ConfigDockingWithShift || g.IO.KeyShift) { float threshold_base = g.FontSize; - //float threshold_base = g.IO.ConfigDockingWithShift ? g.FontSize * 0.5f : g.FontSize; float threshold_x = (threshold_base * 2.2f); float threshold_y = (threshold_base * 1.5f) + ImClamp((ImFabs(g.IO.MouseDragMaxDistanceAbs[0].x) - threshold_base * 2.0f) * 0.20f, 0.0f, threshold_base * 4.0f); //GetForegroundDrawList()->AddRect(ImVec2(bb.Min.x - threshold_x, bb.Min.y - threshold_y), ImVec2(bb.Max.x + threshold_x, bb.Max.y + threshold_y), IM_COL32_WHITE); // [DEBUG] @@ -7953,8 +7954,8 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, float distance_from_edge_y = ImMax(bb.Min.y - g.IO.MousePos.y, g.IO.MousePos.y - bb.Max.y); if (distance_from_edge_y >= threshold_y) undocking_tab = true; - else if (drag_distance_from_edge_x > threshold_x) - if ((tab_bar->ReorderRequestOffset < 0 && tab_bar->GetTabOrder(tab) == 0) || (tab_bar->ReorderRequestOffset > 0 && tab_bar->GetTabOrder(tab) == tab_bar->Tabs.Size - 1)) + if (drag_distance_from_edge_x > threshold_x) + if ((drag_dir < 0 && tab_bar->GetTabOrder(tab) == 0) || (drag_dir > 0 && tab_bar->GetTabOrder(tab) == tab_bar->Tabs.Size - 1)) undocking_tab = true; } From 3ed07a8f0b18c5dfc94a997bde2417ee0453b0fd Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 30 Mar 2021 14:30:36 +0200 Subject: [PATCH 697/828] Docking: removed io.ConfigDockingWithShift option. (#2109) --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 11 +++++------ imgui.h | 5 ++--- imgui_demo.cpp | 16 ++++++---------- imgui_widgets.cpp | 5 +---- 5 files changed, 16 insertions(+), 23 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index e98bbe324f9d..458dd291af66 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -128,6 +128,8 @@ Other Changes: Docking Branch: +- [Breaking] Removed io.ConfigDockingWithShift config option. Behavior always equivalent to having the + option set to false (dock/undock by default, hold shift to avoid docking). (#2109) - Docking: DockSpace() returns its node ID. - Docking: Dockspace() never draws a background. (#3924) - Docking: undocking nodes/windows covering most of the monitor max their size down to 90% to ease further manipulations. diff --git a/imgui.cpp b/imgui.cpp index 1610d96e2923..9902674f79e3 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1101,7 +1101,6 @@ ImGuiIO::ImGuiIO() // Docking options (when ImGuiConfigFlags_DockingEnable is set) ConfigDockingNoSplit = false; - ConfigDockingWithShift = false; ConfigDockingAlwaysTabBar = false; ConfigDockingTransparentPayload = false; @@ -6784,7 +6783,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) { // Docking: Dragging a dockable window (or any of its child) turns it into a drag and drop source. // We need to do this _before_ we overwrite window->DC.LastItemId below because BeginDockableDragDropSource() also overwrites it. - if ((g.MovingWindow == window) && (g.IO.ConfigDockingWithShift == g.IO.KeyShift)) + if (g.MovingWindow == window && g.IO.KeyShift == false) if ((window->RootWindowDockTree->Flags & ImGuiWindowFlags_NoDocking) == 0) BeginDockableDragDropSource(window); @@ -14200,9 +14199,9 @@ static void ImGui::DockNodePreviewDockSetup(ImGuiWindow* host_window, ImGuiDockN } } - // When docking without holding Shift, we only allow and preview docking when hovering over a drop rect or over the title bar + // We only allow and preview docking when hovering over a drop rect or over the title bar data->IsDropAllowed = (data->SplitDir != ImGuiDir_None) || (data->IsCenterAvailable); - if (!is_explicit_target && !data->IsSplitDirExplicit && !g.IO.ConfigDockingWithShift) + if (!is_explicit_target && !data->IsSplitDirExplicit) data->IsDropAllowed = false; // Calculate split area @@ -15472,7 +15471,7 @@ void ImGui::BeginDockableDragDropSource(ImGuiWindow* window) window->DC.LastItemId = window->MoveId; window = window->RootWindowDockTree; IM_ASSERT((window->Flags & ImGuiWindowFlags_NoDocking) == 0); - bool is_drag_docking = (g.IO.ConfigDockingWithShift) || ImRect(0, 0, window->SizeFull.x, GetFrameHeight()).Contains(g.ActiveIdClickOffset); + bool is_drag_docking = ImRect(0, 0, window->SizeFull.x, GetFrameHeight()).Contains(g.ActiveIdClickOffset); // FIXME-DOCKING: Need to make this stateful and explicit if (is_drag_docking && BeginDragDropSource(ImGuiDragDropFlags_SourceNoPreviewTooltip | ImGuiDragDropFlags_SourceNoHoldToOpenOthers | ImGuiDragDropFlags_SourceAutoExpirePayload)) { SetDragDropPayload(IMGUI_PAYLOAD_TYPE_WINDOW, &window, sizeof(window)); @@ -15533,7 +15532,7 @@ void ImGui::BeginDockableDragDropTarget(ImGuiWindow* window) } const ImRect explicit_target_rect = (node && node->TabBar && !node->IsHiddenTabBar() && !node->IsNoTabBar()) ? node->TabBar->BarRect : ImRect(window->Pos, window->Pos + ImVec2(window->Size.x, GetFrameHeight())); - const bool is_explicit_target = g.IO.ConfigDockingWithShift || IsMouseHoveringRect(explicit_target_rect.Min, explicit_target_rect.Max); + const bool is_explicit_target = IsMouseHoveringRect(explicit_target_rect.Min, explicit_target_rect.Max); // Preview docking request and find out split direction/ratio //const bool do_preview = true; // Ignore testing for payload->IsPreview() which removes one frame of delay, but breaks overlapping drop targets within the same window. diff --git a/imgui.h b/imgui.h index 5f15350e9a23..3ca48a376db1 100644 --- a/imgui.h +++ b/imgui.h @@ -759,8 +759,8 @@ namespace ImGui // Docking // [BETA API] Enable with io.ConfigFlags |= ImGuiConfigFlags_DockingEnable. // Note: You can use most Docking facilities without calling any API. You DO NOT need to call DockSpace() to use Docking! - // - To dock windows: if io.ConfigDockingWithShift == false (default) drag window from their title bar. - // - To dock windows: if io.ConfigDockingWithShift == true: hold SHIFT anywhere while moving windows. + // - Drag from window title bar or their tab to dock/undock. Hold SHIFT to disable docking. + // - Drag from window menu button (upper-left button) to undock an entire node (all windows). // About DockSpace: // - Use DockSpace() to create an explicit dock node _within_ an existing window. See Docking demo for details. // - DockSpace() needs to be submitted _before_ any window they can host. If you use a dockspace, submit it early in your app. @@ -1854,7 +1854,6 @@ struct ImGuiIO // Docking options (when ImGuiConfigFlags_DockingEnable is set) bool ConfigDockingNoSplit; // = false // Simplified docking mode: disable window splitting, so docking is limited to merging multiple windows together into tab-bars. - bool ConfigDockingWithShift; // = false // Enable docking with holding Shift key (reduce visual noise, allows dropping in wider space) bool ConfigDockingAlwaysTabBar; // = false // [BETA] [FIXME: This currently creates regression with auto-sizing and general overhead] Make every single floating window display within a docking node. bool ConfigDockingTransparentPayload;// = false // [BETA] Make window or viewport transparent when docking and only display docking boxes on the target viewport. Useful if rendering of multiple viewport cannot be synced. Best used with ConfigViewportsNoAutoMerge. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 42d380b28025..a4657e241582 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -456,14 +456,12 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::SameLine(); HelpMarker("Instruct backend to not alter mouse cursor shape and visibility."); ImGui::CheckboxFlags("io.ConfigFlags: DockingEnable", &io.ConfigFlags, ImGuiConfigFlags_DockingEnable); - ImGui::SameLine(); HelpMarker(io.ConfigDockingWithShift ? "[beta] Use SHIFT to dock window into each others." : "[beta] Drag from title bar to dock windows into each others."); + ImGui::SameLine(); HelpMarker("Drag from window title bar or their tab to dock/undock. Hold SHIFT to disable docking.\n\nDrag from window menu button (upper-left button) to undock an entire node (all windows)."); if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable) { ImGui::Indent(); ImGui::Checkbox("io.ConfigDockingNoSplit", &io.ConfigDockingNoSplit); ImGui::SameLine(); HelpMarker("Simplified docking mode: disable window splitting, so docking is limited to merging multiple windows together into tab-bars."); - ImGui::Checkbox("io.ConfigDockingWithShift", &io.ConfigDockingWithShift); - ImGui::SameLine(); HelpMarker("Enable docking when holding Shift only (allows to drop in wider space, reduce visual noise)"); ImGui::Checkbox("io.ConfigDockingAlwaysTabBar", &io.ConfigDockingAlwaysTabBar); ImGui::SameLine(); HelpMarker("Create a docking node and tab-bar on single floating windows."); ImGui::Checkbox("io.ConfigDockingTransparentPayload", &io.ConfigDockingTransparentPayload); @@ -5741,7 +5739,6 @@ void ImGui::ShowAboutWindow(bool* p_open) if (io.ConfigViewportsNoDecoration) ImGui::Text("io.ConfigViewportsNoDecoration"); if (io.ConfigViewportsNoDefaultParent) ImGui::Text("io.ConfigViewportsNoDefaultParent"); if (io.ConfigDockingNoSplit) ImGui::Text("io.ConfigDockingNoSplit"); - if (io.ConfigDockingWithShift) ImGui::Text("io.ConfigDockingWithShift"); if (io.ConfigDockingAlwaysTabBar) ImGui::Text("io.ConfigDockingAlwaysTabBar"); if (io.ConfigDockingTransparentPayload) ImGui::Text("io.ConfigDockingTransparentPayload"); if (io.ConfigMacOSXBehaviors) ImGui::Text("io.ConfigMacOSXBehaviors"); @@ -7463,7 +7460,7 @@ static void ShowExampleAppCustomRendering(bool* p_open) // Demonstrate using DockSpace() to create an explicit docking node within an existing window. // Note that you dock windows into each others _without_ a dockspace, by just clicking on -// a window title bar and moving it (+ hold SHIFT if io.ConfigDockingWithShift is set). +// a window title bar or tab and moving it. // DockSpace() and DockSpaceOverViewport() are only useful to construct a central docking // location for your application. void ShowExampleAppDockSpace(bool* p_open) @@ -7556,11 +7553,10 @@ void ShowExampleAppDockSpace(bool* p_open) ImGui::EndMenu(); } HelpMarker( - "When docking is enabled, you can ALWAYS dock MOST window into another! Try it now!" "\n\n" - " > if io.ConfigDockingWithShift==false (default):" "\n" - " drag windows from title bar to dock" "\n" - " > if io.ConfigDockingWithShift==true:" "\n" - " drag windows from anywhere and hold Shift to dock" "\n\n" + "When docking is enabled, you can ALWAYS dock MOST window into another! Try it now!" "\n" + "- Drag from window title bar or their tab to dock/undock." "\n" + "- Drag from window menu button (upper-left button) to undock an entire node (all windows)." "\n" + "- Hold SHIFT to disable docking." "\n" "This demo app has nothing to do with it!" "\n\n" "This demo app only demonstrate the use of ImGui::DockSpace() which allows you to manually create a docking node _within_ another window. This is useful so you can decorate your main application window (e.g. with a menu bar)." "\n\n" "ImGui::DockSpace() comes with one hard constraint: it needs to be submitted _before_ any window which may be docked into it. Therefore, if you use a dock spot as the central point of your application, you'll probably want it to be part of the very first window you are submitting to imgui every frame." "\n\n" diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 4539dfb6e53b..972bb84c7cfc 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7920,20 +7920,17 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, else if (held && !tab_appearing && IsMouseDragging(0)) { // Drag and drop: re-order tabs - int drag_dir = 0; float drag_distance_from_edge_x = 0.0f; if (!g.DragDropActive && ((tab_bar->Flags & ImGuiTabBarFlags_Reorderable) || (docked_window != NULL))) { // While moving a tab it will jump on the other side of the mouse, so we also test for MouseDelta.x if (g.IO.MouseDelta.x < 0.0f && g.IO.MousePos.x < bb.Min.x) { - drag_dir = -1; drag_distance_from_edge_x = bb.Min.x - g.IO.MousePos.x; TabBarQueueReorderFromMousePos(tab_bar, tab, g.IO.MousePos); } else if (g.IO.MouseDelta.x > 0.0f && g.IO.MousePos.x > bb.Max.x) { - drag_dir = +1; drag_distance_from_edge_x = g.IO.MousePos.x - bb.Max.x; TabBarQueueReorderFromMousePos(tab_bar, tab, g.IO.MousePos); } @@ -7955,7 +7952,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, if (distance_from_edge_y >= threshold_y) undocking_tab = true; if (drag_distance_from_edge_x > threshold_x) - if ((drag_dir < 0 && tab_bar->GetTabOrder(tab) == 0) || (drag_dir > 0 && tab_bar->GetTabOrder(tab) == tab_bar->Tabs.Size - 1)) + if ((tab_bar->GetTabOrder(tab) == 0) || (tab_bar->GetTabOrder(tab) == tab_bar->Tabs.Size - 1)) undocking_tab = true; } From 9251eac585cf1cdd9c38b9d560f759bcf77d04d7 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 30 Mar 2021 19:01:37 +0200 Subject: [PATCH 698/828] Docking: fix undocking from tab-bar by moving mouse horizontally, amend 3ed07a8 + d705192. Automation system may drag e.g. right-most tab far left (and vice-versa) and one frame and our current logic would fail at it. --- imgui_widgets.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 972bb84c7cfc..4539dfb6e53b 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7920,17 +7920,20 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, else if (held && !tab_appearing && IsMouseDragging(0)) { // Drag and drop: re-order tabs + int drag_dir = 0; float drag_distance_from_edge_x = 0.0f; if (!g.DragDropActive && ((tab_bar->Flags & ImGuiTabBarFlags_Reorderable) || (docked_window != NULL))) { // While moving a tab it will jump on the other side of the mouse, so we also test for MouseDelta.x if (g.IO.MouseDelta.x < 0.0f && g.IO.MousePos.x < bb.Min.x) { + drag_dir = -1; drag_distance_from_edge_x = bb.Min.x - g.IO.MousePos.x; TabBarQueueReorderFromMousePos(tab_bar, tab, g.IO.MousePos); } else if (g.IO.MouseDelta.x > 0.0f && g.IO.MousePos.x > bb.Max.x) { + drag_dir = +1; drag_distance_from_edge_x = g.IO.MousePos.x - bb.Max.x; TabBarQueueReorderFromMousePos(tab_bar, tab, g.IO.MousePos); } @@ -7952,7 +7955,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, if (distance_from_edge_y >= threshold_y) undocking_tab = true; if (drag_distance_from_edge_x > threshold_x) - if ((tab_bar->GetTabOrder(tab) == 0) || (tab_bar->GetTabOrder(tab) == tab_bar->Tabs.Size - 1)) + if ((drag_dir < 0 && tab_bar->GetTabOrder(tab) == 0) || (drag_dir > 0 && tab_bar->GetTabOrder(tab) == tab_bar->Tabs.Size - 1)) undocking_tab = true; } From 65cd14502c2a018b736636de14326be2a86f04da Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 31 Mar 2021 19:09:08 +0200 Subject: [PATCH 699/828] Fix popup positioning, broken by 84e6fe4. (#3991, #3982) --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index bb7817958956..717243364e28 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6226,6 +6226,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // UPDATE CONTENTS SIZE, UPDATE HIDDEN STATUS // Update contents size from last frame for auto-fitting (or use explicit size) + const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesCannotSkipItems > 0); CalcWindowContentSizes(window, &window->ContentSize, &window->ContentSizeIdeal); // FIXME: These flags are decremented before they are used. This means that in order to have these fields produce their intended behaviors @@ -6367,7 +6368,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->Pos = parent_window->DC.CursorPos; } - const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesCannotSkipItems > 0); const bool window_pos_with_pivot = (window->SetWindowPosVal.x != FLT_MAX && window->HiddenFramesCannotSkipItems == 0); if (window_pos_with_pivot) SetWindowPos(window, window->SetWindowPosVal - window->Size * window->SetWindowPosPivot, 0); // Position given a pivot (e.g. for centering) From 539de4387bf553a58ccd1658176d4bc9507145b0 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 12 Apr 2021 16:51:05 +0200 Subject: [PATCH 700/828] Docking: Fixed restoring of tab order within a dockspace or a split node. (tests in "docking_tab_order") --- docs/CHANGELOG.txt | 1 + imgui.cpp | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index cdb70c921a5d..4ec781ad2460 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -137,6 +137,7 @@ Docking Branch: - Docking: DockSpace() returns its node ID. - Docking: Dockspace() never draws a background. (#3924) - Docking: undocking nodes/windows covering most of the monitor max their size down to 90% to ease further manipulations. +- Docking: Fixed restoring of tab order within a dockspace or a split node. - Viewports: Hotfix for crash in monitor array access, caused by 4b9bc4902. (#3967) - Backends, Viewports: GLFW: Add a workaround for stuck keys after closing a GLFW window (#3837). - Backends, Viewports: Vulkan: Rebuild swapchain on VK_SUBOPTIMAL_KHR. (#3881) diff --git a/imgui.cpp b/imgui.cpp index 717243364e28..d9376ef1b5ed 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -15464,9 +15464,9 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) else window->Flags &= ~ImGuiWindowFlags_NoTitleBar; // Clear the NoTitleBar flag in case the user set it: confusingly enough we need a title bar height so we are correctly offset, but it won't be displayed! - // Save new dock order only if the tab bar has been visible once. + // Save new dock order only if the window has been visible once already // This allows multiple windows to be created in the same frame and have their respective dock orders preserved. - if (node->TabBar && node->TabBar->CurrFrameVisible != -1) + if (node->TabBar && window->WasActive) window->DockOrder = (short)DockNodeGetTabOrder(window); if ((node->WantCloseAll || node->WantCloseTabId == window->ID) && p_open != NULL) From e87dd0e65dd2786c50a7cfa411d0448140ffc8f1 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 12 Apr 2021 19:21:49 +0200 Subject: [PATCH 701/828] Docking: Fixed multiple simultaneously reappearing window from appearing undocked in their initial frame. --- docs/CHANGELOG.txt | 1 + imgui.cpp | 22 ++++++++++++++-------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 4ec781ad2460..72ff5f4eaa19 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -138,6 +138,7 @@ Docking Branch: - Docking: Dockspace() never draws a background. (#3924) - Docking: undocking nodes/windows covering most of the monitor max their size down to 90% to ease further manipulations. - Docking: Fixed restoring of tab order within a dockspace or a split node. +- Docking: Fixed multiple simultaneously reappearing window from appearing undocked in their initial frame. - Viewports: Hotfix for crash in monitor array access, caused by 4b9bc4902. (#3967) - Backends, Viewports: GLFW: Add a workaround for stuck keys after closing a GLFW window (#3837). - Backends, Viewports: Vulkan: Rebuild swapchain on VK_SUBOPTIMAL_KHR. (#3881) diff --git a/imgui.cpp b/imgui.cpp index d9376ef1b5ed..4086b3264b5a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -13006,6 +13006,12 @@ int ImGui::DockNodeGetTabOrder(ImGuiWindow* window) return tab ? tab_bar->GetTabOrder(tab) : -1; } +static void DockNodeHideWindowDuringHostWindowCreation(ImGuiWindow* window) +{ + window->Hidden = true; + window->HiddenFramesCanSkipItems = window->Active ? 1 : 2; +} + static void ImGui::DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window, bool add_to_tab_bar) { ImGuiContext& g = *GImGui; (void)g; @@ -13018,6 +13024,12 @@ static void ImGui::DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window, b IM_ASSERT(window->DockNode == NULL || window->DockNodeAsHost == NULL); IMGUI_DEBUG_LOG_DOCKING("DockNodeAddWindow node 0x%08X window '%s'\n", node->ID, window->Name); + // If more than 2 windows appeared on the same frame leading to the creation of a new hosting window, + // we'll hide windows until the host window is ready. Hide the 1st window after its been output (so it is not visible for one frame). + // We will call DockNodeHideWindowDuringHostWindowCreation() on ourselves in Begin() + if (node->HostWindow == NULL && node->Windows.Size == 1 && node->Windows[0]->WasActive == false) + DockNodeHideWindowDuringHostWindowCreation(node->Windows[0]); + node->Windows.push_back(window); node->WantHiddenTabBarUpdate = true; window->DockNode = node; @@ -13025,14 +13037,6 @@ static void ImGui::DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window, b window->DockIsActive = (node->Windows.Size > 1); window->DockTabWantClose = false; - // If more than 2 windows appeared on the same frame, we'll create a new hosting DockNode from the point of the second window submission. - // Then we need to hide the first window (after its been output) otherwise it would be visible as a standalone window for one frame. - if (node->HostWindow == NULL && node->Windows.Size == 2 && node->Windows[0]->WasActive == false) - { - node->Windows[0]->Hidden = true; - node->Windows[0]->HiddenFramesCanSkipItems = 1; - } - // When reactivating a node with one or two loose window, the window pos/size/viewport are authoritative over the node storage. // In particular it is important we init the viewport from the first window so we don't create two viewports and drop one. if (node->HostWindow == NULL && node->IsFloatingNode()) @@ -15427,6 +15431,8 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) { window->DockIsActive = (node->State == ImGuiDockNodeState_HostWindowHiddenBecauseWindowsAreResizing); window->DockTabIsVisible = false; + if (node->Windows.Size > 1) + DockNodeHideWindowDuringHostWindowCreation(window); return; } From 646c87359880ed5aab783a5d1cc17260cd873e94 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 13 Apr 2021 15:50:15 +0200 Subject: [PATCH 702/828] Docking: Fixed reappearing docked windows with no close button showing a tab with extraneous space for one frame. --- docs/CHANGELOG.txt | 3 ++- imgui.cpp | 9 +++++++-- imgui_widgets.cpp | 3 +++ 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 72ff5f4eaa19..75dcc4b6d11d 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -138,7 +138,8 @@ Docking Branch: - Docking: Dockspace() never draws a background. (#3924) - Docking: undocking nodes/windows covering most of the monitor max their size down to 90% to ease further manipulations. - Docking: Fixed restoring of tab order within a dockspace or a split node. -- Docking: Fixed multiple simultaneously reappearing window from appearing undocked in their initial frame. +- Docking: Fixed reappearing docked windows with no close button showing a tab with extraneous space for one frame. +- Docking: Fixed multiple simultaneously reappearing window from appearing undocked for one frame. - Viewports: Hotfix for crash in monitor array access, caused by 4b9bc4902. (#3967) - Backends, Viewports: GLFW: Add a workaround for stuck keys after closing a GLFW window (#3837). - Backends, Viewports: Vulkan: Rebuild swapchain on VK_SUBOPTIMAL_KHR. (#3881) diff --git a/imgui.cpp b/imgui.cpp index 4086b3264b5a..65225622ba05 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -16260,7 +16260,12 @@ void ImGui::ShowMetricsWindow(bool* p_open) if (TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.GetSize())) { for (int n = 0; n < g.TabBars.GetSize(); n++) - DebugNodeTabBar(g.TabBars.GetByIndex(n), "TabBar"); + { + ImGuiTabBar* tab_bar = g.TabBars.GetByIndex(n); + PushID(tab_bar); + DebugNodeTabBar(tab_bar, "TabBar"); + PopID(); + } TreePop(); } @@ -16702,7 +16707,7 @@ void ImGui::DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label) } p += ImFormatString(p, buf_end - p, (tab_bar->Tabs.Size > 3) ? " ... }" : " } "); if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); } - bool open = TreeNode(tab_bar, "%s", buf); + bool open = TreeNode(label, "%s", buf); if (!is_active) { PopStyleColor(); } if (is_active && IsItemHovered()) { diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 31e2a640161e..bdc329635cd4 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7422,6 +7422,9 @@ void ImGui::TabBarAddTab(ImGuiTabBar* tab_bar, ImGuiTabItemFlags tab_flags, ImGu IM_ASSERT(TabBarFindTabByID(tab_bar, window->ID) == NULL); IM_ASSERT(g.CurrentTabBar != tab_bar); // Can't work while the tab bar is active as our tab doesn't have an X offset yet, in theory we could/should test something like (tab_bar->CurrFrameVisible < g.FrameCount) but we'd need to solve why triggers the commented early-out assert in BeginTabBarEx() (probably dock node going from implicit to explicit in same frame) + if (!window->HasCloseButton) + tab_flags |= ImGuiTabItemFlags_NoCloseButton; // Set _NoCloseButton immediately because it will be used for first-frame width calculation. + ImGuiTabItem new_tab; new_tab.ID = window->ID; new_tab.Flags = tab_flags; From 3f16a524c894ed556d21856683d56d345855bbd2 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 13 Apr 2021 16:22:30 +0200 Subject: [PATCH 703/828] Docking: move NavWindow to SelectedTabId application lower to leave a chance for in-between code to alter focus. + store per-node window menu button id to simplify usage. --- imgui.cpp | 14 +++++++++----- imgui_internal.h | 5 +++-- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 65225622ba05..558c790882ec 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -12761,7 +12761,7 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) ImGuiDir split_dir = req->DockSplitDir; if (split_dir != ImGuiDir_None) { - // Split into one, one side will be our payload node unless we are dropping a loose window + // Split into two, one side will be our payload node unless we are dropping a loose window const ImGuiAxis split_axis = (split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y; const int split_inheritor_child_idx = (split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Up) ? 1 : 0; // Current contents will be moved to the opposite side const float split_ratio = req->DockSplitRatio; @@ -12970,6 +12970,7 @@ bool ImGui::DockContextCalcDropPosForDocking(ImGuiWindow* target, ImGuiDockNode* ImGuiDockNode::ImGuiDockNode(ImGuiID id) { ID = id; + WindowMenuButtonId = ImHashStr("#COLLAPSE", 0, ID); SharedFlags = LocalFlags = ImGuiDockNodeFlags_None; ParentNode = ChildNodes[0] = ChildNodes[1] = NULL; TabBar = NULL; @@ -13804,13 +13805,11 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w ImVec2 window_menu_button_pos; DockNodeCalcTabBarLayout(node, &title_bar_rect, &tab_bar_rect, &window_menu_button_pos); - // Submit new tabs and apply NavWindow focus back to the tab bar. They will be added as Unsorted and sorted below based on relative DockOrder value. + // Submit new tabs, they will be added as Unsorted and sorted below based on relative DockOrder value. const int tabs_count_old = tab_bar->Tabs.Size; for (int window_n = 0; window_n < node->Windows.Size; window_n++) { ImGuiWindow* window = node->Windows[window_n]; - if (g.NavWindow && g.NavWindow->RootWindow == window) - tab_bar->SelectedTabId = window->ID; if (TabBarFindTabByID(tab_bar, window->ID) == NULL) TabBarAddTab(tab_bar, ImGuiTabItemFlags_Unsorted, window); } @@ -13824,7 +13823,8 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w // Docking/Collapse button if (has_window_menu_button) { - if (CollapseButton(host_window->GetID("#COLLAPSE"), window_menu_button_pos, node)) + IMGUI_TEST_ENGINE_ID_INFO(node->WindowMenuButtonId, ImGuiDataType_String, "#COLLAPSE"); + if (CollapseButton(node->WindowMenuButtonId, window_menu_button_pos, node)) OpenPopup("#WindowMenu"); if (IsItemActive()) focus_tab_id = tab_bar->SelectedTabId; @@ -13847,6 +13847,10 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w ImQsort(tab_bar->Tabs.Data + tabs_unsorted_start, tab_bar->Tabs.Size - tabs_unsorted_start, sizeof(ImGuiTabItem), TabItemComparerByDockOrder); } + // Apply NavWindow focus back to the tab bar + if (g.NavWindow && g.NavWindow->RootWindow->DockNode == node) + tab_bar->SelectedTabId = g.NavWindow->RootWindow->ID; + // Selected newly added tabs, or persistent tab ID if the tab bar was just recreated if (tab_bar_is_recreated && TabBarFindTabByID(tab_bar, node->SelectedTabId) != NULL) tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = node->SelectedTabId; diff --git a/imgui_internal.h b/imgui_internal.h index 3ad50f8da0ba..cdfc4d9f4cb8 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1227,6 +1227,7 @@ enum ImGuiDockNodeState struct IMGUI_API ImGuiDockNode { ImGuiID ID; + ImGuiID WindowMenuButtonId; // == ImHashStr("#COLLAPSE", ID) ImGuiDockNodeFlags SharedFlags; // Flags shared by all nodes of a same dockspace hierarchy (inherited from the root node) ImGuiDockNodeFlags LocalFlags; // Flags specific to this node ImGuiDockNodeState State; @@ -2860,8 +2861,8 @@ extern void ImGuiTestEngineHook_Log(ImGuiContext* ctx, const char* fmt, #define IMGUI_TEST_ENGINE_ITEM_ADD(_BB,_ID) if (g.TestEngineHookItems) ImGuiTestEngineHook_ItemAdd(&g, _BB, _ID) // Register item bounding box #define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS) if (g.TestEngineHookItems) ImGuiTestEngineHook_ItemInfo(&g, _ID, _LABEL, _FLAGS) // Register item label and status flags (optional) #define IMGUI_TEST_ENGINE_LOG(_FMT,...) if (g.TestEngineHookItems) ImGuiTestEngineHook_Log(&g, _FMT, __VA_ARGS__) // Custom log entry from user land into test log -#define IMGUI_TEST_ENGINE_ID_INFO(_ID,_TYPE,_DATA) if (g.TestEngineHookIdInfo == id) ImGuiTestEngineHook_IdInfo(&g, _TYPE, _ID, (const void*)(_DATA)); -#define IMGUI_TEST_ENGINE_ID_INFO2(_ID,_TYPE,_DATA,_DATA2) if (g.TestEngineHookIdInfo == id) ImGuiTestEngineHook_IdInfo(&g, _TYPE, _ID, (const void*)(_DATA), (const void*)(_DATA2)); +#define IMGUI_TEST_ENGINE_ID_INFO(_ID,_TYPE,_DATA) if (g.TestEngineHookIdInfo == _ID) ImGuiTestEngineHook_IdInfo(&g, _TYPE, _ID, (const void*)(_DATA)); +#define IMGUI_TEST_ENGINE_ID_INFO2(_ID,_TYPE,_DATA,_DATA2) if (g.TestEngineHookIdInfo == _ID) ImGuiTestEngineHook_IdInfo(&g, _TYPE, _ID, (const void*)(_DATA), (const void*)(_DATA2)); #else #define IMGUI_TEST_ENGINE_ITEM_ADD(_BB,_ID) do { } while (0) #define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS) do { } while (0) From e5efa019203a6e6f720f93783631e490b56dbe76 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 14 Apr 2021 14:33:10 +0200 Subject: [PATCH 704/828] Docking: Fix window menu button. Broken by 3f16a52 (#4043) Worked on single-frame click. --- imgui.cpp | 5 ++--- imgui_internal.h | 8 ++++---- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 558c790882ec..e91efc96b925 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11638,6 +11638,7 @@ static void ImGui::UpdateViewportsNewFrame() // - when releasing a moving window we will revert to aiming behind (at viewport_hovered) // - when we are between viewports, our dragged preview will tend to show in the last viewport _even_ if we don't have tooltips in their viewports (when lacking monitor info) // - consider the case of holding on a menu item to browse child menus: even thou a mouse button is held, there's no active id because menu items only react on mouse release. + // FIXME-VIEWPORT: This is essentially broken, when ImGuiBackendFlags_HasMouseHoveredViewport is set we want to trust when viewport_hovered==NULL and use that. const bool is_mouse_dragging_with_an_expected_destination = g.DragDropActive; if (is_mouse_dragging_with_an_expected_destination && viewport_hovered == NULL) viewport_hovered = g.MouseLastHoveredViewport; @@ -12970,7 +12971,6 @@ bool ImGui::DockContextCalcDropPosForDocking(ImGuiWindow* target, ImGuiDockNode* ImGuiDockNode::ImGuiDockNode(ImGuiID id) { ID = id; - WindowMenuButtonId = ImHashStr("#COLLAPSE", 0, ID); SharedFlags = LocalFlags = ImGuiDockNodeFlags_None; ParentNode = ChildNodes[0] = ChildNodes[1] = NULL; TabBar = NULL; @@ -13823,8 +13823,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w // Docking/Collapse button if (has_window_menu_button) { - IMGUI_TEST_ENGINE_ID_INFO(node->WindowMenuButtonId, ImGuiDataType_String, "#COLLAPSE"); - if (CollapseButton(node->WindowMenuButtonId, window_menu_button_pos, node)) + if (CollapseButton(host_window->GetID("#COLLAPSE"), window_menu_button_pos, node)) // == DockNodeGetWindowMenuButtonId(node) OpenPopup("#WindowMenu"); if (IsItemActive()) focus_tab_id = tab_bar->SelectedTabId; diff --git a/imgui_internal.h b/imgui_internal.h index cdfc4d9f4cb8..91d3212a7685 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1227,7 +1227,6 @@ enum ImGuiDockNodeState struct IMGUI_API ImGuiDockNode { ImGuiID ID; - ImGuiID WindowMenuButtonId; // == ImHashStr("#COLLAPSE", ID) ImGuiDockNodeFlags SharedFlags; // Flags shared by all nodes of a same dockspace hierarchy (inherited from the root node) ImGuiDockNodeFlags LocalFlags; // Flags specific to this node ImGuiDockNodeState State; @@ -2592,9 +2591,10 @@ namespace ImGui IMGUI_API bool DockContextCalcDropPosForDocking(ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, bool split_outer, ImVec2* out_pos); IMGUI_API bool DockNodeBeginAmendTabBar(ImGuiDockNode* node); IMGUI_API void DockNodeEndAmendTabBar(); - inline ImGuiDockNode* DockNodeGetRootNode(ImGuiDockNode* node) { while (node->ParentNode) node = node->ParentNode; return node; } - inline int DockNodeGetDepth(const ImGuiDockNode* node) { int depth = 0; while (node->ParentNode) { node = node->ParentNode; depth++; } return depth; } - inline ImGuiDockNode* GetWindowDockNode() { ImGuiContext& g = *GImGui; return g.CurrentWindow->DockNode; } + inline ImGuiDockNode* DockNodeGetRootNode(ImGuiDockNode* node) { while (node->ParentNode) node = node->ParentNode; return node; } + inline int DockNodeGetDepth(const ImGuiDockNode* node) { int depth = 0; while (node->ParentNode) { node = node->ParentNode; depth++; } return depth; } + inline ImGuiID DockNodeGetWindowMenuButtonId(const ImGuiDockNode* node) { return ImHashStr("#COLLAPSE", 0, node->ID); } + inline ImGuiDockNode* GetWindowDockNode() { ImGuiContext& g = *GImGui; return g.CurrentWindow->DockNode; } IMGUI_API bool GetWindowAlwaysWantOwnTabBar(ImGuiWindow* window); IMGUI_API void BeginDocked(ImGuiWindow* window, bool* p_open); IMGUI_API void BeginDockableDragDropSource(ImGuiWindow* window); From 76902c482ffa542cda166fb1230d1cb9ab925ec7 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 14 Apr 2021 19:31:40 +0200 Subject: [PATCH 705/828] Changelog: added docking+entries from 1.72 to 1.82 to increase their visibility. --- docs/CHANGELOG.txt | 151 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 149 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 75dcc4b6d11d..8d76140f3c90 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -130,13 +130,13 @@ Other Changes: - Examples: SDL2: Link with shell32.lib required by SDL2main.lib since SDL 2.0.12. [#3988] - Docs: Improvements to minor mistakes in documentation comments (#3923) [@ANF-Studios] -Docking Branch: +Docking+Viewports Branch: - [Breaking] Removed io.ConfigDockingWithShift config option. Behavior always equivalent to having the option set to false (dock/undock by default, hold shift to avoid docking). (#2109) - Docking: DockSpace() returns its node ID. - Docking: Dockspace() never draws a background. (#3924) -- Docking: undocking nodes/windows covering most of the monitor max their size down to 90% to ease further manipulations. +- Docking: undocking nodes/windows covering most of the monitor max their size down to 90% to ease manipulations. - Docking: Fixed restoring of tab order within a dockspace or a split node. - Docking: Fixed reappearing docked windows with no close button showing a tab with extraneous space for one frame. - Docking: Fixed multiple simultaneously reappearing window from appearing undocked for one frame. @@ -238,6 +238,13 @@ Other Changes: scheduled builds builds are not required. [@rokups] - Log/Capture: Added LogTextV, a va_list variant of LogText. [@PathogenDavid] +Docking+Viewports Branch: + +- Viewports: Fix setting of ImGuiViewportFlags_NoRendererClear. (#3213) +- Viewports: Added GetViewportPlatformMonitor() with a safety net to keep code portable. +- Viewports, Backends: SDL: Fix missing ImGuiBackendFlags_HasSetMousePos flag in docking branch. +- Viewports, Backends: GLFW: Fix application of WantSetMousePos. (#1542, #787) + ----------------------------------------------------------------------- VERSION 1.81 (Released 2021-02-10) @@ -319,6 +326,17 @@ Other Changes: - Examples: WebGPU: Added Emscripten+WebGPU example. (#3632) [@bfierz] - Backends: GLFW: Added ImGui_ImplGlfw_InitForOther() initialization call to use with non OpenGL API. (#3632) +Docking+Viewports Branch: + +- Docking: Fix losing docking information on closed windows for which the hosting node was split. (#3716) [@GamingMinds-DanielC] +- Docking: Fix gap in hit test hole when using ImGuiDockNodeFlags_PassthruCentralNode touching the edge of a viewport. (#3733) +- Viewports: (Breaking) removed ImGuiPlatformIO::MainViewport which is now pretty much unused and duplicate + (and misleading as we will evolve the concept). +- Viewports: (Breaking) turned ImGuiViewport::GetWorkPos(), ImGuiViewport::GetWorkSize() into regular fields + (WorkPos, WorkSize) before exposing in master branch. +- Viewports: Fix issue inferring viewport z-order when new popups gets created. (#3734) + Metrics updates. +- Viewports, Backends: Vulkan: handle VK_ERROR_OUT_OF_DATE_KHR when resizing secondary viewport (#3766, #3758) + ----------------------------------------------------------------------- VERSION 1.80 (Released 2021-01-21) @@ -437,6 +455,14 @@ Other Changes: - Docs: Split examples/README.txt into docs/BACKENDS.md and docs/EXAMPLES.md, and improved them. - Docs: Consistently renamed all occurrences of "binding" and "back-end" to "backend" in comments and docs. +Docking+Viewports Branch: + +- Docking: Docked windows honor change of tab and text colors. (#2771) +- Docking: Support for appending into existing tab-bar made to work with Docking + internal helper DockNodeBeginAmendTabBar(). +- Docking: Added experimental TabItemFlagsOverrideSet to ImGuiWindowClass. +- Viewports: Fixed incorrect whitening of popups above a modal if both use their own viewport. +- Viewports: Backends: Vulkan: Fixed build, removed extraneous pipeline creation. (#3459, #3579) + ----------------------------------------------------------------------- VERSION 1.79 (Released 2020-10-08) @@ -531,6 +557,21 @@ Other Changes: - Examples: DX12: Added '#define ImTextureID ImU64' in project and build files to also allow building on 32-bit systems. Added project to default Visual Studio solution file. (#301) +Docking+Viewports Branch: + +- Docking: DockSpace() emits ItemSize() properly (useful when not filling all space). +- Docking: Fixed docking while hovering a child window. (#3420) broken by 85a661d. Improve metrics debugging. +- Docking: Fix honoring payload filter with overlapping nodes (we incorrectly over-relied on g.HoveredDockNode + when making change for #3398). +- Docking: Fix handling of WindowMenuButtonPosition == ImGuiDir_None in Docking Nodes. (#3499) +- Viewports: Fixed a rare edge-case if the window targeted by CTRL+Tab stops being rendered. +- Viewports, Backends: DX12: Make secondary viewport format match main viewport one (#3462) {@BeastLe9enD] +- Viewports: Backends: Vulkan: Removed unused shader code. Fix leaks. Avoid unnecessary pipeline creation for main + viewport. (#3459) + Add ImGui_ImplVulkanH_CreateWindowSwapChain in ImGui_ImplVulkanH_CreateOrResizeWindow(). +- Viewports: Backends: DirectX9: Recover from D3DERR_DEVICELOST on secondary viewports. (#3424) +- Viewports, Backends: Win32: Fix toggling of ImGuiViewportFlags_TopMost (#3477) [@Kodokuna] +- Viewports: Backends: GLFW: Workaround for cases where glfwGetMonitorWorkarea fails (#3457) [@dougbinks] + ----------------------------------------------------------------------- VERSION 1.78 (Released 2020-08-18) @@ -632,6 +673,23 @@ Other Changes: - Examples: Vulkan: Fixed GLFW+Vulkan and SDL+Vulkan clear color not being set. (#3390) [@RoryO] - CI: Emscripten has stopped their support for their fastcomp backend, switching to latest sdk [@Xipiryon] +Docking+Viewports Branch: + +- Docking: Made DockBuilderAddNode() automatically call DockBuilderRemoveNode(). (#3399, #2109) +- Docking: Storing HoveredDockNode in context which can be useful for easily detecting e.g. hovering an + empty node. (#3398) +- Docking: Fixed docking overlay bits appearing at (0,0), because of 43bd80a. Most typically noticeable + when disabling multi-viewport. +- Docking: Workaround recovery for node created without the _DockSpace flags later becoming a DockSpace. (#3340) +- Docking: Rework size allocations to recover when there's no enough room for nodes + do not hold on + _WantLockSizeOnce forever. (#3328) +- Docking: Rework size allocation to allow user code to override node sizes. Not all edge cases will be + properly handled but this is a step toward toolbar emitting size constraints. +- Docking: Added experimental flags to perform more docking filtering and disable resize per axis. + Designed for toolbar patterns. +- Viewports, Backends, GLFW: Use GLFW_MOUSE_PASSTHROUGH when available. +- Viewports, Backends: DX12: Fixed issue on shutdown when viewports are disabled. (#3347) + ----------------------------------------------------------------------- VERSION 1.77 (Released 2020-06-29) @@ -723,6 +781,17 @@ Other Changes: - Examples: Apple: Fixed example_apple_metal and example_apple_opengl2 using imgui_impl_osx.mm not forwarding right and center mouse clicks. (#3260) [@nburrus] +Docking+Viewports Branch: + +- Viewports: Don't set ImGuiViewportFlags_NoRendererClear when ImGuiWindowFlags_NoBackground is set. (#3213) +- Viewports: Report minimized viewports as zero DisplaySize to be consistent with main branch. (#1542) +- Docking, Settings: Allow reload of settings data at runtime. (#2573) +- Backends, GLFW: Fix windows resizing incorrectly on Linux due to GLFW firing window positioning + callbacks on next frame after window is resized manually. (#2117) +- Backends: DX12: Fix OBJECT_DELETED_WHILE_STILL_IN_USE on viewport resizing. (#3210) +- Backends: DX12: Fix for crash caused by early resource release. (#3121) +- Backends, Win32: Request monitor update when DPI awareness is enabled to make sure they have the correct DPI settings. + ----------------------------------------------------------------------- VERSION 1.76 (Released 2020-04-12) @@ -795,6 +864,20 @@ Other Changes: - Examples: SDL+DX11: Fixed resizing main window. (#3057) [@joeslay] - Examples: Added SDL+Metal example application. (#3017) [@coding-jackalope] +Docking+Viewports Branch: + +- Docking: Fixed assert preventing dockspace from being created instead a hidden tab. (#3101) +- Viewports: Fixed secondary viewports accidentally merging into a minimized host viewport. (#3118) +- Viewports, Docking: Added per-viewport work area system for e.g. menu-bars. Fixed DockspaceOverViewport() + and demo code (overlay etc) accordingly. (#3035, #2889, #2474, #1542, #2109) +- Viewports: Improve menu positioning in multi-monitor setups. [@rokups] +- Viewports: Software mouse cursor is also scaled by current DpiScale. (amend #939) +- Viewports: Avoid manually clipping resize grips and borders, which messes up with automation ability + to locate those items. Also simpler and more standard. +- Viewports: Fix for UWP in the imgui_impl_win32.cpp IME handler. (#2895, #2892). +- Viewports: Bunch of extra of comments to facilitate setting up multi-viewports. +- Viewports, GLFW: Avoid using window positioning workaround for GLFW 3.3+ versions that have it fixed. + ----------------------------------------------------------------------- VERSION 1.75 (Released 2020-02-10) @@ -893,6 +976,21 @@ Other Changes: - Examples: Metal: Wrapped main loop in @autoreleasepool block to ensure allocations get freed even if underlying system event loop gets paused due to app nap. (#2910, #2917) [@bear24rw] +Docking+Viewports Branch: + +- Docking + Nav: Fixed messed up Ctrl+Tab order with docked windows. +- Docking + Nav: Fixed failing to restore NavId when refocusing a child within a docked window. +- Docking + Nav: Fixed failing to restore NavId when refocusing due to missing nav window (when + it stops being submitted). +- Docking: Fixed a bug where the tab bar of a hidden dockspace would keep requesting focus. (#2960) +- Docking: Added experimental DockNodeFlagsOverrideSet/DockNodeFlagsOverrideClear flags in ImGuiWindowClass + (currently experimenting with toolbar idioms). +- Viewports: Fix resizing viewport-owning windows when mouse pos is outside the InnerClipRect + (can happen with OS decoration enabled). +- Viewports: Preserve last known size for minimized main viewport to be consistent with secondary viewports. +- Backends: SDL: Honor NoTaskBarIcon flag under non Win32 OS. (#2117) +- Backends: GLFW, SDL: Platform monitors declared properly even if multi-viewport is not enabled. + ----------------------------------------------------------------------- VERSION 1.74 (Released 2019-11-25) @@ -972,6 +1070,14 @@ Other Changes: - CI: Set up a bunch of continuous-integration tests using GitHub Actions. We now compile many of the example applications on Windows, Linux, MacOS, iOS, Emscripten. Removed Travis integration. (#2865) [@rokups] +Docking+Viewports Branch: + +- Docking: Can undock from the small triangle button. (#2109,. #2645) +- Docking: Fixed node->HasCloseButton not honoring ImGuiDockNodeFlags_NoCloseButton in a floating node, + leading to empty space at the right of tab-bars with those flags. (#2109) +- Docking: Made docked windows not use style.ChildRounding. +- Multi-viewports: Added multi-viewport support in the DX12 back-end. (#2851) [@obfuscate] + ----------------------------------------------------------------------- VERSION 1.73 (Released 2019-09-24) @@ -1034,6 +1140,28 @@ Other Changes: - Misc: Updated stb_rect_pack.h from 0.99 to 1.00 (fixes by @rygorous: off-by-1 bug in best-fit heuristic, fix handling of rectangles too large to fit inside texture). (#2762) [@tido64] +Docking+Viewports Branch: + +- Docking: Fix BeginDocked() path that creates node so that SetNextWindowDockID() doesn't immediately discard the node. (#2109) +- Docking: Fix for node created at the same time as windows that are still resizing (typically with + io.ConfigDockingAlwaysTabBar) to not be zero/min sized. (#2109). The fix delays their visibility by one frame, + which is not ideal but not very problematic as the .ini data gets populated after that. +- Docking: Fix a crash that could occur with a malformed ini file (DockNode Parent value pointing to a missing node). +- Viewport: Fix modal/popup window being stuck in unowned hidden viewport associated to fallback window without stealing + it back. Fix modal reference viewport when opened outside of another window. (#1542) +- Viewport: Modals don't need to set ImGuiViewportFlags_NoFocusOnClick, this also mitigate the issue described by #2445, + which becomes particularly bad with unfocused modal. (#1542) +- Viewport: better case case where host window gets moved and resized simultaneous (toggling maximized state). + There's no perfect solution there, than using io.ConfigViewportsNoAutoMerge = false. (#1542) +- Viewport, Docking: Fixed incorrect assignment of IsFallbackWindow which would tag dock node host windows created + in NewFrame() as such, messing with popup viewport inheritance. +- Viewport: Fixed issue where resize grip would display as hovered while mouse is still off the OS bounds so a click + would miss it and focus the OS window behind expected one. (#1542) +- Viewport: Fix to allow multiple shutdown / calls to DestroyPlatformWindows(). (#2769) +- Viewport: Backends: GLFW: Fix setting window size on macOS (#2767, #2117) [@rokups] +- Viewport: Backends: GLFW+Linux: Fix window having incorrect size after uncollapse. (#2756, #2117) [@rokups] +- Viewport: Backends: DirectX9: Workaround for windows not refreshing when main viewport has no draw call. (#2560) + ----------------------------------------------------------------------- VERSION 1.72b (Released 2019-07-31) @@ -1136,6 +1264,25 @@ Other Changes: (#2482, #2632) [@josiahmanson] - Examples: Added SDL2+DirectX11 example application. (#2632, #2612, #2482) [@vincenthamm] +Docking+Viewports Branch: + +- Docking: Making it possible to undock a node by clicking on the tab bar / title bar for the node. (#2645). +- Docking: Explicitly inhibit constraint when docked for now. Fix clipping issue related to constraints. (#2690). +- Docking: Fixed dragging/resizing from OS decoration not marking settings as dirty. +- Docking: Renamed io.ConfigDockingTabBarOnSingleWindows to io.ConfigDockingAlwaysTabBar. + Added ImGuiWindowClass::DockingAlwaysTabBar to set on individual windows. +- Docking: Perform simple check: assert if Docking or Viewport are enabled exactly on frame 1 (instead of frame 0 + or later), which is a common user error leading to loss of .ini data on load. +- Docking: Fix so that an appearing window making a dock node reappear won't have a zero-size on its first frame. +- Docking: Fixed using ImGuiDockNodeFlags_AutoHideTabBar with io.ConfigDockingTabBarOnSingleWindows. +- Docking: Added ImGuiDockNode to .natvis file. +- Docking: Fixed support for large meshes in GetBackgroundDrawList(), GetForegroundDrawList(). (#2638) +- Viewport: Fix monitor dpi info not being copied to main viewport when multi-viewports are not enabled. (#2621, #1676) +- Viewport: Refactored ImGuiWindowClass's ViewportFlagsOverrideMask + ViewportFlagsOverrideValue into + ViewportFlagsOverrideSet + ViewportFlagsOverrideClear which appears easier to grasp. (#1542) +- Viewport: Added ImGuiViewportFlags_NoAutoMerge to prevent merging into host viewport in a per-window basis + via the ImGuiWindowClass override mechanism. (#1542) + ----------------------------------------------------------------------- VERSION 1.71 (Released 2019-06-12) From 2cdfcb8fd2fcd6e413ba8fcb1014295fdb530019 Mon Sep 17 00:00:00 2001 From: warriormaster12 Date: Tue, 18 May 2021 15:07:17 +0200 Subject: [PATCH 706/828] Backends: Vulkan: Fix for using IMGUI_IMPL_VULKAN_NO_PROTOTYPES (#4151, #3759, #3227) --- backends/imgui_impl_vulkan.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp index ff7b676b0975..77bfede5016e 100644 --- a/backends/imgui_impl_vulkan.cpp +++ b/backends/imgui_impl_vulkan.cpp @@ -186,7 +186,18 @@ void ImGui_ImplVulkanH_CreateWindowCommandBuffers(VkPhysicalDevice physical_devi IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetSwapchainImagesKHR) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkMapMemory) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkUnmapMemory) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkUpdateDescriptorSets) + IMGUI_VULKAN_FUNC_MAP_MACRO(vkUpdateDescriptorSets) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetPhysicalDeviceSurfaceSupportKHR) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkWaitForFences) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdBeginRenderPass) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdEndRenderPass) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkQueuePresentKHR) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkBeginCommandBuffer) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkEndCommandBuffer) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkResetFences) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkQueueSubmit) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkResetCommandPool) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkAcquireNextImageKHR) // Define function pointers #define IMGUI_VULKAN_FUNC_DEF(func) static PFN_##func func; From 105d82d134e62d0aaf73bbd632effeef15377ed6 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 20 May 2021 18:12:08 +0200 Subject: [PATCH 707/828] Docking: Docking node tab bar honors ItemInnerSpacing.x before first tab. Tweak rendering and alignment of dock node menu marker. (#4130) + Fix ~0 in EndFrameDrawDimmedBackgrounds() which is obsolete way of signifying "all round corners". --- docs/CHANGELOG.txt | 4 +++- imgui.cpp | 40 ++++++++++++++++++++++++---------------- imgui_draw.cpp | 4 ++-- imgui_widgets.cpp | 3 +-- 4 files changed, 30 insertions(+), 21 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 8e4f0ba9ad0a..3ea0f6c932c7 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -177,7 +177,9 @@ Docking+Viewports Branch: option set to false (dock/undock by default, hold shift to avoid docking). (#2109) - Docking: DockSpace() returns its node ID. - Docking: Dockspace() never draws a background. (#3924) -- Docking: undocking nodes/windows covering most of the monitor max their size down to 90% to ease manipulations. +- Docking: Undocking nodes/windows covering most of the monitor max their size down to 90% to ease manipulations. +- Docking: Docking node tab bar honors ItemInnerSpacing.x before first tab. (#4130) +- Docking: Tweak rendering and alignment of dock node menu marker. (#4130) - Docking: Fixed restoring of tab order within a dockspace or a split node. - Docking: Fixed reappearing docked windows with no close button showing a tab with extraneous space for one frame. - Docking: Fixed multiple simultaneously reappearing window from appearing undocked for one frame. diff --git a/imgui.cpp b/imgui.cpp index 9aabced00966..aef105208341 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4600,7 +4600,7 @@ static void ImGui::EndFrameDrawDimmedBackgrounds() bb.Expand(-g.FontSize - 1.0f); rounding = window->WindowRounding; } - draw_list->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), rounding, ~0, 3.0f); + draw_list->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), rounding, 0, 3.0f); draw_list->PopClipRect(); } } @@ -5960,7 +5960,7 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar } // Render title text, collapse button, close button -// When inside a dock node, this is handled in DockNodeUpdateTabBar() instead. +// When inside a dock node, this is handled in DockNodeCalcTabBarLayout() instead. void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open) { ImGuiContext& g = *GImGui; @@ -12361,7 +12361,7 @@ namespace ImGui static bool DockNodeIsDropAllowed(ImGuiWindow* host_window, ImGuiWindow* payload_window); static void DockNodePreviewDockSetup(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* payload_window, ImGuiDockPreviewData* preview_data, bool is_explicit_target, bool is_outer_docking); static void DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* payload_window, const ImGuiDockPreviewData* preview_data); - static void DockNodeCalcTabBarLayout(const ImGuiDockNode* node, ImRect* out_title_rect, ImRect* out_tab_bar_rect, ImVec2* out_window_menu_button_pos); + static void DockNodeCalcTabBarLayout(const ImGuiDockNode* node, ImRect* out_title_rect, ImRect* out_tab_bar_rect, ImVec2* out_window_menu_button_pos, ImVec2* out_close_button_pos); static void DockNodeCalcSplitRects(ImVec2& pos_old, ImVec2& size_old, ImVec2& pos_new, ImVec2& size_new, ImGuiDir dir, ImVec2 size_new_desired); static bool DockNodeCalcDropRectsAndTestMousePos(const ImRect& parent, ImGuiDir dir, ImRect& out_draw, bool outer_docking, ImVec2* test_mouse_pos); static const char* DockNodeGetHostWindowTitle(ImGuiDockNode* node, char* buf, int buf_size) { ImFormatString(buf, buf_size, "##DockNode_%02X", node->ID); return buf; } @@ -13872,7 +13872,8 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w // Layout ImRect title_bar_rect, tab_bar_rect; ImVec2 window_menu_button_pos; - DockNodeCalcTabBarLayout(node, &title_bar_rect, &tab_bar_rect, &window_menu_button_pos); + ImVec2 close_button_pos; + DockNodeCalcTabBarLayout(node, &title_bar_rect, &tab_bar_rect, &window_menu_button_pos, &close_button_pos); // Submit new tabs, they will be added as Unsorted and sorted below based on relative DockOrder value. const int tabs_count_old = tab_bar->Tabs.Size; @@ -13996,8 +13997,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w PushItemFlag(ImGuiItemFlags_Disabled, true); PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_Text] * ImVec4(1.0f,1.0f,1.0f,0.4f)); } - const float button_sz = g.FontSize; - if (CloseButton(host_window->GetID("#CLOSE"), title_bar_rect.GetTR() + ImVec2(-style.FramePadding.x * 2.0f - button_sz, 0.0f))) + if (CloseButton(host_window->GetID("#CLOSE"), close_button_pos)) if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_bar->VisibleTabId)) { node->WantCloseTabId = tab->ID; @@ -14115,27 +14115,35 @@ static bool ImGui::DockNodeIsDropAllowed(ImGuiWindow* host_window, ImGuiWindow* } // window menu button == collapse button when not in a dock node. -// FIXME: This is similar to RenderWindowTitleBarContents, may want to share code. -static void ImGui::DockNodeCalcTabBarLayout(const ImGuiDockNode* node, ImRect* out_title_rect, ImRect* out_tab_bar_rect, ImVec2* out_window_menu_button_pos) +// FIXME: This is similar to RenderWindowTitleBarContents(), may want to share code. +static void ImGui::DockNodeCalcTabBarLayout(const ImGuiDockNode* node, ImRect* out_title_rect, ImRect* out_tab_bar_rect, ImVec2* out_window_menu_button_pos, ImVec2* out_close_button_pos) { ImGuiContext& g = *GImGui; + ImGuiStyle& style = g.Style; + ImRect r = ImRect(node->Pos.x, node->Pos.y, node->Pos.x + node->Size.x, node->Pos.y + g.FontSize + g.Style.FramePadding.y * 2.0f); if (out_title_rect) { *out_title_rect = r; } + r.Min.x += style.WindowBorderSize; + r.Max.x -= style.WindowBorderSize; + + float button_sz = g.FontSize; + ImVec2 window_menu_button_pos = r.Min; - r.Min.x += g.Style.FramePadding.x; - r.Max.x -= g.Style.FramePadding.x; + r.Min.x += style.FramePadding.x; + r.Max.x -= style.FramePadding.x; if (node->HasCloseButton) { - r.Max.x -= g.FontSize;// +1.0f; // In DockNodeUpdateTabBar() we currently display a disabled close button even if there is none. + r.Max.x -= button_sz; + if (out_close_button_pos) *out_close_button_pos = ImVec2(r.Max.x - style.FramePadding.x, r.Min.y); } - if (node->HasWindowMenuButton && g.Style.WindowMenuButtonPosition == ImGuiDir_Left) + if (node->HasWindowMenuButton && style.WindowMenuButtonPosition == ImGuiDir_Left) { - r.Min.x += g.FontSize; // + g.Style.ItemInnerSpacing.x; // <-- Adding ItemInnerSpacing makes the title text moves slightly when in a docking tab bar. Instead we adjusted RenderArrowDockMenu() + r.Min.x += button_sz + style.ItemInnerSpacing.x; } - else if (node->HasWindowMenuButton && g.Style.WindowMenuButtonPosition == ImGuiDir_Right) + else if (node->HasWindowMenuButton && style.WindowMenuButtonPosition == ImGuiDir_Right) { - r.Max.x -= g.FontSize + g.Style.FramePadding.x; + r.Max.x -= button_sz + style.FramePadding.x; window_menu_button_pos = ImVec2(r.Max.x, r.Min.y); } if (out_tab_bar_rect) { *out_tab_bar_rect = r; } @@ -14352,7 +14360,7 @@ static void ImGui::DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDock { // Compute target tab bar geometry so we can locate our preview tabs ImRect tab_bar_rect; - DockNodeCalcTabBarLayout(&data->FutureNode, NULL, &tab_bar_rect, NULL); + DockNodeCalcTabBarLayout(&data->FutureNode, NULL, &tab_bar_rect, NULL, NULL); ImVec2 tab_pos = tab_bar_rect.Min; if (host_node && host_node->TabBar) { diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 802458b11ce8..43acfbc34f93 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -3811,8 +3811,8 @@ void ImGui::RenderArrowPointingAt(ImDrawList* draw_list, ImVec2 pos, ImVec2 half // and because the saved space means that the left-most tab label can stay at exactly the same position as the label of a loose window. void ImGui::RenderArrowDockMenu(ImDrawList* draw_list, ImVec2 p_min, float sz, ImU32 col) { - draw_list->AddRectFilled(p_min + ImVec2(sz * 0.10f, sz * 0.15f), p_min + ImVec2(sz * 0.70f, sz * 0.30f), col); - RenderArrowPointingAt(draw_list, p_min + ImVec2(sz * 0.40f, sz * 0.85f), ImVec2(sz * 0.30f, sz * 0.40f), ImGuiDir_Down, col); + draw_list->AddRectFilled(p_min + ImVec2(sz * 0.20f, sz * 0.15f), p_min + ImVec2(sz * 0.80f, sz * 0.30f), col); + RenderArrowPointingAt(draw_list, p_min + ImVec2(sz * 0.50f, sz * 0.85f), ImVec2(sz * 0.30f, sz * 0.40f), ImGuiDir_Down, col); } static inline float ImAcos01(float x) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 3b575cada210..3a7fc9a1c739 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -838,12 +838,11 @@ bool ImGui::CollapseButton(ImGuiID id, const ImVec2& pos, ImGuiDockNode* dock_no // Render //bool is_dock_menu = (window->DockNodeAsHost && !window->Collapsed); - ImVec2 off = dock_node ? ImVec2(IM_FLOOR(-g.Style.ItemInnerSpacing.x * 0.5f) + 0.5f, 0.0f) : ImVec2(0.0f, 0.0f); ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); ImU32 text_col = GetColorU32(ImGuiCol_Text); ImVec2 center = bb.GetCenter(); if (hovered || held) - window->DrawList->AddCircleFilled(center + off + ImVec2(0,-0.5f), g.FontSize * 0.5f + 1.0f, bg_col, 12); + window->DrawList->AddCircleFilled(center + ImVec2(0,-0.5f), g.FontSize * 0.5f + 1.0f, bg_col, 12); if (dock_node) RenderArrowDockMenu(window->DrawList, bb.Min + g.Style.FramePadding, g.FontSize, text_col); From 91704b773e7e636dd83afdd2871a4fa17fa78863 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 31 May 2021 17:06:46 +0200 Subject: [PATCH 708/828] Docking: Fix IsWindowAppearing() and ImGuiCond_Appearing on docked windows. (#4177, #3982, #1497, #1061) --- docs/CHANGELOG.txt | 5 ++++- imgui.cpp | 20 +++++++++++++++----- imgui_internal.h | 1 + 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 3562714ff0a7..23a83868acfd 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -108,7 +108,10 @@ Other Changes: Normally the right way to disable compiling the demo is to set IMGUI_DISABLE_DEMO_WINDOWS, but we want to avoid implying that the file is required. - Backends: OpenGL3: Handle GL_CLIP_ORIGIN on <4.5 contexts if "GL_ARB_clip_control" extension is detected. (#4170, #3998) ->>>>>>> master + +Docking+Viewports Branch: + +- Docking: Fix IsWindowAppearing() and ImGuiCond_Appearing on docked windows. (#4177, #3982, #1497, #1061) ----------------------------------------------------------------------- diff --git a/imgui.cpp b/imgui.cpp index f79634a31a07..8593f7f43bd8 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6100,8 +6100,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame); window->IsFallbackWindow = (g.CurrentWindowStack.Size == 0 && g.WithinFrameScopeWithImplicitWindow); - // Update the Appearing flag - bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on + // Update the Appearing flag (note: the BeginDocked() path may also set this to true later) + bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on if (flags & ImGuiWindowFlags_Popup) { ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size]; @@ -6136,6 +6136,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) { bool has_dock_node = (window->DockId != 0 || window->DockNode != NULL); bool new_auto_dock_node = !has_dock_node && GetWindowAlwaysWantOwnTabBar(window); + bool dock_node_was_visible = window->DockNodeIsVisible; + bool dock_tab_was_visible = window->DockTabIsVisible; if (has_dock_node || new_auto_dock_node) { BeginDocked(window, p_open); @@ -6146,6 +6148,13 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Docking currently override constraints g.NextWindowData.Flags &= ~ImGuiNextWindowDataFlags_HasSizeConstraint; } + + // Update the Appearing flag (again) + if (window->DockTabIsVisible && !dock_tab_was_visible && dock_node_was_visible && !window->Appearing) + { + window->Appearing = true; + SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true); + } } // Parent window is latched only on the first call to Begin() of the frame, so further append-calls can be done from a different window stack @@ -12957,7 +12966,7 @@ void ImGui::DockContextProcessUndockWindow(ImGuiContext* ctx, ImGuiWindow* windo window->DockId = 0; window->Collapsed = false; window->DockIsActive = false; - window->DockTabIsVisible = false; + window->DockNodeIsVisible = window->DockTabIsVisible = false; window->Size = window->SizeFull = FixLargeWindowsWhenUndocking(window->SizeFull, window->Viewport); MarkIniSettingsDirty(); @@ -15503,7 +15512,7 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) else { window->DockIsActive = true; - window->DockTabIsVisible = false; + window->DockNodeIsVisible = window->DockTabIsVisible = false; } return; } @@ -15518,7 +15527,7 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) if (node->HostWindow == NULL) { window->DockIsActive = (node->State == ImGuiDockNodeState_HostWindowHiddenBecauseWindowsAreResizing); - window->DockTabIsVisible = false; + window->DockNodeIsVisible = window->DockTabIsVisible = false; if (node->Windows.Size > 1) DockNodeHideWindowDuringHostWindowCreation(window); return; @@ -15542,6 +15551,7 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) SetNextWindowSize(node->Size); g.NextWindowData.PosUndock = false; // Cancel implicit undocking of SetNextWindowPos() window->DockIsActive = true; + window->DockNodeIsVisible = true; window->DockTabIsVisible = false; if (node->SharedFlags & ImGuiDockNodeFlags_KeepAliveOnly) return; diff --git a/imgui_internal.h b/imgui_internal.h index 2673fc81df00..ce0ea2411844 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2101,6 +2101,7 @@ struct IMGUI_API ImGuiWindow // Docking bool DockIsActive :1; // When docking artifacts are actually visible. When this is set, DockNode is guaranteed to be != NULL. ~~ (DockNode != NULL) && (DockNode->Windows.Size > 1). + bool DockNodeIsVisible :1; bool DockTabIsVisible :1; // Is our window visible this frame? ~~ is the corresponding tab selected? bool DockTabWantClose :1; short DockOrder; // Order of the last time the window was visible within its DockNode. This is used to reorder windows that are reappearing on the same frame. Same value between windows that were active and windows that were none are possible. From fa1f540e6c64ae66623cff579cd4de30d594ffdb Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 3 Jun 2021 16:07:22 +0200 Subject: [PATCH 709/828] Docking: Amend 91704b7, window->DockXXX booleans not properly cleared when window not docked. (#4177, #3982, #1497, #1061) Fix issue with freshly split windows/nodes incorrectly returning true to IsWindowAppearing(). --- imgui.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 8593f7f43bd8..10d2464a14d3 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6148,6 +6148,10 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Docking currently override constraints g.NextWindowData.Flags &= ~ImGuiNextWindowDataFlags_HasSizeConstraint; } + else + { + window->DockIsActive = window->DockNodeIsVisible = window->DockTabIsVisible = false; + } // Update the Appearing flag (again) if (window->DockTabIsVisible && !dock_tab_was_visible && dock_node_was_visible && !window->Appearing) @@ -15457,6 +15461,9 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) ImGuiContext* ctx = GImGui; ImGuiContext& g = *ctx; + // Clear fields ahead so most early-out paths don't have to do it + window->DockIsActive = window->DockNodeIsVisible = window->DockTabIsVisible = false; + const bool auto_dock_node = GetWindowAlwaysWantOwnTabBar(window); if (auto_dock_node) { @@ -15506,14 +15513,9 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) // If the window has been orphaned, transition the docknode to an implicit node processed in DockContextNewFrameUpdateDocking() ImGuiDockNode* root_node = DockNodeGetRootNode(node); if (root_node->LastFrameAlive < g.FrameCount) - { DockContextProcessUndockWindow(ctx, window); - } else - { window->DockIsActive = true; - window->DockNodeIsVisible = window->DockTabIsVisible = false; - } return; } @@ -15526,8 +15528,8 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) // FIXME-DOCK: replace ->HostWindow NULL compare with something more explicit (~was initially intended as a first frame test) if (node->HostWindow == NULL) { - window->DockIsActive = (node->State == ImGuiDockNodeState_HostWindowHiddenBecauseWindowsAreResizing); - window->DockNodeIsVisible = window->DockTabIsVisible = false; + if (node->State == ImGuiDockNodeState_HostWindowHiddenBecauseWindowsAreResizing) + window->DockIsActive = true; if (node->Windows.Size > 1) DockNodeHideWindowDuringHostWindowCreation(window); return; From f03ab2a5c5f4e0962790c8a1e81c218e8d7facab Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 3 Jun 2021 16:46:01 +0200 Subject: [PATCH 710/828] Docking: Fix IsWindowAppearing() unnecessarily returning true twice in a row. (#4177, #3982, #1497, #1061) + added a zealous assert. --- imgui.cpp | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 10d2464a14d3..1d1d2981d25a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6108,13 +6108,15 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed window_just_activated_by_user |= (window != popup_ref.Window); } - window->Appearing = window_just_activated_by_user; - if (window->Appearing) - SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true); // Update Flags, LastFrameActive, BeginOrderXXX fields + const bool window_was_appearing = window->Appearing; if (first_begin_of_the_frame) { + window->Appearing = window_just_activated_by_user; + if (window->Appearing) + SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true); + window->FlagsPreviousFrame = window->Flags; window->Flags = (ImGuiWindowFlags)flags; window->LastFrameActive = current_frame; @@ -6147,18 +6149,18 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Docking currently override constraints g.NextWindowData.Flags &= ~ImGuiNextWindowDataFlags_HasSizeConstraint; + + // Amend the Appearing flag + if (window->DockTabIsVisible && !dock_tab_was_visible && dock_node_was_visible && !window->Appearing && !window_was_appearing) + { + window->Appearing = true; + SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true); + } } else { window->DockIsActive = window->DockNodeIsVisible = window->DockTabIsVisible = false; } - - // Update the Appearing flag (again) - if (window->DockTabIsVisible && !dock_tab_was_visible && dock_node_was_visible && !window->Appearing) - { - window->Appearing = true; - SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true); - } } // Parent window is latched only on the first call to Begin() of the frame, so further append-calls can be done from a different window stack @@ -6940,6 +6942,12 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && window->HiddenFramesCannotSkipItems <= 0) skip_items = true; window->SkipItems = skip_items; + + // Sanity check: there are two spots which can set Appearing = true + // - when 'window_just_activated_by_user' is set -> HiddenFramesCannotSkipItems is set -> SkipItems always false + // - in BeginDocked() path when DockNodeIsVisible == DockTabIsVisible == true -> hidden _should_ be all zero // FIXME: Not formally proven, hence the assert. + if (window->SkipItems && !window->Appearing) + IM_ASSERT(window->Appearing == false); // Please report on GitHub if this triggers: https://github.com/ocornut/imgui/issues/4177 } return !window->SkipItems; From 5e528d9eea9688d59d990140f65ca10866b10509 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 3 Jun 2021 17:22:18 +0200 Subject: [PATCH 711/828] Docking: Clicking on the right-most close button of a docking node closes all windows. (#4186) --- docs/CHANGELOG.txt | 1 + imgui.cpp | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 23a83868acfd..0c60fe1a26d3 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -111,6 +111,7 @@ Other Changes: Docking+Viewports Branch: +- Docking: Clicking on the right-most close button of a docking node closes all windows. (#4186) - Docking: Fix IsWindowAppearing() and ImGuiCond_Appearing on docked windows. (#4177, #3982, #1497, #1061) diff --git a/imgui.cpp b/imgui.cpp index 1d1d2981d25a..e3ea26ea60d6 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -14027,11 +14027,11 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_Text] * ImVec4(1.0f,1.0f,1.0f,0.4f)); } if (CloseButton(host_window->GetID("#CLOSE"), close_button_pos)) - if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_bar->VisibleTabId)) - { - node->WantCloseTabId = tab->ID; - TabBarCloseTab(tab_bar, tab); - } + { + node->WantCloseAll = true; + for (int n = 0; n < tab_bar->Tabs.Size; n++) + TabBarCloseTab(tab_bar, &tab_bar->Tabs[n]); + } //if (IsItemActive()) // focus_tab_id = tab_bar->SelectedTabId; if (!close_button_is_enabled) From f53db3541afb638e230ba34292816714e0c93bc7 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 4 Jun 2021 14:55:40 +0200 Subject: [PATCH 712/828] Docking: comments (#4189) --- imgui.h | 12 ++++++++---- imgui_demo.cpp | 28 ++++++++++++++++++---------- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/imgui.h b/imgui.h index f56cf587bd06..be01efdb728d 100644 --- a/imgui.h +++ b/imgui.h @@ -779,13 +779,17 @@ namespace ImGui // Note: You can use most Docking facilities without calling any API. You DO NOT need to call DockSpace() to use Docking! // - Drag from window title bar or their tab to dock/undock. Hold SHIFT to disable docking. // - Drag from window menu button (upper-left button) to undock an entire node (all windows). - // About DockSpace: + // About dockspaces: // - Use DockSpace() to create an explicit dock node _within_ an existing window. See Docking demo for details. - // - DockSpace() needs to be submitted _before_ any window they can host. If you use a dockspace, submit it early in your app. + // - Use DockSpaceOverViewport() to create an explicit dock node covering the screen or a specific viewport. + // This is often used with ImGuiDockNodeFlags_PassthruCentralNode. + // - Important: Dockspaces need to be submitted _before_ any window they can host. Submit it early in your frame! + // - Important: Dockspaces need to be kept alive if hidden, otherwise windows docked into it will be undocked. + // e.g. if you have multiple tabs with a dockspace inside each tab: submit the non-visible dockspaces with ImGuiDockNodeFlags_KeepAliveOnly. IMGUI_API ImGuiID DockSpace(ImGuiID id, const ImVec2& size = ImVec2(0, 0), ImGuiDockNodeFlags flags = 0, const ImGuiWindowClass* window_class = NULL); IMGUI_API ImGuiID DockSpaceOverViewport(const ImGuiViewport* viewport = NULL, ImGuiDockNodeFlags flags = 0, const ImGuiWindowClass* window_class = NULL); - IMGUI_API void SetNextWindowDockID(ImGuiID dock_id, ImGuiCond cond = 0); // set next window dock id (FIXME-DOCK) - IMGUI_API void SetNextWindowClass(const ImGuiWindowClass* window_class); // set next window class (rare/advanced uses: provide hints to the platform backend via altered viewport flags and parent/child info) + IMGUI_API void SetNextWindowDockID(ImGuiID dock_id, ImGuiCond cond = 0); // set next window dock id + IMGUI_API void SetNextWindowClass(const ImGuiWindowClass* window_class); // set next window class (control docking compatibility + provide hints to platform backend via custom viewport flags and platform parent/child relationship) IMGUI_API ImGuiID GetWindowDockID(); IMGUI_API bool IsWindowDocked(); // is current window docked into another window? diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 953830cbcdbc..ca8701eedf57 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -7401,13 +7401,23 @@ static void ShowExampleAppCustomRendering(bool* p_open) //----------------------------------------------------------------------------- // Demonstrate using DockSpace() to create an explicit docking node within an existing window. -// Note that you dock windows into each others _without_ a dockspace, by just clicking on -// a window title bar or tab and moving it. -// DockSpace() and DockSpaceOverViewport() are only useful to construct a central docking -// location for your application. +// Note: You can use most Docking facilities without calling any API. You DO NOT need to call DockSpace() to use Docking! +// - Drag from window title bar or their tab to dock/undock. Hold SHIFT to disable docking. +// - Drag from window menu button (upper-left button) to undock an entire node (all windows). +// About dockspaces: +// - Use DockSpace() to create an explicit dock node _within_ an existing window. +// - Use DockSpaceOverViewport() to create an explicit dock node covering the screen or a specific viewport. +// This is often used with ImGuiDockNodeFlags_PassthruCentralNode. +// - Important: Dockspaces need to be submitted _before_ any window they can host. Submit it early in your frame! (*) +// - Important: Dockspaces need to be kept alive if hidden, otherwise windows docked into it will be undocked. +// e.g. if you have multiple tabs with a dockspace inside each tab: submit the non-visible dockspaces with ImGuiDockNodeFlags_KeepAliveOnly. +// (*) because of this constraint, the implicit \"Debug\" window can not be docked into an explicit DockSpace() node, +// because that window is submitted as part of the part of the NewFrame() call. An easy workaround is that you can create +// your own implicit "Debug##2" window after calling DockSpace() and leave it in the window stack for anyone to use. void ShowExampleAppDockSpace(bool* p_open) { - // In 99% case you should be able to just call DockSpaceOverViewport() and ignore all the code below! + // If you strip some features of, this demo is pretty much equivalent to calling DockSpaceOverViewport()! + // In most cases you should be able to just call DockSpaceOverViewport() and ignore all the code below! // In this specific demo, we are not using DockSpaceOverViewport() because: // - we allow the host window to be floating/moveable instead of filling the viewport (when opt_fullscreen == false) // - we allow the host window to have padding (when opt_padding == true) @@ -7461,7 +7471,7 @@ void ShowExampleAppDockSpace(bool* p_open) if (opt_fullscreen) ImGui::PopStyleVar(2); - // DockSpace + // Submit the DockSpace ImGuiIO& io = ImGui::GetIO(); if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable) { @@ -7500,10 +7510,8 @@ void ShowExampleAppDockSpace(bool* p_open) "- Drag from window menu button (upper-left button) to undock an entire node (all windows)." "\n" "- Hold SHIFT to disable docking." "\n" "This demo app has nothing to do with it!" "\n\n" - "This demo app only demonstrate the use of ImGui::DockSpace() which allows you to manually create a docking node _within_ another window. This is useful so you can decorate your main application window (e.g. with a menu bar)." "\n\n" - "ImGui::DockSpace() comes with one hard constraint: it needs to be submitted _before_ any window which may be docked into it. Therefore, if you use a dock spot as the central point of your application, you'll probably want it to be part of the very first window you are submitting to imgui every frame." "\n\n" - "(NB: because of this constraint, the implicit \"Debug\" window can not be docked into an explicit DockSpace() node, because that window is submitted as part of the NewFrame() call. An easy workaround is that you can create your own implicit \"Debug##2\" window after calling DockSpace() and leave it in the window stack for anyone to use.)" - ); + "This demo app only demonstrate the use of ImGui::DockSpace() which allows you to manually create a docking node _within_ another window." "\n\n" + "Read comments in ShowExampleAppDockSpace() for more details."); ImGui::EndMenuBar(); } From 865b2ca6f9c869e44e6e1445ed4aec0853f78164 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 9 Jun 2021 15:28:59 +0200 Subject: [PATCH 713/828] Added PushDisabled(), PopDisabled() currently only exposed in imgui_internal.h (#211) --- imgui.cpp | 22 ++++++++++++++++++++++ imgui.h | 2 +- imgui_internal.h | 2 ++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 80029cfda7eb..22f0d3ce1631 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7178,6 +7178,28 @@ void ImGui::PopItemFlag() g.CurrentItemFlags = g.ItemFlagsStack.back(); } +// PushDisabled()/PopDisabled() +// - Those are not yet exposed in imgui.h because we are unsure of how to alter the style in a way that works for everyone. +// We may rework this. Hypothetically, a future styling system may set a flag which make widgets use different colors. +// - Feedback welcome at https://github.com/ocornut/imgui/issues/211 +// - You may trivially implement your own variation of this if needed. +// Here we test (CurrentItemFlags & ImGuiItemFlags_Disabled) to allow nested PushDisabled() calls. +void ImGui::PushDisabled() +{ + ImGuiContext& g = *GImGui; + if ((g.CurrentItemFlags & ImGuiItemFlags_Disabled) == 0) + PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.6f); + PushItemFlag(ImGuiItemFlags_Disabled, true); +} + +void ImGui::PopDisabled() +{ + ImGuiContext& g = *GImGui; + PopItemFlag(); + if ((g.CurrentItemFlags & ImGuiItemFlags_Disabled) == 0) + PopStyleVar(); +} + // FIXME: Look into renaming this once we have settled the new Focus/Activation/TabStop system. void ImGui::PushAllowKeyboardFocus(bool allow_keyboard_focus) { diff --git a/imgui.h b/imgui.h index a3726b6edfd3..9ab14a341b34 100644 --- a/imgui.h +++ b/imgui.h @@ -62,7 +62,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.84 WIP" -#define IMGUI_VERSION_NUM 18305 +#define IMGUI_VERSION_NUM 18306 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE #define IMGUI_HAS_VIEWPORT // Viewport WIP branch diff --git a/imgui_internal.h b/imgui_internal.h index 377e12a11654..b4177ddfdfb9 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2604,6 +2604,8 @@ namespace ImGui IMGUI_API void PushMultiItemsWidths(int components, float width_full); IMGUI_API void PushItemFlag(ImGuiItemFlags option, bool enabled); IMGUI_API void PopItemFlag(); + IMGUI_API void PushDisabled(); + IMGUI_API void PopDisabled(); IMGUI_API bool IsItemToggledSelection(); // Was the last item selection toggled? (after Selectable(), TreeNode() etc. We only returns toggle _event_ in order to handle clipping correctly) IMGUI_API ImVec2 GetContentRegionMaxAbs(); IMGUI_API void ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_excess); From 4161a67b388b2d18b573c0d43a38385805e4892a Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 9 Jun 2021 16:57:35 +0200 Subject: [PATCH 714/828] ImVector: added clear_delete(), clear_destruct() helpers. # Conflicts: # imgui.cpp --- imgui.cpp | 15 ++++----------- imgui.h | 9 ++++++--- imgui_draw.cpp | 9 +++------ misc/freetype/imgui_freetype.cpp | 3 +-- 4 files changed, 14 insertions(+), 22 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 22f0d3ce1631..c08daeb2e4fa 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2971,8 +2971,7 @@ ImGuiWindow::~ImGuiWindow() { IM_ASSERT(DrawList == &DrawListInst); IM_DELETE(Name); - for (int i = 0; i != ColumnsStorage.Size; i++) - ColumnsStorage[i].~ImGuiOldColumns(); + ColumnsStorage.clear_destruct(); } ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end) @@ -4343,9 +4342,7 @@ void ImGui::Shutdown(ImGuiContext* context) CallContextHooks(&g, ImGuiContextHookType_Shutdown); // Clear everything else - for (int i = 0; i < g.Windows.Size; i++) - IM_DELETE(g.Windows[i]); - g.Windows.clear(); + g.Windows.clear_delete(); g.WindowsFocusOrder.clear(); g.WindowsTempSortBuffer.clear(); g.CurrentWindow = NULL; @@ -4362,18 +4359,14 @@ void ImGui::Shutdown(ImGuiContext* context) g.BeginPopupStack.clear(); g.CurrentViewport = g.MouseViewport = g.MouseLastHoveredViewport = NULL; - for (int i = 0; i < g.Viewports.Size; i++) - IM_DELETE(g.Viewports[i]); - g.Viewports.clear(); + g.Viewports.clear_delete(); g.TabBars.Clear(); g.CurrentTabBarStack.clear(); g.ShrinkWidthBuffer.clear(); g.Tables.Clear(); - for (int i = 0; i < g.TablesTempDataStack.Size; i++) - g.TablesTempDataStack[i].~ImGuiTableTempData(); - g.TablesTempDataStack.clear(); + g.TablesTempDataStack.clear_destruct(); g.DrawChannelsTempMergeBuffer.clear(); g.ClipboardHandlerData.clear(); diff --git a/imgui.h b/imgui.h index 9ab14a341b34..c7bca9062b83 100644 --- a/imgui.h +++ b/imgui.h @@ -62,7 +62,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.84 WIP" -#define IMGUI_VERSION_NUM 18306 +#define IMGUI_VERSION_NUM 18307 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE #define IMGUI_HAS_VIEWPORT // Viewport WIP branch @@ -1743,7 +1743,11 @@ struct ImVector inline ImVector() { Size = Capacity = 0; Data = NULL; } inline ImVector(const ImVector& src) { Size = Capacity = 0; Data = NULL; operator=(src); } inline ImVector& operator=(const ImVector& src) { clear(); resize(src.Size); memcpy(Data, src.Data, (size_t)Size * sizeof(T)); return *this; } - inline ~ImVector() { if (Data) IM_FREE(Data); } + inline ~ImVector() { if (Data) IM_FREE(Data); } // Important: does not destruct anything + + inline void clear() { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } } // Important: does not destruct anything + inline void clear_delete() { for (int n = 0; n < Size; n++) IM_DELETE(Data[n]); clear(); } // Important: never called automatically! always explicit. + inline void clear_destruct() { for (int n = 0; n < Size; n++) Data[n].~T(); clear(); } // Important: never called automatically! always explicit. inline bool empty() const { return Size == 0; } inline int size() const { return Size; } @@ -1753,7 +1757,6 @@ struct ImVector inline T& operator[](int i) { IM_ASSERT(i >= 0 && i < Size); return Data[i]; } inline const T& operator[](int i) const { IM_ASSERT(i >= 0 && i < Size); return Data[i]; } - inline void clear() { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } } inline T* begin() { return Data; } inline const T* begin() const { return Data; } inline T* end() { return Data + Size; } diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 6bc7fae9ba2a..2f852c717178 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -2012,9 +2012,7 @@ void ImFontAtlas::ClearTexData() void ImFontAtlas::ClearFonts() { IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!"); - for (int i = 0; i < Fonts.Size; i++) - IM_DELETE(Fonts[i]); - Fonts.clear(); + Fonts.clear_delete(); } void ImFontAtlas::Clear() @@ -2574,9 +2572,8 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas) } } - // Cleanup temporary (ImVector doesn't honor destructor) - for (int src_i = 0; src_i < src_tmp_array.Size; src_i++) - src_tmp_array[src_i].~ImFontBuildSrcData(); + // Cleanup + src_tmp_array.clear_destruct(); ImFontAtlasBuildFinish(atlas); return true; diff --git a/misc/freetype/imgui_freetype.cpp b/misc/freetype/imgui_freetype.cpp index db6ec946fdbb..e6221f4b61f1 100644 --- a/misc/freetype/imgui_freetype.cpp +++ b/misc/freetype/imgui_freetype.cpp @@ -688,8 +688,7 @@ bool ImFontAtlasBuildWithFreeTypeEx(FT_Library ft_library, ImFontAtlas* atlas, u // Cleanup for (int buf_i = 0; buf_i < buf_bitmap_buffers.Size; buf_i++) IM_FREE(buf_bitmap_buffers[buf_i]); - for (int src_i = 0; src_i < src_tmp_array.Size; src_i++) - src_tmp_array[src_i].~ImFontBuildSrcDataFT(); + src_tmp_array.clear_destruct(); ImFontAtlasBuildFinish(atlas); From db0338a1f2b1f0b05f910888222cacd17fec26e8 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 9 Jun 2021 19:15:56 +0200 Subject: [PATCH 715/828] Nav, Drag and Drop, Docking: fixed two issues leading nav result to conflict with moving a window. (#4211, #3025) --- imgui.cpp | 22 ++++++++++++++++------ imgui_internal.h | 1 + imgui_widgets.cpp | 2 ++ 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index c08daeb2e4fa..6bd0adc297f8 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3582,8 +3582,9 @@ void ImGui::StartMouseMovingWindow(ImGuiWindow* window) FocusWindow(window); SetActiveID(window->MoveId, window); g.NavDisableHighlight = true; - g.ActiveIdNoClearOnFocusLoss = true; g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - window->RootWindowDockTree->Pos; + g.ActiveIdNoClearOnFocusLoss = true; + SetActiveIdUsingNavAndKeys(); bool can_move_window = true; if ((window->Flags & ImGuiWindowFlags_NoMove) || (window->RootWindowDockTree->Flags & ImGuiWindowFlags_NoMove)) @@ -3665,8 +3666,8 @@ void ImGui::UpdateMouseMovingWindowNewFrame() // Clear the NoInput window flag set by the Viewport system moving_window->Viewport->Flags &= ~ImGuiViewportFlags_NoInputs; // FIXME-VIEWPORT: Test engine managed to crash here because Viewport was NULL. - ClearActiveID(); g.MovingWindow = NULL; + ClearActiveID(); } } else @@ -5213,6 +5214,16 @@ void ImGui::SetItemUsingMouseWheel() g.ActiveIdUsingMouseWheel = true; } +void ImGui::SetActiveIdUsingNavAndKeys() +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(g.ActiveId != 0); + g.ActiveIdUsingNavDirMask = ~(ImU32)0; + g.ActiveIdUsingNavInputMask = ~(ImU32)0; + g.ActiveIdUsingKeyInputMask = ~(ImU64)0; + NavMoveRequestCancel(); +} + ImVec2 ImGui::GetItemRectMin() { ImGuiWindow* window = GetCurrentWindowRead(); @@ -10571,10 +10582,8 @@ bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags) source_parent_id = window->IDStack.back(); source_drag_active = IsMouseDragging(mouse_button); - // Disable navigation and key inputs while dragging - g.ActiveIdUsingNavDirMask = ~(ImU32)0; - g.ActiveIdUsingNavInputMask = ~(ImU32)0; - g.ActiveIdUsingKeyInputMask = ~(ImU64)0; + // Disable navigation and key inputs while dragging + cancel existing request if any + SetActiveIdUsingNavAndKeys(); } else { @@ -16524,6 +16533,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) Indent(); Text("ActiveId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d, Source: %s", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdTimer, g.ActiveIdAllowOverlap, input_source_names[g.ActiveIdSource]); Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL"); + Text("ActiveIdUsing: Wheel: %d, NavDirMask: %X, NavInputMask: %X, KeyInputMask: %X", g.ActiveIdUsingMouseWheel, g.ActiveIdUsingNavDirMask, g.ActiveIdUsingNavInputMask, g.ActiveIdUsingKeyInputMask); Text("HoveredId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d", g.HoveredId, g.HoveredIdPreviousFrame, g.HoveredIdTimer, g.HoveredIdAllowOverlap); // Data is "in-flight" so depending on when the Metrics window is called we may see current frame information or not Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize); Unindent(); diff --git a/imgui_internal.h b/imgui_internal.h index b4177ddfdfb9..b7b28bd6a27e 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2665,6 +2665,7 @@ namespace ImGui // Inputs // FIXME: Eventually we should aim to move e.g. IsActiveIdUsingKey() into IsKeyXXX functions. IMGUI_API void SetItemUsingMouseWheel(); + IMGUI_API void SetActiveIdUsingNavAndKeys(); inline bool IsActiveIdUsingNavDir(ImGuiDir dir) { ImGuiContext& g = *GImGui; return (g.ActiveIdUsingNavDirMask & (1 << dir)) != 0; } inline bool IsActiveIdUsingNavInput(ImGuiNavInput input) { ImGuiContext& g = *GImGui; return (g.ActiveIdUsingNavInputMask & (1 << input)) != 0; } inline bool IsActiveIdUsingKey(ImGuiKey key) { ImGuiContext& g = *GImGui; IM_ASSERT(key < 64); return (g.ActiveIdUsingKeyInputMask & ((ImU64)1 << key)) != 0; } diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 61e48883c4de..ef0cd287ce6c 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7987,11 +7987,13 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, if (undocking_tab) { // Undock + // FIXME: refactor to share more code with e.g. StartMouseMovingWindow DockContextQueueUndockWindow(&g, docked_window); g.MovingWindow = docked_window; SetActiveID(g.MovingWindow->MoveId, g.MovingWindow); g.ActiveIdClickOffset -= g.MovingWindow->Pos - bb.Min; g.ActiveIdNoClearOnFocusLoss = true; + SetActiveIdUsingNavAndKeys(); } } } From 90deb0959ab3a15f639c92ddc61505a8915de75f Mon Sep 17 00:00:00 2001 From: Michel Lesoinne Date: Mon, 7 Jun 2021 20:09:49 -0600 Subject: [PATCH 716/828] Backends, Viewports: Vulkan: Fix the use of the incorrect fence in wait for fence. (#4208) The fence being waited upon was not the one associated with the current frame. This results in validation error detecting a reset of command buffers still in use and resetting fences while still in use. Read more details in #4208 --- backends/imgui_impl_vulkan.cpp | 10 +++++----- docs/CHANGELOG.txt | 1 + 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp index 888a52d328c1..0ad2f3bf7f35 100644 --- a/backends/imgui_impl_vulkan.cpp +++ b/backends/imgui_impl_vulkan.cpp @@ -1551,6 +1551,11 @@ static void ImGui_ImplVulkan_RenderWindow(ImGuiViewport* viewport, void*) ImGui_ImplVulkanH_Frame* fd = &wd->Frames[wd->FrameIndex]; ImGui_ImplVulkanH_FrameSemaphores* fsd = &wd->FrameSemaphores[wd->SemaphoreIndex]; { + { + err = vkAcquireNextImageKHR(v->Device, wd->Swapchain, UINT64_MAX, fsd->ImageAcquiredSemaphore, VK_NULL_HANDLE, &wd->FrameIndex); + check_vk_result(err); + fd = &wd->Frames[wd->FrameIndex]; + } for (;;) { err = vkWaitForFences(v->Device, 1, &fd->Fence, VK_TRUE, 100); @@ -1558,11 +1563,6 @@ static void ImGui_ImplVulkan_RenderWindow(ImGuiViewport* viewport, void*) if (err == VK_TIMEOUT) continue; check_vk_result(err); } - { - err = vkAcquireNextImageKHR(v->Device, wd->Swapchain, UINT64_MAX, fsd->ImageAcquiredSemaphore, VK_NULL_HANDLE, &wd->FrameIndex); - check_vk_result(err); - fd = &wd->Frames[wd->FrameIndex]; - } { err = vkResetCommandPool(v->Device, fd->CommandPool, 0); check_vk_result(err); diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 652aa99ec3d8..5481052509be 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -126,6 +126,7 @@ Docking+Viewports Branch: - Docking: Clicking on the right-most close button of a docking node closes all windows. (#4186) - Docking: Fix IsWindowAppearing() and ImGuiCond_Appearing on docked windows. (#4177, #3982, #1497, #1061) +- Backends: Vulkan: Fix the use of the incorrect fence for secondary viewports. (#4208) [@FunMiles] ----------------------------------------------------------------------- From 9b417b26d93f421fea9cf110030ca3985717d130 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 14 Jun 2021 18:24:45 +0200 Subject: [PATCH 717/828] Metrics: Tentative fix for bad printf format. Ref b53b8f58dfbd4cb9f9b2a62e7a0a7e4500c491e9, a7a1b3b0a78f64e704c8e3a8b56c6c17bc19b306 --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 6bd0adc297f8..0d627090fbf0 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -16533,7 +16533,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) Indent(); Text("ActiveId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d, Source: %s", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdTimer, g.ActiveIdAllowOverlap, input_source_names[g.ActiveIdSource]); Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL"); - Text("ActiveIdUsing: Wheel: %d, NavDirMask: %X, NavInputMask: %X, KeyInputMask: %X", g.ActiveIdUsingMouseWheel, g.ActiveIdUsingNavDirMask, g.ActiveIdUsingNavInputMask, g.ActiveIdUsingKeyInputMask); + Text("ActiveIdUsing: Wheel: %d, NavDirMask: %X, NavInputMask: %X, KeyInputMask: %llX", g.ActiveIdUsingMouseWheel, g.ActiveIdUsingNavDirMask, g.ActiveIdUsingNavInputMask, g.ActiveIdUsingKeyInputMask); Text("HoveredId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d", g.HoveredId, g.HoveredIdPreviousFrame, g.HoveredIdTimer, g.HoveredIdAllowOverlap); // Data is "in-flight" so depending on when the Metrics window is called we may see current frame information or not Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize); Unindent(); From afabb2f3d80ff13f21012dfdfbccfcb15e4c82a1 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 17 Jun 2021 16:12:22 +0200 Subject: [PATCH 718/828] Viewport: extracted code out of Begin() into WindowSyncOwnedViewport() - no other change --- imgui.cpp | 173 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 91 insertions(+), 82 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 0d627090fbf0..552b361e339d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -946,7 +946,8 @@ const ImGuiID IMGUI_VIEWPORT_DEFAULT_ID = 0x11111111; // Using an arbi static ImGuiViewportP* AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const ImVec2& platform_pos, const ImVec2& size, ImGuiViewportFlags flags); static void UpdateViewportsNewFrame(); static void UpdateViewportsEndFrame(); -static void UpdateSelectWindowViewport(ImGuiWindow* window); +static void WindowSelectViewport(ImGuiWindow* window); +static void WindowSyncOwnedViewport(ImGuiWindow* window, ImGuiWindow* parent_window_in_stack); static bool UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* host_viewport); static bool UpdateTryMergeWindowIntoHostViewports(ImGuiWindow* window); static bool GetWindowAlwaysWantOwnViewport(ImGuiWindow* window); @@ -6315,7 +6316,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // SELECT VIEWPORT // We need to do this before using any style/font sizes, as viewport with a different DPI may affect font sizes. - UpdateSelectWindowViewport(window); + WindowSelectViewport(window); SetCurrentViewport(window, window->Viewport); window->FontDpiScale = (g.IO.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleFonts) ? window->Viewport->DpiScale : 1.0f; SetCurrentWindow(window); @@ -6447,85 +6448,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) SetCurrentWindow(window); } - bool viewport_rect_changed = false; if (window->ViewportOwned) - { - // Synchronize window --> viewport in most situations - // Synchronize viewport -> window in case the platform window has been moved or resized from the OS/WM - if (window->Viewport->PlatformRequestMove) - { - window->Pos = window->Viewport->Pos; - MarkIniSettingsDirty(window); - } - else if (memcmp(&window->Viewport->Pos, &window->Pos, sizeof(window->Pos)) != 0) - { - viewport_rect_changed = true; - window->Viewport->Pos = window->Pos; - } - - if (window->Viewport->PlatformRequestResize) - { - window->Size = window->SizeFull = window->Viewport->Size; - MarkIniSettingsDirty(window); - } - else if (memcmp(&window->Viewport->Size, &window->Size, sizeof(window->Size)) != 0) - { - viewport_rect_changed = true; - window->Viewport->Size = window->Size; - } - window->Viewport->UpdateWorkRect(); - - // The viewport may have changed monitor since the global update in UpdateViewportsNewFrame() - // Either a SetNextWindowPos() call in the current frame or a SetWindowPos() call in the previous frame may have this effect. - if (viewport_rect_changed) - UpdateViewportPlatformMonitor(window->Viewport); - - // Update common viewport flags - const ImGuiViewportFlags viewport_flags_to_clear = ImGuiViewportFlags_TopMost | ImGuiViewportFlags_NoTaskBarIcon | ImGuiViewportFlags_NoDecoration | ImGuiViewportFlags_NoRendererClear; - ImGuiViewportFlags viewport_flags = window->Viewport->Flags & ~viewport_flags_to_clear; - const bool is_modal = (flags & ImGuiWindowFlags_Modal) != 0; - const bool is_short_lived_floating_window = (flags & (ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) != 0; - if (flags & ImGuiWindowFlags_Tooltip) - viewport_flags |= ImGuiViewportFlags_TopMost; - if ((g.IO.ConfigViewportsNoTaskBarIcon || is_short_lived_floating_window) && !is_modal) - viewport_flags |= ImGuiViewportFlags_NoTaskBarIcon; - if (g.IO.ConfigViewportsNoDecoration || is_short_lived_floating_window) - viewport_flags |= ImGuiViewportFlags_NoDecoration; - - // Not correct to set modal as topmost because: - // - Because other popups can be stacked above a modal (e.g. combo box in a modal) - // - ImGuiViewportFlags_TopMost is currently handled different in backends: in Win32 it is "appear top most" whereas in GLFW and SDL it is "stay topmost" - //if (flags & ImGuiWindowFlags_Modal) - // viewport_flags |= ImGuiViewportFlags_TopMost; - - // For popups and menus that may be protruding out of their parent viewport, we enable _NoFocusOnClick so that clicking on them - // won't steal the OS focus away from their parent window (which may be reflected in OS the title bar decoration). - // Setting _NoFocusOnClick would technically prevent us from bringing back to front in case they are being covered by an OS window from a different app, - // but it shouldn't be much of a problem considering those are already popups that are closed when clicking elsewhere. - if (is_short_lived_floating_window && !is_modal) - viewport_flags |= ImGuiViewportFlags_NoFocusOnAppearing | ImGuiViewportFlags_NoFocusOnClick; - - // We can overwrite viewport flags using ImGuiWindowClass (advanced users) - // We don't default to the main viewport because. - if (window->WindowClass.ParentViewportId) - window->Viewport->ParentViewportId = window->WindowClass.ParentViewportId; - else if ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && parent_window_in_stack) - window->Viewport->ParentViewportId = parent_window_in_stack->Viewport->ID; - else - window->Viewport->ParentViewportId = g.IO.ConfigViewportsNoDefaultParent ? 0 : IMGUI_VIEWPORT_DEFAULT_ID; - if (window->WindowClass.ViewportFlagsOverrideSet) - viewport_flags |= window->WindowClass.ViewportFlagsOverrideSet; - if (window->WindowClass.ViewportFlagsOverrideClear) - viewport_flags &= ~window->WindowClass.ViewportFlagsOverrideClear; - - // We can also tell the backend that clearing the platform window won't be necessary, - // as our window background is filling the viewport and we have disabled BgAlpha. - // FIXME: Work on support for per-viewport transparency (#2766) - if (!(flags & ImGuiWindowFlags_NoBackground)) - viewport_flags |= ImGuiViewportFlags_NoRendererClear; - - window->Viewport->Flags = viewport_flags; - } + WindowSyncOwnedViewport(window, parent_window_in_stack); // Calculate the range of allowed position for that window (to be movable and visible past safe area padding) // When clamping to stay visible, we will enforce that window->Pos stays inside of visibility_rect. @@ -11409,7 +11333,8 @@ static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandl // - UpdateViewportsNewFrame() [Internal] // - UpdateViewportsEndFrame() [Internal] // - AddUpdateViewport() [Internal] -// - UpdateSelectWindowViewport() [Internal] +// - WindowSelectViewport() [Internal] +// - WindowSyncOwnedViewport() [Internal] // - UpdatePlatformWindows() // - RenderPlatformWindowsDefault() // - FindPlatformMonitorForPos() [Internal] @@ -11425,6 +11350,7 @@ ImGuiViewport* ImGui::GetMainViewport() return g.Viewports[0]; } +// FIXME: This leaks access to viewports not listed in PlatformIO.Viewports[]. Problematic? (#4236) ImGuiViewport* ImGui::FindViewportByID(ImGuiID id) { ImGuiContext& g = *GImGui; @@ -11856,7 +11782,7 @@ ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const } // FIXME-VIEWPORT: This is all super messy and ought to be clarified or rewritten. -static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) +static void ImGui::WindowSelectViewport(ImGuiWindow* window) { ImGuiContext& g = *GImGui; ImGuiWindowFlags flags = window->Flags; @@ -11983,6 +11909,89 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) // window->Flags |= ImGuiWindowFlags_NoTitleBar; } +void ImGui::WindowSyncOwnedViewport(ImGuiWindow* window, ImGuiWindow* parent_window_in_stack) +{ + ImGuiContext& g = *GImGui; + + bool viewport_rect_changed = false; + + // Synchronize window --> viewport in most situations + // Synchronize viewport -> window in case the platform window has been moved or resized from the OS/WM + if (window->Viewport->PlatformRequestMove) + { + window->Pos = window->Viewport->Pos; + MarkIniSettingsDirty(window); + } + else if (memcmp(&window->Viewport->Pos, &window->Pos, sizeof(window->Pos)) != 0) + { + viewport_rect_changed = true; + window->Viewport->Pos = window->Pos; + } + + if (window->Viewport->PlatformRequestResize) + { + window->Size = window->SizeFull = window->Viewport->Size; + MarkIniSettingsDirty(window); + } + else if (memcmp(&window->Viewport->Size, &window->Size, sizeof(window->Size)) != 0) + { + viewport_rect_changed = true; + window->Viewport->Size = window->Size; + } + window->Viewport->UpdateWorkRect(); + + // The viewport may have changed monitor since the global update in UpdateViewportsNewFrame() + // Either a SetNextWindowPos() call in the current frame or a SetWindowPos() call in the previous frame may have this effect. + if (viewport_rect_changed) + UpdateViewportPlatformMonitor(window->Viewport); + + // Update common viewport flags + const ImGuiViewportFlags viewport_flags_to_clear = ImGuiViewportFlags_TopMost | ImGuiViewportFlags_NoTaskBarIcon | ImGuiViewportFlags_NoDecoration | ImGuiViewportFlags_NoRendererClear; + ImGuiViewportFlags viewport_flags = window->Viewport->Flags & ~viewport_flags_to_clear; + ImGuiWindowFlags window_flags = window->Flags; + const bool is_modal = (window_flags & ImGuiWindowFlags_Modal) != 0; + const bool is_short_lived_floating_window = (window_flags & (ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) != 0; + if (window_flags & ImGuiWindowFlags_Tooltip) + viewport_flags |= ImGuiViewportFlags_TopMost; + if ((g.IO.ConfigViewportsNoTaskBarIcon || is_short_lived_floating_window) && !is_modal) + viewport_flags |= ImGuiViewportFlags_NoTaskBarIcon; + if (g.IO.ConfigViewportsNoDecoration || is_short_lived_floating_window) + viewport_flags |= ImGuiViewportFlags_NoDecoration; + + // Not correct to set modal as topmost because: + // - Because other popups can be stacked above a modal (e.g. combo box in a modal) + // - ImGuiViewportFlags_TopMost is currently handled different in backends: in Win32 it is "appear top most" whereas in GLFW and SDL it is "stay topmost" + //if (flags & ImGuiWindowFlags_Modal) + // viewport_flags |= ImGuiViewportFlags_TopMost; + + // For popups and menus that may be protruding out of their parent viewport, we enable _NoFocusOnClick so that clicking on them + // won't steal the OS focus away from their parent window (which may be reflected in OS the title bar decoration). + // Setting _NoFocusOnClick would technically prevent us from bringing back to front in case they are being covered by an OS window from a different app, + // but it shouldn't be much of a problem considering those are already popups that are closed when clicking elsewhere. + if (is_short_lived_floating_window && !is_modal) + viewport_flags |= ImGuiViewportFlags_NoFocusOnAppearing | ImGuiViewportFlags_NoFocusOnClick; + + // We can overwrite viewport flags using ImGuiWindowClass (advanced users) + if (window->WindowClass.ParentViewportId) + window->Viewport->ParentViewportId = window->WindowClass.ParentViewportId; + else if ((window_flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && parent_window_in_stack) + window->Viewport->ParentViewportId = parent_window_in_stack->Viewport->ID; + else + window->Viewport->ParentViewportId = g.IO.ConfigViewportsNoDefaultParent ? 0 : IMGUI_VIEWPORT_DEFAULT_ID; + if (window->WindowClass.ViewportFlagsOverrideSet) + viewport_flags |= window->WindowClass.ViewportFlagsOverrideSet; + if (window->WindowClass.ViewportFlagsOverrideClear) + viewport_flags &= ~window->WindowClass.ViewportFlagsOverrideClear; + + // We can also tell the backend that clearing the platform window won't be necessary, + // as our window background is filling the viewport and we have disabled BgAlpha. + // FIXME: Work on support for per-viewport transparency (#2766) + if (!(window_flags & ImGuiWindowFlags_NoBackground)) + viewport_flags |= ImGuiViewportFlags_NoRendererClear; + + window->Viewport->Flags = viewport_flags; +} + // Called by user at the end of the main loop, after EndFrame() // This will handle the creation/update of all OS windows via function defined in the ImGuiPlatformIO api. void ImGui::UpdatePlatformWindows() From cce307a2be4d26c34cce7d4844381e18cc49f656 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 17 Jun 2021 16:22:14 +0200 Subject: [PATCH 719/828] Viewports: Fix popup/tooltip created without a parent window from being given a ParentViewportId value of the implicit/fallback window. (#4236, #2409) Amend 3ead9820 --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 17 ++++++++++------- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 5481052509be..49ec2864311d 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -126,6 +126,8 @@ Docking+Viewports Branch: - Docking: Clicking on the right-most close button of a docking node closes all windows. (#4186) - Docking: Fix IsWindowAppearing() and ImGuiCond_Appearing on docked windows. (#4177, #3982, #1497, #1061) +- Viewports: Fix popup/tooltip created without a parent window from being given a ParentViewportId value + from the implicit/fallback window. (#4236, #2409) - Backends: Vulkan: Fix the use of the incorrect fence for secondary viewports. (#4208) [@FunMiles] diff --git a/imgui.cpp b/imgui.cpp index 552b361e339d..9d208c3607ba 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11807,7 +11807,7 @@ static void ImGui::WindowSelectViewport(ImGuiWindow* window) if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasViewport) == 0) { // By default inherit from parent window - if (window->Viewport == NULL && window->ParentWindow && !window->ParentWindow->IsFallbackWindow) + if (window->Viewport == NULL && window->ParentWindow && (!window->ParentWindow->IsFallbackWindow || window->ParentWindow->WasActive)) window->Viewport = window->ParentWindow->Viewport; // Attempt to restore saved viewport id (= window that hasn't been activated yet), try to restore the viewport based on saved 'window->ViewportPos' restored from .ini file @@ -11972,12 +11972,6 @@ void ImGui::WindowSyncOwnedViewport(ImGuiWindow* window, ImGuiWindow* parent_win viewport_flags |= ImGuiViewportFlags_NoFocusOnAppearing | ImGuiViewportFlags_NoFocusOnClick; // We can overwrite viewport flags using ImGuiWindowClass (advanced users) - if (window->WindowClass.ParentViewportId) - window->Viewport->ParentViewportId = window->WindowClass.ParentViewportId; - else if ((window_flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && parent_window_in_stack) - window->Viewport->ParentViewportId = parent_window_in_stack->Viewport->ID; - else - window->Viewport->ParentViewportId = g.IO.ConfigViewportsNoDefaultParent ? 0 : IMGUI_VIEWPORT_DEFAULT_ID; if (window->WindowClass.ViewportFlagsOverrideSet) viewport_flags |= window->WindowClass.ViewportFlagsOverrideSet; if (window->WindowClass.ViewportFlagsOverrideClear) @@ -11990,6 +11984,15 @@ void ImGui::WindowSyncOwnedViewport(ImGuiWindow* window, ImGuiWindow* parent_win viewport_flags |= ImGuiViewportFlags_NoRendererClear; window->Viewport->Flags = viewport_flags; + + // Update parent viewport ID + // (the !IsFallbackWindow test mimic the one done in WindowSelectViewport()) + if (window->WindowClass.ParentViewportId) + window->Viewport->ParentViewportId = window->WindowClass.ParentViewportId; + else if ((window_flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && parent_window_in_stack && (!parent_window_in_stack->IsFallbackWindow || parent_window_in_stack->WasActive)) + window->Viewport->ParentViewportId = parent_window_in_stack->Viewport->ID; + else + window->Viewport->ParentViewportId = g.IO.ConfigViewportsNoDefaultParent ? 0 : IMGUI_VIEWPORT_DEFAULT_ID; } // Called by user at the end of the main loop, after EndFrame() From 0f7eb00f6728aa677b31b158d0886ee4dc5011e2 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 29 Jun 2021 14:35:30 +0200 Subject: [PATCH 720/828] Backends: amends to 1db1066 + merge minor bits from docking incl SetActiveIdUsingNavAndKeys(). No need to clear fields before deletion. DX12: renamed to match docking branch. --- backends/imgui_impl_dx12.cpp | 1 + backends/imgui_impl_glfw.cpp | 4 ---- backends/imgui_impl_opengl3.cpp | 7 +++---- imgui.cpp | 1 + imgui_widgets.cpp | 2 +- 5 files changed, 6 insertions(+), 9 deletions(-) diff --git a/backends/imgui_impl_dx12.cpp b/backends/imgui_impl_dx12.cpp index 8b00e19d6746..5a984a2b35c7 100644 --- a/backends/imgui_impl_dx12.cpp +++ b/backends/imgui_impl_dx12.cpp @@ -179,6 +179,7 @@ struct VERTEX_CONSTANT_BUFFER static void ImGui_ImplDX12_InitPlatformInterface(); static void ImGui_ImplDX12_ShutdownPlatformInterface(); +// Functions static void ImGui_ImplDX12_SetupRenderState(ImDrawData* draw_data, ID3D12GraphicsCommandList* ctx, ImGui_ImplDX12_RenderBuffers* fr) { ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index 14410a915c60..c20a3d6b324d 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -326,14 +326,10 @@ void ImGui_ImplGlfw_Shutdown() glfwSetScrollCallback(bd->Window, bd->PrevUserCallbackScroll); glfwSetKeyCallback(bd->Window, bd->PrevUserCallbackKey); glfwSetCharCallback(bd->Window, bd->PrevUserCallbackChar); - bd->InstalledCallbacks = false; } for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++) - { glfwDestroyCursor(bd->MouseCursors[cursor_n]); - bd->MouseCursors[cursor_n] = NULL; - } io.BackendPlatformName = NULL; io.BackendPlatformUserData = NULL; diff --git a/backends/imgui_impl_opengl3.cpp b/backends/imgui_impl_opengl3.cpp index 17445b737845..438b752fb1ac 100644 --- a/backends/imgui_impl_opengl3.cpp +++ b/backends/imgui_impl_opengl3.cpp @@ -237,19 +237,18 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version) // Store GLSL version string so we can refer to it later in case we recreate shaders. // Note: GLSL version is NOT the same as GL version. Leave this to NULL if unsure. -#if defined(IMGUI_IMPL_OPENGL_ES2) if (glsl_version == NULL) + { +#if defined(IMGUI_IMPL_OPENGL_ES2) glsl_version = "#version 100"; #elif defined(IMGUI_IMPL_OPENGL_ES3) - if (glsl_version == NULL) glsl_version = "#version 300 es"; #elif defined(__APPLE__) - if (glsl_version == NULL) glsl_version = "#version 150"; #else - if (glsl_version == NULL) glsl_version = "#version 130"; #endif + } IM_ASSERT((int)strlen(glsl_version) + 2 < IM_ARRAYSIZE(bd->GlslVersionString)); strcpy(bd->GlslVersionString, glsl_version); strcat(bd->GlslVersionString, "\n"); diff --git a/imgui.cpp b/imgui.cpp index 5ca1e9e73a39..c05c486c92e6 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7170,6 +7170,7 @@ void ImGui::PopTextWrapPos() window->DC.TextWrapPosStack.pop_back(); } +// FIXME: We are exposing the docking hierarchy to end-user here (via IsWindowHovered, IsWindowFocused) which is unusual. bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent) { if (window->RootWindowDockTree == potential_parent) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 272160d80272..f48b0b11f53e 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7991,7 +7991,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, // Click to Select a tab ImGuiButtonFlags button_flags = ((is_tab_button ? ImGuiButtonFlags_PressedOnClickRelease : ImGuiButtonFlags_PressedOnClick) | ImGuiButtonFlags_AllowItemOverlap); - if (g.DragDropActive && !g.DragDropPayload.IsDataType(IMGUI_PAYLOAD_TYPE_WINDOW)) + if (g.DragDropActive && !g.DragDropPayload.IsDataType(IMGUI_PAYLOAD_TYPE_WINDOW)) // FIXME: May be an opt-in property of the payload to disable this button_flags |= ImGuiButtonFlags_PressedOnDragDropHold; bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags); From 36a0d1028ca1d741d0e31a94e163d62f6c8d0345 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 29 Jun 2021 15:34:54 +0200 Subject: [PATCH 721/828] Backends: Viewports: renamed viewport storage structures ImGuiViewportDataXXXX -> ImGui_ImplXXXX_ViewportData and locals (matching naming convention in 70c60385) --- backends/imgui_impl_dx10.cpp | 66 +++++++-------- backends/imgui_impl_dx11.cpp | 66 +++++++-------- backends/imgui_impl_dx12.cpp | 146 ++++++++++++++++---------------- backends/imgui_impl_dx9.cpp | 70 ++++++++-------- backends/imgui_impl_glfw.cpp | 130 ++++++++++++++--------------- backends/imgui_impl_sdl.cpp | 112 ++++++++++++------------- backends/imgui_impl_vulkan.cpp | 56 ++++++------- backends/imgui_impl_vulkan.h | 2 +- backends/imgui_impl_win32.cpp | 148 ++++++++++++++++----------------- 9 files changed, 398 insertions(+), 398 deletions(-) diff --git a/backends/imgui_impl_dx10.cpp b/backends/imgui_impl_dx10.cpp index ca8cb5da27ec..973d2aa31e9f 100644 --- a/backends/imgui_impl_dx10.cpp +++ b/backends/imgui_impl_dx10.cpp @@ -584,20 +584,20 @@ void ImGui_ImplDX10_NewFrame() //-------------------------------------------------------------------------------------------------------- // Helper structure we store in the void* RenderUserData field of each ImGuiViewport to easily retrieve our backend data. -struct ImGuiViewportDataDx10 +struct ImGui_ImplDX10_ViewportData { - IDXGISwapChain* SwapChain; - ID3D10RenderTargetView* RTView; + IDXGISwapChain* SwapChain; + ID3D10RenderTargetView* RTView; - ImGuiViewportDataDx10() { SwapChain = NULL; RTView = NULL; } - ~ImGuiViewportDataDx10() { IM_ASSERT(SwapChain == NULL && RTView == NULL); } + ImGui_ImplDX10_ViewportData() { SwapChain = NULL; RTView = NULL; } + ~ImGui_ImplDX10_ViewportData() { IM_ASSERT(SwapChain == NULL && RTView == NULL); } }; static void ImGui_ImplDX10_CreateWindow(ImGuiViewport* viewport) { ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData(); - ImGuiViewportDataDx10* data = IM_NEW(ImGuiViewportDataDx10)(); - viewport->RendererUserData = data; + ImGui_ImplDX10_ViewportData* vd = IM_NEW(ImGui_ImplDX10_ViewportData)(); + viewport->RendererUserData = vd; // PlatformHandleRaw should always be a HWND, whereas PlatformHandle might be a higher-level handle (e.g. GLFWWindow*, SDL_Window*). // Some backends will leave PlatformHandleRaw NULL, in which case we assume PlatformHandle will contain the HWND. @@ -619,15 +619,15 @@ static void ImGui_ImplDX10_CreateWindow(ImGuiViewport* viewport) sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; sd.Flags = 0; - IM_ASSERT(data->SwapChain == NULL && data->RTView == NULL); - bd->pFactory->CreateSwapChain(bd->pd3dDevice, &sd, &data->SwapChain); + IM_ASSERT(vd->SwapChain == NULL && vd->RTView == NULL); + bd->pFactory->CreateSwapChain(bd->pd3dDevice, &sd, &vd->SwapChain); // Create the render target - if (data->SwapChain) + if (vd->SwapChain) { ID3D10Texture2D* pBackBuffer; - data->SwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer)); - bd->pd3dDevice->CreateRenderTargetView(pBackBuffer, NULL, &data->RTView); + vd->SwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer)); + bd->pd3dDevice->CreateRenderTargetView(pBackBuffer, NULL, &vd->RTView); pBackBuffer->Release(); } } @@ -635,15 +635,15 @@ static void ImGui_ImplDX10_CreateWindow(ImGuiViewport* viewport) static void ImGui_ImplDX10_DestroyWindow(ImGuiViewport* viewport) { // The main viewport (owned by the application) will always have RendererUserData == NULL here since we didn't create the data for it. - if (ImGuiViewportDataDx10* data = (ImGuiViewportDataDx10*)viewport->RendererUserData) + if (ImGui_ImplDX10_ViewportData* vd = (ImGui_ImplDX10_ViewportData*)viewport->RendererUserData) { - if (data->SwapChain) - data->SwapChain->Release(); - data->SwapChain = NULL; - if (data->RTView) - data->RTView->Release(); - data->RTView = NULL; - IM_DELETE(data); + if (vd->SwapChain) + vd->SwapChain->Release(); + vd->SwapChain = NULL; + if (vd->RTView) + vd->RTView->Release(); + vd->RTView = NULL; + IM_DELETE(vd); } viewport->RendererUserData = NULL; } @@ -651,19 +651,19 @@ static void ImGui_ImplDX10_DestroyWindow(ImGuiViewport* viewport) static void ImGui_ImplDX10_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) { ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData(); - ImGuiViewportDataDx10* data = (ImGuiViewportDataDx10*)viewport->RendererUserData; - if (data->RTView) + ImGui_ImplDX10_ViewportData* vd = (ImGui_ImplDX10_ViewportData*)viewport->RendererUserData; + if (vd->RTView) { - data->RTView->Release(); - data->RTView = NULL; + vd->RTView->Release(); + vd->RTView = NULL; } - if (data->SwapChain) + if (vd->SwapChain) { ID3D10Texture2D* pBackBuffer = NULL; - data->SwapChain->ResizeBuffers(0, (UINT)size.x, (UINT)size.y, DXGI_FORMAT_UNKNOWN, 0); - data->SwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer)); + vd->SwapChain->ResizeBuffers(0, (UINT)size.x, (UINT)size.y, DXGI_FORMAT_UNKNOWN, 0); + vd->SwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer)); if (pBackBuffer == NULL) { fprintf(stderr, "ImGui_ImplDX10_SetWindowSize() failed creating buffers.\n"); return; } - bd->pd3dDevice->CreateRenderTargetView(pBackBuffer, NULL, &data->RTView); + bd->pd3dDevice->CreateRenderTargetView(pBackBuffer, NULL, &vd->RTView); pBackBuffer->Release(); } } @@ -671,18 +671,18 @@ static void ImGui_ImplDX10_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) static void ImGui_ImplDX10_RenderViewport(ImGuiViewport* viewport, void*) { ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData(); - ImGuiViewportDataDx10* data = (ImGuiViewportDataDx10*)viewport->RendererUserData; + ImGui_ImplDX10_ViewportData* vd = (ImGui_ImplDX10_ViewportData*)viewport->RendererUserData; ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); - bd->pd3dDevice->OMSetRenderTargets(1, &data->RTView, NULL); + bd->pd3dDevice->OMSetRenderTargets(1, &vd->RTView, NULL); if (!(viewport->Flags & ImGuiViewportFlags_NoRendererClear)) - bd->pd3dDevice->ClearRenderTargetView(data->RTView, (float*)&clear_color); + bd->pd3dDevice->ClearRenderTargetView(vd->RTView, (float*)&clear_color); ImGui_ImplDX10_RenderDrawData(viewport->DrawData); } static void ImGui_ImplDX10_SwapBuffers(ImGuiViewport* viewport, void*) { - ImGuiViewportDataDx10* data = (ImGuiViewportDataDx10*)viewport->RendererUserData; - data->SwapChain->Present(0, 0); // Present without vsync + ImGui_ImplDX10_ViewportData* vd = (ImGui_ImplDX10_ViewportData*)viewport->RendererUserData; + vd->SwapChain->Present(0, 0); // Present without vsync } void ImGui_ImplDX10_InitPlatformInterface() diff --git a/backends/imgui_impl_dx11.cpp b/backends/imgui_impl_dx11.cpp index daeeb483bc84..0aec8567acfe 100644 --- a/backends/imgui_impl_dx11.cpp +++ b/backends/imgui_impl_dx11.cpp @@ -601,20 +601,20 @@ void ImGui_ImplDX11_NewFrame() //-------------------------------------------------------------------------------------------------------- // Helper structure we store in the void* RenderUserData field of each ImGuiViewport to easily retrieve our backend data. -struct ImGuiViewportDataDx11 +struct ImGui_ImplDX11_ViewportData { - IDXGISwapChain* SwapChain; - ID3D11RenderTargetView* RTView; + IDXGISwapChain* SwapChain; + ID3D11RenderTargetView* RTView; - ImGuiViewportDataDx11() { SwapChain = NULL; RTView = NULL; } - ~ImGuiViewportDataDx11() { IM_ASSERT(SwapChain == NULL && RTView == NULL); } + ImGui_ImplDX11_ViewportData() { SwapChain = NULL; RTView = NULL; } + ~ImGui_ImplDX11_ViewportData() { IM_ASSERT(SwapChain == NULL && RTView == NULL); } }; static void ImGui_ImplDX11_CreateWindow(ImGuiViewport* viewport) { ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData(); - ImGuiViewportDataDx11* data = IM_NEW(ImGuiViewportDataDx11)(); - viewport->RendererUserData = data; + ImGui_ImplDX11_ViewportData* vd = IM_NEW(ImGui_ImplDX11_ViewportData)(); + viewport->RendererUserData = vd; // PlatformHandleRaw should always be a HWND, whereas PlatformHandle might be a higher-level handle (e.g. GLFWWindow*, SDL_Window*). // Some backend will leave PlatformHandleRaw NULL, in which case we assume PlatformHandle will contain the HWND. @@ -636,15 +636,15 @@ static void ImGui_ImplDX11_CreateWindow(ImGuiViewport* viewport) sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; sd.Flags = 0; - IM_ASSERT(data->SwapChain == NULL && data->RTView == NULL); - bd->pFactory->CreateSwapChain(bd->pd3dDevice, &sd, &data->SwapChain); + IM_ASSERT(vd->SwapChain == NULL && vd->RTView == NULL); + bd->pFactory->CreateSwapChain(bd->pd3dDevice, &sd, &vd->SwapChain); // Create the render target - if (data->SwapChain) + if (vd->SwapChain) { ID3D11Texture2D* pBackBuffer; - data->SwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer)); - bd->pd3dDevice->CreateRenderTargetView(pBackBuffer, NULL, &data->RTView); + vd->SwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer)); + bd->pd3dDevice->CreateRenderTargetView(pBackBuffer, NULL, &vd->RTView); pBackBuffer->Release(); } } @@ -652,15 +652,15 @@ static void ImGui_ImplDX11_CreateWindow(ImGuiViewport* viewport) static void ImGui_ImplDX11_DestroyWindow(ImGuiViewport* viewport) { // The main viewport (owned by the application) will always have RendererUserData == NULL since we didn't create the data for it. - if (ImGuiViewportDataDx11* data = (ImGuiViewportDataDx11*)viewport->RendererUserData) + if (ImGui_ImplDX11_ViewportData* vd = (ImGui_ImplDX11_ViewportData*)viewport->RendererUserData) { - if (data->SwapChain) - data->SwapChain->Release(); - data->SwapChain = NULL; - if (data->RTView) - data->RTView->Release(); - data->RTView = NULL; - IM_DELETE(data); + if (vd->SwapChain) + vd->SwapChain->Release(); + vd->SwapChain = NULL; + if (vd->RTView) + vd->RTView->Release(); + vd->RTView = NULL; + IM_DELETE(vd); } viewport->RendererUserData = NULL; } @@ -668,19 +668,19 @@ static void ImGui_ImplDX11_DestroyWindow(ImGuiViewport* viewport) static void ImGui_ImplDX11_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) { ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData(); - ImGuiViewportDataDx11* data = (ImGuiViewportDataDx11*)viewport->RendererUserData; - if (data->RTView) + ImGui_ImplDX11_ViewportData* vd = (ImGui_ImplDX11_ViewportData*)viewport->RendererUserData; + if (vd->RTView) { - data->RTView->Release(); - data->RTView = NULL; + vd->RTView->Release(); + vd->RTView = NULL; } - if (data->SwapChain) + if (vd->SwapChain) { ID3D11Texture2D* pBackBuffer = NULL; - data->SwapChain->ResizeBuffers(0, (UINT)size.x, (UINT)size.y, DXGI_FORMAT_UNKNOWN, 0); - data->SwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer)); + vd->SwapChain->ResizeBuffers(0, (UINT)size.x, (UINT)size.y, DXGI_FORMAT_UNKNOWN, 0); + vd->SwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer)); if (pBackBuffer == NULL) { fprintf(stderr, "ImGui_ImplDX11_SetWindowSize() failed creating buffers.\n"); return; } - bd->pd3dDevice->CreateRenderTargetView(pBackBuffer, NULL, &data->RTView); + bd->pd3dDevice->CreateRenderTargetView(pBackBuffer, NULL, &vd->RTView); pBackBuffer->Release(); } } @@ -688,18 +688,18 @@ static void ImGui_ImplDX11_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) static void ImGui_ImplDX11_RenderWindow(ImGuiViewport* viewport, void*) { ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData(); - ImGuiViewportDataDx11* data = (ImGuiViewportDataDx11*)viewport->RendererUserData; + ImGui_ImplDX11_ViewportData* vd = (ImGui_ImplDX11_ViewportData*)viewport->RendererUserData; ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); - bd->pd3dDeviceContext->OMSetRenderTargets(1, &data->RTView, NULL); + bd->pd3dDeviceContext->OMSetRenderTargets(1, &vd->RTView, NULL); if (!(viewport->Flags & ImGuiViewportFlags_NoRendererClear)) - bd->pd3dDeviceContext->ClearRenderTargetView(data->RTView, (float*)&clear_color); + bd->pd3dDeviceContext->ClearRenderTargetView(vd->RTView, (float*)&clear_color); ImGui_ImplDX11_RenderDrawData(viewport->DrawData); } static void ImGui_ImplDX11_SwapBuffers(ImGuiViewport* viewport, void*) { - ImGuiViewportDataDx11* data = (ImGuiViewportDataDx11*)viewport->RendererUserData; - data->SwapChain->Present(0, 0); // Present without vsync + ImGui_ImplDX11_ViewportData* vd = (ImGui_ImplDX11_ViewportData*)viewport->RendererUserData; + vd->SwapChain->Present(0, 0); // Present without vsync } static void ImGui_ImplDX11_InitPlatformInterface() diff --git a/backends/imgui_impl_dx12.cpp b/backends/imgui_impl_dx12.cpp index 5a984a2b35c7..fb6777e55bfd 100644 --- a/backends/imgui_impl_dx12.cpp +++ b/backends/imgui_impl_dx12.cpp @@ -93,7 +93,7 @@ struct ImGui_ImplDX12_FrameContext // Helper structure we store in the void* RendererUserData field of each ImGuiViewport to easily retrieve our backend data. // Main viewport created by application will only use the Resources field. // Secondary viewports created by this backend will use all the fields (including Window fields), -struct ImGuiViewportDataDx12 +struct ImGui_ImplDX12_ViewportData { // Window ID3D12CommandQueue* CommandQueue; @@ -110,7 +110,7 @@ struct ImGuiViewportDataDx12 UINT FrameIndex; ImGui_ImplDX12_RenderBuffers* FrameRenderBuffers; - ImGuiViewportDataDx12(UINT num_frames_in_flight) + ImGui_ImplDX12_ViewportData(UINT num_frames_in_flight) { CommandQueue = NULL; CommandList = NULL; @@ -136,7 +136,7 @@ struct ImGuiViewportDataDx12 FrameRenderBuffers[i].IndexBufferSize = 10000; } } - ~ImGuiViewportDataDx12() + ~ImGui_ImplDX12_ViewportData() { IM_ASSERT(CommandQueue == NULL && CommandList == NULL); IM_ASSERT(RtvDescHeap == NULL); @@ -245,9 +245,9 @@ void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandL return; ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); - ImGuiViewportDataDx12* render_data = (ImGuiViewportDataDx12*)draw_data->OwnerViewport->RendererUserData; - render_data->FrameIndex++; - ImGui_ImplDX12_RenderBuffers* fr = &render_data->FrameRenderBuffers[render_data->FrameIndex % bd->numFramesInFlight]; + ImGui_ImplDX12_ViewportData* vd = (ImGui_ImplDX12_ViewportData*)draw_data->OwnerViewport->RendererUserData; + vd->FrameIndex++; + ImGui_ImplDX12_RenderBuffers* fr = &vd->FrameRenderBuffers[vd->FrameIndex % bd->numFramesInFlight]; // Create and grow vertex/index buffers if needed if (fr->VertexBuffer == NULL || fr->VertexBufferSize < draw_data->TotalVtxCount) @@ -785,10 +785,10 @@ bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FO if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) ImGui_ImplDX12_InitPlatformInterface(); - // Create a dummy ImGuiViewportDataDx12 holder for the main viewport, + // Create a dummy ImGui_ImplDX12_ViewportData holder for the main viewport, // Since this is created and managed by the application, we will only use the ->Resources[] fields. ImGuiViewport* main_viewport = ImGui::GetMainViewport(); - main_viewport->RendererUserData = IM_NEW(ImGuiViewportDataDx12)(bd->numFramesInFlight); + main_viewport->RendererUserData = IM_NEW(ImGui_ImplDX12_ViewportData)(bd->numFramesInFlight); return true; } @@ -800,12 +800,12 @@ void ImGui_ImplDX12_Shutdown() // Manually delete main viewport render resources in-case we haven't initialized for viewports ImGuiViewport* main_viewport = ImGui::GetMainViewport(); - if (ImGuiViewportDataDx12* data = (ImGuiViewportDataDx12*)main_viewport->RendererUserData) + if (ImGui_ImplDX12_ViewportData* vd = (ImGui_ImplDX12_ViewportData*)main_viewport->RendererUserData) { // We could just call ImGui_ImplDX12_DestroyWindow(main_viewport) as a convenience but that would be misleading since we only use data->Resources[] for (UINT i = 0; i < bd->numFramesInFlight; i++) - ImGui_ImplDX12_DestroyRenderBuffers(&data->FrameRenderBuffers[i]); - IM_DELETE(data); + ImGui_ImplDX12_DestroyRenderBuffers(&vd->FrameRenderBuffers[i]); + IM_DELETE(vd); main_viewport->RendererUserData = NULL; } @@ -834,15 +834,15 @@ void ImGui_ImplDX12_NewFrame() static void ImGui_ImplDX12_CreateWindow(ImGuiViewport* viewport) { ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); - ImGuiViewportDataDx12* data = IM_NEW(ImGuiViewportDataDx12)(bd->numFramesInFlight); - viewport->RendererUserData = data; + ImGui_ImplDX12_ViewportData* vd = IM_NEW(ImGui_ImplDX12_ViewportData)(bd->numFramesInFlight); + viewport->RendererUserData = vd; // PlatformHandleRaw should always be a HWND, whereas PlatformHandle might be a higher-level handle (e.g. GLFWWindow*, SDL_Window*). // Some backends will leave PlatformHandleRaw NULL, in which case we assume PlatformHandle will contain the HWND. HWND hwnd = viewport->PlatformHandleRaw ? (HWND)viewport->PlatformHandleRaw : (HWND)viewport->PlatformHandle; IM_ASSERT(hwnd != 0); - data->FrameIndex = UINT_MAX; + vd->FrameIndex = UINT_MAX; // Create command queue. D3D12_COMMAND_QUEUE_DESC queue_desc = {}; @@ -850,27 +850,27 @@ static void ImGui_ImplDX12_CreateWindow(ImGuiViewport* viewport) queue_desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; HRESULT res = S_OK; - res = bd->pd3dDevice->CreateCommandQueue(&queue_desc, IID_PPV_ARGS(&data->CommandQueue)); + res = bd->pd3dDevice->CreateCommandQueue(&queue_desc, IID_PPV_ARGS(&vd->CommandQueue)); IM_ASSERT(res == S_OK); // Create command allocator. for (UINT i = 0; i < bd->numFramesInFlight; ++i) { - res = bd->pd3dDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&data->FrameCtx[i].CommandAllocator)); + res = bd->pd3dDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&vd->FrameCtx[i].CommandAllocator)); IM_ASSERT(res == S_OK); } // Create command list. - res = bd->pd3dDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, data->FrameCtx[0].CommandAllocator, NULL, IID_PPV_ARGS(&data->CommandList)); + res = bd->pd3dDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, vd->FrameCtx[0].CommandAllocator, NULL, IID_PPV_ARGS(&vd->CommandList)); IM_ASSERT(res == S_OK); - data->CommandList->Close(); + vd->CommandList->Close(); // Create fence. - res = bd->pd3dDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&data->Fence)); + res = bd->pd3dDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&vd->Fence)); IM_ASSERT(res == S_OK); - data->FenceEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - IM_ASSERT(data->FenceEvent != NULL); + vd->FenceEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + IM_ASSERT(vd->FenceEvent != NULL); // Create swap chain // FIXME-VIEWPORT: May want to copy/inherit swap chain settings from the user/application. @@ -893,18 +893,18 @@ static void ImGui_ImplDX12_CreateWindow(ImGuiViewport* viewport) IM_ASSERT(res == S_OK); IDXGISwapChain1* swap_chain = NULL; - res = dxgi_factory->CreateSwapChainForHwnd(data->CommandQueue, hwnd, &sd1, NULL, NULL, &swap_chain); + res = dxgi_factory->CreateSwapChainForHwnd(vd->CommandQueue, hwnd, &sd1, NULL, NULL, &swap_chain); IM_ASSERT(res == S_OK); dxgi_factory->Release(); // Or swapChain.As(&mSwapChain) - IM_ASSERT(data->SwapChain == NULL); - swap_chain->QueryInterface(IID_PPV_ARGS(&data->SwapChain)); + IM_ASSERT(vd->SwapChain == NULL); + swap_chain->QueryInterface(IID_PPV_ARGS(&vd->SwapChain)); swap_chain->Release(); // Create the render targets - if (data->SwapChain) + if (vd->SwapChain) { D3D12_DESCRIPTOR_HEAP_DESC desc = {}; desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; @@ -912,42 +912,42 @@ static void ImGui_ImplDX12_CreateWindow(ImGuiViewport* viewport) desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; desc.NodeMask = 1; - HRESULT hr = bd->pd3dDevice->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&data->RtvDescHeap)); + HRESULT hr = bd->pd3dDevice->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&vd->RtvDescHeap)); IM_ASSERT(hr == S_OK); SIZE_T rtv_descriptor_size = bd->pd3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV); - D3D12_CPU_DESCRIPTOR_HANDLE rtv_handle = data->RtvDescHeap->GetCPUDescriptorHandleForHeapStart(); + D3D12_CPU_DESCRIPTOR_HANDLE rtv_handle = vd->RtvDescHeap->GetCPUDescriptorHandleForHeapStart(); for (UINT i = 0; i < bd->numFramesInFlight; i++) { - data->FrameCtx[i].RenderTargetCpuDescriptors = rtv_handle; + vd->FrameCtx[i].RenderTargetCpuDescriptors = rtv_handle; rtv_handle.ptr += rtv_descriptor_size; } ID3D12Resource* back_buffer; for (UINT i = 0; i < bd->numFramesInFlight; i++) { - IM_ASSERT(data->FrameCtx[i].RenderTarget == NULL); - data->SwapChain->GetBuffer(i, IID_PPV_ARGS(&back_buffer)); - bd->pd3dDevice->CreateRenderTargetView(back_buffer, NULL, data->FrameCtx[i].RenderTargetCpuDescriptors); - data->FrameCtx[i].RenderTarget = back_buffer; + IM_ASSERT(vd->FrameCtx[i].RenderTarget == NULL); + vd->SwapChain->GetBuffer(i, IID_PPV_ARGS(&back_buffer)); + bd->pd3dDevice->CreateRenderTargetView(back_buffer, NULL, vd->FrameCtx[i].RenderTargetCpuDescriptors); + vd->FrameCtx[i].RenderTarget = back_buffer; } } for (UINT i = 0; i < bd->numFramesInFlight; i++) - ImGui_ImplDX12_DestroyRenderBuffers(&data->FrameRenderBuffers[i]); + ImGui_ImplDX12_DestroyRenderBuffers(&vd->FrameRenderBuffers[i]); } -static void ImGui_WaitForPendingOperations(ImGuiViewportDataDx12* data) +static void ImGui_WaitForPendingOperations(ImGui_ImplDX12_ViewportData* vd) { HRESULT hr = S_FALSE; - if (data && data->CommandQueue && data->Fence && data->FenceEvent) + if (vd && vd->CommandQueue && vd->Fence && vd->FenceEvent) { - hr = data->CommandQueue->Signal(data->Fence, ++data->FenceSignaledValue); + hr = vd->CommandQueue->Signal(vd->Fence, ++vd->FenceSignaledValue); IM_ASSERT(hr == S_OK); - ::WaitForSingleObject(data->FenceEvent, 0); // Reset any forgotten waits - hr = data->Fence->SetEventOnCompletion(data->FenceSignaledValue, data->FenceEvent); + ::WaitForSingleObject(vd->FenceEvent, 0); // Reset any forgotten waits + hr = vd->Fence->SetEventOnCompletion(vd->FenceSignaledValue, vd->FenceEvent); IM_ASSERT(hr == S_OK); - ::WaitForSingleObject(data->FenceEvent, INFINITE); + ::WaitForSingleObject(vd->FenceEvent, INFINITE); } } @@ -955,25 +955,25 @@ static void ImGui_ImplDX12_DestroyWindow(ImGuiViewport* viewport) { // The main viewport (owned by the application) will always have RendererUserData == NULL since we didn't create the data for it. ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); - if (ImGuiViewportDataDx12* data = (ImGuiViewportDataDx12*)viewport->RendererUserData) + if (ImGui_ImplDX12_ViewportData* vd = (ImGui_ImplDX12_ViewportData*)viewport->RendererUserData) { - ImGui_WaitForPendingOperations(data); + ImGui_WaitForPendingOperations(vd); - SafeRelease(data->CommandQueue); - SafeRelease(data->CommandList); - SafeRelease(data->SwapChain); - SafeRelease(data->RtvDescHeap); - SafeRelease(data->Fence); - ::CloseHandle(data->FenceEvent); - data->FenceEvent = NULL; + SafeRelease(vd->CommandQueue); + SafeRelease(vd->CommandList); + SafeRelease(vd->SwapChain); + SafeRelease(vd->RtvDescHeap); + SafeRelease(vd->Fence); + ::CloseHandle(vd->FenceEvent); + vd->FenceEvent = NULL; for (UINT i = 0; i < bd->numFramesInFlight; i++) { - SafeRelease(data->FrameCtx[i].RenderTarget); - SafeRelease(data->FrameCtx[i].CommandAllocator); - ImGui_ImplDX12_DestroyRenderBuffers(&data->FrameRenderBuffers[i]); + SafeRelease(vd->FrameCtx[i].RenderTarget); + SafeRelease(vd->FrameCtx[i].CommandAllocator); + ImGui_ImplDX12_DestroyRenderBuffers(&vd->FrameRenderBuffers[i]); } - IM_DELETE(data); + IM_DELETE(vd); } viewport->RendererUserData = NULL; } @@ -981,22 +981,22 @@ static void ImGui_ImplDX12_DestroyWindow(ImGuiViewport* viewport) static void ImGui_ImplDX12_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) { ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); - ImGuiViewportDataDx12* data = (ImGuiViewportDataDx12*)viewport->RendererUserData; + ImGui_ImplDX12_ViewportData* vd = (ImGui_ImplDX12_ViewportData*)viewport->RendererUserData; - ImGui_WaitForPendingOperations(data); + ImGui_WaitForPendingOperations(vd); for (UINT i = 0; i < bd->numFramesInFlight; i++) - SafeRelease(data->FrameCtx[i].RenderTarget); + SafeRelease(vd->FrameCtx[i].RenderTarget); - if (data->SwapChain) + if (vd->SwapChain) { ID3D12Resource* back_buffer = NULL; - data->SwapChain->ResizeBuffers(0, (UINT)size.x, (UINT)size.y, DXGI_FORMAT_UNKNOWN, 0); + vd->SwapChain->ResizeBuffers(0, (UINT)size.x, (UINT)size.y, DXGI_FORMAT_UNKNOWN, 0); for (UINT i = 0; i < bd->numFramesInFlight; i++) { - data->SwapChain->GetBuffer(i, IID_PPV_ARGS(&back_buffer)); - bd->pd3dDevice->CreateRenderTargetView(back_buffer, NULL, data->FrameCtx[i].RenderTargetCpuDescriptors); - data->FrameCtx[i].RenderTarget = back_buffer; + vd->SwapChain->GetBuffer(i, IID_PPV_ARGS(&back_buffer)); + bd->pd3dDevice->CreateRenderTargetView(back_buffer, NULL, vd->FrameCtx[i].RenderTargetCpuDescriptors); + vd->FrameCtx[i].RenderTarget = back_buffer; } } } @@ -1004,29 +1004,29 @@ static void ImGui_ImplDX12_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) static void ImGui_ImplDX12_RenderWindow(ImGuiViewport* viewport, void*) { ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); - ImGuiViewportDataDx12* data = (ImGuiViewportDataDx12*)viewport->RendererUserData; + ImGui_ImplDX12_ViewportData* vd = (ImGui_ImplDX12_ViewportData*)viewport->RendererUserData; - ImGui_ImplDX12_FrameContext* frame_context = &data->FrameCtx[data->FrameIndex % bd->numFramesInFlight]; - UINT back_buffer_idx = data->SwapChain->GetCurrentBackBufferIndex(); + ImGui_ImplDX12_FrameContext* frame_context = &vd->FrameCtx[vd->FrameIndex % bd->numFramesInFlight]; + UINT back_buffer_idx = vd->SwapChain->GetCurrentBackBufferIndex(); const ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); D3D12_RESOURCE_BARRIER barrier = {}; barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; - barrier.Transition.pResource = data->FrameCtx[back_buffer_idx].RenderTarget; + barrier.Transition.pResource = vd->FrameCtx[back_buffer_idx].RenderTarget; barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT; barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET; // Draw - ID3D12GraphicsCommandList* cmd_list = data->CommandList; + ID3D12GraphicsCommandList* cmd_list = vd->CommandList; frame_context->CommandAllocator->Reset(); cmd_list->Reset(frame_context->CommandAllocator, NULL); cmd_list->ResourceBarrier(1, &barrier); - cmd_list->OMSetRenderTargets(1, &data->FrameCtx[back_buffer_idx].RenderTargetCpuDescriptors, FALSE, NULL); + cmd_list->OMSetRenderTargets(1, &vd->FrameCtx[back_buffer_idx].RenderTargetCpuDescriptors, FALSE, NULL); if (!(viewport->Flags & ImGuiViewportFlags_NoRendererClear)) - cmd_list->ClearRenderTargetView(data->FrameCtx[back_buffer_idx].RenderTargetCpuDescriptors, (float*)&clear_color, 0, NULL); + cmd_list->ClearRenderTargetView(vd->FrameCtx[back_buffer_idx].RenderTargetCpuDescriptors, (float*)&clear_color, 0, NULL); cmd_list->SetDescriptorHeaps(1, &bd->pd3dSrvDescHeap); ImGui_ImplDX12_RenderDrawData(viewport->DrawData, cmd_list); @@ -1036,17 +1036,17 @@ static void ImGui_ImplDX12_RenderWindow(ImGuiViewport* viewport, void*) cmd_list->ResourceBarrier(1, &barrier); cmd_list->Close(); - data->CommandQueue->Wait(data->Fence, data->FenceSignaledValue); - data->CommandQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*)&cmd_list); - data->CommandQueue->Signal(data->Fence, ++data->FenceSignaledValue); + vd->CommandQueue->Wait(vd->Fence, vd->FenceSignaledValue); + vd->CommandQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*)&cmd_list); + vd->CommandQueue->Signal(vd->Fence, ++vd->FenceSignaledValue); } static void ImGui_ImplDX12_SwapBuffers(ImGuiViewport* viewport, void*) { - ImGuiViewportDataDx12* data = (ImGuiViewportDataDx12*)viewport->RendererUserData; + ImGui_ImplDX12_ViewportData* vd = (ImGui_ImplDX12_ViewportData*)viewport->RendererUserData; - data->SwapChain->Present(0, 0); - while (data->Fence->GetCompletedValue() < data->FenceSignaledValue) + vd->SwapChain->Present(0, 0); + while (vd->Fence->GetCompletedValue() < vd->FenceSignaledValue) ::SwitchToThread(); } diff --git a/backends/imgui_impl_dx9.cpp b/backends/imgui_impl_dx9.cpp index 98620c098f85..ac13c083cef9 100644 --- a/backends/imgui_impl_dx9.cpp +++ b/backends/imgui_impl_dx9.cpp @@ -393,52 +393,52 @@ void ImGui_ImplDX9_NewFrame() //-------------------------------------------------------------------------------------------------------- // Helper structure we store in the void* RenderUserData field of each ImGuiViewport to easily retrieve our backend data. -struct ImGuiViewportDataDx9 +struct ImGui_ImplDX9_ViewportData { IDirect3DSwapChain9* SwapChain; D3DPRESENT_PARAMETERS d3dpp; - ImGuiViewportDataDx9() { SwapChain = NULL; ZeroMemory(&d3dpp, sizeof(D3DPRESENT_PARAMETERS)); } - ~ImGuiViewportDataDx9() { IM_ASSERT(SwapChain == NULL); } + ImGui_ImplDX9_ViewportData() { SwapChain = NULL; ZeroMemory(&d3dpp, sizeof(D3DPRESENT_PARAMETERS)); } + ~ImGui_ImplDX9_ViewportData() { IM_ASSERT(SwapChain == NULL); } }; static void ImGui_ImplDX9_CreateWindow(ImGuiViewport* viewport) { ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData(); - ImGuiViewportDataDx9* data = IM_NEW(ImGuiViewportDataDx9)(); - viewport->RendererUserData = data; + ImGui_ImplDX9_ViewportData* vd = IM_NEW(ImGui_ImplDX9_ViewportData)(); + viewport->RendererUserData = vd; // PlatformHandleRaw should always be a HWND, whereas PlatformHandle might be a higher-level handle (e.g. GLFWWindow*, SDL_Window*). // Some backends will leave PlatformHandleRaw NULL, in which case we assume PlatformHandle will contain the HWND. HWND hwnd = viewport->PlatformHandleRaw ? (HWND)viewport->PlatformHandleRaw : (HWND)viewport->PlatformHandle; IM_ASSERT(hwnd != 0); - ZeroMemory(&data->d3dpp, sizeof(D3DPRESENT_PARAMETERS)); - data->d3dpp.Windowed = TRUE; - data->d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; - data->d3dpp.BackBufferWidth = (UINT)viewport->Size.x; - data->d3dpp.BackBufferHeight = (UINT)viewport->Size.y; - data->d3dpp.BackBufferFormat = D3DFMT_UNKNOWN; - data->d3dpp.hDeviceWindow = hwnd; - data->d3dpp.EnableAutoDepthStencil = FALSE; - data->d3dpp.AutoDepthStencilFormat = D3DFMT_D16; - data->d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; // Present without vsync - - HRESULT hr = bd->pd3dDevice->CreateAdditionalSwapChain(&data->d3dpp, &data->SwapChain); IM_UNUSED(hr); + ZeroMemory(&vd->d3dpp, sizeof(D3DPRESENT_PARAMETERS)); + vd->d3dpp.Windowed = TRUE; + vd->d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; + vd->d3dpp.BackBufferWidth = (UINT)viewport->Size.x; + vd->d3dpp.BackBufferHeight = (UINT)viewport->Size.y; + vd->d3dpp.BackBufferFormat = D3DFMT_UNKNOWN; + vd->d3dpp.hDeviceWindow = hwnd; + vd->d3dpp.EnableAutoDepthStencil = FALSE; + vd->d3dpp.AutoDepthStencilFormat = D3DFMT_D16; + vd->d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; // Present without vsync + + HRESULT hr = bd->pd3dDevice->CreateAdditionalSwapChain(&vd->d3dpp, &vd->SwapChain); IM_UNUSED(hr); IM_ASSERT(hr == D3D_OK); - IM_ASSERT(data->SwapChain != NULL); + IM_ASSERT(vd->SwapChain != NULL); } static void ImGui_ImplDX9_DestroyWindow(ImGuiViewport* viewport) { // The main viewport (owned by the application) will always have RendererUserData == NULL since we didn't create the data for it. - if (ImGuiViewportDataDx9* data = (ImGuiViewportDataDx9*)viewport->RendererUserData) + if (ImGui_ImplDX9_ViewportData* vd = (ImGui_ImplDX9_ViewportData*)viewport->RendererUserData) { - if (data->SwapChain) - data->SwapChain->Release(); - data->SwapChain = NULL; - ZeroMemory(&data->d3dpp, sizeof(D3DPRESENT_PARAMETERS)); - IM_DELETE(data); + if (vd->SwapChain) + vd->SwapChain->Release(); + vd->SwapChain = NULL; + ZeroMemory(&vd->d3dpp, sizeof(D3DPRESENT_PARAMETERS)); + IM_DELETE(vd); } viewport->RendererUserData = NULL; } @@ -446,14 +446,14 @@ static void ImGui_ImplDX9_DestroyWindow(ImGuiViewport* viewport) static void ImGui_ImplDX9_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) { ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData(); - ImGuiViewportDataDx9* data = (ImGuiViewportDataDx9*)viewport->RendererUserData; - if (data->SwapChain) + ImGui_ImplDX9_ViewportData* vd = (ImGui_ImplDX9_ViewportData*)viewport->RendererUserData; + if (vd->SwapChain) { - data->SwapChain->Release(); - data->SwapChain = NULL; - data->d3dpp.BackBufferWidth = (UINT)size.x; - data->d3dpp.BackBufferHeight = (UINT)size.y; - HRESULT hr = bd->pd3dDevice->CreateAdditionalSwapChain(&data->d3dpp, &data->SwapChain); IM_UNUSED(hr); + vd->SwapChain->Release(); + vd->SwapChain = NULL; + vd->d3dpp.BackBufferWidth = (UINT)size.x; + vd->d3dpp.BackBufferHeight = (UINT)size.y; + HRESULT hr = bd->pd3dDevice->CreateAdditionalSwapChain(&vd->d3dpp, &vd->SwapChain); IM_UNUSED(hr); IM_ASSERT(hr == D3D_OK); } } @@ -461,13 +461,13 @@ static void ImGui_ImplDX9_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) static void ImGui_ImplDX9_RenderWindow(ImGuiViewport* viewport, void*) { ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData(); - ImGuiViewportDataDx9* data = (ImGuiViewportDataDx9*)viewport->RendererUserData; + ImGui_ImplDX9_ViewportData* vd = (ImGui_ImplDX9_ViewportData*)viewport->RendererUserData; ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); LPDIRECT3DSURFACE9 render_target = NULL; LPDIRECT3DSURFACE9 last_render_target = NULL; LPDIRECT3DSURFACE9 last_depth_stencil = NULL; - data->SwapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &render_target); + vd->SwapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &render_target); bd->pd3dDevice->GetRenderTarget(0, &last_render_target); bd->pd3dDevice->GetDepthStencilSurface(&last_depth_stencil); bd->pd3dDevice->SetRenderTarget(0, render_target); @@ -491,8 +491,8 @@ static void ImGui_ImplDX9_RenderWindow(ImGuiViewport* viewport, void*) static void ImGui_ImplDX9_SwapBuffers(ImGuiViewport* viewport, void*) { - ImGuiViewportDataDx9* data = (ImGuiViewportDataDx9*)viewport->RendererUserData; - HRESULT hr = data->SwapChain->Present(NULL, NULL, data->d3dpp.hDeviceWindow, NULL, 0); + ImGui_ImplDX9_ViewportData* vd = (ImGui_ImplDX9_ViewportData*)viewport->RendererUserData; + HRESULT hr = vd->SwapChain->Present(NULL, NULL, vd->d3dpp.hDeviceWindow, NULL, 0); // Let main application handle D3DERR_DEVICELOST by resetting the device. IM_ASSERT(hr == D3D_OK || hr == D3DERR_DEVICELOST); } diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index c20a3d6b324d..0a1712cc2ff3 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -545,15 +545,15 @@ void ImGui_ImplGlfw_NewFrame() //-------------------------------------------------------------------------------------------------------- // Helper structure we store in the void* RenderUserData field of each ImGuiViewport to easily retrieve our backend data. -struct ImGuiViewportDataGlfw +struct ImGui_ImplGlfw_ViewportData { GLFWwindow* Window; bool WindowOwned; int IgnoreWindowPosEventFrame; int IgnoreWindowSizeEventFrame; - ImGuiViewportDataGlfw() { Window = NULL; WindowOwned = false; IgnoreWindowSizeEventFrame = IgnoreWindowPosEventFrame = -1; } - ~ImGuiViewportDataGlfw() { IM_ASSERT(Window == NULL); } + ImGui_ImplGlfw_ViewportData() { Window = NULL; WindowOwned = false; IgnoreWindowSizeEventFrame = IgnoreWindowPosEventFrame = -1; } + ~ImGui_ImplGlfw_ViewportData() { IM_ASSERT(Window == NULL); } }; static void ImGui_ImplGlfw_WindowCloseCallback(GLFWwindow* window) @@ -572,9 +572,9 @@ static void ImGui_ImplGlfw_WindowPosCallback(GLFWwindow* window, int, int) { if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle(window)) { - if (ImGuiViewportDataGlfw* data = (ImGuiViewportDataGlfw*)viewport->PlatformUserData) + if (ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData) { - bool ignore_event = (ImGui::GetFrameCount() <= data->IgnoreWindowPosEventFrame + 1); + bool ignore_event = (ImGui::GetFrameCount() <= vd->IgnoreWindowPosEventFrame + 1); //data->IgnoreWindowPosEventFrame = -1; if (ignore_event) return; @@ -587,9 +587,9 @@ static void ImGui_ImplGlfw_WindowSizeCallback(GLFWwindow* window, int, int) { if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle(window)) { - if (ImGuiViewportDataGlfw* data = (ImGuiViewportDataGlfw*)viewport->PlatformUserData) + if (ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData) { - bool ignore_event = (ImGui::GetFrameCount() <= data->IgnoreWindowSizeEventFrame + 1); + bool ignore_event = (ImGui::GetFrameCount() <= vd->IgnoreWindowSizeEventFrame + 1); //data->IgnoreWindowSizeEventFrame = -1; if (ignore_event) return; @@ -601,8 +601,8 @@ static void ImGui_ImplGlfw_WindowSizeCallback(GLFWwindow* window, int, int) static void ImGui_ImplGlfw_CreateWindow(ImGuiViewport* viewport) { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); - ImGuiViewportDataGlfw* data = IM_NEW(ImGuiViewportDataGlfw)(); - viewport->PlatformUserData = data; + ImGui_ImplGlfw_ViewportData* vd = IM_NEW(ImGui_ImplGlfw_ViewportData)(); + viewport->PlatformUserData = vd; // GLFW 3.2 unfortunately always set focus on glfwCreateWindow() if GLFW_VISIBLE is set, regardless of GLFW_FOCUSED // With GLFW 3.3, the hint GLFW_FOCUS_ON_SHOW fixes this problem @@ -616,25 +616,25 @@ static void ImGui_ImplGlfw_CreateWindow(ImGuiViewport* viewport) glfwWindowHint(GLFW_FLOATING, (viewport->Flags & ImGuiViewportFlags_TopMost) ? true : false); #endif GLFWwindow* share_window = (bd->ClientApi == GlfwClientApi_OpenGL) ? bd->Window : NULL; - data->Window = glfwCreateWindow((int)viewport->Size.x, (int)viewport->Size.y, "No Title Yet", NULL, share_window); - data->WindowOwned = true; - viewport->PlatformHandle = (void*)data->Window; + vd->Window = glfwCreateWindow((int)viewport->Size.x, (int)viewport->Size.y, "No Title Yet", NULL, share_window); + vd->WindowOwned = true; + viewport->PlatformHandle = (void*)vd->Window; #ifdef _WIN32 - viewport->PlatformHandleRaw = glfwGetWin32Window(data->Window); + viewport->PlatformHandleRaw = glfwGetWin32Window(vd->Window); #endif - glfwSetWindowPos(data->Window, (int)viewport->Pos.x, (int)viewport->Pos.y); + glfwSetWindowPos(vd->Window, (int)viewport->Pos.x, (int)viewport->Pos.y); // Install GLFW callbacks for secondary viewports - glfwSetMouseButtonCallback(data->Window, ImGui_ImplGlfw_MouseButtonCallback); - glfwSetScrollCallback(data->Window, ImGui_ImplGlfw_ScrollCallback); - glfwSetKeyCallback(data->Window, ImGui_ImplGlfw_KeyCallback); - glfwSetCharCallback(data->Window, ImGui_ImplGlfw_CharCallback); - glfwSetWindowCloseCallback(data->Window, ImGui_ImplGlfw_WindowCloseCallback); - glfwSetWindowPosCallback(data->Window, ImGui_ImplGlfw_WindowPosCallback); - glfwSetWindowSizeCallback(data->Window, ImGui_ImplGlfw_WindowSizeCallback); + glfwSetMouseButtonCallback(vd->Window, ImGui_ImplGlfw_MouseButtonCallback); + glfwSetScrollCallback(vd->Window, ImGui_ImplGlfw_ScrollCallback); + glfwSetKeyCallback(vd->Window, ImGui_ImplGlfw_KeyCallback); + glfwSetCharCallback(vd->Window, ImGui_ImplGlfw_CharCallback); + glfwSetWindowCloseCallback(vd->Window, ImGui_ImplGlfw_WindowCloseCallback); + glfwSetWindowPosCallback(vd->Window, ImGui_ImplGlfw_WindowPosCallback); + glfwSetWindowSizeCallback(vd->Window, ImGui_ImplGlfw_WindowSizeCallback); if (bd->ClientApi == GlfwClientApi_OpenGL) { - glfwMakeContextCurrent(data->Window); + glfwMakeContextCurrent(vd->Window); glfwSwapInterval(0); } } @@ -642,9 +642,9 @@ static void ImGui_ImplGlfw_CreateWindow(ImGuiViewport* viewport) static void ImGui_ImplGlfw_DestroyWindow(ImGuiViewport* viewport) { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); - if (ImGuiViewportDataGlfw* data = (ImGuiViewportDataGlfw*)viewport->PlatformUserData) + if (ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData) { - if (data->WindowOwned) + if (vd->WindowOwned) { #if !GLFW_HAS_MOUSE_PASSTHROUGH && GLFW_HAS_WINDOW_HOVERED && defined(_WIN32) HWND hwnd = (HWND)viewport->PlatformHandleRaw; @@ -654,13 +654,13 @@ static void ImGui_ImplGlfw_DestroyWindow(ImGuiViewport* viewport) // Release any keys that were pressed in the window being destroyed and are still held down, // because we will not receive any release events after window is destroyed. for (int i = 0; i < IM_ARRAYSIZE(bd->KeyOwnerWindows); i++) - if (bd->KeyOwnerWindows[i] == data->Window) - ImGui_ImplGlfw_KeyCallback(data->Window, i, 0, GLFW_RELEASE, 0); // Later params are only used for main viewport, on which this function is never called. + if (bd->KeyOwnerWindows[i] == vd->Window) + ImGui_ImplGlfw_KeyCallback(vd->Window, i, 0, GLFW_RELEASE, 0); // Later params are only used for main viewport, on which this function is never called. - glfwDestroyWindow(data->Window); + glfwDestroyWindow(vd->Window); } - data->Window = NULL; - IM_DELETE(data); + vd->Window = NULL; + IM_DELETE(vd); } viewport->PlatformUserData = viewport->PlatformHandle = NULL; } @@ -687,7 +687,7 @@ static LRESULT CALLBACK WndProcNoInputs(HWND hWnd, UINT msg, WPARAM wParam, LPAR static void ImGui_ImplGlfw_ShowWindow(ImGuiViewport* viewport) { - ImGuiViewportDataGlfw* data = (ImGuiViewportDataGlfw*)viewport->PlatformUserData; + ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; #if defined(_WIN32) // GLFW hack: Hide icon from task bar @@ -721,60 +721,60 @@ static void ImGui_ImplGlfw_ShowWindow(ImGuiViewport* viewport) #endif #endif - glfwShowWindow(data->Window); + glfwShowWindow(vd->Window); } static ImVec2 ImGui_ImplGlfw_GetWindowPos(ImGuiViewport* viewport) { - ImGuiViewportDataGlfw* data = (ImGuiViewportDataGlfw*)viewport->PlatformUserData; + ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; int x = 0, y = 0; - glfwGetWindowPos(data->Window, &x, &y); + glfwGetWindowPos(vd->Window, &x, &y); return ImVec2((float)x, (float)y); } static void ImGui_ImplGlfw_SetWindowPos(ImGuiViewport* viewport, ImVec2 pos) { - ImGuiViewportDataGlfw* data = (ImGuiViewportDataGlfw*)viewport->PlatformUserData; - data->IgnoreWindowPosEventFrame = ImGui::GetFrameCount(); - glfwSetWindowPos(data->Window, (int)pos.x, (int)pos.y); + ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; + vd->IgnoreWindowPosEventFrame = ImGui::GetFrameCount(); + glfwSetWindowPos(vd->Window, (int)pos.x, (int)pos.y); } static ImVec2 ImGui_ImplGlfw_GetWindowSize(ImGuiViewport* viewport) { - ImGuiViewportDataGlfw* data = (ImGuiViewportDataGlfw*)viewport->PlatformUserData; + ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; int w = 0, h = 0; - glfwGetWindowSize(data->Window, &w, &h); + glfwGetWindowSize(vd->Window, &w, &h); return ImVec2((float)w, (float)h); } static void ImGui_ImplGlfw_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) { - ImGuiViewportDataGlfw* data = (ImGuiViewportDataGlfw*)viewport->PlatformUserData; + ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; #if __APPLE__ && !GLFW_HAS_OSX_WINDOW_POS_FIX // Native OS windows are positioned from the bottom-left corner on macOS, whereas on other platforms they are // positioned from the upper-left corner. GLFW makes an effort to convert macOS style coordinates, however it // doesn't handle it when changing size. We are manually moving the window in order for changes of size to be based // on the upper-left corner. int x, y, width, height; - glfwGetWindowPos(data->Window, &x, &y); - glfwGetWindowSize(data->Window, &width, &height); - glfwSetWindowPos(data->Window, x, y - height + size.y); + glfwGetWindowPos(vd->Window, &x, &y); + glfwGetWindowSize(vd->Window, &width, &height); + glfwSetWindowPos(vd->Window, x, y - height + size.y); #endif - data->IgnoreWindowSizeEventFrame = ImGui::GetFrameCount(); - glfwSetWindowSize(data->Window, (int)size.x, (int)size.y); + vd->IgnoreWindowSizeEventFrame = ImGui::GetFrameCount(); + glfwSetWindowSize(vd->Window, (int)size.x, (int)size.y); } static void ImGui_ImplGlfw_SetWindowTitle(ImGuiViewport* viewport, const char* title) { - ImGuiViewportDataGlfw* data = (ImGuiViewportDataGlfw*)viewport->PlatformUserData; - glfwSetWindowTitle(data->Window, title); + ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; + glfwSetWindowTitle(vd->Window, title); } static void ImGui_ImplGlfw_SetWindowFocus(ImGuiViewport* viewport) { #if GLFW_HAS_FOCUS_WINDOW - ImGuiViewportDataGlfw* data = (ImGuiViewportDataGlfw*)viewport->PlatformUserData; - glfwFocusWindow(data->Window); + ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; + glfwFocusWindow(vd->Window); #else // FIXME: What are the effect of not having this function? At the moment imgui doesn't actually call SetWindowFocus - we set that up ahead, will answer that question later. (void)viewport; @@ -783,40 +783,40 @@ static void ImGui_ImplGlfw_SetWindowFocus(ImGuiViewport* viewport) static bool ImGui_ImplGlfw_GetWindowFocus(ImGuiViewport* viewport) { - ImGuiViewportDataGlfw* data = (ImGuiViewportDataGlfw*)viewport->PlatformUserData; - return glfwGetWindowAttrib(data->Window, GLFW_FOCUSED) != 0; + ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; + return glfwGetWindowAttrib(vd->Window, GLFW_FOCUSED) != 0; } static bool ImGui_ImplGlfw_GetWindowMinimized(ImGuiViewport* viewport) { - ImGuiViewportDataGlfw* data = (ImGuiViewportDataGlfw*)viewport->PlatformUserData; - return glfwGetWindowAttrib(data->Window, GLFW_ICONIFIED) != 0; + ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; + return glfwGetWindowAttrib(vd->Window, GLFW_ICONIFIED) != 0; } #if GLFW_HAS_WINDOW_ALPHA static void ImGui_ImplGlfw_SetWindowAlpha(ImGuiViewport* viewport, float alpha) { - ImGuiViewportDataGlfw* data = (ImGuiViewportDataGlfw*)viewport->PlatformUserData; - glfwSetWindowOpacity(data->Window, alpha); + ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; + glfwSetWindowOpacity(vd->Window, alpha); } #endif static void ImGui_ImplGlfw_RenderWindow(ImGuiViewport* viewport, void*) { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); - ImGuiViewportDataGlfw* data = (ImGuiViewportDataGlfw*)viewport->PlatformUserData; + ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; if (bd->ClientApi == GlfwClientApi_OpenGL) - glfwMakeContextCurrent(data->Window); + glfwMakeContextCurrent(vd->Window); } static void ImGui_ImplGlfw_SwapBuffers(ImGuiViewport* viewport, void*) { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); - ImGuiViewportDataGlfw* data = (ImGuiViewportDataGlfw*)viewport->PlatformUserData; + ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; if (bd->ClientApi == GlfwClientApi_OpenGL) { - glfwMakeContextCurrent(data->Window); - glfwSwapBuffers(data->Window); + glfwMakeContextCurrent(vd->Window); + glfwSwapBuffers(vd->Window); } } @@ -867,9 +867,9 @@ extern "C" { extern GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance static int ImGui_ImplGlfw_CreateVkSurface(ImGuiViewport* viewport, ImU64 vk_instance, const void* vk_allocator, ImU64* out_vk_surface) { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); - ImGuiViewportDataGlfw* data = (ImGuiViewportDataGlfw*)viewport->PlatformUserData; + ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; IM_ASSERT(bd->ClientApi == GlfwClientApi_Vulkan); - VkResult err = glfwCreateWindowSurface((VkInstance)vk_instance, data->Window, (const VkAllocationCallbacks*)vk_allocator, (VkSurfaceKHR*)out_vk_surface); + VkResult err = glfwCreateWindowSurface((VkInstance)vk_instance, vd->Window, (const VkAllocationCallbacks*)vk_allocator, (VkSurfaceKHR*)out_vk_surface); return (int)err; } #endif // GLFW_HAS_VULKAN @@ -905,10 +905,10 @@ static void ImGui_ImplGlfw_InitPlatformInterface() // Register main window handle (which is owned by the main application, not by us) // This is mostly for simplicity and consistency, so that our code (e.g. mouse handling etc.) can use same logic for main and secondary viewports. ImGuiViewport* main_viewport = ImGui::GetMainViewport(); - ImGuiViewportDataGlfw* data = IM_NEW(ImGuiViewportDataGlfw)(); - data->Window = bd->Window; - data->WindowOwned = false; - main_viewport->PlatformUserData = data; + ImGui_ImplGlfw_ViewportData* vd = IM_NEW(ImGui_ImplGlfw_ViewportData)(); + vd->Window = bd->Window; + vd->WindowOwned = false; + main_viewport->PlatformUserData = vd; main_viewport->PlatformHandle = (void*)bd->Window; } diff --git a/backends/imgui_impl_sdl.cpp b/backends/imgui_impl_sdl.cpp index 7a882c4fd03a..2fd748a1c2ba 100644 --- a/backends/imgui_impl_sdl.cpp +++ b/backends/imgui_impl_sdl.cpp @@ -522,25 +522,25 @@ void ImGui_ImplSDL2_NewFrame(SDL_Window* window) //-------------------------------------------------------------------------------------------------------- // Helper structure we store in the void* RenderUserData field of each ImGuiViewport to easily retrieve our backend data. -struct ImGuiViewportDataSDL2 +struct ImGui_ImplSDL2_ViewportData { SDL_Window* Window; Uint32 WindowID; bool WindowOwned; SDL_GLContext GLContext; - ImGuiViewportDataSDL2() { Window = NULL; WindowID = 0; WindowOwned = false; GLContext = NULL; } - ~ImGuiViewportDataSDL2() { IM_ASSERT(Window == NULL && GLContext == NULL); } + ImGui_ImplSDL2_ViewportData() { Window = NULL; WindowID = 0; WindowOwned = false; GLContext = NULL; } + ~ImGui_ImplSDL2_ViewportData() { IM_ASSERT(Window == NULL && GLContext == NULL); } }; static void ImGui_ImplSDL2_CreateWindow(ImGuiViewport* viewport) { ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData(); - ImGuiViewportDataSDL2* data = IM_NEW(ImGuiViewportDataSDL2)(); - viewport->PlatformUserData = data; + ImGui_ImplSDL2_ViewportData* vd = IM_NEW(ImGui_ImplSDL2_ViewportData)(); + viewport->PlatformUserData = vd; ImGuiViewport* main_viewport = ImGui::GetMainViewport(); - ImGuiViewportDataSDL2* main_viewport_data = (ImGuiViewportDataSDL2*)main_viewport->PlatformUserData; + ImGui_ImplSDL2_ViewportData* main_viewport_data = (ImGui_ImplSDL2_ViewportData*)main_viewport->PlatformUserData; // Share GL resources with main context bool use_opengl = (main_viewport_data->GLContext != NULL); @@ -565,43 +565,43 @@ static void ImGui_ImplSDL2_CreateWindow(ImGuiViewport* viewport) #if SDL_HAS_ALWAYS_ON_TOP sdl_flags |= (viewport->Flags & ImGuiViewportFlags_TopMost) ? SDL_WINDOW_ALWAYS_ON_TOP : 0; #endif - data->Window = SDL_CreateWindow("No Title Yet", (int)viewport->Pos.x, (int)viewport->Pos.y, (int)viewport->Size.x, (int)viewport->Size.y, sdl_flags); - data->WindowOwned = true; + vd->Window = SDL_CreateWindow("No Title Yet", (int)viewport->Pos.x, (int)viewport->Pos.y, (int)viewport->Size.x, (int)viewport->Size.y, sdl_flags); + vd->WindowOwned = true; if (use_opengl) { - data->GLContext = SDL_GL_CreateContext(data->Window); + vd->GLContext = SDL_GL_CreateContext(vd->Window); SDL_GL_SetSwapInterval(0); } if (use_opengl && backup_context) - SDL_GL_MakeCurrent(data->Window, backup_context); + SDL_GL_MakeCurrent(vd->Window, backup_context); - viewport->PlatformHandle = (void*)data->Window; + viewport->PlatformHandle = (void*)vd->Window; #if defined(_WIN32) SDL_SysWMinfo info; SDL_VERSION(&info.version); - if (SDL_GetWindowWMInfo(data->Window, &info)) + if (SDL_GetWindowWMInfo(vd->Window, &info)) viewport->PlatformHandleRaw = info.info.win.window; #endif } static void ImGui_ImplSDL2_DestroyWindow(ImGuiViewport* viewport) { - if (ImGuiViewportDataSDL2* data = (ImGuiViewportDataSDL2*)viewport->PlatformUserData) + if (ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData) { - if (data->GLContext && data->WindowOwned) - SDL_GL_DeleteContext(data->GLContext); - if (data->Window && data->WindowOwned) - SDL_DestroyWindow(data->Window); - data->GLContext = NULL; - data->Window = NULL; - IM_DELETE(data); + if (vd->GLContext && vd->WindowOwned) + SDL_GL_DeleteContext(vd->GLContext); + if (vd->Window && vd->WindowOwned) + SDL_DestroyWindow(vd->Window); + vd->GLContext = NULL; + vd->Window = NULL; + IM_DELETE(vd); } viewport->PlatformUserData = viewport->PlatformHandle = NULL; } static void ImGui_ImplSDL2_ShowWindow(ImGuiViewport* viewport) { - ImGuiViewportDataSDL2* data = (ImGuiViewportDataSDL2*)viewport->PlatformUserData; + ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData; #if defined(_WIN32) HWND hwnd = (HWND)viewport->PlatformHandleRaw; @@ -623,83 +623,83 @@ static void ImGui_ImplSDL2_ShowWindow(ImGuiViewport* viewport) } #endif - SDL_ShowWindow(data->Window); + SDL_ShowWindow(vd->Window); } static ImVec2 ImGui_ImplSDL2_GetWindowPos(ImGuiViewport* viewport) { - ImGuiViewportDataSDL2* data = (ImGuiViewportDataSDL2*)viewport->PlatformUserData; + ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData; int x = 0, y = 0; - SDL_GetWindowPosition(data->Window, &x, &y); + SDL_GetWindowPosition(vd->Window, &x, &y); return ImVec2((float)x, (float)y); } static void ImGui_ImplSDL2_SetWindowPos(ImGuiViewport* viewport, ImVec2 pos) { - ImGuiViewportDataSDL2* data = (ImGuiViewportDataSDL2*)viewport->PlatformUserData; - SDL_SetWindowPosition(data->Window, (int)pos.x, (int)pos.y); + ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData; + SDL_SetWindowPosition(vd->Window, (int)pos.x, (int)pos.y); } static ImVec2 ImGui_ImplSDL2_GetWindowSize(ImGuiViewport* viewport) { - ImGuiViewportDataSDL2* data = (ImGuiViewportDataSDL2*)viewport->PlatformUserData; + ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData; int w = 0, h = 0; - SDL_GetWindowSize(data->Window, &w, &h); + SDL_GetWindowSize(vd->Window, &w, &h); return ImVec2((float)w, (float)h); } static void ImGui_ImplSDL2_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) { - ImGuiViewportDataSDL2* data = (ImGuiViewportDataSDL2*)viewport->PlatformUserData; - SDL_SetWindowSize(data->Window, (int)size.x, (int)size.y); + ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData; + SDL_SetWindowSize(vd->Window, (int)size.x, (int)size.y); } static void ImGui_ImplSDL2_SetWindowTitle(ImGuiViewport* viewport, const char* title) { - ImGuiViewportDataSDL2* data = (ImGuiViewportDataSDL2*)viewport->PlatformUserData; - SDL_SetWindowTitle(data->Window, title); + ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData; + SDL_SetWindowTitle(vd->Window, title); } #if SDL_HAS_WINDOW_ALPHA static void ImGui_ImplSDL2_SetWindowAlpha(ImGuiViewport* viewport, float alpha) { - ImGuiViewportDataSDL2* data = (ImGuiViewportDataSDL2*)viewport->PlatformUserData; - SDL_SetWindowOpacity(data->Window, alpha); + ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData; + SDL_SetWindowOpacity(vd->Window, alpha); } #endif static void ImGui_ImplSDL2_SetWindowFocus(ImGuiViewport* viewport) { - ImGuiViewportDataSDL2* data = (ImGuiViewportDataSDL2*)viewport->PlatformUserData; - SDL_RaiseWindow(data->Window); + ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData; + SDL_RaiseWindow(vd->Window); } static bool ImGui_ImplSDL2_GetWindowFocus(ImGuiViewport* viewport) { - ImGuiViewportDataSDL2* data = (ImGuiViewportDataSDL2*)viewport->PlatformUserData; - return (SDL_GetWindowFlags(data->Window) & SDL_WINDOW_INPUT_FOCUS) != 0; + ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData; + return (SDL_GetWindowFlags(vd->Window) & SDL_WINDOW_INPUT_FOCUS) != 0; } static bool ImGui_ImplSDL2_GetWindowMinimized(ImGuiViewport* viewport) { - ImGuiViewportDataSDL2* data = (ImGuiViewportDataSDL2*)viewport->PlatformUserData; - return (SDL_GetWindowFlags(data->Window) & SDL_WINDOW_MINIMIZED) != 0; + ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData; + return (SDL_GetWindowFlags(vd->Window) & SDL_WINDOW_MINIMIZED) != 0; } static void ImGui_ImplSDL2_RenderWindow(ImGuiViewport* viewport, void*) { - ImGuiViewportDataSDL2* data = (ImGuiViewportDataSDL2*)viewport->PlatformUserData; - if (data->GLContext) - SDL_GL_MakeCurrent(data->Window, data->GLContext); + ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData; + if (vd->GLContext) + SDL_GL_MakeCurrent(vd->Window, vd->GLContext); } static void ImGui_ImplSDL2_SwapBuffers(ImGuiViewport* viewport, void*) { - ImGuiViewportDataSDL2* data = (ImGuiViewportDataSDL2*)viewport->PlatformUserData; - if (data->GLContext) + ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData; + if (vd->GLContext) { - SDL_GL_MakeCurrent(data->Window, data->GLContext); - SDL_GL_SwapWindow(data->Window); + SDL_GL_MakeCurrent(vd->Window, vd->GLContext); + SDL_GL_SwapWindow(vd->Window); } } @@ -709,9 +709,9 @@ static void ImGui_ImplSDL2_SwapBuffers(ImGuiViewport* viewport, void*) #include static int ImGui_ImplSDL2_CreateVkSurface(ImGuiViewport* viewport, ImU64 vk_instance, const void* vk_allocator, ImU64* out_vk_surface) { - ImGuiViewportDataSDL2* data = (ImGuiViewportDataSDL2*)viewport->PlatformUserData; + ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData; (void)vk_allocator; - SDL_bool ret = SDL_Vulkan_CreateSurface(data->Window, (VkInstance)vk_instance, (VkSurfaceKHR*)out_vk_surface); + SDL_bool ret = SDL_Vulkan_CreateSurface(vd->Window, (VkInstance)vk_instance, (VkSurfaceKHR*)out_vk_surface); return ret ? 0 : 1; // ret ? VK_SUCCESS : VK_NOT_READY } #endif // SDL_HAS_VULKAN @@ -748,13 +748,13 @@ static void ImGui_ImplSDL2_InitPlatformInterface(SDL_Window* window, void* sdl_g // Register main window handle (which is owned by the main application, not by us) // This is mostly for simplicity and consistency, so that our code (e.g. mouse handling etc.) can use same logic for main and secondary viewports. ImGuiViewport* main_viewport = ImGui::GetMainViewport(); - ImGuiViewportDataSDL2* data = IM_NEW(ImGuiViewportDataSDL2)(); - data->Window = window; - data->WindowID = SDL_GetWindowID(window); - data->WindowOwned = false; - data->GLContext = sdl_gl_context; - main_viewport->PlatformUserData = data; - main_viewport->PlatformHandle = data->Window; + ImGui_ImplSDL2_ViewportData* vd = IM_NEW(ImGui_ImplSDL2_ViewportData)(); + vd->Window = window; + vd->WindowID = SDL_GetWindowID(window); + vd->WindowOwned = false; + vd->GLContext = sdl_gl_context; + main_viewport->PlatformUserData = vd; + main_viewport->PlatformHandle = vd->Window; } static void ImGui_ImplSDL2_ShutdownPlatformInterface() diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp index 49adee974318..8ee9a3d3ca2b 100644 --- a/backends/imgui_impl_vulkan.cpp +++ b/backends/imgui_impl_vulkan.cpp @@ -81,14 +81,14 @@ struct ImGui_ImplVulkanH_WindowRenderBuffers // For multi-viewport support: // Helper structure we store in the void* RenderUserData field of each ImGuiViewport to easily retrieve our backend data. -struct ImGuiViewportDataVulkan +struct ImGui_ImplVulkan_ViewportData { bool WindowOwned; ImGui_ImplVulkanH_Window Window; // Used by secondary viewports only ImGui_ImplVulkanH_WindowRenderBuffers RenderBuffers; // Used by all viewports - ImGuiViewportDataVulkan() { WindowOwned = false; memset(&RenderBuffers, 0, sizeof(RenderBuffers)); } - ~ImGuiViewportDataVulkan() { } + ImGui_ImplVulkan_ViewportData() { WindowOwned = false; memset(&RenderBuffers, 0, sizeof(RenderBuffers)); } + ~ImGui_ImplVulkan_ViewportData() { } }; // Vulkan data @@ -114,7 +114,7 @@ struct ImGui_ImplVulkan_Data VkDeviceMemory UploadBufferMemory; VkBuffer UploadBuffer; - // Render buffers + // Render buffers for main window ImGui_ImplVulkanH_WindowRenderBuffers MainWindowRenderBuffers; ImGui_ImplVulkan_Data() @@ -460,7 +460,7 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm pipeline = bd->Pipeline; // Allocate array to store enough vertex/index buffers. Each unique viewport gets its own storage. - ImGuiViewportDataVulkan* viewport_renderer_data = (ImGuiViewportDataVulkan*)draw_data->OwnerViewport->RendererUserData; + ImGui_ImplVulkan_ViewportData* viewport_renderer_data = (ImGui_ImplVulkan_ViewportData*)draw_data->OwnerViewport->RendererUserData; IM_ASSERT(viewport_renderer_data != NULL); ImGui_ImplVulkanH_WindowRenderBuffers* wrb = &viewport_renderer_data->RenderBuffers; if (wrb->FrameRenderBuffers == NULL) @@ -1080,7 +1080,7 @@ bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info, VkRenderPass rend // Our render function expect RendererUserData to be storing the window render buffer we need (for the main viewport we won't use ->Window) ImGuiViewport* main_viewport = ImGui::GetMainViewport(); - main_viewport->RendererUserData = IM_NEW(ImGuiViewportDataVulkan)(); + main_viewport->RendererUserData = IM_NEW(ImGui_ImplVulkan_ViewportData)(); if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) ImGui_ImplVulkan_InitPlatformInterface(); @@ -1095,8 +1095,8 @@ void ImGui_ImplVulkan_Shutdown() // Manually delete main viewport render data in-case we haven't initialized for viewports ImGuiViewport* main_viewport = ImGui::GetMainViewport(); - if (ImGuiViewportDataVulkan* data = (ImGuiViewportDataVulkan*)main_viewport->RendererUserData) - IM_DELETE(data); + if (ImGui_ImplVulkan_ViewportData* vd = (ImGui_ImplVulkan_ViewportData*)main_viewport->RendererUserData) + IM_DELETE(vd); main_viewport->RendererUserData = NULL; // Clean up windows @@ -1516,8 +1516,8 @@ void ImGui_ImplVulkanH_DestroyAllViewportsRenderBuffers(VkDevice device, const V { ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); for (int n = 0; n < platform_io.Viewports.Size; n++) - if (ImGuiViewportDataVulkan* data = (ImGuiViewportDataVulkan*)platform_io.Viewports[n]->RendererUserData) - ImGui_ImplVulkanH_DestroyWindowRenderBuffers(device, &data->RenderBuffers, allocator); + if (ImGui_ImplVulkan_ViewportData* vd = (ImGui_ImplVulkan_ViewportData*)platform_io.Viewports[n]->RendererUserData) + ImGui_ImplVulkanH_DestroyWindowRenderBuffers(device, &vd->RenderBuffers, allocator); } //-------------------------------------------------------------------------------------------------------- @@ -1529,9 +1529,9 @@ void ImGui_ImplVulkanH_DestroyAllViewportsRenderBuffers(VkDevice device, const V static void ImGui_ImplVulkan_CreateWindow(ImGuiViewport* viewport) { ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); - ImGuiViewportDataVulkan* data = IM_NEW(ImGuiViewportDataVulkan)(); - viewport->RendererUserData = data; - ImGui_ImplVulkanH_Window* wd = &data->Window; + ImGui_ImplVulkan_ViewportData* vd = IM_NEW(ImGui_ImplVulkan_ViewportData)(); + viewport->RendererUserData = vd; + ImGui_ImplVulkanH_Window* wd = &vd->Window; ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; // Create surface @@ -1562,20 +1562,20 @@ static void ImGui_ImplVulkan_CreateWindow(ImGuiViewport* viewport) // Create SwapChain, RenderPass, Framebuffer, etc. wd->ClearEnable = (viewport->Flags & ImGuiViewportFlags_NoRendererClear) ? false : true; ImGui_ImplVulkanH_CreateOrResizeWindow(v->Instance, v->PhysicalDevice, v->Device, wd, v->QueueFamily, v->Allocator, (int)viewport->Size.x, (int)viewport->Size.y, v->MinImageCount); - data->WindowOwned = true; + vd->WindowOwned = true; } static void ImGui_ImplVulkan_DestroyWindow(ImGuiViewport* viewport) { // The main viewport (owned by the application) will always have RendererUserData == NULL since we didn't create the data for it. ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); - if (ImGuiViewportDataVulkan* data = (ImGuiViewportDataVulkan*)viewport->RendererUserData) + if (ImGui_ImplVulkan_ViewportData* vd = (ImGui_ImplVulkan_ViewportData*)viewport->RendererUserData) { ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; - if (data->WindowOwned) - ImGui_ImplVulkanH_DestroyWindow(v->Instance, v->Device, &data->Window, v->Allocator); - ImGui_ImplVulkanH_DestroyWindowRenderBuffers(v->Device, &data->RenderBuffers, v->Allocator); - IM_DELETE(data); + if (vd->WindowOwned) + ImGui_ImplVulkanH_DestroyWindow(v->Instance, v->Device, &vd->Window, v->Allocator); + ImGui_ImplVulkanH_DestroyWindowRenderBuffers(v->Device, &vd->RenderBuffers, v->Allocator); + IM_DELETE(vd); } viewport->RendererUserData = NULL; } @@ -1583,19 +1583,19 @@ static void ImGui_ImplVulkan_DestroyWindow(ImGuiViewport* viewport) static void ImGui_ImplVulkan_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) { ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); - ImGuiViewportDataVulkan* data = (ImGuiViewportDataVulkan*)viewport->RendererUserData; - if (data == NULL) // This is NULL for the main viewport (which is left to the user/app to handle) + ImGui_ImplVulkan_ViewportData* vd = (ImGui_ImplVulkan_ViewportData*)viewport->RendererUserData; + if (vd == NULL) // This is NULL for the main viewport (which is left to the user/app to handle) return; ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; - data->Window.ClearEnable = (viewport->Flags & ImGuiViewportFlags_NoRendererClear) ? false : true; - ImGui_ImplVulkanH_CreateOrResizeWindow(v->Instance, v->PhysicalDevice, v->Device, &data->Window, v->QueueFamily, v->Allocator, (int)size.x, (int)size.y, v->MinImageCount); + vd->Window.ClearEnable = (viewport->Flags & ImGuiViewportFlags_NoRendererClear) ? false : true; + ImGui_ImplVulkanH_CreateOrResizeWindow(v->Instance, v->PhysicalDevice, v->Device, &vd->Window, v->QueueFamily, v->Allocator, (int)size.x, (int)size.y, v->MinImageCount); } static void ImGui_ImplVulkan_RenderWindow(ImGuiViewport* viewport, void*) { ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); - ImGuiViewportDataVulkan* data = (ImGuiViewportDataVulkan*)viewport->RendererUserData; - ImGui_ImplVulkanH_Window* wd = &data->Window; + ImGui_ImplVulkan_ViewportData* vd = (ImGui_ImplVulkan_ViewportData*)viewport->RendererUserData; + ImGui_ImplVulkanH_Window* wd = &vd->Window; ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; VkResult err; @@ -1668,8 +1668,8 @@ static void ImGui_ImplVulkan_RenderWindow(ImGuiViewport* viewport, void*) static void ImGui_ImplVulkan_SwapBuffers(ImGuiViewport* viewport, void*) { ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); - ImGuiViewportDataVulkan* data = (ImGuiViewportDataVulkan*)viewport->RendererUserData; - ImGui_ImplVulkanH_Window* wd = &data->Window; + ImGui_ImplVulkan_ViewportData* vd = (ImGui_ImplVulkan_ViewportData*)viewport->RendererUserData; + ImGui_ImplVulkanH_Window* wd = &vd->Window; ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; VkResult err; @@ -1685,7 +1685,7 @@ static void ImGui_ImplVulkan_SwapBuffers(ImGuiViewport* viewport, void*) info.pImageIndices = &present_index; err = vkQueuePresentKHR(v->Queue, &info); if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) - ImGui_ImplVulkanH_CreateOrResizeWindow(v->Instance, v->PhysicalDevice, v->Device, &data->Window, v->QueueFamily, v->Allocator, (int)viewport->Size.x, (int)viewport->Size.y, v->MinImageCount); + ImGui_ImplVulkanH_CreateOrResizeWindow(v->Instance, v->PhysicalDevice, v->Device, &vd->Window, v->QueueFamily, v->Allocator, (int)viewport->Size.x, (int)viewport->Size.y, v->MinImageCount); else check_vk_result(err); diff --git a/backends/imgui_impl_vulkan.h b/backends/imgui_impl_vulkan.h index eaf7c65a35a5..58b68f08a77d 100644 --- a/backends/imgui_impl_vulkan.h +++ b/backends/imgui_impl_vulkan.h @@ -7,7 +7,7 @@ // [ ] Platform: Multi-viewport / platform windows. // [ ] Renderer: User texture binding. Changes of ImTextureID aren't supported by this backend! See https://github.com/ocornut/imgui/pull/914 -// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. // Read online: https://github.com/ocornut/imgui/tree/master/docs diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index 20e46f26d839..411edb5f6222 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -673,15 +673,15 @@ static void ImGui_ImplWin32_SetImeInputPos(ImGuiViewport* viewport, ImVec2 pos) //-------------------------------------------------------------------------------------------------------- // Helper structure we store in the void* RenderUserData field of each ImGuiViewport to easily retrieve our backend data. -struct ImGuiViewportDataWin32 +struct ImGui_ImplWin32_ViewportData { HWND Hwnd; bool HwndOwned; DWORD DwStyle; DWORD DwExStyle; - ImGuiViewportDataWin32() { Hwnd = NULL; HwndOwned = false; DwStyle = DwExStyle = 0; } - ~ImGuiViewportDataWin32() { IM_ASSERT(Hwnd == NULL); } + ImGui_ImplWin32_ViewportData() { Hwnd = NULL; HwndOwned = false; DwStyle = DwExStyle = 0; } + ~ImGui_ImplWin32_ViewportData() { IM_ASSERT(Hwnd == NULL); } }; static void ImGui_ImplWin32_GetWin32StyleFromViewportFlags(ImGuiViewportFlags flags, DWORD* out_style, DWORD* out_ex_style) @@ -702,11 +702,11 @@ static void ImGui_ImplWin32_GetWin32StyleFromViewportFlags(ImGuiViewportFlags fl static void ImGui_ImplWin32_CreateWindow(ImGuiViewport* viewport) { - ImGuiViewportDataWin32* data = IM_NEW(ImGuiViewportDataWin32)(); - viewport->PlatformUserData = data; + ImGui_ImplWin32_ViewportData* vd = IM_NEW(ImGui_ImplWin32_ViewportData)(); + viewport->PlatformUserData = vd; // Select style and parent window - ImGui_ImplWin32_GetWin32StyleFromViewportFlags(viewport->Flags, &data->DwStyle, &data->DwExStyle); + ImGui_ImplWin32_GetWin32StyleFromViewportFlags(viewport->Flags, &vd->DwStyle, &vd->DwExStyle); HWND parent_window = NULL; if (viewport->ParentViewportId != 0) if (ImGuiViewport* parent_viewport = ImGui::FindViewportByID(viewport->ParentViewportId)) @@ -714,170 +714,170 @@ static void ImGui_ImplWin32_CreateWindow(ImGuiViewport* viewport) // Create window RECT rect = { (LONG)viewport->Pos.x, (LONG)viewport->Pos.y, (LONG)(viewport->Pos.x + viewport->Size.x), (LONG)(viewport->Pos.y + viewport->Size.y) }; - ::AdjustWindowRectEx(&rect, data->DwStyle, FALSE, data->DwExStyle); - data->Hwnd = ::CreateWindowEx( - data->DwExStyle, _T("ImGui Platform"), _T("Untitled"), data->DwStyle, // Style, class name, window name + ::AdjustWindowRectEx(&rect, vd->DwStyle, FALSE, vd->DwExStyle); + vd->Hwnd = ::CreateWindowEx( + vd->DwExStyle, _T("ImGui Platform"), _T("Untitled"), vd->DwStyle, // Style, class name, window name rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, // Window area parent_window, NULL, ::GetModuleHandle(NULL), NULL); // Parent window, Menu, Instance, Param - data->HwndOwned = true; + vd->HwndOwned = true; viewport->PlatformRequestResize = false; - viewport->PlatformHandle = viewport->PlatformHandleRaw = data->Hwnd; + viewport->PlatformHandle = viewport->PlatformHandleRaw = vd->Hwnd; } static void ImGui_ImplWin32_DestroyWindow(ImGuiViewport* viewport) { ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); - if (ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData) + if (ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData) { - if (::GetCapture() == data->Hwnd) + if (::GetCapture() == vd->Hwnd) { // Transfer capture so if we started dragging from a window that later disappears, we'll still receive the MOUSEUP event. ::ReleaseCapture(); ::SetCapture(bd->hWnd); } - if (data->Hwnd && data->HwndOwned) - ::DestroyWindow(data->Hwnd); - data->Hwnd = NULL; - IM_DELETE(data); + if (vd->Hwnd && vd->HwndOwned) + ::DestroyWindow(vd->Hwnd); + vd->Hwnd = NULL; + IM_DELETE(vd); } viewport->PlatformUserData = viewport->PlatformHandle = NULL; } static void ImGui_ImplWin32_ShowWindow(ImGuiViewport* viewport) { - ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData; - IM_ASSERT(data->Hwnd != 0); + ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData; + IM_ASSERT(vd->Hwnd != 0); if (viewport->Flags & ImGuiViewportFlags_NoFocusOnAppearing) - ::ShowWindow(data->Hwnd, SW_SHOWNA); + ::ShowWindow(vd->Hwnd, SW_SHOWNA); else - ::ShowWindow(data->Hwnd, SW_SHOW); + ::ShowWindow(vd->Hwnd, SW_SHOW); } static void ImGui_ImplWin32_UpdateWindow(ImGuiViewport* viewport) { // (Optional) Update Win32 style if it changed _after_ creation. // Generally they won't change unless configuration flags are changed, but advanced uses (such as manually rewriting viewport flags) make this useful. - ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData; - IM_ASSERT(data->Hwnd != 0); + ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData; + IM_ASSERT(vd->Hwnd != 0); DWORD new_style; DWORD new_ex_style; ImGui_ImplWin32_GetWin32StyleFromViewportFlags(viewport->Flags, &new_style, &new_ex_style); // Only reapply the flags that have been changed from our point of view (as other flags are being modified by Windows) - if (data->DwStyle != new_style || data->DwExStyle != new_ex_style) + if (vd->DwStyle != new_style || vd->DwExStyle != new_ex_style) { // (Optional) Update TopMost state if it changed _after_ creation - bool top_most_changed = (data->DwExStyle & WS_EX_TOPMOST) != (new_ex_style & WS_EX_TOPMOST); + bool top_most_changed = (vd->DwExStyle & WS_EX_TOPMOST) != (new_ex_style & WS_EX_TOPMOST); HWND insert_after = top_most_changed ? ((viewport->Flags & ImGuiViewportFlags_TopMost) ? HWND_TOPMOST : HWND_NOTOPMOST) : 0; UINT swp_flag = top_most_changed ? 0 : SWP_NOZORDER; // Apply flags and position (since it is affected by flags) - data->DwStyle = new_style; - data->DwExStyle = new_ex_style; - ::SetWindowLong(data->Hwnd, GWL_STYLE, data->DwStyle); - ::SetWindowLong(data->Hwnd, GWL_EXSTYLE, data->DwExStyle); + vd->DwStyle = new_style; + vd->DwExStyle = new_ex_style; + ::SetWindowLong(vd->Hwnd, GWL_STYLE, vd->DwStyle); + ::SetWindowLong(vd->Hwnd, GWL_EXSTYLE, vd->DwExStyle); RECT rect = { (LONG)viewport->Pos.x, (LONG)viewport->Pos.y, (LONG)(viewport->Pos.x + viewport->Size.x), (LONG)(viewport->Pos.y + viewport->Size.y) }; - ::AdjustWindowRectEx(&rect, data->DwStyle, FALSE, data->DwExStyle); // Client to Screen - ::SetWindowPos(data->Hwnd, insert_after, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, swp_flag | SWP_NOACTIVATE | SWP_FRAMECHANGED); - ::ShowWindow(data->Hwnd, SW_SHOWNA); // This is necessary when we alter the style + ::AdjustWindowRectEx(&rect, vd->DwStyle, FALSE, vd->DwExStyle); // Client to Screen + ::SetWindowPos(vd->Hwnd, insert_after, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, swp_flag | SWP_NOACTIVATE | SWP_FRAMECHANGED); + ::ShowWindow(vd->Hwnd, SW_SHOWNA); // This is necessary when we alter the style viewport->PlatformRequestMove = viewport->PlatformRequestResize = true; } } static ImVec2 ImGui_ImplWin32_GetWindowPos(ImGuiViewport* viewport) { - ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData; - IM_ASSERT(data->Hwnd != 0); + ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData; + IM_ASSERT(vd->Hwnd != 0); POINT pos = { 0, 0 }; - ::ClientToScreen(data->Hwnd, &pos); + ::ClientToScreen(vd->Hwnd, &pos); return ImVec2((float)pos.x, (float)pos.y); } static void ImGui_ImplWin32_SetWindowPos(ImGuiViewport* viewport, ImVec2 pos) { - ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData; - IM_ASSERT(data->Hwnd != 0); + ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData; + IM_ASSERT(vd->Hwnd != 0); RECT rect = { (LONG)pos.x, (LONG)pos.y, (LONG)pos.x, (LONG)pos.y }; - ::AdjustWindowRectEx(&rect, data->DwStyle, FALSE, data->DwExStyle); - ::SetWindowPos(data->Hwnd, NULL, rect.left, rect.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE); + ::AdjustWindowRectEx(&rect, vd->DwStyle, FALSE, vd->DwExStyle); + ::SetWindowPos(vd->Hwnd, NULL, rect.left, rect.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE); } static ImVec2 ImGui_ImplWin32_GetWindowSize(ImGuiViewport* viewport) { - ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData; - IM_ASSERT(data->Hwnd != 0); + ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData; + IM_ASSERT(vd->Hwnd != 0); RECT rect; - ::GetClientRect(data->Hwnd, &rect); + ::GetClientRect(vd->Hwnd, &rect); return ImVec2(float(rect.right - rect.left), float(rect.bottom - rect.top)); } static void ImGui_ImplWin32_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) { - ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData; - IM_ASSERT(data->Hwnd != 0); + ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData; + IM_ASSERT(vd->Hwnd != 0); RECT rect = { 0, 0, (LONG)size.x, (LONG)size.y }; - ::AdjustWindowRectEx(&rect, data->DwStyle, FALSE, data->DwExStyle); // Client to Screen - ::SetWindowPos(data->Hwnd, NULL, 0, 0, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE); + ::AdjustWindowRectEx(&rect, vd->DwStyle, FALSE, vd->DwExStyle); // Client to Screen + ::SetWindowPos(vd->Hwnd, NULL, 0, 0, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE); } static void ImGui_ImplWin32_SetWindowFocus(ImGuiViewport* viewport) { - ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData; - IM_ASSERT(data->Hwnd != 0); - ::BringWindowToTop(data->Hwnd); - ::SetForegroundWindow(data->Hwnd); - ::SetFocus(data->Hwnd); + ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData; + IM_ASSERT(vd->Hwnd != 0); + ::BringWindowToTop(vd->Hwnd); + ::SetForegroundWindow(vd->Hwnd); + ::SetFocus(vd->Hwnd); } static bool ImGui_ImplWin32_GetWindowFocus(ImGuiViewport* viewport) { - ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData; - IM_ASSERT(data->Hwnd != 0); - return ::GetForegroundWindow() == data->Hwnd; + ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData; + IM_ASSERT(vd->Hwnd != 0); + return ::GetForegroundWindow() == vd->Hwnd; } static bool ImGui_ImplWin32_GetWindowMinimized(ImGuiViewport* viewport) { - ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData; - IM_ASSERT(data->Hwnd != 0); - return ::IsIconic(data->Hwnd) != 0; + ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData; + IM_ASSERT(vd->Hwnd != 0); + return ::IsIconic(vd->Hwnd) != 0; } static void ImGui_ImplWin32_SetWindowTitle(ImGuiViewport* viewport, const char* title) { // ::SetWindowTextA() doesn't properly handle UTF-8 so we explicitely convert our string. - ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData; - IM_ASSERT(data->Hwnd != 0); + ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData; + IM_ASSERT(vd->Hwnd != 0); int n = ::MultiByteToWideChar(CP_UTF8, 0, title, -1, NULL, 0); ImVector title_w; title_w.resize(n); ::MultiByteToWideChar(CP_UTF8, 0, title, -1, title_w.Data, n); - ::SetWindowTextW(data->Hwnd, title_w.Data); + ::SetWindowTextW(vd->Hwnd, title_w.Data); } static void ImGui_ImplWin32_SetWindowAlpha(ImGuiViewport* viewport, float alpha) { - ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData; - IM_ASSERT(data->Hwnd != 0); + ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData; + IM_ASSERT(vd->Hwnd != 0); IM_ASSERT(alpha >= 0.0f && alpha <= 1.0f); if (alpha < 1.0f) { - DWORD style = ::GetWindowLongW(data->Hwnd, GWL_EXSTYLE) | WS_EX_LAYERED; - ::SetWindowLongW(data->Hwnd, GWL_EXSTYLE, style); - ::SetLayeredWindowAttributes(data->Hwnd, 0, (BYTE)(255 * alpha), LWA_ALPHA); + DWORD style = ::GetWindowLongW(vd->Hwnd, GWL_EXSTYLE) | WS_EX_LAYERED; + ::SetWindowLongW(vd->Hwnd, GWL_EXSTYLE, style); + ::SetLayeredWindowAttributes(vd->Hwnd, 0, (BYTE)(255 * alpha), LWA_ALPHA); } else { - DWORD style = ::GetWindowLongW(data->Hwnd, GWL_EXSTYLE) & ~WS_EX_LAYERED; - ::SetWindowLongW(data->Hwnd, GWL_EXSTYLE, style); + DWORD style = ::GetWindowLongW(vd->Hwnd, GWL_EXSTYLE) & ~WS_EX_LAYERED; + ::SetWindowLongW(vd->Hwnd, GWL_EXSTYLE, style); } } static float ImGui_ImplWin32_GetWindowDpiScale(ImGuiViewport* viewport) { - ImGuiViewportDataWin32* data = (ImGuiViewportDataWin32*)viewport->PlatformUserData; - IM_ASSERT(data->Hwnd != 0); - return ImGui_ImplWin32_GetDpiScaleForHwnd(data->Hwnd); + ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData; + IM_ASSERT(vd->Hwnd != 0); + return ImGui_ImplWin32_GetDpiScaleForHwnd(vd->Hwnd); } // FIXME-DPI: Testing DPI related ideas @@ -976,10 +976,10 @@ static void ImGui_ImplWin32_InitPlatformInterface() // This is mostly for simplicity and consistency, so that our code (e.g. mouse handling etc.) can use same logic for main and secondary viewports. ImGuiViewport* main_viewport = ImGui::GetMainViewport(); ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); - ImGuiViewportDataWin32* data = IM_NEW(ImGuiViewportDataWin32)(); - data->Hwnd = bd->hWnd; - data->HwndOwned = false; - main_viewport->PlatformUserData = data; + ImGui_ImplWin32_ViewportData* vd = IM_NEW(ImGui_ImplWin32_ViewportData)(); + vd->Hwnd = bd->hWnd; + vd->HwndOwned = false; + main_viewport->PlatformUserData = vd; main_viewport->PlatformHandle = (void*)bd->hWnd; } From 0a8ab75e4b0a04677a1c06b22f71654a9058c4e8 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 5 Jul 2021 12:50:53 +0200 Subject: [PATCH 722/828] Docking: removed DockNodeFlagsOverrideClear flags from ImGuiWindowClass. (#2999, #3521, #3633) + extraded bits of metrics into DebugNodeDockNodeFlags() --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 39 +++++++++++++++++++++++++++++---------- imgui.h | 1 - 3 files changed, 31 insertions(+), 11 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 2be2e1b36170..6b3229ba28f3 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -144,6 +144,8 @@ Docking+Viewports Branch: - Docking: Clicking on the right-most close button of a docking node closes all windows. (#4186) - Docking: Fix IsWindowAppearing() and ImGuiCond_Appearing on docked windows. (#4177, #3982, #1497, #1061) +- Docking: (Internal/Experimental) Removed DockNodeFlagsOverrideClear flags from ImGuiWindowClass as + it is ambiguous how to apply them and we haven't got a use out of them yet. - Viewports: Fix popup/tooltip created without a parent window from being given a ParentViewportId value from the implicit/fallback window. (#4236, #2409) - Backends: Vulkan: Fix the use of the incorrect fence for secondary viewports. (#4208) [@FunMiles] diff --git a/imgui.cpp b/imgui.cpp index c05c486c92e6..9b0afe382cc3 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -13442,7 +13442,7 @@ static void ImGui::DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* nod else { // FIXME-DOCKING: Missing policies for conflict resolution, hence the "Experimental" tag on this. - node->LocalFlags &= ~window->WindowClass.DockNodeFlagsOverrideClear; + //node->LocalFlags &= ~window->WindowClass.DockNodeFlagsOverrideClear; node->LocalFlags |= window->WindowClass.DockNodeFlagsOverrideSet; } } @@ -16677,6 +16677,33 @@ void ImGui::DebugNodeColumns(ImGuiOldColumns* columns) TreePop(); } +static void DebugNodeDockNodeFlags(ImGuiDockNodeFlags* p_flags, const char* label, bool enabled) +{ + using namespace ImGui; + PushID(label); + PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, 0.0f)); + Text("%s:", label); + if (!enabled) + PushDisabled(); + CheckboxFlags("NoSplit", p_flags, ImGuiDockNodeFlags_NoSplit); + CheckboxFlags("NoResize", p_flags, ImGuiDockNodeFlags_NoResize); + CheckboxFlags("NoResizeX", p_flags, ImGuiDockNodeFlags_NoResizeX); + CheckboxFlags("NoResizeY",p_flags, ImGuiDockNodeFlags_NoResizeY); + CheckboxFlags("NoTabBar", p_flags, ImGuiDockNodeFlags_NoTabBar); + CheckboxFlags("HiddenTabBar", p_flags, ImGuiDockNodeFlags_HiddenTabBar); + CheckboxFlags("NoWindowMenuButton", p_flags, ImGuiDockNodeFlags_NoWindowMenuButton); + CheckboxFlags("NoCloseButton", p_flags, ImGuiDockNodeFlags_NoCloseButton); + CheckboxFlags("NoDocking", p_flags, ImGuiDockNodeFlags_NoDocking); + CheckboxFlags("NoDockingSplitMe", p_flags, ImGuiDockNodeFlags_NoDockingSplitMe); + CheckboxFlags("NoDockingSplitOther", p_flags, ImGuiDockNodeFlags_NoDockingSplitOther); + CheckboxFlags("NoDockingOverMe", p_flags, ImGuiDockNodeFlags_NoDockingOverMe); + CheckboxFlags("NoDockingOverOther", p_flags, ImGuiDockNodeFlags_NoDockingOverOther); + if (!enabled) + PopDisabled(); + PopStyleVar(); + PopID(); +} + // [DEBUG] Display contents of ImDockNode void ImGui::DebugNodeDockNode(ImGuiDockNode* node, const char* label) { @@ -16709,15 +16736,7 @@ void ImGui::DebugNodeDockNode(ImGuiDockNode* node, const char* label) node->WantLockSizeOnce ? " WantLockSizeOnce" : ""); if (TreeNode("flags", "LocalFlags: 0x%04X SharedFlags: 0x%04X", node->LocalFlags, node->SharedFlags)) { - CheckboxFlags("LocalFlags: NoDocking", &node->LocalFlags, ImGuiDockNodeFlags_NoDocking); - CheckboxFlags("LocalFlags: NoSplit", &node->LocalFlags, ImGuiDockNodeFlags_NoSplit); - CheckboxFlags("LocalFlags: NoResize", &node->LocalFlags, ImGuiDockNodeFlags_NoResize); - CheckboxFlags("LocalFlags: NoResizeX", &node->LocalFlags, ImGuiDockNodeFlags_NoResizeX); - CheckboxFlags("LocalFlags: NoResizeY", &node->LocalFlags, ImGuiDockNodeFlags_NoResizeY); - CheckboxFlags("LocalFlags: NoTabBar", &node->LocalFlags, ImGuiDockNodeFlags_NoTabBar); - CheckboxFlags("LocalFlags: HiddenTabBar", &node->LocalFlags, ImGuiDockNodeFlags_HiddenTabBar); - CheckboxFlags("LocalFlags: NoWindowMenuButton", &node->LocalFlags, ImGuiDockNodeFlags_NoWindowMenuButton); - CheckboxFlags("LocalFlags: NoCloseButton", &node->LocalFlags, ImGuiDockNodeFlags_NoCloseButton); + DebugNodeDockNodeFlags(&node->LocalFlags, "LocalFlags", true); TreePop(); } if (node->ParentNode) diff --git a/imgui.h b/imgui.h index 8f07cfc5cb2a..cbdd67e361ad 100644 --- a/imgui.h +++ b/imgui.h @@ -2068,7 +2068,6 @@ struct ImGuiWindowClass ImGuiViewportFlags ViewportFlagsOverrideClear; // Viewport flags to clear when a window of this class owns a viewport. This allows you to enforce OS decoration or task bar icon, override the defaults on a per-window basis. ImGuiTabItemFlags TabItemFlagsOverrideSet; // [EXPERIMENTAL] TabItem flags to set when a window of this class gets submitted into a dock node tab bar. May use with ImGuiTabItemFlags_Leading or ImGuiTabItemFlags_Trailing. ImGuiDockNodeFlags DockNodeFlagsOverrideSet; // [EXPERIMENTAL] Dock node flags to set when a window of this class is hosted by a dock node (it doesn't have to be selected!) - ImGuiDockNodeFlags DockNodeFlagsOverrideClear; // [EXPERIMENTAL] bool DockingAlwaysTabBar; // Set to true to enforce single floating windows of this class always having their own docking node (equivalent of setting the global io.ConfigDockingAlwaysTabBar) bool DockingAllowUnclassed; // Set to true to allow windows of this class to be docked/merged with an unclassed window. // FIXME-DOCK: Move to DockNodeFlags override? From 6136b3844bb03311c1c0f09cfb00d8c80fe24026 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 5 Jul 2021 13:05:18 +0200 Subject: [PATCH 723/828] Docking: Reworked node flags saving/inheritance... (#4292, #3834, #3633, #3521, #3492, #3335, #2999, #2648) ..so that flags enforced by docked windows via the DockNodeFlagsOverrideSet mechanism are are not left in empty dockspace nodes once the windows gets undocked. --- docs/CHANGELOG.txt | 3 ++ imgui.cpp | 70 ++++++++++++++++++++++++++++------------------ imgui_internal.h | 22 +++++++++------ 3 files changed, 59 insertions(+), 36 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 6b3229ba28f3..546907b34321 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -144,6 +144,9 @@ Docking+Viewports Branch: - Docking: Clicking on the right-most close button of a docking node closes all windows. (#4186) - Docking: Fix IsWindowAppearing() and ImGuiCond_Appearing on docked windows. (#4177, #3982, #1497, #1061) +- Docking: Reworked node flags saving/inheritance so that flags enforced by docked windows via the + DockNodeFlagsOverrideSet mechanism are are not left in empty dockspace nodes once the windows gets undocked. + (#4292, #3834, #3633, #3521, #3492, #3335, #2999, #2648) - Docking: (Internal/Experimental) Removed DockNodeFlagsOverrideClear flags from ImGuiWindowClass as it is ambiguous how to apply them and we haven't got a use out of them yet. - Viewports: Fix popup/tooltip created without a parent window from being given a ParentViewportId value diff --git a/imgui.cpp b/imgui.cpp index 9b0afe382cc3..a49dc9deeea0 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -12770,7 +12770,7 @@ static void ImGui::DockContextBuildNodesFromSettings(ImGuiContext* ctx, ImGuiDoc node->ParentNode->ChildNodes[1] = node; node->SelectedTabId = settings->SelectedTabId; node->SplitAxis = (ImGuiAxis)settings->SplitAxis; - node->LocalFlags |= (settings->Flags & ImGuiDockNodeFlags_SavedFlagsMask_); + node->SetLocalFlags(settings->Flags & ImGuiDockNodeFlags_SavedFlagsMask_); // Bind host window immediately if it already exist (in case of a rebuild) // This is useful as the RootWindowForTitleBarHighlight links necessary to highlight the currently focused node requires node->HostWindow to be set. @@ -12912,7 +12912,7 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) new_node->HostWindow = node->HostWindow; node = new_node; } - node->LocalFlags &= ~ImGuiDockNodeFlags_HiddenTabBar; + node->SetLocalFlags(node->LocalFlags & ~ImGuiDockNodeFlags_HiddenTabBar); if (node != payload_node) { @@ -12950,8 +12950,8 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) IM_ASSERT(last_focused_node != NULL); ImGuiDockNode* last_focused_root_node = DockNodeGetRootNode(last_focused_node); IM_ASSERT(last_focused_root_node == DockNodeGetRootNode(payload_node)); - last_focused_node->LocalFlags |= ImGuiDockNodeFlags_CentralNode; - node->LocalFlags &= ~ImGuiDockNodeFlags_CentralNode; + last_focused_node->SetLocalFlags(last_focused_node->LocalFlags |= ImGuiDockNodeFlags_CentralNode); + node->SetLocalFlags(node->LocalFlags & ~ImGuiDockNodeFlags_CentralNode); last_focused_root_node->CentralNode = last_focused_node; } @@ -13112,7 +13112,7 @@ bool ImGui::DockContextCalcDropPosForDocking(ImGuiWindow* target, ImGuiDockNode* ImGuiDockNode::ImGuiDockNode(ImGuiID id) { ID = id; - SharedFlags = LocalFlags = ImGuiDockNodeFlags_None; + SharedFlags = LocalFlags = LocalFlagsInWindows = MergedFlags = ImGuiDockNodeFlags_None; ParentNode = ChildNodes[0] = ChildNodes[1] = NULL; TabBar = NULL; SplitAxis = ImGuiAxis_None; @@ -13416,6 +13416,7 @@ static void ImGui::DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* nod // Remove inactive windows // Merge node flags overrides stored in windows + node->LocalFlagsInWindows = ImGuiDockNodeFlags_None; for (int window_n = 0; window_n < node->Windows.Size; window_n++) { ImGuiWindow* window = node->Windows[window_n]; @@ -13442,13 +13443,14 @@ static void ImGui::DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* nod else { // FIXME-DOCKING: Missing policies for conflict resolution, hence the "Experimental" tag on this. - //node->LocalFlags &= ~window->WindowClass.DockNodeFlagsOverrideClear; - node->LocalFlags |= window->WindowClass.DockNodeFlagsOverrideSet; + //node->LocalFlagsInWindow &= ~window->WindowClass.DockNodeFlagsOverrideClear; + node->LocalFlagsInWindows |= window->WindowClass.DockNodeFlagsOverrideSet; } } + node->UpdateMergedFlags(); // Auto-hide tab bar option - ImGuiDockNodeFlags node_flags = node->GetMergedFlags(); + ImGuiDockNodeFlags node_flags = node->MergedFlags; if (node->WantHiddenTabBarUpdate && node->Windows.Size == 1 && (node_flags & ImGuiDockNodeFlags_AutoHideTabBar) && !node->IsHiddenTabBar()) node->WantHiddenTabBarToggle = true; node->WantHiddenTabBarUpdate = false; @@ -13459,9 +13461,9 @@ static void ImGui::DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* nod // Apply toggles at a single point of the frame (here!) if (node->Windows.Size > 1) - node->LocalFlags &= ~ImGuiDockNodeFlags_HiddenTabBar; + node->SetLocalFlags(node->LocalFlags & ~ImGuiDockNodeFlags_HiddenTabBar); else if (node->WantHiddenTabBarToggle) - node->LocalFlags ^= ImGuiDockNodeFlags_HiddenTabBar; + node->SetLocalFlags(node->LocalFlags ^ ImGuiDockNodeFlags_HiddenTabBar); node->WantHiddenTabBarToggle = false; DockNodeUpdateVisibleFlag(node); @@ -13599,7 +13601,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) } } - const ImGuiDockNodeFlags node_flags = node->GetMergedFlags(); + const ImGuiDockNodeFlags node_flags = node->MergedFlags; // Decide if the node will have a close button and a window menu button node->HasWindowMenuButton = (node->Windows.Size > 0) && (node_flags & ImGuiDockNodeFlags_NoWindowMenuButton) == 0; @@ -13851,7 +13853,7 @@ bool ImGui::DockNodeBeginAmendTabBar(ImGuiDockNode* node) { if (node->TabBar == NULL || node->HostWindow == NULL) return false; - if (node->SharedFlags & ImGuiDockNodeFlags_KeepAliveOnly) + if (node->MergedFlags & ImGuiDockNodeFlags_KeepAliveOnly) return false; Begin(node->HostWindow->Name); PushOverrideID(node->ID); @@ -13929,7 +13931,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w ImGuiID focus_tab_id = 0; node->IsFocused = is_focused; - const ImGuiDockNodeFlags node_flags = node->GetMergedFlags(); + const ImGuiDockNodeFlags node_flags = node->MergedFlags; const bool has_window_menu_button = (node_flags & ImGuiDockNodeFlags_NoWindowMenuButton) == 0 && (style.WindowMenuButtonPosition != ImGuiDir_None); // In a dock node, the Collapse Button turns into the Window Menu button. @@ -14323,8 +14325,8 @@ static void ImGui::DockNodePreviewDockSetup(ImGuiWindow* host_window, ImGuiDockN IM_ASSERT(ref_node_for_rect->IsVisible); // Filter, figure out where we are allowed to dock - ImGuiDockNodeFlags src_node_flags = root_payload_as_host ? root_payload_as_host->GetMergedFlags() : root_payload->WindowClass.DockNodeFlagsOverrideSet; - ImGuiDockNodeFlags dst_node_flags = host_node ? host_node->GetMergedFlags() : host_window->WindowClass.DockNodeFlagsOverrideSet; + ImGuiDockNodeFlags src_node_flags = root_payload_as_host ? root_payload_as_host->MergedFlags : root_payload->WindowClass.DockNodeFlagsOverrideSet; + ImGuiDockNodeFlags dst_node_flags = host_node ? host_node->MergedFlags : host_window->WindowClass.DockNodeFlagsOverrideSet; data->IsCenterAvailable = true; if (is_outer_docking) data->IsCenterAvailable = false; @@ -14504,7 +14506,7 @@ static void ImGui::DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDock } // Stop after ImGuiDir_None - if ((host_node && (host_node->GetMergedFlags() & ImGuiDockNodeFlags_NoSplit)) || g.IO.ConfigDockingNoSplit) + if ((host_node && (host_node->MergedFlags & ImGuiDockNodeFlags_NoSplit)) || g.IO.ConfigDockingNoSplit) return; } } @@ -14557,6 +14559,9 @@ void ImGui::DockNodeTreeSplit(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImG child_1->SharedFlags = parent_node->SharedFlags & ImGuiDockNodeFlags_SharedFlagsInheritMask_; child_inheritor->LocalFlags = parent_node->LocalFlags & ImGuiDockNodeFlags_LocalFlagsTransferMask_; parent_node->LocalFlags &= ~ImGuiDockNodeFlags_LocalFlagsTransferMask_; + child_0->UpdateMergedFlags(); + child_1->UpdateMergedFlags(); + parent_node->UpdateMergedFlags(); if (child_inheritor->IsCentralNode()) DockNodeGetRootNode(parent_node)->CentralNode = child_inheritor; } @@ -14596,6 +14601,8 @@ void ImGui::DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImG parent_node->LocalFlags &= ~ImGuiDockNodeFlags_LocalFlagsTransferMask_; // Preserve Dockspace flag parent_node->LocalFlags |= (child_0 ? child_0->LocalFlags : 0) & ImGuiDockNodeFlags_LocalFlagsTransferMask_; parent_node->LocalFlags |= (child_1 ? child_1->LocalFlags : 0) & ImGuiDockNodeFlags_LocalFlagsTransferMask_; + parent_node->LocalFlagsInWindows = (child_0 ? child_0->LocalFlagsInWindows : 0) | (child_1 ? child_1->LocalFlagsInWindows : 0); // FIXME: Would be more consistent to update from actual windows + parent_node->UpdateMergedFlags(); if (child_0) { @@ -14727,7 +14734,7 @@ void ImGui::DockNodeTreeUpdateSplitter(ImGuiDockNode* node) bb.Max[axis ^ 1] += child_1->Size[axis ^ 1]; //if (g.IO.KeyCtrl) GetForegroundDrawList(g.CurrentWindow->Viewport)->AddRect(bb.Min, bb.Max, IM_COL32(255,0,255,255)); - const ImGuiDockNodeFlags merged_flags = child_0->GetMergedFlags() | child_1->GetMergedFlags(); + const ImGuiDockNodeFlags merged_flags = child_0->MergedFlags | child_1->MergedFlags; // Merged flags for BOTH childs const ImGuiDockNodeFlags no_resize_axis_flag = (axis == ImGuiAxis_X) ? ImGuiDockNodeFlags_NoResizeX : ImGuiDockNodeFlags_NoResizeY; if ((merged_flags & ImGuiDockNodeFlags_NoResize) || (merged_flags & no_resize_axis_flag)) { @@ -14921,7 +14928,7 @@ ImGuiID ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags { IMGUI_DEBUG_LOG_DOCKING("DockSpace: dockspace node 0x%08X created\n", id); node = DockContextAddNode(ctx, id); - node->LocalFlags |= ImGuiDockNodeFlags_CentralNode; + node->SetLocalFlags(ImGuiDockNodeFlags_CentralNode); } if (window_class && window_class->ClassId != node->WindowClass.ClassId) IMGUI_DEBUG_LOG_DOCKING("DockSpace: dockspace node 0x%08X: setup WindowClass 0x%08X -> 0x%08X\n", id, node->WindowClass.ClassId, window_class->ClassId); @@ -14933,10 +14940,10 @@ ImGuiID ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags if (node->LastFrameActive == g.FrameCount && !(flags & ImGuiDockNodeFlags_KeepAliveOnly)) { IM_ASSERT(node->IsDockSpace() == false && "Cannot call DockSpace() twice a frame with the same ID"); - node->LocalFlags |= ImGuiDockNodeFlags_DockSpace; + node->SetLocalFlags(node->LocalFlags | ImGuiDockNodeFlags_DockSpace); return id; } - node->LocalFlags |= ImGuiDockNodeFlags_DockSpace; + node->SetLocalFlags(node->LocalFlags | ImGuiDockNodeFlags_DockSpace); // Keep alive mode, this is allow windows docked into this node so stay docked even if they are not visible if (flags & ImGuiDockNodeFlags_KeepAliveOnly) @@ -14988,7 +14995,7 @@ ImGuiID ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags // The specific sub-property of _CentralNode we are interested in recovering here is the "Don't delete when empty" property, // as it doesn't make sense for an empty dockspace to not have this property. if (node->IsLeafNode() && !node->IsCentralNode()) - node->LocalFlags |= ImGuiDockNodeFlags_CentralNode; + node->SetLocalFlags(node->LocalFlags | ImGuiDockNodeFlags_CentralNode); // Update the node DockNodeUpdate(node); @@ -15127,7 +15134,7 @@ ImGuiID ImGui::DockBuilderAddNode(ImGuiID id, ImGuiDockNodeFlags flags) else { node = DockContextAddNode(ctx, id); - node->LocalFlags = flags; + node->SetLocalFlags(flags); } node->LastFrameAlive = ctx->FrameCount; // Set this otherwise BeginDocked will undock during the same frame. return node->ID; @@ -15142,7 +15149,7 @@ void ImGui::DockBuilderRemoveNode(ImGuiID node_id) DockBuilderRemoveNodeDockedWindows(node_id, true); DockBuilderRemoveNodeChildNodes(node_id); if (node->IsCentralNode() && node->ParentNode) - node->ParentNode->LocalFlags |= ImGuiDockNodeFlags_CentralNode; + node->ParentNode->SetLocalFlags(node->ParentNode->LocalFlags | ImGuiDockNodeFlags_CentralNode); DockContextRemoveNode(ctx, node, true); } @@ -15212,8 +15219,8 @@ void ImGui::DockBuilderRemoveNodeChildNodes(ImGuiID root_id) } else if (has_central_node) { - root_node->LocalFlags |= ImGuiDockNodeFlags_CentralNode; root_node->CentralNode = root_node; + root_node->SetLocalFlags(root_node->LocalFlags | ImGuiDockNodeFlags_CentralNode); } } @@ -15295,10 +15302,12 @@ static ImGuiDockNode* DockBuilderCopyNodeRec(ImGuiDockNode* src_node, ImGuiID ds ImGuiDockNode* dst_node = ImGui::DockContextAddNode(ctx, dst_node_id_if_known); dst_node->SharedFlags = src_node->SharedFlags; dst_node->LocalFlags = src_node->LocalFlags; + dst_node->LocalFlagsInWindows = ImGuiDockNodeFlags_None; dst_node->Pos = src_node->Pos; dst_node->Size = src_node->Size; dst_node->SizeRef = src_node->SizeRef; dst_node->SplitAxis = src_node->SplitAxis; + dst_node->UpdateMergedFlags(); out_node_remap_pairs->push_back(src_node->ID); out_node_remap_pairs->push_back(dst_node->ID); @@ -15606,7 +15615,7 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) window->DockIsActive = true; window->DockNodeIsVisible = true; window->DockTabIsVisible = false; - if (node->SharedFlags & ImGuiDockNodeFlags_KeepAliveOnly) + if (node->MergedFlags & ImGuiDockNodeFlags_KeepAliveOnly) return; // When the window is selected we mark it as visible. @@ -16734,9 +16743,16 @@ void ImGui::DebugNodeDockNode(ImGuiDockNode* node, const char* label) node->IsCentralNode() ? " IsCentralNode" : "", is_alive ? " IsAlive" : "", is_active ? " IsActive" : "", node->WantLockSizeOnce ? " WantLockSizeOnce" : ""); - if (TreeNode("flags", "LocalFlags: 0x%04X SharedFlags: 0x%04X", node->LocalFlags, node->SharedFlags)) + if (TreeNode("flags", "Flags Merged: 0x%04X, Local: 0x%04X, InWindows: 0x%04X, Shared: 0x%04X", node->MergedFlags, node->LocalFlags, node->LocalFlagsInWindows, node->SharedFlags)) { - DebugNodeDockNodeFlags(&node->LocalFlags, "LocalFlags", true); + if (BeginTable("flags", 4)) + { + TableNextColumn(); DebugNodeDockNodeFlags(&node->MergedFlags, "MergedFlags", false); + TableNextColumn(); DebugNodeDockNodeFlags(&node->LocalFlags, "LocalFlags", true); + TableNextColumn(); DebugNodeDockNodeFlags(&node->LocalFlagsInWindows, "LocalFlagsInWindows", false); + TableNextColumn(); DebugNodeDockNodeFlags(&node->SharedFlags, "SharedFlags", true); + EndTable(); + } TreePop(); } if (node->ParentNode) diff --git a/imgui_internal.h b/imgui_internal.h index 6e3f791a7eee..bcf0214cc1b7 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1312,12 +1312,14 @@ enum ImGuiDockNodeState ImGuiDockNodeState_HostWindowVisible }; -// sizeof() 116~160 +// sizeof() 156~192 struct IMGUI_API ImGuiDockNode { ImGuiID ID; - ImGuiDockNodeFlags SharedFlags; // Flags shared by all nodes of a same dockspace hierarchy (inherited from the root node) - ImGuiDockNodeFlags LocalFlags; // Flags specific to this node + ImGuiDockNodeFlags SharedFlags; // (Write) Flags shared by all nodes of a same dockspace hierarchy (inherited from the root node) + ImGuiDockNodeFlags LocalFlags; // (Write) Flags specific to this node + ImGuiDockNodeFlags LocalFlagsInWindows; // (Write) Flags specific to this node, applied from windows + ImGuiDockNodeFlags MergedFlags; // (Read) Effective flags (== SharedFlags | LocalFlagsInNode | LocalFlagsInWindows) ImGuiDockNodeState State; ImGuiDockNode* ParentNode; ImGuiDockNode* ChildNodes[2]; // [Split node only] Child nodes (left/right or top/bottom). Consider switching to an array. @@ -1356,16 +1358,18 @@ struct IMGUI_API ImGuiDockNode ImGuiDockNode(ImGuiID id); ~ImGuiDockNode(); bool IsRootNode() const { return ParentNode == NULL; } - bool IsDockSpace() const { return (LocalFlags & ImGuiDockNodeFlags_DockSpace) != 0; } - bool IsFloatingNode() const { return ParentNode == NULL && (LocalFlags & ImGuiDockNodeFlags_DockSpace) == 0; } - bool IsCentralNode() const { return (LocalFlags & ImGuiDockNodeFlags_CentralNode) != 0; } - bool IsHiddenTabBar() const { return (LocalFlags & ImGuiDockNodeFlags_HiddenTabBar) != 0; } // Hidden tab bar can be shown back by clicking the small triangle - bool IsNoTabBar() const { return (LocalFlags & ImGuiDockNodeFlags_NoTabBar) != 0; } // Never show a tab bar + bool IsDockSpace() const { return (MergedFlags & ImGuiDockNodeFlags_DockSpace) != 0; } + bool IsFloatingNode() const { return ParentNode == NULL && (MergedFlags & ImGuiDockNodeFlags_DockSpace) == 0; } + bool IsCentralNode() const { return (MergedFlags & ImGuiDockNodeFlags_CentralNode) != 0; } + bool IsHiddenTabBar() const { return (MergedFlags & ImGuiDockNodeFlags_HiddenTabBar) != 0; } // Hidden tab bar can be shown back by clicking the small triangle + bool IsNoTabBar() const { return (MergedFlags & ImGuiDockNodeFlags_NoTabBar) != 0; } // Never show a tab bar bool IsSplitNode() const { return ChildNodes[0] != NULL; } bool IsLeafNode() const { return ChildNodes[0] == NULL; } bool IsEmpty() const { return ChildNodes[0] == NULL && Windows.Size == 0; } - ImGuiDockNodeFlags GetMergedFlags() const { return SharedFlags | LocalFlags; } ImRect Rect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } + + void SetLocalFlags(ImGuiDockNodeFlags flags) { LocalFlags = flags; UpdateMergedFlags(); } + void UpdateMergedFlags() { MergedFlags = SharedFlags | LocalFlags | LocalFlagsInWindows; } }; // List of colors that are stored at the time of Begin() into Docked Windows. From 9e8e5ac36310607012e551bb04633039c2125c87 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 5 Jul 2021 13:28:54 +0200 Subject: [PATCH 724/828] Docking: Added ImGuiDockNodeFlags_NoDockingOverEmpty. Breaking definition of ImGuiDockNodeFlags_NoDockingOverOther which now means "non empty node". (#3492, #2648, #4292) --- imgui.cpp | 7 ++++++- imgui_internal.h | 7 ++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index a49dc9deeea0..5de5f63e1173 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -14336,7 +14336,11 @@ static void ImGui::DockNodePreviewDockSetup(ImGuiWindow* host_window, ImGuiDockN data->IsCenterAvailable = false; else if ((!host_node || !host_node->IsEmpty()) && root_payload_as_host && root_payload_as_host->IsSplitNode() && (root_payload_as_host->OnlyNodeWithWindows == NULL)) // Is _visibly_ split? data->IsCenterAvailable = false; - else if ((dst_node_flags & ImGuiDockNodeFlags_NoDockingOverMe) || (src_node_flags & ImGuiDockNodeFlags_NoDockingOverOther)) + else if (dst_node_flags & ImGuiDockNodeFlags_NoDockingOverMe) + data->IsCenterAvailable = false; + else if ((src_node_flags & ImGuiDockNodeFlags_NoDockingOverOther) && (!host_node || !host_node->IsEmpty())) + data->IsCenterAvailable = false; + else if ((src_node_flags & ImGuiDockNodeFlags_NoDockingOverEmpty) && host_node && host_node->IsEmpty()) data->IsCenterAvailable = false; data->IsSidesAvailable = true; @@ -16707,6 +16711,7 @@ static void DebugNodeDockNodeFlags(ImGuiDockNodeFlags* p_flags, const char* labe CheckboxFlags("NoDockingSplitOther", p_flags, ImGuiDockNodeFlags_NoDockingSplitOther); CheckboxFlags("NoDockingOverMe", p_flags, ImGuiDockNodeFlags_NoDockingOverMe); CheckboxFlags("NoDockingOverOther", p_flags, ImGuiDockNodeFlags_NoDockingOverOther); + CheckboxFlags("NoDockingOverEmpty", p_flags, ImGuiDockNodeFlags_NoDockingOverEmpty); if (!enabled) PopDisabled(); PopStyleVar(); diff --git a/imgui_internal.h b/imgui_internal.h index bcf0214cc1b7..d1e164a5d4ae 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1286,9 +1286,10 @@ enum ImGuiDockNodeFlagsPrivate_ ImGuiDockNodeFlags_NoDockingSplitMe = 1 << 17, // [EXPERIMENTAL] Prevent another window/node from splitting this node. ImGuiDockNodeFlags_NoDockingSplitOther = 1 << 18, // [EXPERIMENTAL] Prevent this node from splitting another window/node. ImGuiDockNodeFlags_NoDockingOverMe = 1 << 19, // [EXPERIMENTAL] Prevent another window/node to be docked over this node. - ImGuiDockNodeFlags_NoDockingOverOther = 1 << 20, // [EXPERIMENTAL] Prevent this node to be docked over another window/node. - ImGuiDockNodeFlags_NoResizeX = 1 << 21, // [EXPERIMENTAL] - ImGuiDockNodeFlags_NoResizeY = 1 << 22, // [EXPERIMENTAL] + ImGuiDockNodeFlags_NoDockingOverOther = 1 << 20, // [EXPERIMENTAL] Prevent this node to be docked over another window or non-empty node. + ImGuiDockNodeFlags_NoDockingOverEmpty = 1 << 21, // [EXPERIMENTAL] Prevent this node to be docked over an empty node (e.g. DockSpace with no other windows) + ImGuiDockNodeFlags_NoResizeX = 1 << 22, // [EXPERIMENTAL] + ImGuiDockNodeFlags_NoResizeY = 1 << 23, // [EXPERIMENTAL] ImGuiDockNodeFlags_SharedFlagsInheritMask_ = ~0, ImGuiDockNodeFlags_NoResizeFlagsMask_ = ImGuiDockNodeFlags_NoResize | ImGuiDockNodeFlags_NoResizeX | ImGuiDockNodeFlags_NoResizeY, ImGuiDockNodeFlags_LocalFlagsMask_ = ImGuiDockNodeFlags_NoSplit | ImGuiDockNodeFlags_NoResizeFlagsMask_ | ImGuiDockNodeFlags_AutoHideTabBar | ImGuiDockNodeFlags_DockSpace | ImGuiDockNodeFlags_CentralNode | ImGuiDockNodeFlags_NoTabBar | ImGuiDockNodeFlags_HiddenTabBar | ImGuiDockNodeFlags_NoWindowMenuButton | ImGuiDockNodeFlags_NoCloseButton | ImGuiDockNodeFlags_NoDocking, From cd4cc9ff0d2995b683129fb77c05a856110141a9 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 7 Jul 2021 16:18:11 +0200 Subject: [PATCH 725/828] Docking: Fixed crash issues using DockBuilderRemoveNode() in some situations. (#3111, #3179, #3203, #4295) If the deleted root node isn't part of a dockspace with a central node, it won't be "protected" but removed when last window gets removed. --- docs/CHANGELOG.txt | 1 + imgui.cpp | 12 +++++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 546907b34321..b1647740d8d7 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -144,6 +144,7 @@ Docking+Viewports Branch: - Docking: Clicking on the right-most close button of a docking node closes all windows. (#4186) - Docking: Fix IsWindowAppearing() and ImGuiCond_Appearing on docked windows. (#4177, #3982, #1497, #1061) +- Docking: Fix crash issues using DockBuilderRemoveNode() in some situations. (#3111, #3179, #3203, #4295) [@hsimyu] - Docking: Reworked node flags saving/inheritance so that flags enforced by docked windows via the DockNodeFlagsOverrideSet mechanism are are not left in empty dockspace nodes once the windows gets undocked. (#4292, #3834, #3633, #3521, #3492, #3335, #2999, #2648) diff --git a/imgui.cpp b/imgui.cpp index 5de5f63e1173..4a1f51929d39 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -12617,7 +12617,8 @@ static ImGuiDockNode* ImGui::DockContextFindNodeByID(ImGuiContext* ctx, ImGuiID ImGuiID ImGui::DockContextGenNodeID(ImGuiContext* ctx) { // Generate an ID for new node (the exact ID value doesn't matter as long as it is not already used) - // FIXME-OPT FIXME-DOCK: This is suboptimal, even if the node count is small enough not to be a worry. We should poke in ctx->Nodes to find a suitable ID faster. + // FIXME-OPT FIXME-DOCK: This is suboptimal, even if the node count is small enough not to be a worry.0 + // We should poke in ctx->Nodes to find a suitable ID faster. Even more so trivial that ctx->Nodes lookup is already sorted. ImGuiID id = 0x0001; while (DockContextFindNodeByID(ctx, id) != NULL) id++; @@ -15125,11 +15126,11 @@ void ImGui::DockBuilderSetNodeSize(ImGuiID node_id, ImVec2 size) ImGuiID ImGui::DockBuilderAddNode(ImGuiID id, ImGuiDockNodeFlags flags) { ImGuiContext* ctx = GImGui; - ImGuiDockNode* node = NULL; if (id != 0) DockBuilderRemoveNode(id); + ImGuiDockNode* node = NULL; if (flags & ImGuiDockNodeFlags_DockSpace) { DockSpace(id, ImVec2(0, 0), (flags & ~ImGuiDockNodeFlags_DockSpace) | ImGuiDockNodeFlags_KeepAliveOnly); @@ -15152,6 +15153,10 @@ void ImGui::DockBuilderRemoveNode(ImGuiID node_id) return; DockBuilderRemoveNodeDockedWindows(node_id, true); DockBuilderRemoveNodeChildNodes(node_id); + // Node may have moved or deleted if e.g. any merge happened + node = DockContextFindNodeByID(ctx, node_id); + if (node == NULL) + return; if (node->IsCentralNode() && node->ParentNode) node->ParentNode->SetLocalFlags(node->ParentNode->LocalFlags | ImGuiDockNodeFlags_CentralNode); DockContextRemoveNode(ctx, node, true); @@ -15334,11 +15339,12 @@ void ImGui::DockBuilderCopyNode(ImGuiID src_node_id, ImGuiID dst_node_id, ImVect IM_ASSERT(dst_node_id != 0); IM_ASSERT(out_node_remap_pairs != NULL); + DockBuilderRemoveNode(dst_node_id); + ImGuiDockNode* src_node = DockContextFindNodeByID(ctx, src_node_id); IM_ASSERT(src_node != NULL); out_node_remap_pairs->clear(); - DockBuilderRemoveNode(dst_node_id); DockBuilderCopyNodeRec(src_node, dst_node_id, out_node_remap_pairs); IM_ASSERT((out_node_remap_pairs->Size % 2) == 0); From eb6f4b1a6a03eb979256c7f982773fb2f7049f61 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 7 Jul 2021 18:22:55 +0200 Subject: [PATCH 726/828] Docking: Fix crash when a dock node gets re-qualified as dockspace>floating>dockspace.. (#3203, #4295) Which tends to happen when incorrectly calling DockBuilderAddNode() without ImGuiDockNodeFlags_Dockspace and using it as a Dockspace on the next frame after the floating window hosting the node has been automatically created. --- docs/CHANGELOG.txt | 5 ++++- imgui.cpp | 21 +++++++++++++++++---- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index b1647740d8d7..67d0ed5e2690 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -144,7 +144,10 @@ Docking+Viewports Branch: - Docking: Clicking on the right-most close button of a docking node closes all windows. (#4186) - Docking: Fix IsWindowAppearing() and ImGuiCond_Appearing on docked windows. (#4177, #3982, #1497, #1061) -- Docking: Fix crash issues using DockBuilderRemoveNode() in some situations. (#3111, #3179, #3203, #4295) [@hsimyu] +- Docking: Fix crash using DockBuilderRemoveNode() in some situations. (#3111, #3179, #3203, #4295) [@hsimyu] +- Docking: Fix crash when a dock node gets re-qualified as dockspace>floating>dockspace, which tends to happen + when incorrectly calling DockBuilderAddNode() without ImGuiDockNodeFlags_Dockspace and using it as a Dockspace + on the next frame after the floating window hosting the node has been automatically created. (#3203, #4295) - Docking: Reworked node flags saving/inheritance so that flags enforced by docked windows via the DockNodeFlagsOverrideSet mechanism are are not left in empty dockspace nodes once the windows gets undocked. (#4292, #3834, #3633, #3521, #3492, #3335, #2999, #2648) diff --git a/imgui.cpp b/imgui.cpp index 4a1f51929d39..09774d488417 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -13520,6 +13520,20 @@ static void ImGui::DockNodeUpdateForRootNode(ImGuiDockNode* node) } } +static void DockNodeSetupHostWindow(ImGuiDockNode* node, ImGuiWindow* host_window) +{ + // Remove ourselves from any previous different host window + // This can happen if a user mistakenly does (see #4295 for details): + // - N+0: DockBuilderAddNode(id, 0) // missing ImGuiDockNodeFlags_DockSpace + // - N+1: NewFrame() // will create floating host window for that node + // - N+1: DockSpace(id) // requalify node as dockspace, moving host window + if (node->HostWindow && node->HostWindow != host_window && node->HostWindow->DockNodeAsHost == node) + node->HostWindow->DockNodeAsHost = NULL; + + host_window->DockNodeAsHost = node; + node->HostWindow = host_window; +} + static void ImGui::DockNodeUpdate(ImGuiDockNode* node) { ImGuiContext& g = *GImGui; @@ -13668,8 +13682,8 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) PopStyleVar(); beginned_into_host_window = true; - node->HostWindow = host_window = g.CurrentWindow; - host_window->DockNodeAsHost = node; + host_window = g.CurrentWindow; + DockNodeSetupHostWindow(node, host_window); host_window->DC.CursorPos = host_window->Pos; node->Pos = host_window->Pos; node->Size = host_window->Size; @@ -14986,9 +15000,8 @@ ImGuiID ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags PopStyleVar(); ImGuiWindow* host_window = g.CurrentWindow; - host_window->DockNodeAsHost = node; + DockNodeSetupHostWindow(node, host_window); host_window->ChildId = window->GetID(title); - node->HostWindow = host_window; node->OnlyNodeWithWindows = NULL; IM_ASSERT(node->IsRootNode()); From 3d5dc0d93939bd9d547f6764025e141e124fac56 Mon Sep 17 00:00:00 2001 From: David Maas Date: Sun, 11 Jul 2021 05:29:13 -0500 Subject: [PATCH 727/828] Added missing IMGUI_API to GetViewportPlatformMonitor. (#4309) --- imgui_internal.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/imgui_internal.h b/imgui_internal.h index c8c8c3dac370..d087d5a08a49 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2585,11 +2585,11 @@ namespace ImGui IMGUI_API void CallContextHooks(ImGuiContext* context, ImGuiContextHookType type); // Viewports - IMGUI_API void TranslateWindowsInViewport(ImGuiViewportP* viewport, const ImVec2& old_pos, const ImVec2& new_pos); - IMGUI_API void ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale); - IMGUI_API void DestroyPlatformWindow(ImGuiViewportP* viewport); - IMGUI_API void SetCurrentViewport(ImGuiWindow* window, ImGuiViewportP* viewport); - const ImGuiPlatformMonitor* GetViewportPlatformMonitor(ImGuiViewport* viewport); + IMGUI_API void TranslateWindowsInViewport(ImGuiViewportP* viewport, const ImVec2& old_pos, const ImVec2& new_pos); + IMGUI_API void ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale); + IMGUI_API void DestroyPlatformWindow(ImGuiViewportP* viewport); + IMGUI_API void SetCurrentViewport(ImGuiWindow* window, ImGuiViewportP* viewport); + IMGUI_API const ImGuiPlatformMonitor* GetViewportPlatformMonitor(ImGuiViewport* viewport); // Settings IMGUI_API void MarkIniSettingsDirty(); From 80b5fb51edba2fd3dea76ec3e88153e2492243d1 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 2 Aug 2021 15:48:20 +0200 Subject: [PATCH 728/828] Backends: Win32, SDL, GLFW: only honor io.WantSetMousePos when focused + fix GLFW uninstalling handler + tweaks to reduce branch drift with docking. (#787, #2445, #2696, #3751, #4377) # Conflicts: # backends/imgui_impl_glfw.cpp # backends/imgui_impl_sdl.cpp # backends/imgui_impl_win32.cpp --- backends/imgui_impl_glfw.cpp | 9 +++++---- backends/imgui_impl_sdl.cpp | 11 +++++------ backends/imgui_impl_win32.cpp | 3 +-- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index 6489bd0238c9..61e1e377ef64 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -369,7 +369,7 @@ static void ImGui_ImplGlfw_UpdateMousePosAndButtons() io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); io.MouseHoveredViewport = 0; - // Update mouse button + // Update mouse buttons // (if a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame) for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) { @@ -384,10 +384,11 @@ static void ImGui_ImplGlfw_UpdateMousePosAndButtons() #ifdef __EMSCRIPTEN__ const bool focused = true; - IM_ASSERT(platform_io.Viewports.Size == 1); #else const bool focused = glfwGetWindowAttrib(window, GLFW_FOCUSED) != 0; #endif + GLFWwindow* mouse_window = (bd->MouseWindow == window || focused) ? window : NULL; + // Update mouse buttons if (focused) for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) @@ -399,10 +400,10 @@ static void ImGui_ImplGlfw_UpdateMousePosAndButtons() glfwSetCursorPos(window, (double)(mouse_pos_prev.x - viewport->Pos.x), (double)(mouse_pos_prev.y - viewport->Pos.y)); // Set Dear ImGui mouse position from OS position - if (bd->MouseWindow == window || focused) + if (mouse_window != NULL) { double mouse_x, mouse_y; - glfwGetCursorPos(window, &mouse_x, &mouse_y); + glfwGetCursorPos(mouse_window, &mouse_x, &mouse_y); if (io.ConfigFlags & 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) diff --git a/backends/imgui_impl_sdl.cpp b/backends/imgui_impl_sdl.cpp index 03a6499e4cee..aa2acbd118aa 100644 --- a/backends/imgui_impl_sdl.cpp +++ b/backends/imgui_impl_sdl.cpp @@ -74,7 +74,7 @@ static const Uint32 SDL_WINDOW_VULKAN = 0x10000000; #endif - // SDL Data +// SDL Data struct ImGui_ImplSDL2_Data { SDL_Window* Window; @@ -254,7 +254,7 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window, void* sdl_gl_context) // Our mouse update function expect PlatformHandle to be filled for the main viewport ImGuiViewport* main_viewport = ImGui::GetMainViewport(); main_viewport->PlatformHandle = (void*)window; -#if defined(_WIN32) +#ifdef _WIN32 SDL_SysWMinfo info; SDL_VERSION(&info.version); if (SDL_GetWindowWMInfo(window, &info)) @@ -318,7 +318,6 @@ void ImGui_ImplSDL2_Shutdown() ImGui_ImplSDL2_ShutdownPlatformInterface(); - // Destroy last known clipboard data if (bd->ClipboardTextData) SDL_free(bd->ClipboardTextData); for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++) @@ -332,12 +331,12 @@ void ImGui_ImplSDL2_Shutdown() // This code is incredibly messy because some of the functions we need for full viewport support are not available in SDL < 2.0.4. static void ImGui_ImplSDL2_UpdateMousePosAndButtons() { - ImGuiIO& io = ImGui::GetIO(); ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData(); + ImGuiIO& io = ImGui::GetIO(); ImVec2 mouse_pos_prev = io.MousePos; - io.MouseHoveredViewport = 0; io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); + io.MouseHoveredViewport = 0; // Update mouse buttons int mouse_x_local, mouse_y_local; @@ -391,7 +390,7 @@ static void ImGui_ImplSDL2_UpdateMousePosAndButtons() 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) - // Unlike local position obtained earlier this will be valid when straying out of bounds too. + // Unlike local position obtained earlier this will be valid when straying out of bounds. int window_x, window_y; SDL_GetWindowPosition(mouse_window, &window_x, &window_y); io.MousePos = ImVec2((float)(mouse_x_global - window_x), (float)(mouse_y_global - window_y)); diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index 71c3a56c9eec..051a1b7dacbb 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -72,7 +72,6 @@ static void ImGui_ImplWin32_InitPlatformInterface(); static void ImGui_ImplWin32_ShutdownPlatformInterface(); static void ImGui_ImplWin32_UpdateMonitors(); -// Win32 struct ImGui_ImplWin32_Data { HWND hWnd; @@ -259,7 +258,7 @@ static void ImGui_ImplWin32_UpdateMousePos() // 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.WantSetMousePos && mouse_window != NULL) + if (io.WantSetMousePos) { POINT pos = { (int)mouse_pos_prev.x, (int)mouse_pos_prev.y }; if ((io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) == 0) From 9c3359ef3900ad62e04cd38c847552018fcf23ba Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 19 Aug 2021 17:35:27 +0200 Subject: [PATCH 729/828] IO: modify io.AddFocusEvent() to tolerate in/out for multi-viewports. Amend 2f40be6. (#3532) --- imgui.cpp | 22 ++++++++++++++++------ imgui.h | 4 +++- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index c05b493df2e4..5dc7a3376f35 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1214,13 +1214,8 @@ void ImGuiIO::ClearInputCharacters() InputQueueCharacters.resize(0); } -void ImGuiIO::AddFocusEvent(bool focused) +void ImGuiIO::ClearInputKeys() { - if (focused) - return; - - // Clear buttons state when focus is lost - // (this is useful so e.g. releasing Alt after focus loss on Alt-Tab doesn't trigger the Alt menu toggle) memset(KeysDown, 0, sizeof(KeysDown)); for (int n = 0; n < IM_ARRAYSIZE(KeysDownDuration); n++) KeysDownDuration[n] = KeysDownDurationPrev[n] = -1.0f; @@ -1230,6 +1225,13 @@ void ImGuiIO::AddFocusEvent(bool focused) NavInputsDownDuration[n] = NavInputsDownDurationPrev[n] = -1.0f; } +void ImGuiIO::AddFocusEvent(bool focused) +{ + // We intentionally overwrite this and process in NewFrame(), in order to give a chance + // to multi-viewports backends to queue AddFocusEvent(false),AddFocusEvent(true) in same frame. + AppFocusLost = !focused; +} + //----------------------------------------------------------------------------- // [SECTION] MISC HELPERS/UTILITIES (Geometry functions) //----------------------------------------------------------------------------- @@ -4187,6 +4189,14 @@ void ImGui::NewFrame() g.DragDropWithinTarget = false; g.DragDropHoldJustPressedId = 0; + // Clear buttons state when focus is lost + // (this is useful so e.g. releasing Alt after focus loss on Alt-Tab doesn't trigger the Alt menu toggle) + if (g.IO.AppFocusLost) + { + g.IO.ClearInputKeys(); + g.IO.AppFocusLost = false; + } + // Update keyboard input state // Synchronize io.KeyMods with individual modifiers io.KeyXXX bools g.IO.KeyMods = GetMergedKeyModFlags(); diff --git a/imgui.h b/imgui.h index 4c0e395d96a9..4d9e8ddac38a 100644 --- a/imgui.h +++ b/imgui.h @@ -1950,8 +1950,9 @@ struct ImGuiIO IMGUI_API void AddInputCharacter(unsigned int c); // Queue new character input IMGUI_API void AddInputCharacterUTF16(ImWchar16 c); // Queue new character input from an UTF-16 character, it can be a surrogate IMGUI_API void AddInputCharactersUTF8(const char* str); // Queue new characters input from an UTF-8 string - IMGUI_API void ClearInputCharacters(); // Clear the text input buffer manually IMGUI_API void AddFocusEvent(bool focused); // Notifies Dear ImGui when hosting platform windows lose or gain input focus + IMGUI_API void ClearInputCharacters(); // [Internal] Clear the text input buffer manually + IMGUI_API void ClearInputKeys(); // [Internal] Release all keys //------------------------------------------------------------------ // Output - Updated by NewFrame() or EndFrame()/Render() @@ -1997,6 +1998,7 @@ struct ImGuiIO float NavInputsDownDuration[ImGuiNavInput_COUNT]; float NavInputsDownDurationPrev[ImGuiNavInput_COUNT]; float PenPressure; // Touch/Pen pressure (0.0f to 1.0f, should be >0.0f only when MouseDown[0] == true). Helper storage currently unused by Dear ImGui. + bool AppFocusLost; ImWchar16 InputQueueSurrogate; // For AddInputCharacterUTF16 ImVector InputQueueCharacters; // Queue of _characters_ input (obtained by platform backend). Fill using AddInputCharacter() helper. From 2e01952b359050d6e4bb3294bfb8eb21b3c0eed7 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 20 Aug 2021 20:30:51 +0200 Subject: [PATCH 730/828] Fix BeginDisabled(false), (#211, #4452) --- imgui.cpp | 7 ++----- imgui.h | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index aa391f89b97c..e484e96cd851 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7171,12 +7171,9 @@ void ImGui::BeginDisabled(bool disabled) { ImGuiContext& g = *GImGui; bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0; + g.DisabledAlphaBackup = g.Style.Alpha; if (!was_disabled && disabled) - { - //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * g.Style.DisabledAlpha); - g.DisabledAlphaBackup = g.Style.Alpha; - g.Style.Alpha *= g.Style.DisabledAlpha; - } + g.Style.Alpha *= g.Style.DisabledAlpha; // PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * g.Style.DisabledAlpha); //PushItemFlag(ImGuiItemFlags_Disabled, was_disabled || disabled); g.CurrentItemFlags |= ImGuiItemFlags_Disabled; g.ItemFlagsStack.push_back(g.CurrentItemFlags); diff --git a/imgui.h b/imgui.h index de3b9ceac1d7..8c77e2209b5f 100644 --- a/imgui.h +++ b/imgui.h @@ -62,7 +62,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.84" -#define IMGUI_VERSION_NUM 18400 +#define IMGUI_VERSION_NUM 18401 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE #define IMGUI_HAS_VIEWPORT // Viewport WIP branch From 47fb332fb20921658732107e115aa397e9b08cbe Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 20 Aug 2021 23:59:46 +0200 Subject: [PATCH 731/828] Fix BeginDisabled(false), again, (#211, #4452, #4453) Version 1.84.1 (forced pushed since our earlier versioning didn't sort correctly in github web) # Conflicts: # docs/CHANGELOG.txt --- docs/CHANGELOG.txt | 11 +++++++++++ imgui.cpp | 6 +++--- imgui.h | 5 +++-- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 772928d36bfe..62fbe7743762 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -99,6 +99,17 @@ Other changes: Note that Linux/Mac still have inconsistent support for multi-viewports. If you want to help see https://github.com/ocornut/imgui/issues/2117. +----------------------------------------------------------------------- + VERSION 1.84.1 (Released 2021-08-20) +----------------------------------------------------------------------- + +Decorated log: https://github.com/ocornut/imgui/releases/tag/v1.84.1 + +Other Changes: + +- Fixed BeginDisabled(false) - BeginDisabled(true) was working. (#211, #4452, #4453) + + ----------------------------------------------------------------------- VERSION 1.84 (Released 2021-08-20) ----------------------------------------------------------------------- diff --git a/imgui.cpp b/imgui.cpp index e484e96cd851..bc1616e4e063 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7165,7 +7165,7 @@ void ImGui::PopItemFlag() // - Those can be nested but this cannot be used to enable an already disabled section (a single BeginDisabled(true) in the stack is enough to keep things disabled) // - Visually this is currently altering alpha, but it is expected that in a future styling system this would work differently. // - Feedback welcome at https://github.com/ocornut/imgui/issues/211 -// - BeginDisabled(false) essentially does nothing but is provided to facilitate use of boolean expressions +// - BeginDisabled(false) essentially does nothing useful but is provided to facilitate use of boolean expressions. If you can avoid calling BeginDisabled(False)/EndDisabled() best to avoid it. // - Optimized shortcuts instead of PushStyleVar() + PushItemFlag() void ImGui::BeginDisabled(bool disabled) { @@ -7174,8 +7174,8 @@ void ImGui::BeginDisabled(bool disabled) g.DisabledAlphaBackup = g.Style.Alpha; if (!was_disabled && disabled) g.Style.Alpha *= g.Style.DisabledAlpha; // PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * g.Style.DisabledAlpha); - //PushItemFlag(ImGuiItemFlags_Disabled, was_disabled || disabled); - g.CurrentItemFlags |= ImGuiItemFlags_Disabled; + if (was_disabled || disabled) + g.CurrentItemFlags |= ImGuiItemFlags_Disabled; g.ItemFlagsStack.push_back(g.CurrentItemFlags); } diff --git a/imgui.h b/imgui.h index 8c77e2209b5f..99106b570d5c 100644 --- a/imgui.h +++ b/imgui.h @@ -61,8 +61,8 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) -#define IMGUI_VERSION "1.84" -#define IMGUI_VERSION_NUM 18401 +#define IMGUI_VERSION "1.84.1" +#define IMGUI_VERSION_NUM 18403 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE #define IMGUI_HAS_VIEWPORT // Viewport WIP branch @@ -831,6 +831,7 @@ namespace ImGui // Disabling [BETA API] // - Disable all user interactions and dim items visuals (applying style.DisabledAlpha over current colors) + // - BeginDisabled(false) essentially does nothing useful but is provided to facilitate use of boolean expressions. If you can avoid calling BeginDisabled(False)/EndDisabled() best to avoid it. IMGUI_API void BeginDisabled(bool disabled = true); IMGUI_API void EndDisabled(); From 6bd447c885f845776f2e14459e79dc790f493a7b Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 22 Aug 2021 18:46:49 +0200 Subject: [PATCH 732/828] Backends: GLFW: Fixed unused variable warning for empty assert macro. (#4459) --- backends/imgui_impl_glfw.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index 2cfbbf7eb18a..29632dcc74cc 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -913,6 +913,7 @@ static int ImGui_ImplGlfw_CreateVkSurface(ImGuiViewport* viewport, ImU64 vk_inst { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; + IM_UNUSED(bd); IM_ASSERT(bd->ClientApi == GlfwClientApi_Vulkan); VkResult err = glfwCreateWindowSurface((VkInstance)vk_instance, vd->Window, (const VkAllocationCallbacks*)vk_allocator, (VkSurfaceKHR*)out_vk_surface); return (int)err; From 58f5092c53dd7c3208d7ca717c861325616f58b0 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 24 Aug 2021 20:33:00 +0200 Subject: [PATCH 733/828] Docking: fixed settings load issue when mouse wheeling. (#4310) --- docs/CHANGELOG.txt | 5 +++++ imgui.cpp | 6 +++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 62fbe7743762..3f106c219703 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -99,6 +99,11 @@ Other changes: Note that Linux/Mac still have inconsistent support for multi-viewports. If you want to help see https://github.com/ocornut/imgui/issues/2117. +Docking+Viewports Branch: + +- Docking: fixed settings load issue when mouse wheeling. (#4310) + + ----------------------------------------------------------------------- VERSION 1.84.1 (Released 2021-08-20) ----------------------------------------------------------------------- diff --git a/imgui.cpp b/imgui.cpp index bc1616e4e063..bc425d79d28c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -13111,7 +13111,10 @@ void ImGui::DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node) DockNodeMoveWindows(new_node, node); DockSettingsRenameNodeReferences(node->ID, new_node->ID); for (int n = 0; n < new_node->Windows.Size; n++) + { + new_node->Windows[n]->Flags &= ~ImGuiWindowFlags_ChildWindow; UpdateWindowParentAndRootLinks(new_node->Windows[n], new_node->Windows[n]->Flags, NULL); + } node = new_node; } else @@ -13293,7 +13296,8 @@ static void ImGui::DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window window->DockNode = NULL; window->DockIsActive = window->DockTabWantClose = false; window->DockId = save_dock_id; - UpdateWindowParentAndRootLinks(window, window->Flags & ~ImGuiWindowFlags_ChildWindow, NULL); // Update immediately + window->Flags &= ~ImGuiWindowFlags_ChildWindow; + UpdateWindowParentAndRootLinks(window, window->Flags, NULL); // Update immediately // Remove window bool erased = false; From 0eb45a057783ab4ad0aa5b6ce7fadb87ba3b5afe Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 27 Aug 2021 21:29:27 +0200 Subject: [PATCH 734/828] Docking: fix 58f5092 (#4310) If we clear _ChildWindow flag we must remove it from here otherwise render loop will fail. --- imgui.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index bc425d79d28c..6700a2ae2c20 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -13112,8 +13112,11 @@ void ImGui::DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node) DockSettingsRenameNodeReferences(node->ID, new_node->ID); for (int n = 0; n < new_node->Windows.Size; n++) { - new_node->Windows[n]->Flags &= ~ImGuiWindowFlags_ChildWindow; - UpdateWindowParentAndRootLinks(new_node->Windows[n], new_node->Windows[n]->Flags, NULL); + ImGuiWindow* window = new_node->Windows[n]; + window->Flags &= ~ImGuiWindowFlags_ChildWindow; + if (window->ParentWindow) + window->ParentWindow->DC.ChildWindows.find_erase(window); + UpdateWindowParentAndRootLinks(window, window->Flags, NULL); } node = new_node; } @@ -13297,6 +13300,8 @@ static void ImGui::DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window window->DockIsActive = window->DockTabWantClose = false; window->DockId = save_dock_id; window->Flags &= ~ImGuiWindowFlags_ChildWindow; + if (window->ParentWindow) + window->ParentWindow->DC.ChildWindows.find_erase(window); UpdateWindowParentAndRootLinks(window, window->Flags, NULL); // Update immediately // Remove window From 53589092b22b6fa5e92140f30621b6d40eea4eb2 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 30 Aug 2021 20:53:22 +0200 Subject: [PATCH 735/828] Docking: warning fix for when IM_ASSERT() is empty --- imgui.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 5e86bc1d489c..3728c37b5f23 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -13356,7 +13356,8 @@ static void ImGui::DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window erased = true; break; } - IM_ASSERT(erased); + if (!erased) + IM_ASSERT(erased); if (node->VisibleWindow == window) node->VisibleWindow = NULL; From 40caab47480c93407243d1462e907506cab463ce Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 7 Sep 2021 18:21:47 +0200 Subject: [PATCH 736/828] Fixed bad merge of Changelog in docking branch --- docs/CHANGELOG.txt | 109 +++++++++++++++++++++------------------------ 1 file changed, 50 insertions(+), 59 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 69525b86c34b..97961447e632 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -30,62 +30,6 @@ HOW TO UPDATE? and API updates have been a little more frequent lately. They are documented below and in imgui.cpp and should not affect all users. - Please report any issue! ------------------------------------------------------------------------ - VERSION 1.85 WIP (In Progress) ------------------------------------------------------------------------ - -Breaking Changes: - -- Removed GetWindowContentRegionWidth() function. keep inline redirection helper. - Can use 'GetWindowContentRegionMax().x - GetWindowContentRegionMin().x' instead but it's not - very useful in practice, and the only use of it in the demo was illfit. - -Other Changes: - -- Windows: Fixed background order of overlapping childs submitted sequentially. (#4493) -- InputTextMultiline: Fixed label size not being included into window contents rect unless - the whole widget is clipped. -- InputText: Allow cancelling/validating input with gamepad nav events to facilitate undoing - an accidental press on NavInput (Triangle button on PS4/PS5) without a wired keyboard. (#2321) -- TextUnformatted: Accept null ranges including (NULL,NULL) without asserting, in order to conform - to common idioms (e.g. passing .data(), .data() + .size() from a null string). (#3615) -- Nav: Fixed toggling menu layer with Alt or exiting menu layer with Esc not moving mouse when - the NavEnableSetMousePos config flag is set. -- Menus: adjust closing logic to accomodate for varying font size and dpi. -- Drag and Drop: Fixed using BeginDragDropSource() inside a BeginChild() that returned false. (#4515) -- PlotHistogram: Fixed zero-line position when manually specifying min<0 and max>0. (#4349) [@filippocrocchini] -- IO: Added 'io.WantCaptureMouseUnlessPopupClose' alternative to `io.WantCaptureMouse'. (#4480) - This allows apps to receive the click on void when that click is used to close popup (by default, - clicking on a void when a popup is open will close the popup but not release io.WantCaptureMouse). -- Fonts: imgui_freetype: Fixed crash when FT_Render_Glyph() fails to render a glyph and returns NULL - (which apparently happens with Freetype 2.11). (#4394, #4145?). -- Fonts: Fixed ImFontAtlas::ClearInputData() marking atlas as not built. (#4455, #3487) -- Backends: OpenGL3: Fixed our custom GL loader conflicting with user using GL3W. (#4445) [@rokups] -- Backends: WebGPU: Fixed for latest specs. (#4472) [@Kangz] -- Backends: Metal: Fixed a crash when clipping rect larger than framebuffer is submitted via - a direct unclipped PushClipRect() call. (#4464) -- Backends: All renderers: Normalize clipping rect handling across backends. (#4464) - - ------------------------------------------------------------------------ - VERSION 1.84.2 (Released 2021-08-23) ------------------------------------------------------------------------ - -Decorated log: https://github.com/ocornut/imgui/releases/tag/v1.84.2 - -- Disabled: Fixed nested BeginDisabled()/EndDisabled() calls. (#211, #4452, #4453, #4462) [@Legulysse] -- Backends: OpenGL3: OpenGL: Fixed ES 3.0 shader ("#version 300 es") to use normal precision - floats. Avoid wobbly rendering at HD resolutions. (#4463) [@nicolasnoble] - - ------------------------------------------------------------------------ - VERSION 1.84.1 (Released 2021-08-20) ------------------------------------------------------------------------ - -Decorated log: https://github.com/ocornut/imgui/releases/tag/v1.84.1 - -- Disabled: Fixed BeginDisabled(false) - BeginDisabled(true) was working. (#211, #4452, #4453) - ----------------------------------------------------------------------- DOCKING+MULTI-VIEWPORT BRANCH (In Progress) @@ -155,9 +99,58 @@ Other changes: Note that Linux/Mac still have inconsistent support for multi-viewports. If you want to help see https://github.com/ocornut/imgui/issues/2117. +----------------------------------------------------------------------- + VERSION 1.85 WIP (In Progress) +----------------------------------------------------------------------- + +Breaking Changes: + +- Removed GetWindowContentRegionWidth() function. keep inline redirection helper. + Can use 'GetWindowContentRegionMax().x - GetWindowContentRegionMin().x' instead but it's not + very useful in practice, and the only use of it in the demo was illfit. + +Other Changes: + +- Windows: Fixed background order of overlapping childs submitted sequentially. (#4493) +- InputTextMultiline: Fixed label size not being included into window contents rect unless + the whole widget is clipped. +- InputText: Allow cancelling/validating input with gamepad nav events to facilitate undoing + an accidental press on NavInput (Triangle button on PS4/PS5) without a wired keyboard. (#2321) +- TextUnformatted: Accept null ranges including (NULL,NULL) without asserting, in order to conform + to common idioms (e.g. passing .data(), .data() + .size() from a null string). (#3615) +- Nav: Fixed toggling menu layer with Alt or exiting menu layer with Esc not moving mouse when + the NavEnableSetMousePos config flag is set. +- Menus: adjust closing logic to accomodate for varying font size and dpi. +- Drag and Drop: Fixed using BeginDragDropSource() inside a BeginChild() that returned false. (#4515) +- PlotHistogram: Fixed zero-line position when manually specifying min<0 and max>0. (#4349) [@filippocrocchini] +- IO: Added 'io.WantCaptureMouseUnlessPopupClose' alternative to `io.WantCaptureMouse'. (#4480) + This allows apps to receive the click on void when that click is used to close popup (by default, + clicking on a void when a popup is open will close the popup but not release io.WantCaptureMouse). +- Fonts: imgui_freetype: Fixed crash when FT_Render_Glyph() fails to render a glyph and returns NULL + (which apparently happens with Freetype 2.11). (#4394, #4145?). +- Fonts: Fixed ImFontAtlas::ClearInputData() marking atlas as not built. (#4455, #3487) +- Backends: OpenGL3: Fixed our custom GL loader conflicting with user using GL3W. (#4445) [@rokups] +- Backends: WebGPU: Fixed for latest specs. (#4472) [@Kangz] +- Backends: Metal: Fixed a crash when clipping rect larger than framebuffer is submitted via + a direct unclipped PushClipRect() call. (#4464) +- Backends: All renderers: Normalize clipping rect handling across backends. (#4464) + Docking+Viewports Branch: - Docking: fixed settings load issue when mouse wheeling. (#4310) +- Drag and Drop: Fixed using BeginDragDropSource() or BeginDragDropTarget() inside a Begin() that + returned false because the window is docked. (#4515) + + +----------------------------------------------------------------------- + VERSION 1.84.2 (Released 2021-08-23) +----------------------------------------------------------------------- + +Decorated log: https://github.com/ocornut/imgui/releases/tag/v1.84.2 + +- Disabled: Fixed nested BeginDisabled()/EndDisabled() calls. (#211, #4452, #4453, #4462) [@Legulysse] +- Backends: OpenGL3: OpenGL: Fixed ES 3.0 shader ("#version 300 es") to use normal precision + floats. Avoid wobbly rendering at HD resolutions. (#4463) [@nicolasnoble] ----------------------------------------------------------------------- @@ -166,9 +159,7 @@ Docking+Viewports Branch: Decorated log: https://github.com/ocornut/imgui/releases/tag/v1.84.1 -Other Changes: - -- Fixed BeginDisabled(false) - BeginDisabled(true) was working. (#211, #4452, #4453) +- Disabled: Fixed BeginDisabled(false) - BeginDisabled(true) was working. (#211, #4452, #4453) ----------------------------------------------------------------------- From cfb837203c5e7b550b03315d2a0fe1da0fa17b40 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 8 Sep 2021 15:05:27 +0200 Subject: [PATCH 737/828] Internals: refactored IsWindowHovered()/IsWindowFocused() to make their logic more similar + change underlying value of ImGuiHoveredFlags_AllowWhenBlockedByPopup + comment out docking only flags. --- imgui.cpp | 66 +++++++++++++++++++++++++------------------------------ imgui.h | 10 +++++---- 2 files changed, 36 insertions(+), 40 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index bb5f7d8572c9..ba47e2ec94af 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7286,37 +7286,31 @@ bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags) { IM_ASSERT((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0); // Flags not supported by this function ImGuiContext& g = *GImGui; - if (g.HoveredWindow == NULL) + ImGuiWindow* ref_window = g.HoveredWindow; + ImGuiWindow* cur_window = g.CurrentWindow; + if (ref_window == NULL) return false; if ((flags & ImGuiHoveredFlags_AnyWindow) == 0) { - ImGuiWindow* window = g.CurrentWindow; - switch (flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) - { - case ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows: - if (g.HoveredWindow->RootWindow != window->RootWindow) - return false; - break; - case ImGuiHoveredFlags_RootWindow: - if (g.HoveredWindow != window->RootWindow) - return false; - break; - case ImGuiHoveredFlags_ChildWindows: - if (!IsWindowChildOf(g.HoveredWindow, window)) - return false; - break; - default: - if (g.HoveredWindow != window) - return false; - break; - } + IM_ASSERT(cur_window); // Not inside a Begin()/End() + + if (flags & ImGuiHoveredFlags_RootWindow) + cur_window = cur_window->RootWindow; + + bool result; + if (flags & ImGuiHoveredFlags_ChildWindows) + result = IsWindowChildOf(ref_window, cur_window); + else + result = (ref_window == cur_window); + if (!result) + return false; } - if (!IsWindowContentHoverable(g.HoveredWindow, flags)) + if (!IsWindowContentHoverable(ref_window, flags)) return false; if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) - if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != g.HoveredWindow->MoveId) + if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != ref_window->MoveId) return false; return true; } @@ -7324,22 +7318,22 @@ bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags) bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags) { ImGuiContext& g = *GImGui; + ImGuiWindow* ref_window = g.NavWindow; + ImGuiWindow* cur_window = g.CurrentWindow; + if (ref_window == NULL) + return false; if (flags & ImGuiFocusedFlags_AnyWindow) - return g.NavWindow != NULL; + return true; + IM_ASSERT(cur_window); // Not inside a Begin()/End() - IM_ASSERT(g.CurrentWindow); // Not inside a Begin()/End() - switch (flags & (ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows)) - { - case ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows: - return g.NavWindow && g.NavWindow->RootWindow == g.CurrentWindow->RootWindow; - case ImGuiFocusedFlags_RootWindow: - return g.NavWindow == g.CurrentWindow->RootWindow; - case ImGuiFocusedFlags_ChildWindows: - return g.NavWindow && IsWindowChildOf(g.NavWindow, g.CurrentWindow); - default: - return g.NavWindow == g.CurrentWindow; - } + if (flags & ImGuiHoveredFlags_RootWindow) + cur_window = cur_window->RootWindow; + + if (flags & ImGuiHoveredFlags_ChildWindows) + return IsWindowChildOf(ref_window, cur_window); + else + return (ref_window == cur_window); } ImGuiID ImGui::GetWindowDockID() diff --git a/imgui.h b/imgui.h index 1dfc4e8829bc..ac11ce15aaef 100644 --- a/imgui.h +++ b/imgui.h @@ -1309,9 +1309,10 @@ enum ImGuiTableBgTarget_ enum ImGuiFocusedFlags_ { ImGuiFocusedFlags_None = 0, - ImGuiFocusedFlags_ChildWindows = 1 << 0, // IsWindowFocused(): Return true if any children of the window is focused - ImGuiFocusedFlags_RootWindow = 1 << 1, // IsWindowFocused(): Test from root window (top most parent of the current hierarchy) - ImGuiFocusedFlags_AnyWindow = 1 << 2, // IsWindowFocused(): Return true if any window is focused. Important: If you are trying to tell how to dispatch your low-level inputs, do NOT use this. Use 'io.WantCaptureMouse' instead! Please read the FAQ! + ImGuiFocusedFlags_ChildWindows = 1 << 0, // Return true if any children of the window is focused + ImGuiFocusedFlags_RootWindow = 1 << 1, // Test from root window (top most parent of the current hierarchy) + ImGuiFocusedFlags_AnyWindow = 1 << 2, // Return true if any window is focused. Important: If you are trying to tell how to dispatch your low-level inputs, do NOT use this. Use 'io.WantCaptureMouse' instead! Please read the FAQ! + //ImGuiFocusedFlags_DockHierarchy = 1 << 3, // Consider docking hierarchy (treat dockspace host as parent of docked window) ImGuiFocusedFlags_RootAndChildWindows = ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows }; @@ -1324,7 +1325,8 @@ enum ImGuiHoveredFlags_ ImGuiHoveredFlags_ChildWindows = 1 << 0, // IsWindowHovered() only: Return true if any children of the window is hovered ImGuiHoveredFlags_RootWindow = 1 << 1, // IsWindowHovered() only: Test from root window (top most parent of the current hierarchy) ImGuiHoveredFlags_AnyWindow = 1 << 2, // IsWindowHovered() only: Return true if any window is hovered - ImGuiHoveredFlags_AllowWhenBlockedByPopup = 1 << 3, // Return true even if a popup window is normally blocking access to this item/window + //ImGuiHoveredFlags_DockHierarchy = 1 << 3, // IsWindowHovered() only: Consider docking hierarchy (treat dockspace host as parent of docked window) + ImGuiHoveredFlags_AllowWhenBlockedByPopup = 1 << 4, // Return true even if a popup window is normally blocking access to this item/window //ImGuiHoveredFlags_AllowWhenBlockedByModal = 1 << 4, // Return true even if a modal popup window is normally blocking access to this item/window. FIXME-TODO: Unavailable yet. ImGuiHoveredFlags_AllowWhenBlockedByActiveItem = 1 << 5, // Return true even if an active item is blocking access to this item/window. Useful for Drag and Drop patterns. ImGuiHoveredFlags_AllowWhenOverlapped = 1 << 6, // Return true even if the position is obstructed or overlapped by another window From 6b1e094cfbd353792ab926dc054125e25d8370d8 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 8 Sep 2021 16:49:35 +0200 Subject: [PATCH 738/828] Fixed _ChildWindows from leaking docking hierarchy. Added ImGuiFocusedFlags_DockHierarchy and ImGuiHoveredFlags_DockHierarchy. --- docs/CHANGELOG.txt | 8 ++++++++ imgui.cpp | 20 ++++++++++++-------- imgui.h | 6 +++--- imgui_demo.cpp | 8 ++++++++ imgui_internal.h | 2 +- 5 files changed, 32 insertions(+), 12 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 97961447e632..a7e5064bcbb3 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -108,6 +108,10 @@ Breaking Changes: - Removed GetWindowContentRegionWidth() function. keep inline redirection helper. Can use 'GetWindowContentRegionMax().x - GetWindowContentRegionMin().x' instead but it's not very useful in practice, and the only use of it in the demo was illfit. +- (Docking branch) IsWindowFocused() and IsWindowHovered() with only the _ChildWindows flag + and without the _RootWindow flag used to leak docking hierarchy, so a docked window would + return as the child of the window hosting the dockspace. This was inconsistent and incorrect + with other behaviors so we fixed it. Added a new _DockHierarchy flag to opt-in this behavior. Other Changes: @@ -137,6 +141,10 @@ Other Changes: Docking+Viewports Branch: +- IsWindowFocused: Fixed using ImGuiFocusedFlags_ChildWindows (without _RootWindow) from leaking the + docking hierarchy. Added ImGuiFocusedFlags_DockHierarchy flag to consider docking hierarchy in the test. +- IsWindowHovered: Fixed using ImGuiHoveredFlags_ChildWindows (without _RootWindow) from leaking the + docking hierarchy. Added ImGuiHoveredFlags_DockHierarchy flag to consider docking hierarchy in the test. - Docking: fixed settings load issue when mouse wheeling. (#4310) - Drag and Drop: Fixed using BeginDragDropSource() or BeginDragDropTarget() inside a Begin() that returned false because the window is docked. (#4515) diff --git a/imgui.cpp b/imgui.cpp index ba47e2ec94af..634f6062dc4e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4024,7 +4024,7 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags() // Modal windows prevents mouse from hovering behind them. ImGuiWindow* modal_window = GetTopMostPopupModal(); - if (modal_window && g.HoveredWindow && !IsWindowChildOf(g.HoveredWindow->RootWindowDockTree, modal_window)) + if (modal_window && g.HoveredWindow && !IsWindowChildOf(g.HoveredWindow->RootWindowDockTree, modal_window, true)) clear_hovered_windows = true; // Disabled mouse? @@ -7254,15 +7254,16 @@ void ImGui::PopTextWrapPos() window->DC.TextWrapPosStack.pop_back(); } -// FIXME: We are exposing the docking hierarchy to end-user here (via IsWindowHovered, IsWindowFocused) which is unusual. -bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent) +bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool dock_hierarchy) { - if (window->RootWindowDockTree == potential_parent) + if ((dock_hierarchy ? window->RootWindowDockTree : window->RootWindow) == potential_parent) return true; while (window != NULL) { if (window == potential_parent) return true; + if (window == (dock_hierarchy ? window->RootWindowDockTree : window->RootWindow)) + return false; window = window->ParentWindow; } return false; @@ -7294,13 +7295,14 @@ bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags) if ((flags & ImGuiHoveredFlags_AnyWindow) == 0) { IM_ASSERT(cur_window); // Not inside a Begin()/End() + const bool dock_hierarchy = (flags & ImGuiHoveredFlags_DockHierarchy) != 0; if (flags & ImGuiHoveredFlags_RootWindow) - cur_window = cur_window->RootWindow; + cur_window = dock_hierarchy ? cur_window->RootWindowDockTree : cur_window->RootWindow; bool result; if (flags & ImGuiHoveredFlags_ChildWindows) - result = IsWindowChildOf(ref_window, cur_window); + result = IsWindowChildOf(ref_window, cur_window, dock_hierarchy); else result = (ref_window == cur_window); if (!result) @@ -7325,13 +7327,15 @@ bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags) return false; if (flags & ImGuiFocusedFlags_AnyWindow) return true; + IM_ASSERT(cur_window); // Not inside a Begin()/End() + const bool dock_hierarchy = (flags & ImGuiHoveredFlags_DockHierarchy) != 0; if (flags & ImGuiHoveredFlags_RootWindow) - cur_window = cur_window->RootWindow; + cur_window = dock_hierarchy ? cur_window->RootWindowDockTree : cur_window->RootWindow; if (flags & ImGuiHoveredFlags_ChildWindows) - return IsWindowChildOf(ref_window, cur_window); + return IsWindowChildOf(ref_window, cur_window, dock_hierarchy); else return (ref_window == cur_window); } diff --git a/imgui.h b/imgui.h index ac11ce15aaef..b95ea7784dc2 100644 --- a/imgui.h +++ b/imgui.h @@ -1312,7 +1312,7 @@ enum ImGuiFocusedFlags_ ImGuiFocusedFlags_ChildWindows = 1 << 0, // Return true if any children of the window is focused ImGuiFocusedFlags_RootWindow = 1 << 1, // Test from root window (top most parent of the current hierarchy) ImGuiFocusedFlags_AnyWindow = 1 << 2, // Return true if any window is focused. Important: If you are trying to tell how to dispatch your low-level inputs, do NOT use this. Use 'io.WantCaptureMouse' instead! Please read the FAQ! - //ImGuiFocusedFlags_DockHierarchy = 1 << 3, // Consider docking hierarchy (treat dockspace host as parent of docked window) + ImGuiFocusedFlags_DockHierarchy = 1 << 3, // Consider docking hierarchy (treat dockspace host as parent of docked window) ImGuiFocusedFlags_RootAndChildWindows = ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows }; @@ -1325,7 +1325,7 @@ enum ImGuiHoveredFlags_ ImGuiHoveredFlags_ChildWindows = 1 << 0, // IsWindowHovered() only: Return true if any children of the window is hovered ImGuiHoveredFlags_RootWindow = 1 << 1, // IsWindowHovered() only: Test from root window (top most parent of the current hierarchy) ImGuiHoveredFlags_AnyWindow = 1 << 2, // IsWindowHovered() only: Return true if any window is hovered - //ImGuiHoveredFlags_DockHierarchy = 1 << 3, // IsWindowHovered() only: Consider docking hierarchy (treat dockspace host as parent of docked window) + ImGuiHoveredFlags_DockHierarchy = 1 << 3, // IsWindowHovered() only: Consider docking hierarchy (treat dockspace host as parent of docked window) ImGuiHoveredFlags_AllowWhenBlockedByPopup = 1 << 4, // Return true even if a popup window is normally blocking access to this item/window //ImGuiHoveredFlags_AllowWhenBlockedByModal = 1 << 4, // Return true even if a modal popup window is normally blocking access to this item/window. FIXME-TODO: Unavailable yet. ImGuiHoveredFlags_AllowWhenBlockedByActiveItem = 1 << 5, // Return true even if an active item is blocking access to this item/window. Useful for Drag and Drop patterns. @@ -1346,7 +1346,7 @@ enum ImGuiDockNodeFlags_ ImGuiDockNodeFlags_NoDockingInCentralNode = 1 << 2, // Shared // Disable docking inside the Central Node, which will be always kept empty. ImGuiDockNodeFlags_PassthruCentralNode = 1 << 3, // Shared // Enable passthru dockspace: 1) DockSpace() will render a ImGuiCol_WindowBg background covering everything excepted the Central Node when empty. Meaning the host window should probably use SetNextWindowBgAlpha(0.0f) prior to Begin() when using this. 2) When Central Node is empty: let inputs pass-through + won't display a DockingEmptyBg background. See demo for details. ImGuiDockNodeFlags_NoSplit = 1 << 4, // Shared/Local // Disable splitting the node into smaller nodes. Useful e.g. when embedding dockspaces into a main root one (the root one may have splitting disabled to reduce confusion). Note: when turned off, existing splits will be preserved. - ImGuiDockNodeFlags_NoResize = 1 << 5, // Shared/Local // Disable resizing node using the splitter/separators. Useful with programatically setup dockspaces. + ImGuiDockNodeFlags_NoResize = 1 << 5, // Shared/Local // Disable resizing node using the splitter/separators. Useful with programmatically setup dockspaces. ImGuiDockNodeFlags_AutoHideTabBar = 1 << 6 // Shared/Local // Tab bar will automatically hide when there is a single window in the dock node. }; diff --git a/imgui_demo.cpp b/imgui_demo.cpp index b48f11f7068f..ebd1d962a3b3 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2334,11 +2334,15 @@ static void ShowDemoWindowWidgets() "IsWindowFocused() = %d\n" "IsWindowFocused(_ChildWindows) = %d\n" "IsWindowFocused(_ChildWindows|_RootWindow) = %d\n" + "IsWindowFocused(_ChildWindows|_RootWindow|_DockHierarchy) = %d\n" + "IsWindowFocused(_RootWindow|_DockHierarchy) = %d\n" "IsWindowFocused(_RootWindow) = %d\n" "IsWindowFocused(_AnyWindow) = %d\n", ImGui::IsWindowFocused(), ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows), ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow), + ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_DockHierarchy), + ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_DockHierarchy), ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow), ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow)); @@ -2350,6 +2354,8 @@ static void ShowDemoWindowWidgets() "IsWindowHovered(_AllowWhenBlockedByActiveItem) = %d\n" "IsWindowHovered(_ChildWindows) = %d\n" "IsWindowHovered(_ChildWindows|_RootWindow) = %d\n" + "IsWindowHovered(_ChildWindows|_RootWindow|_DockHierarchy) = %d\n" + "IsWindowHovered(_RootWindow|_DockHierarchy) = %d\n" "IsWindowHovered(_ChildWindows|_AllowWhenBlockedByPopup) = %d\n" "IsWindowHovered(_RootWindow) = %d\n" "IsWindowHovered(_AnyWindow) = %d\n", @@ -2358,6 +2364,8 @@ static void ShowDemoWindowWidgets() ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem), ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows), ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow), + ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_DockHierarchy), + ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_DockHierarchy), ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_AllowWhenBlockedByPopup), ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow), ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow)); diff --git a/imgui_internal.h b/imgui_internal.h index 52ff98741ba2..af48bcda1405 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2557,7 +2557,7 @@ namespace ImGui IMGUI_API ImGuiWindow* FindWindowByName(const char* name); IMGUI_API void UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window); IMGUI_API ImVec2 CalcWindowNextAutoFitSize(ImGuiWindow* window); - IMGUI_API bool IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent); + IMGUI_API bool IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool dock_hierarchy); IMGUI_API bool IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below); IMGUI_API bool IsWindowNavFocusable(ImGuiWindow* window); IMGUI_API void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond = 0); From 9a49c1ddbd8447c44c66867269a0a93d3f412800 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 9 Sep 2021 20:51:53 +0200 Subject: [PATCH 739/828] Viewports: fixed unnecessary creation of temporary viewports when multiple docked windows got reassigned to a new node (created mid-frame) which already has a HostWindow --- docs/CHANGELOG.txt | 6 ++++-- imgui.cpp | 10 +++++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index a7e5064bcbb3..ce1080790423 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -146,8 +146,10 @@ Docking+Viewports Branch: - IsWindowHovered: Fixed using ImGuiHoveredFlags_ChildWindows (without _RootWindow) from leaking the docking hierarchy. Added ImGuiHoveredFlags_DockHierarchy flag to consider docking hierarchy in the test. - Docking: fixed settings load issue when mouse wheeling. (#4310) -- Drag and Drop: Fixed using BeginDragDropSource() or BeginDragDropTarget() inside a Begin() that - returned false because the window is docked. (#4515) +- Docking + Drag and Drop: Fixed using BeginDragDropSource() or BeginDragDropTarget() inside a Begin() + that returned false because the window is docked. (#4515) +- Viewports: Viewports: fixed unnecessary creation of temporary viewports when multiple docked windows + got reassigned to a new node (created mid-frame) which already has a HostWindow. ----------------------------------------------------------------------- diff --git a/imgui.cpp b/imgui.cpp index 634f6062dc4e..d3f0391e233d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -12008,8 +12008,15 @@ static void ImGui::WindowSelectViewport(ImGuiWindow* window) else if ((flags & ImGuiWindowFlags_ChildWindow) || (flags & ImGuiWindowFlags_ChildMenu)) { // Always inherit viewport from parent window + if (window->DockNode && window->DockNode->HostWindow) + IM_ASSERT(window->DockNode->HostWindow->Viewport == window->ParentWindow->Viewport); window->Viewport = window->ParentWindow->Viewport; } + else if (window->DockNode && window->DockNode->HostWindow) + { + // This covers the "always inherit viewport from parent window" case for when a window reattach to a node that was just created mid-frame + window->Viewport = window->DockNode->HostWindow->Viewport; + } else if (flags & ImGuiWindowFlags_Tooltip) { window->Viewport = g.MouseViewport; @@ -12052,7 +12059,7 @@ static void ImGui::WindowSelectViewport(ImGuiWindow* window) else window->ViewportAllowPlatformMonitorExtend = window->Viewport->PlatformMonitor; } - else if (window->Viewport && window != window->Viewport->Window && window->Viewport->Window && !(flags & ImGuiWindowFlags_ChildWindow)) + else if (window->Viewport && window != window->Viewport->Window && window->Viewport->Window && !(flags & ImGuiWindowFlags_ChildWindow) && window->DockNode == NULL) { // When called from Begin() we don't have access to a proper version of the Hidden flag yet, so we replicate this code. const bool will_be_visible = (window->DockIsActive && !window->DockTabIsVisible) ? false : true; @@ -13445,6 +13452,7 @@ static void ImGui::DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window if (node->HostWindow->ViewportOwned && node->IsRootNode()) { // Transfer viewport back to the remaining loose window + IMGUI_DEBUG_LOG_VIEWPORT("Node %08X transfer Viewport %08X=>%08X for Window '%s'\n", node->ID, node->HostWindow->Viewport->ID, remaining_window->ID, remaining_window->Name); IM_ASSERT(node->HostWindow->Viewport->Window == node->HostWindow); node->HostWindow->Viewport->Window = remaining_window; node->HostWindow->Viewport->ID = remaining_window->ID; From 6b77668171240d52ffe85e1c6aa3ee24fe22aaf2 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 9 Sep 2021 21:05:40 +0200 Subject: [PATCH 740/828] Viewports: Fixed a crash while a window owning its viewport disappear while being dragged. t would manifest when e.g. reconfiguring dock nodes while dragging. --- docs/CHANGELOG.txt | 4 +++- imgui.cpp | 29 ++++++++++++++++++----------- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index ce1080790423..3cd090e68464 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -148,7 +148,9 @@ Docking+Viewports Branch: - Docking: fixed settings load issue when mouse wheeling. (#4310) - Docking + Drag and Drop: Fixed using BeginDragDropSource() or BeginDragDropTarget() inside a Begin() that returned false because the window is docked. (#4515) -- Viewports: Viewports: fixed unnecessary creation of temporary viewports when multiple docked windows +- Viewports: Fixed a crash while a window owning its viewport disappear while being dragged. + It would manifest when e.g. reconfiguring dock nodes while dragging. +- Viewports: Fixed unnecessary creation of temporary viewports when multiple docked windows got reassigned to a new node (created mid-frame) which already has a HostWindow. diff --git a/imgui.cpp b/imgui.cpp index d3f0391e233d..edbc0a15e59d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3690,7 +3690,10 @@ void ImGui::UpdateMouseMovingWindowNewFrame() KeepAliveID(g.ActiveId); IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindowDockTree); ImGuiWindow* moving_window = g.MovingWindow->RootWindowDockTree; - if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos)) + + // When a window stop being submitted while being dragged, it may will its viewport until next Begin() + const bool window_disappared = (!moving_window->WasActive || moving_window->Viewport == NULL); + if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos) && !window_disappared) { ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset; if (moving_window->Pos.x != pos.x || moving_window->Pos.y != pos.y) @@ -3707,17 +3710,20 @@ void ImGui::UpdateMouseMovingWindowNewFrame() } else { - // Try to merge the window back into the main viewport. - // This works because MouseViewport should be != MovingWindow->Viewport on release (as per code in UpdateViewports) - if (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable) - UpdateTryMergeWindowIntoHostViewport(moving_window, g.MouseViewport); + if (!window_disappared) + { + // Try to merge the window back into the main viewport. + // This works because MouseViewport should be != MovingWindow->Viewport on release (as per code in UpdateViewports) + if (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable) + UpdateTryMergeWindowIntoHostViewport(moving_window, g.MouseViewport); - // Restore the mouse viewport so that we don't hover the viewport _under_ the moved window during the frame we released the mouse button. - if (!IsDragDropPayloadBeingAccepted()) - g.MouseViewport = moving_window->Viewport; + // Restore the mouse viewport so that we don't hover the viewport _under_ the moved window during the frame we released the mouse button. + if (!IsDragDropPayloadBeingAccepted()) + g.MouseViewport = moving_window->Viewport; - // Clear the NoInput window flag set by the Viewport system - moving_window->Viewport->Flags &= ~ImGuiViewportFlags_NoInputs; // FIXME-VIEWPORT: Test engine managed to crash here because Viewport was NULL. + // Clear the NoInput window flag set by the Viewport system + moving_window->Viewport->Flags &= ~ImGuiViewportFlags_NoInputs; // FIXME-VIEWPORT: Test engine managed to crash here because Viewport was NULL. + } g.MovingWindow = NULL; ClearActiveID(); @@ -11855,7 +11861,8 @@ static void ImGui::UpdateViewportsNewFrame() // Update mouse reference viewport // (when moving a window we aim at its viewport, but this will be overwritten below if we go in drag and drop mode) - if (g.MovingWindow) + // (MovingViewport->Viewport will be NULL in the rare situation where the window disappared while moving, set UpdateMouseMovingWindowNewFrame() for details) + if (g.MovingWindow && g.MovingWindow->Viewport) g.MouseViewport = g.MovingWindow->Viewport; else g.MouseViewport = g.MouseLastHoveredViewport; From 79d39b190b9266a6c6e7fb72196323cd6c753868 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 10 Sep 2021 14:58:45 +0200 Subject: [PATCH 741/828] Viewports: fix window with viewport ini data immediately merged into a host viewport from leaving a temporary viewport alive for a frame (would leak into backend). --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 3cd090e68464..807f3c255e2b 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -152,6 +152,8 @@ Docking+Viewports Branch: It would manifest when e.g. reconfiguring dock nodes while dragging. - Viewports: Fixed unnecessary creation of temporary viewports when multiple docked windows got reassigned to a new node (created mid-frame) which already has a HostWindow. +- Viewports: Fixed window with viewport ini data immediately merged into a host viewport from + leaving a temporary viewport alive for a frame (would leak into backend). ----------------------------------------------------------------------- diff --git a/imgui.cpp b/imgui.cpp index edbc0a15e59d..5e39be104b5a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11574,6 +11574,10 @@ void ImGui::SetCurrentViewport(ImGuiWindow* current_window, ImGuiViewportP* view static void SetWindowViewport(ImGuiWindow* window, ImGuiViewportP* viewport) { + // Abandon viewport + if (window->ViewportOwned && window->Viewport->Window == window) + window->Viewport->Size = ImVec2(0.0f, 0.0f); + window->Viewport = viewport; window->ViewportId = viewport->ID; window->ViewportOwned = (viewport->Window == window); @@ -11606,6 +11610,7 @@ static bool ImGui::UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImG if (GetWindowAlwaysWantOwnViewport(window)) return false; + // FIXME: Can't use g.WindowsFocusOrder[] for root windows only as we care about Z order. If we maintained a DisplayOrder along with FocusOrder we could.. for (int n = 0; n < g.Windows.Size; n++) { ImGuiWindow* window_behind = g.Windows[n]; From 92a39f78b97ddea0e4d23b37aca2c9d3e97fa834 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sat, 11 Sep 2021 14:05:53 +0200 Subject: [PATCH 742/828] Fixed IsWindowFocused/IsWindowHovered with _ChildWindows for not following through popup parents (amend 6b1e094c, fix #4527) --- imgui.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 5e39be104b5a..1bfdaa7497ab 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7268,8 +7268,11 @@ bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, { if (window == potential_parent) return true; - if (window == (dock_hierarchy ? window->RootWindowDockTree : window->RootWindow)) - return false; + // 2021-09-11: we broke the unexpressed contract that this function (prior to 6b1e094, #4527) + // would follow through popup parents as well. Restoring this for now. May want to add a ImGuiFocusedFlags_PopupHierarchy flag later. + if ((window->Flags & ImGuiWindowFlags_Popup) == 0) + if (window == (dock_hierarchy ? window->RootWindowDockTree : window->RootWindow)) + return false; window = window->ParentWindow; } return false; From 5d95e7eef9cad6ca1f09dcfb9c8b520b6624eb98 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sat, 11 Sep 2021 14:06:43 +0200 Subject: [PATCH 743/828] Viewports: extracted DestroyViewport() out of UpdateViewportsNewFrame() function. --- imgui.cpp | 44 +++++++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 1bfdaa7497ab..a0003fd837d2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -956,6 +956,7 @@ static void EndFrameDrawDimmedBackgrounds(); // Viewports const ImGuiID IMGUI_VIEWPORT_DEFAULT_ID = 0x11111111; // Using an arbitrary constant instead of e.g. ImHashStr("ViewportDefault", 0); so it's easier to spot in the debugger. The exact value doesn't matter. static ImGuiViewportP* AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const ImVec2& platform_pos, const ImVec2& size, ImGuiViewportFlags flags); +static void DestroyViewport(ImGuiViewportP* viewport); static void UpdateViewportsNewFrame(); static void UpdateViewportsEndFrame(); static void WindowSelectViewport(ImGuiWindow* window); @@ -11747,22 +11748,7 @@ static void ImGui::UpdateViewportsNewFrame() // Erase unused viewports if (n > 0 && viewport->LastFrameActive < g.FrameCount - 2) { - // Clear references to this viewport in windows (window->ViewportId becomes the master data) - for (int window_n = 0; window_n < g.Windows.Size; window_n++) - if (g.Windows[window_n]->Viewport == viewport) - { - g.Windows[window_n]->Viewport = NULL; - g.Windows[window_n]->ViewportOwned = false; - } - if (viewport == g.MouseLastHoveredViewport) - g.MouseLastHoveredViewport = NULL; - g.Viewports.erase(g.Viewports.Data + n); - - // Destroy - IMGUI_DEBUG_LOG_VIEWPORT("Delete Viewport %08X (%s)\n", viewport->ID, viewport->Window ? viewport->Window->Name : "n/a"); - DestroyPlatformWindow(viewport); // In most circumstances the platform window will already be destroyed here. - IM_ASSERT(g.PlatformIO.Viewports.contains(viewport) == false); - IM_DELETE(viewport); + DestroyViewport(viewport); n--; continue; } @@ -11794,7 +11780,7 @@ static void ImGui::UpdateViewportsNewFrame() // Reset alpha every frame. Users of transparency (docking) needs to request a lower alpha back. viewport->Alpha = 1.0f; - // Translate imgui windows when a Host Viewport has been moved + // Translate Dear ImGui windows when a Host Viewport has been moved // (This additionally keeps windows at the same place when ImGuiConfigFlags_ViewportsEnable is toggled!) const ImVec2 viewport_delta_pos = viewport->Pos - viewport->LastPos; if ((viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) && (viewport_delta_pos.x != 0.0f || viewport_delta_pos.y != 0.0f)) @@ -11974,6 +11960,30 @@ ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const return viewport; } +static void ImGui::DestroyViewport(ImGuiViewportP* viewport) +{ + // Clear references to this viewport in windows (window->ViewportId becomes the master data) + ImGuiContext& g = *GImGui; + for (int window_n = 0; window_n < g.Windows.Size; window_n++) + { + ImGuiWindow* window = g.Windows[window_n]; + if (window->Viewport != viewport) + continue; + window->Viewport = NULL; + window->ViewportOwned = false; + } + if (viewport == g.MouseLastHoveredViewport) + g.MouseLastHoveredViewport = NULL; + + // Destroy + IMGUI_DEBUG_LOG_VIEWPORT("Delete Viewport %08X (%s)\n", viewport->ID, viewport->Window ? viewport->Window->Name : "n/a"); + DestroyPlatformWindow(viewport); // In most circumstances the platform window will already be destroyed here. + IM_ASSERT(g.PlatformIO.Viewports.contains(viewport) == false); + IM_ASSERT(g.Viewports[viewport->Idx] == viewport); + g.Viewports.erase(g.Viewports.Data + viewport->Idx); + IM_DELETE(viewport); +} + // FIXME-VIEWPORT: This is all super messy and ought to be clarified or rewritten. static void ImGui::WindowSelectViewport(ImGuiWindow* window) { From 8dfb52245bc92fef3e58b14f88135bd75a28faf8 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 13 Sep 2021 18:03:22 +0200 Subject: [PATCH 744/828] Docking: bits. --- imgui.cpp | 92 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 50 insertions(+), 42 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index a0003fd837d2..826374db3281 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -12493,7 +12493,7 @@ void ImGui::DestroyPlatformWindows() // | - DockContextProcessDock() - process one docking request // | - DockNodeUpdate() // | - DockNodeUpdateForRootNode() -// | - DockNodeUpdateVisibleFlagAndInactiveChilds() +// | - DockNodeUpdateFlagsAndCollapse() // | - DockNodeFindInfo() // | - destroy unused node or tab bar // | - create dock node host window @@ -12626,7 +12626,7 @@ namespace ImGui static void DockNodeHideHostWindow(ImGuiDockNode* node); static void DockNodeUpdate(ImGuiDockNode* node); static void DockNodeUpdateForRootNode(ImGuiDockNode* node); - static void DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* node); + static void DockNodeUpdateFlagsAndCollapse(ImGuiDockNode* node); static void DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_window); static void DockNodeAddTabBar(ImGuiDockNode* node); static void DockNodeRemoveTabBar(ImGuiDockNode* node); @@ -13300,7 +13300,7 @@ bool ImGui::DockContextCalcDropPosForDocking(ImGuiWindow* target, ImGuiDockNode* // - ImGuiDockNodeFindInfoResults // - DockNodeFindInfo() // - DockNodeFindWindowByID() -// - DockNodeUpdateVisibleFlagAndInactiveChilds() +// - DockNodeUpdateFlagsAndCollapse() // - DockNodeUpdateVisibleFlag() // - DockNodeStartMouseMovingWindow() // - DockNodeUpdate() @@ -13568,36 +13568,36 @@ static void ImGui::DockNodeHideHostWindow(ImGuiDockNode* node) } // Search function called once by root node in DockNodeUpdate() -struct ImGuiDockNodeFindInfoResults +struct ImGuiDockNodeTreeInfo { ImGuiDockNode* CentralNode; ImGuiDockNode* FirstNodeWithWindows; int CountNodesWithWindows; //ImGuiWindowClass WindowClassForMerges; - ImGuiDockNodeFindInfoResults() { CentralNode = FirstNodeWithWindows = NULL; CountNodesWithWindows = 0; } + ImGuiDockNodeTreeInfo() { memset(this, 0, sizeof(*this)); } }; -static void DockNodeFindInfo(ImGuiDockNode* node, ImGuiDockNodeFindInfoResults* results) +static void DockNodeFindInfo(ImGuiDockNode* node, ImGuiDockNodeTreeInfo* info) { if (node->Windows.Size > 0) { - if (results->FirstNodeWithWindows == NULL) - results->FirstNodeWithWindows = node; - results->CountNodesWithWindows++; + if (info->FirstNodeWithWindows == NULL) + info->FirstNodeWithWindows = node; + info->CountNodesWithWindows++; } if (node->IsCentralNode()) { - IM_ASSERT(results->CentralNode == NULL); // Should be only one + IM_ASSERT(info->CentralNode == NULL); // Should be only one IM_ASSERT(node->IsLeafNode() && "If you get this assert: please submit .ini file + repro of actions leading to this."); - results->CentralNode = node; + info->CentralNode = node; } - if (results->CountNodesWithWindows > 1 && results->CentralNode != NULL) + if (info->CountNodesWithWindows > 1 && info->CentralNode != NULL) return; if (node->ChildNodes[0]) - DockNodeFindInfo(node->ChildNodes[0], results); + DockNodeFindInfo(node->ChildNodes[0], info); if (node->ChildNodes[1]) - DockNodeFindInfo(node->ChildNodes[1], results); + DockNodeFindInfo(node->ChildNodes[1], info); } static ImGuiWindow* ImGui::DockNodeFindWindowByID(ImGuiDockNode* node, ImGuiID id) @@ -13611,7 +13611,7 @@ static ImGuiWindow* ImGui::DockNodeFindWindowByID(ImGuiDockNode* node, ImGuiID i // - Remove inactive windows/nodes. // - Update visibility flag. -static void ImGui::DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* node) +static void ImGui::DockNodeUpdateFlagsAndCollapse(ImGuiDockNode* node) { ImGuiContext& g = *GImGui; IM_ASSERT(node->ParentNode == NULL || node->ParentNode->ChildNodes[0] == node || node->ParentNode->ChildNodes[1] == node); @@ -13625,11 +13625,11 @@ static void ImGui::DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* nod // If 'node->ChildNode[0]' delete itself, then 'node->ChildNode[1]->Windows' will be moved into 'node' // If 'node->ChildNode[1]' delete itself, then 'node->ChildNode[0]->Windows' will be moved into 'node' and the "remove inactive windows" loop will have run twice on those windows (harmless) if (node->ChildNodes[0]) - DockNodeUpdateVisibleFlagAndInactiveChilds(node->ChildNodes[0]); + DockNodeUpdateFlagsAndCollapse(node->ChildNodes[0]); if (node->ChildNodes[1]) - DockNodeUpdateVisibleFlagAndInactiveChilds(node->ChildNodes[1]); + DockNodeUpdateFlagsAndCollapse(node->ChildNodes[1]); - // Remove inactive windows + // Remove inactive windows, collapse nodes // Merge node flags overrides stored in windows node->LocalFlagsInWindows = ImGuiDockNodeFlags_None; for (int window_n = 0; window_n < node->Windows.Size; window_n++) @@ -13654,13 +13654,12 @@ static void ImGui::DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* nod } DockNodeRemoveWindow(node, window, node->ID); window_n--; + continue; } - else - { - // FIXME-DOCKING: Missing policies for conflict resolution, hence the "Experimental" tag on this. - //node->LocalFlagsInWindow &= ~window->WindowClass.DockNodeFlagsOverrideClear; - node->LocalFlagsInWindows |= window->WindowClass.DockNodeFlagsOverrideSet; - } + + // FIXME-DOCKING: Missing policies for conflict resolution, hence the "Experimental" tag on this. + //node->LocalFlagsInWindow &= ~window->WindowClass.DockNodeFlagsOverrideClear; + node->LocalFlagsInWindows |= window->WindowClass.DockNodeFlagsOverrideSet; } node->UpdateMergedFlags(); @@ -13707,22 +13706,22 @@ static void ImGui::DockNodeStartMouseMovingWindow(ImGuiDockNode* node, ImGuiWind // Update CentralNode, OnlyNodeWithWindows, LastFocusedNodeID. Copy window class. static void ImGui::DockNodeUpdateForRootNode(ImGuiDockNode* node) { - DockNodeUpdateVisibleFlagAndInactiveChilds(node); + DockNodeUpdateFlagsAndCollapse(node); - // FIXME-DOCK: Merge this scan into the one above. // - Setup central node pointers // - Find if there's only a single visible window in the hierarchy (in which case we need to display a regular title bar -> FIXME-DOCK: that last part is not done yet!) - ImGuiDockNodeFindInfoResults results; - DockNodeFindInfo(node, &results); - node->CentralNode = results.CentralNode; - node->OnlyNodeWithWindows = (results.CountNodesWithWindows == 1) ? results.FirstNodeWithWindows : NULL; - if (node->LastFocusedNodeId == 0 && results.FirstNodeWithWindows != NULL) - node->LastFocusedNodeId = results.FirstNodeWithWindows->ID; + // Cannot merge this with DockNodeUpdateFlagsAndCollapse() because FirstNodeWithWindows is found after window removal and child collapsing + ImGuiDockNodeTreeInfo info; + DockNodeFindInfo(node, &info); + node->CentralNode = info.CentralNode; + node->OnlyNodeWithWindows = (info.CountNodesWithWindows == 1) ? info.FirstNodeWithWindows : NULL; + if (node->LastFocusedNodeId == 0 && info.FirstNodeWithWindows != NULL) + node->LastFocusedNodeId = info.FirstNodeWithWindows->ID; // Copy the window class from of our first window so it can be used for proper dock filtering. // When node has mixed windows, prioritize the class with the most constraint (DockingAllowUnclassed = false) as the reference to copy. // FIXME-DOCK: We don't recurse properly, this code could be reworked to work from DockNodeUpdateScanRec. - if (ImGuiDockNode* first_node_with_windows = results.FirstNodeWithWindows) + if (ImGuiDockNode* first_node_with_windows = info.FirstNodeWithWindows) { node->WindowClass = first_node_with_windows->Windows[0]->WindowClass; for (int n = 1; n < first_node_with_windows->Windows.Size; n++) @@ -14850,6 +14849,7 @@ void ImGui::DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImG } // Update Pos/Size for a node hierarchy (don't affect child Windows yet) +// (Depth-first, Pre-Order) void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 size, bool only_write_to_marked_nodes) { // During the regular dock node update we write to all nodes. @@ -14879,6 +14879,10 @@ void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 si ImGuiContext& g = *GImGui; const float size_min_each = ImFloor(ImMin(size_avail, g.Style.WindowMinSize[axis] * 2.0f) * 0.5f); + // FIXME: Blocks 2) and 3) are essentially doing nearly the same thing. + // Difference are: write-back to SizeRef; application of a minimum size; rounding before ImFloor() + // Clarify and rework differences between Size & SizeRef and purpose of WantLockSizeOnce + // 2) Process locked absolute size (during a splitter resize we preserve the child of nodes not touching the splitter edge) if (child_0->WantLockSizeOnce && !child_1->WantLockSizeOnce) { @@ -14896,19 +14900,19 @@ void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 si { // FIXME-DOCK: We cannot honor the requested size, so apply ratio. // Currently this path will only be taken if code programmatically sets WantLockSizeOnce - float ratio_0 = child_0_size[axis] / (child_0_size[axis] + child_1_size[axis]); - child_0_size[axis] = child_0->SizeRef[axis] = ImFloor(size_avail * ratio_0); + float split_ratio = child_0_size[axis] / (child_0_size[axis] + child_1_size[axis]); + child_0_size[axis] = child_0->SizeRef[axis] = ImFloor(size_avail * split_ratio); child_1_size[axis] = child_1->SizeRef[axis] = (size_avail - child_0_size[axis]); IM_ASSERT(child_0->SizeRef[axis] > 0.0f && child_1->SizeRef[axis] > 0.0f); } // 3) If one window is the central node (~ use remaining space, should be made explicit!), use explicit size from the other, and remainder for the central node - else if (child_1->IsCentralNode() && child_0->SizeRef[axis] != 0.0f) + else if (child_0->SizeRef[axis] != 0.0f && child_1->IsCentralNode()) { child_0_size[axis] = ImMin(size_avail - size_min_each, child_0->SizeRef[axis]); child_1_size[axis] = (size_avail - child_0_size[axis]); } - else if (child_0->IsCentralNode() && child_1->SizeRef[axis] != 0.0f) + else if (child_1->SizeRef[axis] != 0.0f && child_0->IsCentralNode()) { child_1_size[axis] = ImMin(size_avail - size_min_each, child_1->SizeRef[axis]); child_0_size[axis] = (size_avail - child_1_size[axis]); @@ -14917,7 +14921,7 @@ void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 si { // 4) Otherwise distribute according to the relative ratio of each SizeRef value float split_ratio = child_0->SizeRef[axis] / (child_0->SizeRef[axis] + child_1->SizeRef[axis]); - child_0_size[axis] = ImMax(size_min_each, ImFloor(size_avail * split_ratio + 0.5F)); + child_0_size[axis] = ImMax(size_min_each, ImFloor(size_avail * split_ratio + 0.5f)); child_1_size[axis] = (size_avail - child_0_size[axis]); } @@ -14946,6 +14950,7 @@ static void DockNodeTreeUpdateSplitterFindTouchingNode(ImGuiDockNode* node, ImGu DockNodeTreeUpdateSplitterFindTouchingNode(node->ChildNodes[1], axis, side, touching_nodes); } +// (Depth-First, Pre-Order) void ImGui::DockNodeTreeUpdateSplitter(ImGuiDockNode* node) { if (node->IsLeafNode()) @@ -14980,7 +14985,7 @@ void ImGui::DockNodeTreeUpdateSplitter(ImGuiDockNode* node) //bb.Max[axis] -= 1; PushID(node->ID); - // Gather list of nodes that are touching the splitter line. Find resizing limits based on those nodes. + // Find resizing limits by gathering list of nodes that are touching the splitter line. ImVector touching_nodes[2]; float min_size = g.Style.WindowMinSize[axis]; float resize_limits[2]; @@ -14988,9 +14993,8 @@ void ImGui::DockNodeTreeUpdateSplitter(ImGuiDockNode* node) resize_limits[1] = node->ChildNodes[1]->Pos[axis] + node->ChildNodes[1]->Size[axis] - min_size; ImGuiID splitter_id = GetID("##Splitter"); - if (g.ActiveId == splitter_id) + if (g.ActiveId == splitter_id) // Only process when splitter is active { - // Only process when splitter is active DockNodeTreeUpdateSplitterFindTouchingNode(child_0, axis, 1, &touching_nodes[0]); DockNodeTreeUpdateSplitterFindTouchingNode(child_1, axis, 0, &touching_nodes[1]); for (int touching_node_n = 0; touching_node_n < touching_nodes[0].Size; touching_node_n++) @@ -14998,14 +15002,18 @@ void ImGui::DockNodeTreeUpdateSplitter(ImGuiDockNode* node) for (int touching_node_n = 0; touching_node_n < touching_nodes[1].Size; touching_node_n++) resize_limits[1] = ImMin(resize_limits[1], touching_nodes[1][touching_node_n]->Rect().Max[axis] - min_size); + // [DEBUG] Render touching nodes & limits /* - // [DEBUG] Render limits ImDrawList* draw_list = node->HostWindow ? GetForegroundDrawList(node->HostWindow) : GetForegroundDrawList(GetMainViewport()); for (int n = 0; n < 2; n++) + { + for (int touching_node_n = 0; touching_node_n < touching_nodes[n].Size; touching_node_n++) + draw_list->AddRect(touching_nodes[n][touching_node_n]->Pos, touching_nodes[n][touching_node_n]->Pos + touching_nodes[n][touching_node_n]->Size, IM_COL32(0, 255, 0, 255)); if (axis == ImGuiAxis_X) draw_list->AddLine(ImVec2(resize_limits[n], node->ChildNodes[n]->Pos.y), ImVec2(resize_limits[n], node->ChildNodes[n]->Pos.y + node->ChildNodes[n]->Size.y), IM_COL32(255, 0, 255, 255), 3.0f); else draw_list->AddLine(ImVec2(node->ChildNodes[n]->Pos.x, resize_limits[n]), ImVec2(node->ChildNodes[n]->Pos.x + node->ChildNodes[n]->Size.x, resize_limits[n]), IM_COL32(255, 0, 255, 255), 3.0f); + } */ } From 29828d04692016e1aa7e812b962442739db9f4f1 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 14 Sep 2021 16:58:35 +0200 Subject: [PATCH 745/828] Docking: floating node with a central node hides properly when nothing is docked + rename. --- docs/CHANGELOG.txt | 3 ++- imgui.cpp | 11 +++++++++-- imgui_internal.h | 3 ++- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 807f3c255e2b..0cd8dfc0cd55 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -145,7 +145,8 @@ Docking+Viewports Branch: docking hierarchy. Added ImGuiFocusedFlags_DockHierarchy flag to consider docking hierarchy in the test. - IsWindowHovered: Fixed using ImGuiHoveredFlags_ChildWindows (without _RootWindow) from leaking the docking hierarchy. Added ImGuiHoveredFlags_DockHierarchy flag to consider docking hierarchy in the test. -- Docking: fixed settings load issue when mouse wheeling. (#4310) +- Docking: Fixed settings load issue when mouse wheeling. (#4310) +- Docking: Fixed manually created floating node with a central node from not hiding when windows are gone. - Docking + Drag and Drop: Fixed using BeginDragDropSource() or BeginDragDropTarget() inside a Begin() that returned false because the window is docked. (#4515) - Viewports: Fixed a crash while a window owning its viewport disappear while being dragged. diff --git a/imgui.cpp b/imgui.cpp index 826374db3281..8c7d7c52b5c1 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -13330,6 +13330,7 @@ ImGuiDockNode::ImGuiDockNode(ImGuiID id) State = ImGuiDockNodeState_Unknown; HostWindow = VisibleWindow = NULL; CentralNode = OnlyNodeWithWindows = NULL; + CountNodeWithWindows = 0; LastFrameAlive = LastFrameActive = LastFrameFocused = -1; LastFocusedNodeId = 0; SelectedTabId = 0; @@ -13715,6 +13716,7 @@ static void ImGui::DockNodeUpdateForRootNode(ImGuiDockNode* node) DockNodeFindInfo(node, &info); node->CentralNode = info.CentralNode; node->OnlyNodeWithWindows = (info.CountNodesWithWindows == 1) ? info.FirstNodeWithWindows : NULL; + node->CountNodeWithWindows = info.CountNodesWithWindows; if (node->LastFocusedNodeId == 0 && info.FirstNodeWithWindows != NULL) node->LastFocusedNodeId = info.FirstNodeWithWindows->ID; @@ -13764,9 +13766,14 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) // Early out for hidden root dock nodes (when all DockId references are in inactive windows, or there is only 1 floating window holding on the DockId) bool want_to_hide_host_window = false; - if (node->Windows.Size <= 1 && node->IsFloatingNode() && node->IsLeafNode()) - if (!g.IO.ConfigDockingAlwaysTabBar && (node->Windows.Size == 0 || !node->Windows[0]->WindowClass.DockingAlwaysTabBar)) + if (node->IsFloatingNode()) + { + if (node->Windows.Size <= 1 && node->IsLeafNode()) + if (!g.IO.ConfigDockingAlwaysTabBar && (node->Windows.Size == 0 || !node->Windows[0]->WindowClass.DockingAlwaysTabBar)) + want_to_hide_host_window = true; + if (node->CountNodeWithWindows == 0) want_to_hide_host_window = true; + } if (want_to_hide_host_window) { if (node->Windows.Size == 1) diff --git a/imgui_internal.h b/imgui_internal.h index af48bcda1405..a2da0a6077e6 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1363,6 +1363,7 @@ struct IMGUI_API ImGuiDockNode ImGuiWindow* VisibleWindow; // Generally point to window which is ID is == SelectedTabID, but when CTRL+Tabbing this can be a different window. ImGuiDockNode* CentralNode; // [Root node only] Pointer to central node. ImGuiDockNode* OnlyNodeWithWindows; // [Root node only] Set when there is a single visible node within the hierarchy. + int CountNodeWithWindows; // [Root node only] int LastFrameAlive; // Last frame number the node was updated or kept alive explicitly with DockSpace() + ImGuiDockNodeFlags_KeepAliveOnly int LastFrameActive; // Last frame number the node was updated. int LastFrameFocused; // Last frame number the node was focused. @@ -1617,7 +1618,7 @@ struct ImGuiContext ImGuiWindow* CurrentWindow; // Window being drawn into ImGuiWindow* HoveredWindow; // Window the mouse is hovering. Will typically catch mouse inputs. ImGuiWindow* HoveredWindowUnderMovingWindow; // Hovered window ignoring MovingWindow. Only set if MovingWindow is set. - ImGuiDockNode* HoveredDockNode; // Hovered dock node. + ImGuiDockNode* HoveredDockNode; // [Debug] Hovered dock node. ImGuiWindow* MovingWindow; // Track the window we clicked on (in order to preserve focus). The actual window that is moved is generally MovingWindow->RootWindowDockTree. ImGuiWindow* WheelingWindow; // Track the window we started mouse-wheeling on. Until a timer elapse or mouse has moved, generally keep scrolling the same window even if during the course of scrolling the mouse ends up hovering a child window. ImVec2 WheelingWindowRefMousePos; From e7cc53436706ea1d25ec68f9ed5bb9a7cb08de8e Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 14 Sep 2021 17:57:47 +0200 Subject: [PATCH 746/828] Docking: Improved resizing system so that non-central zone are better at keeping their fixed size. --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 43 ++++++++++++++++++++++++++++++++++++++----- imgui_internal.h | 1 + 3 files changed, 41 insertions(+), 5 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 0cd8dfc0cd55..9bd2f0d0fe95 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -145,6 +145,8 @@ Docking+Viewports Branch: docking hierarchy. Added ImGuiFocusedFlags_DockHierarchy flag to consider docking hierarchy in the test. - IsWindowHovered: Fixed using ImGuiHoveredFlags_ChildWindows (without _RootWindow) from leaking the docking hierarchy. Added ImGuiHoveredFlags_DockHierarchy flag to consider docking hierarchy in the test. +- Docking: Improved resizing system so that non-central zone are better at keeping their fixed size. + The algorithm is still not handling the repartition of size idealy for nested sibling, but it got better. - Docking: Fixed settings load issue when mouse wheeling. (#4310) - Docking: Fixed manually created floating node with a central node from not hiding when windows are gone. - Docking + Drag and Drop: Fixed using BeginDragDropSource() or BeginDragDropTarget() inside a Begin() diff --git a/imgui.cpp b/imgui.cpp index 8c7d7c52b5c1..7a0f081f63a0 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -12627,6 +12627,7 @@ namespace ImGui static void DockNodeUpdate(ImGuiDockNode* node); static void DockNodeUpdateForRootNode(ImGuiDockNode* node); static void DockNodeUpdateFlagsAndCollapse(ImGuiDockNode* node); + static void DockNodeUpdateHasCentralNodeChild(ImGuiDockNode* node); static void DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_window); static void DockNodeAddTabBar(ImGuiDockNode* node); static void DockNodeRemoveTabBar(ImGuiDockNode* node); @@ -13301,6 +13302,7 @@ bool ImGui::DockContextCalcDropPosForDocking(ImGuiWindow* target, ImGuiDockNode* // - DockNodeFindInfo() // - DockNodeFindWindowByID() // - DockNodeUpdateFlagsAndCollapse() +// - DockNodeUpdateHasCentralNodeFlag() // - DockNodeUpdateVisibleFlag() // - DockNodeStartMouseMovingWindow() // - DockNodeUpdate() @@ -13338,7 +13340,7 @@ ImGuiDockNode::ImGuiDockNode(ImGuiID id) AuthorityForPos = AuthorityForSize = ImGuiDataAuthority_DockNode; AuthorityForViewport = ImGuiDataAuthority_Auto; IsVisible = true; - IsFocused = HasCloseButton = HasWindowMenuButton = false; + IsFocused = HasCloseButton = HasWindowMenuButton = HasCentralNodeChild = false; WantCloseAll = WantLockSizeOnce = WantMouseMove = WantHiddenTabBarUpdate = WantHiddenTabBarToggle = false; MarkedForPosSizeWrite = false; } @@ -13625,6 +13627,7 @@ static void ImGui::DockNodeUpdateFlagsAndCollapse(ImGuiDockNode* node) // There is the possibility that one of our child becoming empty will delete itself and moving its sibling contents into 'node'. // If 'node->ChildNode[0]' delete itself, then 'node->ChildNode[1]->Windows' will be moved into 'node' // If 'node->ChildNode[1]' delete itself, then 'node->ChildNode[0]->Windows' will be moved into 'node' and the "remove inactive windows" loop will have run twice on those windows (harmless) + node->HasCentralNodeChild = false; if (node->ChildNodes[0]) DockNodeUpdateFlagsAndCollapse(node->ChildNodes[0]); if (node->ChildNodes[1]) @@ -13684,6 +13687,25 @@ static void ImGui::DockNodeUpdateFlagsAndCollapse(ImGuiDockNode* node) DockNodeUpdateVisibleFlag(node); } +// This is rarely called as DockNodeUpdateForRootNode() generally does it most frames. +static void ImGui::DockNodeUpdateHasCentralNodeChild(ImGuiDockNode* node) +{ + node->HasCentralNodeChild = false; + if (node->ChildNodes[0]) + DockNodeUpdateHasCentralNodeChild(node->ChildNodes[0]); + if (node->ChildNodes[1]) + DockNodeUpdateHasCentralNodeChild(node->ChildNodes[1]); + if (node->IsRootNode()) + { + ImGuiDockNode* mark_node = node->CentralNode; + while (mark_node) + { + mark_node->HasCentralNodeChild = true; + mark_node = mark_node->ParentNode; + } + } +} + static void ImGui::DockNodeUpdateVisibleFlag(ImGuiDockNode* node) { // Update visibility flag @@ -13733,6 +13755,13 @@ static void ImGui::DockNodeUpdateForRootNode(ImGuiDockNode* node) break; } } + + ImGuiDockNode* mark_node = node->CentralNode; + while (mark_node) + { + mark_node->HasCentralNodeChild = true; + mark_node = mark_node->ParentNode; + } } static void DockNodeSetupHostWindow(ImGuiDockNode* node, ImGuiWindow* host_window) @@ -14791,6 +14820,7 @@ void ImGui::DockNodeTreeSplit(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImG DockNodeMoveWindows(parent_node->ChildNodes[split_inheritor_child_idx], parent_node); DockSettingsRenameNodeReferences(parent_node->ID, parent_node->ChildNodes[split_inheritor_child_idx]->ID); + DockNodeUpdateHasCentralNodeChild(DockNodeGetRootNode(parent_node)); DockNodeTreeUpdatePosSize(parent_node, parent_node->Pos, parent_node->Size); // Flags transfer (e.g. this is where we transfer the ImGuiDockNodeFlags_CentralNode property) @@ -14914,12 +14944,12 @@ void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 si } // 3) If one window is the central node (~ use remaining space, should be made explicit!), use explicit size from the other, and remainder for the central node - else if (child_0->SizeRef[axis] != 0.0f && child_1->IsCentralNode()) + else if (child_0->SizeRef[axis] != 0.0f && child_1->HasCentralNodeChild) { child_0_size[axis] = ImMin(size_avail - size_min_each, child_0->SizeRef[axis]); child_1_size[axis] = (size_avail - child_0_size[axis]); } - else if (child_1->SizeRef[axis] != 0.0f && child_0->IsCentralNode()) + else if (child_1->SizeRef[axis] != 0.0f && child_0->HasCentralNodeChild) { child_1_size[axis] = ImMin(size_avail - size_min_each, child_1->SizeRef[axis]); child_0_size[axis] = (size_avail - child_1_size[axis]); @@ -15696,6 +15726,7 @@ void ImGui::DockBuilderCopyDockSpace(ImGuiID src_dockspace_id, ImGuiID dst_docks } } +// FIXME-DOCK: This is awkward because in series of split user is likely to loose access to its root node. void ImGui::DockBuilderFinish(ImGuiID root_id) { ImGuiContext* ctx = GImGui; @@ -15759,6 +15790,7 @@ static ImGuiDockNode* ImGui::DockContextBindNodeToWindow(ImGuiContext* ctx, ImGu ancestor_node = ancestor_node->ParentNode; } IM_ASSERT(ancestor_node->Size.x > 0.0f && ancestor_node->Size.y > 0.0f); + DockNodeUpdateHasCentralNodeChild(DockNodeGetRootNode(ancestor_node)); DockNodeTreeUpdatePosSize(ancestor_node, ancestor_node->Pos, ancestor_node->Size, true); } @@ -16993,11 +17025,12 @@ void ImGui::DebugNodeDockNode(ImGuiDockNode* node, const char* label) DebugNodeWindow(node->HostWindow, "HostWindow"); DebugNodeWindow(node->VisibleWindow, "VisibleWindow"); BulletText("SelectedTabID: 0x%08X, LastFocusedNodeID: 0x%08X", node->SelectedTabId, node->LastFocusedNodeId); - BulletText("Misc:%s%s%s%s%s", + BulletText("Misc:%s%s%s%s%s%s", node->IsDockSpace() ? " IsDockSpace" : "", node->IsCentralNode() ? " IsCentralNode" : "", is_alive ? " IsAlive" : "", is_active ? " IsActive" : "", - node->WantLockSizeOnce ? " WantLockSizeOnce" : ""); + node->WantLockSizeOnce ? " WantLockSizeOnce" : "", + node->HasCentralNodeChild ? " HasCentralNodeChild" : ""); if (TreeNode("flags", "Flags Merged: 0x%04X, Local: 0x%04X, InWindows: 0x%04X, Shared: 0x%04X", node->MergedFlags, node->LocalFlags, node->LocalFlagsInWindows, node->SharedFlags)) { if (BeginTable("flags", 4)) diff --git a/imgui_internal.h b/imgui_internal.h index a2da0a6077e6..ae0c4efb3224 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1377,6 +1377,7 @@ struct IMGUI_API ImGuiDockNode bool IsFocused :1; bool HasCloseButton :1; // Provide space for a close button (if any of the docked window has one). Note that button may be hidden on window without one. bool HasWindowMenuButton :1; + bool HasCentralNodeChild :1; bool WantCloseAll :1; // Set when closing all tabs at once. bool WantLockSizeOnce :1; bool WantMouseMove :1; // After a node extraction we need to transition toward moving the newly created host window From fa0a314f59ffe317a0de9af5f292f3564ca3a566 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 21 Sep 2021 13:16:00 +0200 Subject: [PATCH 747/828] Nav: Fixed an issue with losing focus on docked windows when pressing Alt while keyboard navigation is disabled. (#4547, #4439) --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 732620181dcc..8f1d8151154e 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -152,6 +152,8 @@ Docking+Viewports Branch: docking hierarchy. Added ImGuiFocusedFlags_DockHierarchy flag to consider docking hierarchy in the test. - IsWindowHovered: Fixed using ImGuiHoveredFlags_ChildWindows (without _RootWindow) from leaking the docking hierarchy. Added ImGuiHoveredFlags_DockHierarchy flag to consider docking hierarchy in the test. +- Nav: Fixed an issue with losing focus on docked windows when pressing Alt while keyboard navigation + is disabled. (#4547, #4439) [@PathogenDavid] - Docking: Improved resizing system so that non-central zone are better at keeping their fixed size. The algorithm is still not handling the repartition of size idealy for nested sibling, but it got better. - Docking: Fixed settings load issue when mouse wheeling. (#4310) diff --git a/imgui.cpp b/imgui.cpp index 7a0f081f63a0..765e291bde19 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10413,8 +10413,9 @@ static void ImGui::NavUpdateWindowing() } // Start CTRL-TAB or Square+L/R window selection - bool start_windowing_with_gamepad = allow_windowing && !g.NavWindowingTarget && IsNavInputTest(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed); - bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && io.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard); + const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; + const bool start_windowing_with_gamepad = allow_windowing && !g.NavWindowingTarget && IsNavInputTest(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed); + const bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && nav_keyboard_active && io.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab); if (start_windowing_with_gamepad || start_windowing_with_keyboard) if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1)) { @@ -10465,7 +10466,7 @@ static void ImGui::NavUpdateWindowing() // Keyboard: Press and Release ALT to toggle menu layer // - Testing that only Alt is tested prevents Alt+Shift or AltGR from toggling menu layer. // - AltGR is normally Alt+Ctrl but we can't reliably detect it (not all backends/systems/layout emit it as Alt+Ctrl). But even on keyboards without AltGR we don't want Alt+Ctrl to open menu anyway. - if (io.KeyMods == ImGuiKeyModFlags_Alt && (io.KeyModsPrev & ImGuiKeyModFlags_Alt) == 0) + if (nav_keyboard_active && io.KeyMods == ImGuiKeyModFlags_Alt && (io.KeyModsPrev & ImGuiKeyModFlags_Alt) == 0) { g.NavWindowingToggleLayer = true; g.NavInputSource = ImGuiInputSource_Keyboard; From ca097c2c681f45da0aba8e7da87731ebe95d0f3f Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 22 Sep 2021 14:05:21 +0200 Subject: [PATCH 748/828] Docking: Fixed IsItemHovered() and functions depending on it (e.g. BeginPopupContextItem()) when called after Begin() on a docked window (#3851) Fix ee643b2a --- docs/CHANGELOG.txt | 2 ++ imgui_widgets.cpp | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 8f1d8151154e..4ec275267a3b 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -154,6 +154,8 @@ Docking+Viewports Branch: docking hierarchy. Added ImGuiHoveredFlags_DockHierarchy flag to consider docking hierarchy in the test. - Nav: Fixed an issue with losing focus on docked windows when pressing Alt while keyboard navigation is disabled. (#4547, #4439) [@PathogenDavid] +- Docking: Fixed IsItemHovered() and functions depending on it (e.g. BeginPopupContextItem()) when + called after Begin() on a docked window (broken 2021/03/04). (#3851) - Docking: Improved resizing system so that non-central zone are better at keeping their fixed size. The algorithm is still not handling the repartition of size idealy for nested sibling, but it got better. - Docking: Fixed settings load issue when mouse wheeling. (#4310) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 621ed2e02599..d939fdda01ed 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -8158,6 +8158,11 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, TabBarCloseTab(tab_bar, tab); } + // Forward Hovered state so IsItemHovered() after Begin() can work (even though we are technically hovering our parent) + // That state is copied to window->DockTabItemStatusFlags by our caller. + if (docked_window && (hovered || g.HoveredId == close_button_id)) + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow; + // Restore main window position so user can draw there if (want_clip_rect) PopClipRect(); From 65ad63de84bea27207721a79d93665f44d7fd150 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 24 Sep 2021 13:58:00 +0200 Subject: [PATCH 749/828] Added ImGuiFocusedFlags_NoPopupHierarchy and ImGuiHoveredFlags_NoPopupHierarchy (followup #4527) IsWindowFocused: fix flag usage (amend 6b1e094c) was technically harmless because of light typing. --- docs/CHANGELOG.txt | 4 ++++ imgui.cpp | 40 ++++++++++++++++++++++++++-------------- imgui.h | 16 +++++++++------- imgui_demo.cpp | 43 ++++++++++++++++++++++++++++++++----------- imgui_internal.h | 7 ++++--- 5 files changed, 75 insertions(+), 35 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 4ec275267a3b..20dc4e6e8698 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -116,6 +116,10 @@ Breaking Changes: Other Changes: - Windows: Fixed background order of overlapping childs submitted sequentially. (#4493) +- IsWindowFocused: Added ImGuiFocusedFlags_NoPopupHierarchy flag allowing to exclude child popups + from the tested windows when combined with _ChildWindows. +- IsWindowHovered: Added ImGuiHoveredFlags_NoPopupHierarchy flag allowing to exclude child popups + from the tested windows when combined with _ChildWindows. - InputTextMultiline: Fixed label size not being included into window contents rect unless the whole widget is clipped. - InputText: Allow cancelling/validating input with gamepad nav events to facilitate undoing diff --git a/imgui.cpp b/imgui.cpp index 765e291bde19..f4f4964b51ea 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4031,7 +4031,7 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags() // Modal windows prevents mouse from hovering behind them. ImGuiWindow* modal_window = GetTopMostPopupModal(); - if (modal_window && g.HoveredWindow && !IsWindowChildOf(g.HoveredWindow->RootWindowDockTree, modal_window, true)) + if (modal_window && g.HoveredWindow && !IsWindowChildOf(g.HoveredWindow->RootWindowDockTree, modal_window, true, true)) clear_hovered_windows = true; // Disabled mouse? @@ -6137,13 +6137,15 @@ void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& titl void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window) { window->ParentWindow = parent_window; - window->RootWindow = window->RootWindowDockTree = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window; + window->RootWindow = window->RootWindowPopupTree = window->RootWindowDockTree = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window; if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip)) { window->RootWindowDockTree = parent_window->RootWindowDockTree; if (!window->DockIsActive && !(parent_window->Flags & ImGuiWindowFlags_DockNodeHost)) window->RootWindow = parent_window->RootWindow; } + if (parent_window && (flags & ImGuiWindowFlags_Popup)) + window->RootWindowPopupTree = parent_window->RootWindowPopupTree; if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup))) window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight; while (window->RootWindowForNav->Flags & ImGuiWindowFlags_NavFlattened) @@ -7261,19 +7263,27 @@ void ImGui::PopTextWrapPos() window->DC.TextWrapPosStack.pop_back(); } -bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool dock_hierarchy) +static ImGuiWindow* GetCombinedRootWindow(ImGuiWindow* window, bool popup_hierarchy, bool dock_hierarchy) { - if ((dock_hierarchy ? window->RootWindowDockTree : window->RootWindow) == potential_parent) + window = window->RootWindow; + if (popup_hierarchy) + window = window->RootWindowPopupTree; + if (dock_hierarchy) + window = window->RootWindowDockTree; + return window; +} + +bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool popup_hierarchy, bool dock_hierarchy) +{ + ImGuiWindow* window_root = GetCombinedRootWindow(window, popup_hierarchy, dock_hierarchy); + if (window_root == potential_parent) return true; while (window != NULL) { if (window == potential_parent) return true; - // 2021-09-11: we broke the unexpressed contract that this function (prior to 6b1e094, #4527) - // would follow through popup parents as well. Restoring this for now. May want to add a ImGuiFocusedFlags_PopupHierarchy flag later. - if ((window->Flags & ImGuiWindowFlags_Popup) == 0) - if (window == (dock_hierarchy ? window->RootWindowDockTree : window->RootWindow)) - return false; + if (window == window_root) // end of chain + return false; window = window->ParentWindow; } return false; @@ -7305,14 +7315,15 @@ bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags) if ((flags & ImGuiHoveredFlags_AnyWindow) == 0) { IM_ASSERT(cur_window); // Not inside a Begin()/End() + const bool popup_hierarchy = (flags & ImGuiHoveredFlags_NoPopupHierarchy) == 0; const bool dock_hierarchy = (flags & ImGuiHoveredFlags_DockHierarchy) != 0; if (flags & ImGuiHoveredFlags_RootWindow) - cur_window = dock_hierarchy ? cur_window->RootWindowDockTree : cur_window->RootWindow; + cur_window = GetCombinedRootWindow(cur_window, popup_hierarchy, dock_hierarchy); bool result; if (flags & ImGuiHoveredFlags_ChildWindows) - result = IsWindowChildOf(ref_window, cur_window, dock_hierarchy); + result = IsWindowChildOf(ref_window, cur_window, popup_hierarchy, dock_hierarchy); else result = (ref_window == cur_window); if (!result) @@ -7339,13 +7350,14 @@ bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags) return true; IM_ASSERT(cur_window); // Not inside a Begin()/End() - const bool dock_hierarchy = (flags & ImGuiHoveredFlags_DockHierarchy) != 0; + const bool popup_hierarchy = (flags & ImGuiFocusedFlags_NoPopupHierarchy) == 0; + const bool dock_hierarchy = (flags & ImGuiFocusedFlags_DockHierarchy) != 0; if (flags & ImGuiHoveredFlags_RootWindow) - cur_window = dock_hierarchy ? cur_window->RootWindowDockTree : cur_window->RootWindow; + cur_window = GetCombinedRootWindow(cur_window, popup_hierarchy, dock_hierarchy); if (flags & ImGuiHoveredFlags_ChildWindows) - return IsWindowChildOf(ref_window, cur_window, dock_hierarchy); + return IsWindowChildOf(ref_window, cur_window, popup_hierarchy, dock_hierarchy); else return (ref_window == cur_window); } diff --git a/imgui.h b/imgui.h index e3e29e3c4d5e..cb49e1eb70b8 100644 --- a/imgui.h +++ b/imgui.h @@ -1312,7 +1312,8 @@ enum ImGuiFocusedFlags_ ImGuiFocusedFlags_ChildWindows = 1 << 0, // Return true if any children of the window is focused ImGuiFocusedFlags_RootWindow = 1 << 1, // Test from root window (top most parent of the current hierarchy) ImGuiFocusedFlags_AnyWindow = 1 << 2, // Return true if any window is focused. Important: If you are trying to tell how to dispatch your low-level inputs, do NOT use this. Use 'io.WantCaptureMouse' instead! Please read the FAQ! - ImGuiFocusedFlags_DockHierarchy = 1 << 3, // Consider docking hierarchy (treat dockspace host as parent of docked window) + ImGuiFocusedFlags_NoPopupHierarchy = 1 << 3, // Do not consider popup hierarchy (do not treat popup emitter as parent of popup) (when used with _ChildWindows or _RootWindow) + ImGuiFocusedFlags_DockHierarchy = 1 << 4, // Consider docking hierarchy (treat dockspace host as parent of docked window) (when used with _ChildWindows or _RootWindow) ImGuiFocusedFlags_RootAndChildWindows = ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows }; @@ -1325,12 +1326,13 @@ enum ImGuiHoveredFlags_ ImGuiHoveredFlags_ChildWindows = 1 << 0, // IsWindowHovered() only: Return true if any children of the window is hovered ImGuiHoveredFlags_RootWindow = 1 << 1, // IsWindowHovered() only: Test from root window (top most parent of the current hierarchy) ImGuiHoveredFlags_AnyWindow = 1 << 2, // IsWindowHovered() only: Return true if any window is hovered - ImGuiHoveredFlags_DockHierarchy = 1 << 3, // IsWindowHovered() only: Consider docking hierarchy (treat dockspace host as parent of docked window) - ImGuiHoveredFlags_AllowWhenBlockedByPopup = 1 << 4, // Return true even if a popup window is normally blocking access to this item/window - //ImGuiHoveredFlags_AllowWhenBlockedByModal = 1 << 4, // Return true even if a modal popup window is normally blocking access to this item/window. FIXME-TODO: Unavailable yet. - ImGuiHoveredFlags_AllowWhenBlockedByActiveItem = 1 << 5, // Return true even if an active item is blocking access to this item/window. Useful for Drag and Drop patterns. - ImGuiHoveredFlags_AllowWhenOverlapped = 1 << 6, // Return true even if the position is obstructed or overlapped by another window - ImGuiHoveredFlags_AllowWhenDisabled = 1 << 7, // Return true even if the item is disabled + ImGuiHoveredFlags_NoPopupHierarchy = 1 << 3, // IsWindowHovered() only: Do not consider popup hierarchy (do not treat popup emitter as parent of popup) (when used with _ChildWindows or _RootWindow) + ImGuiHoveredFlags_DockHierarchy = 1 << 4, // IsWindowHovered() only: Consider docking hierarchy (treat dockspace host as parent of docked window) (when used with _ChildWindows or _RootWindow) + ImGuiHoveredFlags_AllowWhenBlockedByPopup = 1 << 5, // Return true even if a popup window is normally blocking access to this item/window + //ImGuiHoveredFlags_AllowWhenBlockedByModal = 1 << 6, // Return true even if a modal popup window is normally blocking access to this item/window. FIXME-TODO: Unavailable yet. + ImGuiHoveredFlags_AllowWhenBlockedByActiveItem = 1 << 7, // Return true even if an active item is blocking access to this item/window. Useful for Drag and Drop patterns. + ImGuiHoveredFlags_AllowWhenOverlapped = 1 << 8, // Return true even if the position is obstructed or overlapped by another window + ImGuiHoveredFlags_AllowWhenDisabled = 1 << 9, // Return true even if the item is disabled ImGuiHoveredFlags_RectOnly = ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenOverlapped, ImGuiHoveredFlags_RootAndChildWindows = ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows }; diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 3165c42d954c..716d98347166 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2238,7 +2238,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } - if (ImGui::TreeNode("Querying Status (Edited/Active/Hovered etc.)")) + if (ImGui::TreeNode("Querying Item Status (Edited/Active/Hovered etc.)")) { // Select an item type const char* item_names[] = @@ -2324,51 +2324,75 @@ static void ShowDemoWindowWidgets() if (item_disabled) ImGui::EndDisabled(); + char buf[1] = ""; + ImGui::InputText("unused", buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_ReadOnly); + ImGui::SameLine(); + HelpMarker("This widget is only here to be able to tab-out of the widgets above and see e.g. Deactivated() status."); + + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Querying Window Status (Focused/Hovered etc.)")) + { static bool embed_all_inside_a_child_window = false; - ImGui::Checkbox("Embed everything inside a child window (for additional testing)", &embed_all_inside_a_child_window); + ImGui::Checkbox("Embed everything inside a child window for testing _RootWindow flag.", &embed_all_inside_a_child_window); if (embed_all_inside_a_child_window) ImGui::BeginChild("outer_child", ImVec2(0, ImGui::GetFontSize() * 20.0f), true); // Testing IsWindowFocused() function with its various flags. - // Note that the ImGuiFocusedFlags_XXX flags can be combined. ImGui::BulletText( "IsWindowFocused() = %d\n" "IsWindowFocused(_ChildWindows) = %d\n" + "IsWindowFocused(_ChildWindows|_NoPopupHierarchy) = %d\n" + "IsWindowFocused(_ChildWindows|_DockHierarchy) = %d\n" "IsWindowFocused(_ChildWindows|_RootWindow) = %d\n" + "IsWindowFocused(_ChildWindows|_RootWindow|_NoPopupHierarchy) = %d\n" "IsWindowFocused(_ChildWindows|_RootWindow|_DockHierarchy) = %d\n" - "IsWindowFocused(_RootWindow|_DockHierarchy) = %d\n" "IsWindowFocused(_RootWindow) = %d\n" + "IsWindowFocused(_RootWindow|_NoPopupHierarchy) = %d\n" + "IsWindowFocused(_RootWindow|_DockHierarchy) = %d\n" "IsWindowFocused(_AnyWindow) = %d\n", ImGui::IsWindowFocused(), ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows), + ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_NoPopupHierarchy), + ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_DockHierarchy), ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow), + ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_NoPopupHierarchy), ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_DockHierarchy), - ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_DockHierarchy), ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow), + ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_NoPopupHierarchy), + ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_DockHierarchy), ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow)); // Testing IsWindowHovered() function with its various flags. - // Note that the ImGuiHoveredFlags_XXX flags can be combined. ImGui::BulletText( "IsWindowHovered() = %d\n" "IsWindowHovered(_AllowWhenBlockedByPopup) = %d\n" "IsWindowHovered(_AllowWhenBlockedByActiveItem) = %d\n" "IsWindowHovered(_ChildWindows) = %d\n" + "IsWindowHovered(_ChildWindows|_NoPopupHierarchy) = %d\n" + "IsWindowHovered(_ChildWindows|_DockHierarchy) = %d\n" "IsWindowHovered(_ChildWindows|_RootWindow) = %d\n" + "IsWindowHovered(_ChildWindows|_RootWindow|_NoPopupHierarchy) = %d\n" "IsWindowHovered(_ChildWindows|_RootWindow|_DockHierarchy) = %d\n" + "IsWindowHovered(_RootWindow) = %d\n" + "IsWindowHovered(_RootWindow|_NoPopupHierarchy) = %d\n" "IsWindowHovered(_RootWindow|_DockHierarchy) = %d\n" "IsWindowHovered(_ChildWindows|_AllowWhenBlockedByPopup) = %d\n" - "IsWindowHovered(_RootWindow) = %d\n" "IsWindowHovered(_AnyWindow) = %d\n", ImGui::IsWindowHovered(), ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup), ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem), ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows), + ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_NoPopupHierarchy), + ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_DockHierarchy), ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow), + ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_NoPopupHierarchy), ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_DockHierarchy), + ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow), + ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_NoPopupHierarchy), ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_DockHierarchy), ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_AllowWhenBlockedByPopup), - ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow), ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow)); ImGui::BeginChild("child", ImVec2(0, 50), true); @@ -2377,9 +2401,6 @@ static void ShowDemoWindowWidgets() if (embed_all_inside_a_child_window) ImGui::EndChild(); - static char unused_str[] = "This widget is only here to be able to tab-out of the widgets above."; - ImGui::InputText("unused", unused_str, IM_ARRAYSIZE(unused_str), ImGuiInputTextFlags_ReadOnly); - // Calling IsItemHovered() after begin returns the hovered status of the title bar. // This is useful in particular if you want to create a context menu associated to the title bar of a window. // This will also work when docked into a Tab (the Tab replace the Title Bar and guarantee the same properties). diff --git a/imgui_internal.h b/imgui_internal.h index ae0c4efb3224..34a6924f11be 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2153,8 +2153,9 @@ struct IMGUI_API ImGuiWindow ImDrawList* DrawList; // == &DrawListInst (for backward compatibility reason with code using imgui_internal.h we keep this a pointer) ImDrawList DrawListInst; - ImGuiWindow* ParentWindow; // If we are a child _or_ popup window, this is pointing to our parent. Otherwise NULL. - ImGuiWindow* RootWindow; // Point to ourself or first ancestor that is not a child window. Doesn't cross through dock nodes. We use this so IsWindowFocused() can behave consistently regardless of docking state. + ImGuiWindow* ParentWindow; // If we are a child _or_ popup _or_ docked window, this is pointing to our parent. Otherwise NULL. + ImGuiWindow* RootWindow; // Point to ourself or first ancestor that is not a child window. Doesn't cross through popups/dock nodes. We use this so IsWindowFocused() can behave consistently regardless of docking state. + ImGuiWindow* RootWindowPopupTree; // Point to ourself or first ancestor that is not a child window. Cross through popups parent<>child. ImGuiWindow* RootWindowDockTree; // Point to ourself or first ancestor that is not a child window. Cross through dock nodes. ImGuiWindow* RootWindowForTitleBarHighlight; // Point to ourself or first ancestor which will display TitleBgActive color when this window is active. ImGuiWindow* RootWindowForNav; // Point to ourself or first ancestor which doesn't have the NavFlattened flag. @@ -2559,7 +2560,7 @@ namespace ImGui IMGUI_API ImGuiWindow* FindWindowByName(const char* name); IMGUI_API void UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window); IMGUI_API ImVec2 CalcWindowNextAutoFitSize(ImGuiWindow* window); - IMGUI_API bool IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool dock_hierarchy); + IMGUI_API bool IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool popup_hierarchy, bool dock_hierarchy); IMGUI_API bool IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below); IMGUI_API bool IsWindowNavFocusable(ImGuiWindow* window); IMGUI_API void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond = 0); From 29653273c1c6cec453be68d9cbe625f5d5c69fe8 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 13 Oct 2021 16:45:43 +0200 Subject: [PATCH 750/828] Docking: reinstate io.ConfigDockingWithShift option. (#4643) This more or less reverts commit 3ed07a8f0b18c5dfc94a997bde2417ee0453b0fd. --- docs/CHANGELOG.txt | 9 +++++++++ imgui.cpp | 11 ++++++----- imgui.h | 6 ++++-- imgui_demo.cpp | 15 ++++++++++++--- 4 files changed, 31 insertions(+), 10 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index d2fd846d1092..9600efa912c1 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -99,6 +99,15 @@ Other changes: Note that Linux/Mac still have inconsistent support for multi-viewports. If you want to help see https://github.com/ocornut/imgui/issues/2117. +----------------------------------------------------------------------- + VERSION 1.86 WIP (In Progress) +----------------------------------------------------------------------- + +Docking+Viewports Branch: + +- Revert removal of io.ConfigDockingWithShift config option (removed in 1.83). (#4643) + + ----------------------------------------------------------------------- VERSION 1.85 (Released 2021-10-12) ----------------------------------------------------------------------- diff --git a/imgui.cpp b/imgui.cpp index 9e0cbae9c87e..ee110c132609 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1124,6 +1124,7 @@ ImGuiIO::ImGuiIO() // Docking options (when ImGuiConfigFlags_DockingEnable is set) ConfigDockingNoSplit = false; + ConfigDockingWithShift = false; ConfigDockingAlwaysTabBar = false; ConfigDockingTransparentPayload = false; @@ -6836,7 +6837,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) { // Docking: Dragging a dockable window (or any of its child) turns it into a drag and drop source. // We need to do this _before_ we overwrite window->DC.LastItemId below because BeginDockableDragDropSource() also overwrites it. - if (g.MovingWindow == window && g.IO.KeyShift == false) + if ((g.MovingWindow == window) && (g.IO.ConfigDockingWithShift == g.IO.KeyShift)) if ((window->RootWindowDockTree->Flags & ImGuiWindowFlags_NoDocking) == 0) BeginDockableDragDropSource(window); @@ -14781,9 +14782,9 @@ static void ImGui::DockNodePreviewDockSetup(ImGuiWindow* host_window, ImGuiDockN } } - // We only allow and preview docking when hovering over a drop rect or over the title bar + // When docking without holding Shift, we only allow and preview docking when hovering over a drop rect or over the title bar data->IsDropAllowed = (data->SplitDir != ImGuiDir_None) || (data->IsCenterAvailable); - if (!is_explicit_target && !data->IsSplitDirExplicit) + if (!is_explicit_target && !data->IsSplitDirExplicit && !g.IO.ConfigDockingWithShift) data->IsDropAllowed = false; // Calculate split area @@ -16078,7 +16079,7 @@ void ImGui::BeginDockableDragDropSource(ImGuiWindow* window) g.LastItemData.ID = window->MoveId; window = window->RootWindowDockTree; IM_ASSERT((window->Flags & ImGuiWindowFlags_NoDocking) == 0); - bool is_drag_docking = ImRect(0, 0, window->SizeFull.x, GetFrameHeight()).Contains(g.ActiveIdClickOffset); // FIXME-DOCKING: Need to make this stateful and explicit + bool is_drag_docking = (g.IO.ConfigDockingWithShift) || ImRect(0, 0, window->SizeFull.x, GetFrameHeight()).Contains(g.ActiveIdClickOffset); // FIXME-DOCKING: Need to make this stateful and explicit if (is_drag_docking && BeginDragDropSource(ImGuiDragDropFlags_SourceNoPreviewTooltip | ImGuiDragDropFlags_SourceNoHoldToOpenOthers | ImGuiDragDropFlags_SourceAutoExpirePayload)) { SetDragDropPayload(IMGUI_PAYLOAD_TYPE_WINDOW, &window, sizeof(window)); @@ -16139,7 +16140,7 @@ void ImGui::BeginDockableDragDropTarget(ImGuiWindow* window) } const ImRect explicit_target_rect = (node && node->TabBar && !node->IsHiddenTabBar() && !node->IsNoTabBar()) ? node->TabBar->BarRect : ImRect(window->Pos, window->Pos + ImVec2(window->Size.x, GetFrameHeight())); - const bool is_explicit_target = IsMouseHoveringRect(explicit_target_rect.Min, explicit_target_rect.Max); + const bool is_explicit_target = g.IO.ConfigDockingWithShift || IsMouseHoveringRect(explicit_target_rect.Min, explicit_target_rect.Max); // Preview docking request and find out split direction/ratio //const bool do_preview = true; // Ignore testing for payload->IsPreview() which removes one frame of delay, but breaks overlapping drop targets within the same window. diff --git a/imgui.h b/imgui.h index d04b5e8e1eef..604b3c2aed23 100644 --- a/imgui.h +++ b/imgui.h @@ -65,7 +65,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.85" -#define IMGUI_VERSION_NUM 18500 +#define IMGUI_VERSION_NUM 18501 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE #define IMGUI_HAS_VIEWPORT // Viewport WIP branch @@ -805,8 +805,9 @@ namespace ImGui // Docking // [BETA API] Enable with io.ConfigFlags |= ImGuiConfigFlags_DockingEnable. // Note: You can use most Docking facilities without calling any API. You DO NOT need to call DockSpace() to use Docking! - // - Drag from window title bar or their tab to dock/undock. Hold SHIFT to disable docking. + // - Drag from window title bar or their tab to dock/undock. Hold SHIFT to disable docking/undocking. // - Drag from window menu button (upper-left button) to undock an entire node (all windows). + // - When io.ConfigDockingWithShift == true, you instead need to hold SHIFT to _enable_ docking/undocking. // About dockspaces: // - Use DockSpace() to create an explicit dock node _within_ an existing window. See Docking demo for details. // - Use DockSpaceOverViewport() to create an explicit dock node covering the screen or a specific viewport. @@ -1922,6 +1923,7 @@ struct ImGuiIO // Docking options (when ImGuiConfigFlags_DockingEnable is set) bool ConfigDockingNoSplit; // = false // Simplified docking mode: disable window splitting, so docking is limited to merging multiple windows together into tab-bars. + bool ConfigDockingWithShift; // = false // Enable docking with holding Shift key (reduce visual noise, allows dropping in wider space) bool ConfigDockingAlwaysTabBar; // = false // [BETA] [FIXME: This currently creates regression with auto-sizing and general overhead] Make every single floating window display within a docking node. bool ConfigDockingTransparentPayload;// = false // [BETA] Make window or viewport transparent when docking and only display docking boxes on the target viewport. Useful if rendering of multiple viewport cannot be synced. Best used with ConfigViewportsNoAutoMerge. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 1ee3d7cc079b..da82bcc699e1 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -465,12 +465,18 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::SameLine(); HelpMarker("Instruct backend to not alter mouse cursor shape and visibility."); ImGui::CheckboxFlags("io.ConfigFlags: DockingEnable", &io.ConfigFlags, ImGuiConfigFlags_DockingEnable); - ImGui::SameLine(); HelpMarker("Drag from window title bar or their tab to dock/undock. Hold SHIFT to disable docking.\n\nDrag from window menu button (upper-left button) to undock an entire node (all windows)."); + ImGui::SameLine(); + if (io.ConfigDockingWithShift) + HelpMarker("Drag from window title bar or their tab to dock/undock. Hold SHIFT to enable docking.\n\nDrag from window menu button (upper-left button) to undock an entire node (all windows)."); + else + HelpMarker("Drag from window title bar or their tab to dock/undock. Hold SHIFT to disable docking.\n\nDrag from window menu button (upper-left button) to undock an entire node (all windows)."); if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable) { ImGui::Indent(); ImGui::Checkbox("io.ConfigDockingNoSplit", &io.ConfigDockingNoSplit); ImGui::SameLine(); HelpMarker("Simplified docking mode: disable window splitting, so docking is limited to merging multiple windows together into tab-bars."); + ImGui::Checkbox("io.ConfigDockingWithShift", &io.ConfigDockingWithShift); + ImGui::SameLine(); HelpMarker("Enable docking when holding Shift only (allow to drop in wider space, reduce visual noise)"); ImGui::Checkbox("io.ConfigDockingAlwaysTabBar", &io.ConfigDockingAlwaysTabBar); ImGui::SameLine(); HelpMarker("Create a docking node and tab-bar on single floating windows."); ImGui::Checkbox("io.ConfigDockingTransparentPayload", &io.ConfigDockingTransparentPayload); @@ -5848,6 +5854,7 @@ void ImGui::ShowAboutWindow(bool* p_open) if (io.ConfigViewportsNoDecoration) ImGui::Text("io.ConfigViewportsNoDecoration"); if (io.ConfigViewportsNoDefaultParent) ImGui::Text("io.ConfigViewportsNoDefaultParent"); if (io.ConfigDockingNoSplit) ImGui::Text("io.ConfigDockingNoSplit"); + if (io.ConfigDockingWithShift) ImGui::Text("io.ConfigDockingWithShift"); if (io.ConfigDockingAlwaysTabBar) ImGui::Text("io.ConfigDockingAlwaysTabBar"); if (io.ConfigDockingTransparentPayload) ImGui::Text("io.ConfigDockingTransparentPayload"); if (io.ConfigMacOSXBehaviors) ImGui::Text("io.ConfigMacOSXBehaviors"); @@ -7475,6 +7482,7 @@ static void ShowExampleAppCustomRendering(bool* p_open) // Note: You can use most Docking facilities without calling any API. You DO NOT need to call DockSpace() to use Docking! // - Drag from window title bar or their tab to dock/undock. Hold SHIFT to disable docking. // - Drag from window menu button (upper-left button) to undock an entire node (all windows). +// - When io.ConfigDockingWithShift == true, you instead need to hold SHIFT to _enable_ docking/undocking. // About dockspaces: // - Use DockSpace() to create an explicit dock node _within_ an existing window. // - Use DockSpaceOverViewport() to create an explicit dock node covering the screen or a specific viewport. @@ -7579,8 +7587,9 @@ void ShowExampleAppDockSpace(bool* p_open) "When docking is enabled, you can ALWAYS dock MOST window into another! Try it now!" "\n" "- Drag from window title bar or their tab to dock/undock." "\n" "- Drag from window menu button (upper-left button) to undock an entire node (all windows)." "\n" - "- Hold SHIFT to disable docking." "\n" - "This demo app has nothing to do with it!" "\n\n" + "- Hold SHIFT to disable docking (if io.ConfigDockingWithShift == false, default)" "\n" + "- Hold SHIFT to enable docking (if io.ConfigDockingWithShift == true)" "\n" + "This demo app has nothing to do with enabling docking!" "\n\n" "This demo app only demonstrate the use of ImGui::DockSpace() which allows you to manually create a docking node _within_ another window." "\n\n" "Read comments in ShowExampleAppDockSpace() for more details."); From 05877c14df36d300366644cf1a1106a2513f8751 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 15 Oct 2021 11:51:01 +0200 Subject: [PATCH 751/828] Fixed nested BeginDisabled()/EndDisabled() bug in Docking branch due to bad merge. (#4655, #4452, #4453, #4462) --- docs/CHANGELOG.txt | 4 ++++ imgui.cpp | 1 - 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index cfcfd9314228..86679cdb40c2 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -119,6 +119,10 @@ Other Changes: - Misc: Fix MinGW DLL build issue (when IMGUI_API is defined). [@rokups] - CI: Add MinGW DLL build to test suite. [@rokups] +Docking+Viewports Branch: + +- Disabled: Fixed nested BeginDisabled()/EndDisabled() bug in Docking branch due to bad merge. (#4655, #4452, #4453, #4462) + ----------------------------------------------------------------------- VERSION 1.85 (Released 2021-10-12) diff --git a/imgui.cpp b/imgui.cpp index 8104f7b64437..3088739fafa3 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7187,7 +7187,6 @@ void ImGui::BeginDisabled(bool disabled) { ImGuiContext& g = *GImGui; bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0; - g.DisabledAlphaBackup = g.Style.Alpha; if (!was_disabled && disabled) { g.DisabledAlphaBackup = g.Style.Alpha; From bac748fa95ac003c7b354139980f8b4b7f6ac5da Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 15 Oct 2021 17:16:26 +0200 Subject: [PATCH 752/828] Backends: Made it possible to shutdown default Platform Backends before the Renderer backends. (#4656) --- backends/imgui_impl_glfw.cpp | 1 + backends/imgui_impl_sdl.cpp | 1 + backends/imgui_impl_win32.cpp | 1 + docs/CHANGELOG.txt | 3 ++- 4 files changed, 5 insertions(+), 1 deletion(-) diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index 5d7a915b4f40..018b537c5bec 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -961,4 +961,5 @@ static void ImGui_ImplGlfw_InitPlatformInterface() static void ImGui_ImplGlfw_ShutdownPlatformInterface() { + ImGui::DestroyPlatformWindows(); } diff --git a/backends/imgui_impl_sdl.cpp b/backends/imgui_impl_sdl.cpp index 42b06a570769..f87393f55546 100644 --- a/backends/imgui_impl_sdl.cpp +++ b/backends/imgui_impl_sdl.cpp @@ -776,4 +776,5 @@ static void ImGui_ImplSDL2_InitPlatformInterface(SDL_Window* window, void* sdl_g static void ImGui_ImplSDL2_ShutdownPlatformInterface() { + ImGui::DestroyPlatformWindows(); } diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index 3d584c87248f..f18553b33599 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -1006,6 +1006,7 @@ static void ImGui_ImplWin32_InitPlatformInterface() static void ImGui_ImplWin32_ShutdownPlatformInterface() { ::UnregisterClass(_T("ImGui Platform"), ::GetModuleHandle(NULL)); + ImGui::DestroyPlatformWindows(); } //--------------------------------------------------------------------------------------------------------- diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 86679cdb40c2..152b0b106b58 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -105,7 +105,8 @@ Other changes: Docking+Viewports Branch: -- Revert removal of io.ConfigDockingWithShift config option (removed in 1.83). (#4643) +- Docking: Revert removal of io.ConfigDockingWithShift config option (removed in 1.83). (#4643) +- Backends: Made it possible to shutdown default Platform Backends before the Renderer backends. (#4656) ----------------------------------------------------------------------- From 2080d12bd930124ddaece6f61befe51f47436837 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 16 Nov 2021 01:02:25 +0100 Subject: [PATCH 753/828] Viewports: Made it possible to explicitly assign ImGuiWindowClass::ParentViewportId to 0. (#3152, #2871) --- docs/CHANGELOG.txt | 3 +++ imgui.cpp | 2 +- imgui.h | 4 ++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index fb0059a41e13..3ab3774ce182 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -154,6 +154,9 @@ Other Changes: Docking+Viewports Branch: +- Viewports: Made it possible to explicitly assign ImGuiWindowClass::ParentViewportId to 0 in order + to ensure a window is not parented. Previously this would use the global default (which might be 0, + but not always as it would depend on io.ConfigViewportsNoDefaultParent). (#3152, #2871) - Disabled: Fixed nested BeginDisabled()/EndDisabled() bug in Docking branch due to bad merge. (#4655, #4452, #4453, #4462) diff --git a/imgui.cpp b/imgui.cpp index ace13208ffe1..730a6a8758d5 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -12457,7 +12457,7 @@ void ImGui::WindowSyncOwnedViewport(ImGuiWindow* window, ImGuiWindow* parent_win // Update parent viewport ID // (the !IsFallbackWindow test mimic the one done in WindowSelectViewport()) - if (window->WindowClass.ParentViewportId) + if (window->WindowClass.ParentViewportId != (ImGuiID)-1) window->Viewport->ParentViewportId = window->WindowClass.ParentViewportId; else if ((window_flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && parent_window_in_stack && (!parent_window_in_stack->IsFallbackWindow || parent_window_in_stack->WasActive)) window->Viewport->ParentViewportId = parent_window_in_stack->Viewport->ID; diff --git a/imgui.h b/imgui.h index 9ad5a2bc77b1..f922fc0e5542 100644 --- a/imgui.h +++ b/imgui.h @@ -2100,7 +2100,7 @@ struct ImGuiSizeCallbackData struct ImGuiWindowClass { ImGuiID ClassId; // User data. 0 = Default class (unclassed). Windows of different classes cannot be docked with each others. - ImGuiID ParentViewportId; // Hint for the platform backend. If non-zero, the platform backend can create a parent<>child relationship between the platform windows. Not conforming backends are free to e.g. parent every viewport to the main viewport or not. + ImGuiID ParentViewportId; // Hint for the platform backend. -1: use default. 0: request platform backend to not parent the platform. != 0: request platform backend to create a parent<>child relationship between the platform windows. Not conforming backends are free to e.g. parent every viewport to the main viewport or not. ImGuiViewportFlags ViewportFlagsOverrideSet; // Viewport flags to set when a window of this class owns a viewport. This allows you to enforce OS decoration or task bar icon, override the defaults on a per-window basis. ImGuiViewportFlags ViewportFlagsOverrideClear; // Viewport flags to clear when a window of this class owns a viewport. This allows you to enforce OS decoration or task bar icon, override the defaults on a per-window basis. ImGuiTabItemFlags TabItemFlagsOverrideSet; // [EXPERIMENTAL] TabItem flags to set when a window of this class gets submitted into a dock node tab bar. May use with ImGuiTabItemFlags_Leading or ImGuiTabItemFlags_Trailing. @@ -2108,7 +2108,7 @@ struct ImGuiWindowClass bool DockingAlwaysTabBar; // Set to true to enforce single floating windows of this class always having their own docking node (equivalent of setting the global io.ConfigDockingAlwaysTabBar) bool DockingAllowUnclassed; // Set to true to allow windows of this class to be docked/merged with an unclassed window. // FIXME-DOCK: Move to DockNodeFlags override? - ImGuiWindowClass() { memset(this, 0, sizeof(*this)); DockingAllowUnclassed = true; } + ImGuiWindowClass() { memset(this, 0, sizeof(*this)); ParentViewportId = (ImGuiID)-1; DockingAllowUnclassed = true; } }; // Data payload for Drag and Drop operations: AcceptDragDropPayload(), GetDragDropPayload() From b50b22d7873d7709df978011b514e5ee9ccc30fd Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 16 Nov 2021 16:13:04 +0100 Subject: [PATCH 754/828] Fixed tooltip in own viewport over modal from being incorrectly dimmed. (#4729) Normally we would aim to ensure that g.Windows[] gets maintained to reflect display layer but it is presently non trivial. --- docs/CHANGELOG.txt | 13 +++---------- imgui.cpp | 16 +++++++++++++--- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 3ab3774ce182..77a414818c04 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -99,16 +99,6 @@ Other changes: Note that Linux/Mac still have inconsistent support for multi-viewports. If you want to help see https://github.com/ocornut/imgui/issues/2117. ------------------------------------------------------------------------ - VERSION 1.86 WIP (In Progress) ------------------------------------------------------------------------ - -Docking+Viewports Branch: - -- Docking: Revert removal of io.ConfigDockingWithShift config option (removed in 1.83). (#4643) -- Backends: Made it possible to shutdown default Platform Backends before the Renderer backends. (#4656) - - ----------------------------------------------------------------------- VERSION 1.86 WIP (In Progress) ----------------------------------------------------------------------- @@ -154,9 +144,12 @@ Other Changes: Docking+Viewports Branch: +- Docking: Revert removal of io.ConfigDockingWithShift config option (removed in 1.83). (#4643) - Viewports: Made it possible to explicitly assign ImGuiWindowClass::ParentViewportId to 0 in order to ensure a window is not parented. Previously this would use the global default (which might be 0, but not always as it would depend on io.ConfigViewportsNoDefaultParent). (#3152, #2871) +- Viewports: Fixed tooltip in own viewport over modal from being incorrectly dimmed. (#4729) +- Backends: Made it possible to shutdown default Platform Backends before the Renderer backends. (#4656) - Disabled: Fixed nested BeginDisabled()/EndDisabled() bug in Docking branch due to bad merge. (#4655, #4452, #4453, #4462) diff --git a/imgui.cpp b/imgui.cpp index 730a6a8758d5..87c2bbfec0f7 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4534,11 +4534,15 @@ static void AddWindowToDrawData(ImGuiWindow* window, int layer) } } +static inline int GetWindowDisplayLayer(ImGuiWindow* window) +{ + return (window->Flags & ImGuiWindowFlags_Tooltip) ? 1 : 0; +} + // Layer is locked for the root window, however child windows may use a different viewport (e.g. extruding menu) -static void AddRootWindowToDrawData(ImGuiWindow* window) +static inline void AddRootWindowToDrawData(ImGuiWindow* window) { - int layer = (window->Flags & ImGuiWindowFlags_Tooltip) ? 1 : 0; - AddWindowToDrawData(window, layer); + AddWindowToDrawData(window, GetWindowDisplayLayer(window)); } void ImDrawDataBuilder::FlattenIntoSingleLayer() @@ -7303,6 +7307,12 @@ bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool ImGui::IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below) { ImGuiContext& g = *GImGui; + + // It would be saner to ensure that display layer is always reflected in the g.Windows[] order, which would likely requires altering all manipulations of that array + const int display_layer_delta = GetWindowDisplayLayer(potential_above) - GetWindowDisplayLayer(potential_below); + if (display_layer_delta != 0) + return display_layer_delta > 0; + for (int i = g.Windows.Size - 1; i >= 0; i--) { ImGuiWindow* candidate_window = g.Windows[i]; From 7f38773b738e8d37b1a3c1d627205821300ef765 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 17 Nov 2021 12:45:33 +0100 Subject: [PATCH 755/828] Fixed crash on right-click without modal, introduced by previous commit a3667f46, (#4729) --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 87c2bbfec0f7..3663a46fabb1 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3831,7 +3831,7 @@ void ImGui::UpdateMouseMovingWindowEndFrame() // Find the top-most window between HoveredWindow and the top-most Modal Window. // This is where we can trim the popup stack. ImGuiWindow* modal = GetTopMostPopupModal(); - bool hovered_window_above_modal = g.HoveredWindow && IsWindowAbove(g.HoveredWindow, modal); + bool hovered_window_above_modal = g.HoveredWindow && (modal == NULL || IsWindowAbove(g.HoveredWindow, modal)); ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal, true); } } From ea83d040e60d179248ac6213cf53b3343e95b371 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 24 Nov 2021 16:46:40 +0100 Subject: [PATCH 756/828] Viewports: fix missing default per-window value for ParentViewportId due to zero-cleared in-window instance (#4756) Broken by 2080d12b --- imgui.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/imgui.cpp b/imgui.cpp index 3663a46fabb1..0621e7324f7b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3092,6 +3092,7 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) : DrawListInst DrawList = &DrawListInst; DrawList->_Data = &context->DrawListSharedData; DrawList->_OwnerName = Name; + IM_PLACEMENT_NEW(&WindowClass) ImGuiWindowClass(); } ImGuiWindow::~ImGuiWindow() From 719d9313041b85227a3e6deb289a313819aaeab3 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 24 Nov 2021 17:49:21 +0100 Subject: [PATCH 757/828] Docking: Fixed a bug undocking windows docked into a non-visible or _KeepAliveOnly dockspace. (#4757) --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 77a414818c04..54d79fc3f9ef 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -145,6 +145,8 @@ Other Changes: Docking+Viewports Branch: - Docking: Revert removal of io.ConfigDockingWithShift config option (removed in 1.83). (#4643) +- Docking: Fixed a bug undocking windows docked into a non-visible or _KeepAliveOnly dockspace + when unrelated windows submitted before the dockspace have dynamic visibility. (#4757) - Viewports: Made it possible to explicitly assign ImGuiWindowClass::ParentViewportId to 0 in order to ensure a window is not parented. Previously this would use the global default (which might be 0, but not always as it would depend on io.ConfigViewportsNoDefaultParent). (#3152, #2871) diff --git a/imgui.cpp b/imgui.cpp index 0621e7324f7b..8341889a34d3 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -16157,7 +16157,7 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) node->State = ImGuiDockNodeState_HostWindowVisible; // Undock if we are submitted earlier than the host window - if (window->BeginOrderWithinContext < node->HostWindow->BeginOrderWithinContext) + if (!(node->MergedFlags & ImGuiDockNodeFlags_KeepAliveOnly) && window->BeginOrderWithinContext < node->HostWindow->BeginOrderWithinContext) { DockContextProcessUndockWindow(ctx, window); return; From 5f5ba8eb119dc2c692a1cc81f4b93b482287f0ec Mon Sep 17 00:00:00 2001 From: Mikko Sivulainen Date: Wed, 1 Dec 2021 12:10:37 +0200 Subject: [PATCH 758/828] Docking: Fix typo (had no side effect) (#4778) Co-authored-by: Mikko Sivulainen --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 8341889a34d3..adc4d6b84a9e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -13426,7 +13426,7 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) IM_ASSERT(last_focused_node != NULL); ImGuiDockNode* last_focused_root_node = DockNodeGetRootNode(last_focused_node); IM_ASSERT(last_focused_root_node == DockNodeGetRootNode(payload_node)); - last_focused_node->SetLocalFlags(last_focused_node->LocalFlags |= ImGuiDockNodeFlags_CentralNode); + last_focused_node->SetLocalFlags(last_focused_node->LocalFlags | ImGuiDockNodeFlags_CentralNode); node->SetLocalFlags(node->LocalFlags & ~ImGuiDockNodeFlags_CentralNode); last_focused_root_node->CentralNode = last_focused_node; } From 1ab3007752a78b8bb41df2cdda2729d8522e8328 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 1 Dec 2021 14:42:58 +0100 Subject: [PATCH 759/828] Viewports: Fixed CTRL+TAB highlight outline on docked windows not always fitting in host viewport + moved EndFrameDrawDimmedBackgrounds() call + removed duplicate code in Begin() already in EndFrameDrawDimmedBackgrounds() --- docs/CHANGELOG.txt | 1 + imgui.cpp | 22 ++++------------------ 2 files changed, 5 insertions(+), 18 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 54d79fc3f9ef..682bd0193421 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -151,6 +151,7 @@ Docking+Viewports Branch: to ensure a window is not parented. Previously this would use the global default (which might be 0, but not always as it would depend on io.ConfigViewportsNoDefaultParent). (#3152, #2871) - Viewports: Fixed tooltip in own viewport over modal from being incorrectly dimmed. (#4729) +- Viewports: Fixed CTRL+TAB highlight outline on docked windows not always fitting in host viewport. - Backends: Made it possible to shutdown default Platform Backends before the Renderer backends. (#4656) - Disabled: Fixed nested BeginDisabled()/EndDisabled() bug in Docking branch due to bad merge. (#4655, #4452, #4453, #4462) diff --git a/imgui.cpp b/imgui.cpp index adc4d6b84a9e..fd1e563bcaa8 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4664,7 +4664,7 @@ static void ImGui::EndFrameDrawDimmedBackgrounds() float rounding = ImMax(window->WindowRounding, g.Style.WindowRounding); ImRect bb = window->Rect(); bb.Expand(g.FontSize); - if (bb.Contains(window->Viewport->GetMainRect())) // If a window fits the entire viewport, adjust its highlight inward + if (!window->Viewport->GetMainRect().Contains(bb)) // If a window fits the entire viewport, adjust its highlight inward { bb.Expand(-g.FontSize - 1.0f); rounding = window->WindowRounding; @@ -4704,9 +4704,6 @@ void ImGui::EndFrame() g.CurrentWindow->Active = false; End(); - // Draw modal whitening background on _other_ viewports than the one the modal is one - EndFrameDrawDimmedBackgrounds(); - // Update navigation: CTRL+Tab, wrap-around requests NavEndFrame(); @@ -4736,6 +4733,9 @@ void ImGui::EndFrame() // Initiate moving window + handle left-click and right-click focus UpdateMouseMovingWindowEndFrame(); + // Draw modal/window whitening backgrounds + EndFrameDrawDimmedBackgrounds(); + // Update user-facing viewport list (g.Viewports -> g.PlatformIO.Viewports after filtering out some) UpdateViewportsEndFrame(); @@ -6743,20 +6743,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DrawList = &window->DrawListInst; } - // Draw navigation selection/windowing rectangle border - if (g.NavWindowingTargetAnim == window) - { - float rounding = ImMax(window->WindowRounding, g.Style.WindowRounding); - ImRect bb = window->Rect(); - bb.Expand(g.FontSize); - if (bb.Contains(viewport_rect)) // If a window fits the entire viewport, adjust its highlight inward - { - bb.Expand(-g.FontSize - 1.0f); - rounding = window->WindowRounding; - } - window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), rounding, 0, 3.0f); - } - // UPDATE RECTANGLES (2- THOSE AFFECTED BY SCROLLING) // Work rectangle. From 6afcfe3442c07ef654c8a6d99af4e21f04629ad7 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 1 Dec 2021 17:46:04 +0100 Subject: [PATCH 760/828] Docking: Fixed incorrectly rounded tab bars for dock node that are not at the top of their dock tree. --- docs/CHANGELOG.txt | 1 + imgui.cpp | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index d31e753ae263..12547d620c8a 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -158,6 +158,7 @@ Docking+Viewports Branch: - Docking: Revert removal of io.ConfigDockingWithShift config option (removed in 1.83). (#4643) - Docking: Fixed a bug undocking windows docked into a non-visible or _KeepAliveOnly dockspace when unrelated windows submitted before the dockspace have dynamic visibility. (#4757) +- Docking: Fixed incorrectly rounded tab bars for dock node that are not at the top of their dock tree. - Viewports: Made it possible to explicitly assign ImGuiWindowClass::ParentViewportId to 0 in order to ensure a window is not parented. Previously this would use the global default (which might be 0, but not always as it would depend on io.ConfigViewportsNoDefaultParent). (#3152, #2871) diff --git a/imgui.cpp b/imgui.cpp index 399058744882..28c31b54d11d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4646,7 +4646,7 @@ static void ImGui::EndFrameDrawDimmedBackgrounds() draw_list->AddRectFilled(viewport->Pos, viewport->Pos + viewport->Size, dim_bg_col); } - // Draw modal whitening background between CTRL-TAB list + // Draw modal whitening background behind CTRL-TAB list if (dim_bg_for_window_list && g.NavWindowingTargetAnim->Active) { // Choose a draw list that will be front-most across all our children @@ -14488,7 +14488,11 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w if (is_focused) node->LastFrameFocused = g.FrameCount; ImU32 title_bar_col = GetColorU32(host_window->Collapsed ? ImGuiCol_TitleBgCollapsed : is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg); - host_window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, host_window->WindowRounding, ImDrawFlags_RoundCornersTop); + bool rounding_t = node->Pos.y <= host_window->Pos.y + DOCKING_SPLITTER_SIZE; + bool rounding_tl = rounding_t && (node->Pos.x <= host_window->Pos.x + DOCKING_SPLITTER_SIZE); + bool rounding_tr = rounding_t && (node->Pos.x + node->Size.x >= host_window->Pos.x + host_window->Size.x - DOCKING_SPLITTER_SIZE); + ImDrawFlags rounding_flags = ImDrawFlags_RoundCornersNone | (rounding_tl ? ImDrawFlags_RoundCornersTopLeft : 0) | (rounding_tr ? ImDrawFlags_RoundCornersTopRight : 0); + host_window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, host_window->WindowRounding, rounding_flags); // Docking/Collapse button if (has_window_menu_button) From 8733ca49b0a2f29d858e2121f41759187bc7e234 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 2 Dec 2021 18:39:52 +0100 Subject: [PATCH 761/828] Docking: Fixed single-frame node pos/size inconsistencies when window stop or start being submitted. Fix 718e15c7d while preserving its intended property. Tested by "docking_window_appearing_layout". (#2109) --- docs/CHANGELOG.txt | 1 + imgui.cpp | 41 ++++++++++++++++++++++------------------- imgui_internal.h | 2 +- 3 files changed, 24 insertions(+), 20 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 12547d620c8a..09a3e527901b 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -159,6 +159,7 @@ Docking+Viewports Branch: - Docking: Fixed a bug undocking windows docked into a non-visible or _KeepAliveOnly dockspace when unrelated windows submitted before the dockspace have dynamic visibility. (#4757) - Docking: Fixed incorrectly rounded tab bars for dock node that are not at the top of their dock tree. +- Docking: Fixed single-frame node pos/size inconsistencies when window stop or start being submitted. - Viewports: Made it possible to explicitly assign ImGuiWindowClass::ParentViewportId to 0 in order to ensure a window is not parented. Previously this would use the global default (which might be 0, but not always as it would depend on io.ConfigViewportsNoDefaultParent). (#3152, #2871) diff --git a/imgui.cpp b/imgui.cpp index 28c31b54d11d..291b6f39cd57 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -12908,7 +12908,7 @@ namespace ImGui // ImGuiDockNode tree manipulations static void DockNodeTreeSplit(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiAxis split_axis, int split_first_child, float split_ratio, ImGuiDockNode* new_node); static void DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiDockNode* merge_lead_child); - static void DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 size, bool only_write_to_marked_nodes = false); + static void DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 size, ImGuiDockNode* only_write_to_single_node = NULL); static void DockNodeTreeUpdateSplitter(ImGuiDockNode* node); static ImGuiDockNode* DockNodeTreeFindVisibleNodeByPos(ImGuiDockNode* node, ImVec2 pos); static ImGuiDockNode* DockNodeTreeFindFallbackLeafNode(ImGuiDockNode* node); @@ -13604,7 +13604,6 @@ ImGuiDockNode::ImGuiDockNode(ImGuiID id) IsVisible = true; IsFocused = HasCloseButton = HasWindowMenuButton = HasCentralNodeChild = false; WantCloseAll = WantLockSizeOnce = WantMouseMove = WantHiddenTabBarUpdate = WantHiddenTabBarToggle = false; - MarkedForPosSizeWrite = false; } ImGuiDockNode::~ImGuiDockNode() @@ -14045,7 +14044,6 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) ImGuiContext& g = *GImGui; IM_ASSERT(node->LastFrameActive != g.FrameCount); node->LastFrameAlive = g.FrameCount; - node->MarkedForPosSizeWrite = false; node->CentralNode = node->OnlyNodeWithWindows = NULL; if (node->IsRootNode()) @@ -15153,11 +15151,11 @@ void ImGui::DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImG // Update Pos/Size for a node hierarchy (don't affect child Windows yet) // (Depth-first, Pre-Order) -void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 size, bool only_write_to_marked_nodes) +void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 size, ImGuiDockNode* only_write_to_single_node) { // During the regular dock node update we write to all nodes. - // 'only_write_to_marked_nodes' is only set when turning a node visible mid-frame and we need its size right-away. - const bool write_to_node = (only_write_to_marked_nodes == false) || (node->MarkedForPosSizeWrite); + // 'only_write_to_single_node' is only set when turning a node visible mid-frame and we need its size right-away. + const bool write_to_node = only_write_to_single_node == NULL || only_write_to_single_node == node; if (write_to_node) { node->Pos = pos; @@ -15171,15 +15169,21 @@ void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 si ImGuiDockNode* child_1 = node->ChildNodes[1]; ImVec2 child_0_pos = pos, child_1_pos = pos; ImVec2 child_0_size = size, child_1_size = size; - if (child_0->IsVisible && child_1->IsVisible) + + const bool child_0_is_toward_single_node = (only_write_to_single_node != NULL && DockNodeIsInHierarchyOf(only_write_to_single_node, child_0)); + const bool child_1_is_toward_single_node = (only_write_to_single_node != NULL && DockNodeIsInHierarchyOf(only_write_to_single_node, child_1)); + const bool child_0_is_or_will_be_visible = child_0->IsVisible || child_0_is_toward_single_node; + const bool child_1_is_or_will_be_visible = child_1->IsVisible || child_1_is_toward_single_node; + + if (child_0_is_or_will_be_visible && child_1_is_or_will_be_visible) { + ImGuiContext& g = *GImGui; const float spacing = DOCKING_SPLITTER_SIZE; const ImGuiAxis axis = (ImGuiAxis)node->SplitAxis; const float size_avail = ImMax(size[axis] - spacing, 0.0f); // Size allocation policy // 1) The first 0..WindowMinSize[axis]*2 are allocated evenly to both windows. - ImGuiContext& g = *GImGui; const float size_min_each = ImFloor(ImMin(size_avail, g.Style.WindowMinSize[axis] * 2.0f) * 0.5f); // FIXME: Blocks 2) and 3) are essentially doing nearly the same thing. @@ -15230,11 +15234,15 @@ void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 si child_1_pos[axis] += spacing + child_0_size[axis]; } - child_0->WantLockSizeOnce = child_1->WantLockSizeOnce = false; - if (child_0->IsVisible) + if (only_write_to_single_node == NULL) + child_0->WantLockSizeOnce = child_1->WantLockSizeOnce = false; + + const bool child_0_recurse = only_write_to_single_node ? child_0_is_toward_single_node : child_0->IsVisible; + const bool child_1_recurse = only_write_to_single_node ? child_1_is_toward_single_node : child_1->IsVisible; + if (child_0_recurse) DockNodeTreeUpdatePosSize(child_0, child_0_pos, child_0_size); - if (child_1->IsVisible) + if (child_1_recurse) DockNodeTreeUpdatePosSize(child_1, child_1_pos, child_1_size); } @@ -16048,16 +16056,11 @@ static ImGuiDockNode* ImGui::DockContextBindNodeToWindow(ImGuiContext* ctx, ImGu if (!node->IsVisible) { ImGuiDockNode* ancestor_node = node; - while (!ancestor_node->IsVisible) - { - ancestor_node->IsVisible = true; - ancestor_node->MarkedForPosSizeWrite = true; - if (ancestor_node->ParentNode) - ancestor_node = ancestor_node->ParentNode; - } + while (!ancestor_node->IsVisible && ancestor_node->ParentNode) + ancestor_node = ancestor_node->ParentNode; IM_ASSERT(ancestor_node->Size.x > 0.0f && ancestor_node->Size.y > 0.0f); DockNodeUpdateHasCentralNodeChild(DockNodeGetRootNode(ancestor_node)); - DockNodeTreeUpdatePosSize(ancestor_node, ancestor_node->Pos, ancestor_node->Size, true); + DockNodeTreeUpdatePosSize(ancestor_node, ancestor_node->Pos, ancestor_node->Size, node); } // Add window to node diff --git a/imgui_internal.h b/imgui_internal.h index 0a2b3c4aa443..fa41c67ea8f5 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1457,7 +1457,6 @@ struct IMGUI_API ImGuiDockNode bool WantMouseMove :1; // After a node extraction we need to transition toward moving the newly created host window bool WantHiddenTabBarUpdate :1; bool WantHiddenTabBarToggle :1; - bool MarkedForPosSizeWrite :1; // Update by DockNodeTreeUpdatePosSize() write-filtering ImGuiDockNode(ImGuiID id); ~ImGuiDockNode(); @@ -2837,6 +2836,7 @@ namespace ImGui IMGUI_API bool DockNodeBeginAmendTabBar(ImGuiDockNode* node); IMGUI_API void DockNodeEndAmendTabBar(); inline ImGuiDockNode* DockNodeGetRootNode(ImGuiDockNode* node) { while (node->ParentNode) node = node->ParentNode; return node; } + inline bool DockNodeIsInHierarchyOf(ImGuiDockNode* node, ImGuiDockNode* parent) { while (node) { if (node == parent) return true; node = node->ParentNode; } return false; } inline int DockNodeGetDepth(const ImGuiDockNode* node) { int depth = 0; while (node->ParentNode) { node = node->ParentNode; depth++; } return depth; } inline ImGuiID DockNodeGetWindowMenuButtonId(const ImGuiDockNode* node) { return ImHashStr("#COLLAPSE", 0, node->ID); } inline ImGuiDockNode* GetWindowDockNode() { ImGuiContext& g = *GImGui; return g.CurrentWindow->DockNode; } From bf80204e630f6bd30dfd77a82cd63358a44fe1ab Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 2 Dec 2021 19:20:59 +0100 Subject: [PATCH 762/828] Docking: internals: extracted rounding corner calculation into reusable CalcRoundingFlagsForRectInRect() function. --- imgui.cpp | 7 ++----- imgui_draw.cpp | 11 +++++++++++ imgui_internal.h | 1 + 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 291b6f39cd57..18187728830d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -14486,10 +14486,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w if (is_focused) node->LastFrameFocused = g.FrameCount; ImU32 title_bar_col = GetColorU32(host_window->Collapsed ? ImGuiCol_TitleBgCollapsed : is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg); - bool rounding_t = node->Pos.y <= host_window->Pos.y + DOCKING_SPLITTER_SIZE; - bool rounding_tl = rounding_t && (node->Pos.x <= host_window->Pos.x + DOCKING_SPLITTER_SIZE); - bool rounding_tr = rounding_t && (node->Pos.x + node->Size.x >= host_window->Pos.x + host_window->Size.x - DOCKING_SPLITTER_SIZE); - ImDrawFlags rounding_flags = ImDrawFlags_RoundCornersNone | (rounding_tl ? ImDrawFlags_RoundCornersTopLeft : 0) | (rounding_tr ? ImDrawFlags_RoundCornersTopRight : 0); + ImDrawFlags rounding_flags = CalcRoundingFlagsForRectInRect(title_bar_rect, host_window->Rect(), DOCKING_SPLITTER_SIZE); host_window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, host_window->WindowRounding, rounding_flags); // Docking/Collapse button @@ -14958,7 +14955,7 @@ static void ImGui::DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDock overlay_rect.Min.y += GetFrameHeight(); if (data->SplitDir != ImGuiDir_None || data->IsCenterAvailable) for (int overlay_n = 0; overlay_n < overlay_draw_lists_count; overlay_n++) - overlay_draw_lists[overlay_n]->AddRectFilled(overlay_rect.Min, overlay_rect.Max, overlay_col_main, host_window->WindowRounding); + overlay_draw_lists[overlay_n]->AddRectFilled(overlay_rect.Min, overlay_rect.Max, overlay_col_main, host_window->WindowRounding, CalcRoundingFlagsForRectInRect(overlay_rect, host_window->Rect(), DOCKING_SPLITTER_SIZE)); } // Display tab shape/label preview unless we are splitting node (it generally makes the situation harder to read) diff --git a/imgui_draw.cpp b/imgui_draw.cpp index cc3e45afcf68..437a4a4110ac 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -3926,6 +3926,17 @@ void ImGui::RenderRectFilledWithHole(ImDrawList* draw_list, ImRect outer, ImRect if (fill_R && fill_D) draw_list->AddRectFilled(ImVec2(inner.Max.x, inner.Max.y), ImVec2(outer.Max.x, outer.Max.y), col, rounding, ImDrawFlags_RoundCornersBottomRight); } +ImDrawFlags ImGui::CalcRoundingFlagsForRectInRect(const ImRect& r_in, const ImRect& r_outer, float threshold) +{ + bool round_l = r_in.Min.x <= r_outer.Min.x + threshold; + bool round_r = r_in.Max.x >= r_outer.Max.x - threshold; + bool round_t = r_in.Min.y <= r_outer.Min.y + threshold; + bool round_b = r_in.Max.y >= r_outer.Max.y - threshold; + return ImDrawFlags_RoundCornersNone + | ((round_t && round_l) ? ImDrawFlags_RoundCornersTopLeft : 0) | ((round_t && round_r) ? ImDrawFlags_RoundCornersTopRight : 0) + | ((round_b && round_l) ? ImDrawFlags_RoundCornersBottomLeft : 0) | ((round_b && round_r) ? ImDrawFlags_RoundCornersBottomRight : 0); +} + // Helper for ColorPicker4() // NB: This is rather brittle and will show artifact when rounding this enabled if rounded corners overlap multiple cells. Caller currently responsible for avoiding that. // Spent a non reasonable amount of time trying to getting this right for ColorButton with rounding+anti-aliasing+ImGuiColorEditFlags_HalfAlphaPreview flag + various grid sizes and offsets, and eventually gave up... probably more reasonable to disable rounding altogether. diff --git a/imgui_internal.h b/imgui_internal.h index fa41c67ea8f5..4c7cd4af3a27 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2976,6 +2976,7 @@ namespace ImGui IMGUI_API void RenderArrowDockMenu(ImDrawList* draw_list, ImVec2 p_min, float sz, ImU32 col); IMGUI_API void RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, ImU32 col, float x_start_norm, float x_end_norm, float rounding); IMGUI_API void RenderRectFilledWithHole(ImDrawList* draw_list, ImRect outer, ImRect inner, ImU32 col, float rounding); + IMGUI_API ImDrawFlags CalcRoundingFlagsForRectInRect(const ImRect& r_in, const ImRect& r_outer, float threshold); #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS // [1.71: 2019/06/07: Updating prototypes of some of the internal functions. Leaving those for reference for a short while] From b16f738d0414b2586b43da2740528d8e93a053c4 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 2 Dec 2021 22:44:49 +0100 Subject: [PATCH 763/828] Docking: docked windows honor ImGuiCol_WindowBg. Host window in charge of rendering seams. (#2700, #2539 + Docked windows honor display their border properly. (#2522) Plus: better support for transparent one in nodes Side effects: DockContextBindNodeToWindow doesn't alter node->IsVisible. Side effects: ImDrawList:: _ResetForNewFrame() needs to merge, sane (in case of (Amended, force-pushed) --- docs/CHANGELOG.txt | 2 + imgui.cpp | 94 +++++++++++++++++++++++++++++++++++++--------- imgui_draw.cpp | 2 + imgui_internal.h | 5 ++- imgui_widgets.cpp | 6 ++- 5 files changed, 88 insertions(+), 21 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 09a3e527901b..f184f7a60c56 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -158,6 +158,8 @@ Docking+Viewports Branch: - Docking: Revert removal of io.ConfigDockingWithShift config option (removed in 1.83). (#4643) - Docking: Fixed a bug undocking windows docked into a non-visible or _KeepAliveOnly dockspace when unrelated windows submitted before the dockspace have dynamic visibility. (#4757) +- Docking, Style: Docked windows honor ImGuiCol_WindowBg. (#2700, #2539) +- Docking, Style: Docked windows honor display their border properly. (#2522) - Docking: Fixed incorrectly rounded tab bars for dock node that are not at the top of their dock tree. - Docking: Fixed single-frame node pos/size inconsistencies when window stop or start being submitted. - Viewports: Made it possible to explicitly assign ImGuiWindowClass::ParentViewportId to 0 in order diff --git a/imgui.cpp b/imgui.cpp index 18187728830d..2d0a096f2798 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4528,6 +4528,8 @@ static void AddWindowToDrawData(ImGuiWindow* window, int layer) ImGuiContext& g = *GImGui; ImGuiViewportP* viewport = window->Viewport; g.IO.MetricsRenderWindows++; + if (window->Flags & ImGuiWindowFlags_DockNodeHost) + window->DrawList->ChannelsMerge(); AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[layer], window->DrawList); for (int i = 0; i < window->DC.ChildWindows.Size; i++) { @@ -4709,6 +4711,9 @@ void ImGui::EndFrame() // Update navigation: CTRL+Tab, wrap-around requests NavEndFrame(); + // Update docking + DockContextEndFrame(&g); + SetCurrentViewport(NULL, NULL); // Drag and Drop: Elapse payload (if delivered, or if source stops being submitted) @@ -5653,11 +5658,11 @@ ImVec2 ImGui::CalcWindowNextAutoFitSize(ImGuiWindow* window) return size_final; } -static ImGuiCol GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags) +static ImGuiCol GetWindowBgColorIdx(ImGuiWindow* window) { - if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) + if (window->Flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) return ImGuiCol_PopupBg; - if (flags & ImGuiWindowFlags_ChildWindow) + if ((window->Flags & ImGuiWindowFlags_ChildWindow) && !window->DockIsActive) return ImGuiCol_ChildBg; return ImGuiCol_WindowBg; } @@ -5950,7 +5955,7 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar if (g.DragDropPayload.IsDataType(IMGUI_PAYLOAD_TYPE_WINDOW) && *(ImGuiWindow**)g.DragDropPayload.Data == window) is_docking_transparent_payload = true; - ImU32 bg_col = GetColorU32(GetWindowBgColorIdxFromFlags(flags)); + ImU32 bg_col = GetColorU32(GetWindowBgColorIdx(window)); if (window->ViewportOwned) { // No alpha @@ -5976,8 +5981,21 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar if (override_alpha) bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(alpha) << IM_COL32_A_SHIFT); } - window->DrawList->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight()), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? 0 : ImDrawFlags_RoundCornersBottom); + + // Render, for docked windows and host windows we ensure bg goes before decorations + ImDrawList* bg_draw_list = window->DockIsActive ? window->DockNode->HostWindow->DrawList : window->DrawList; + if (window->DockIsActive || (flags & ImGuiWindowFlags_DockNodeHost)) + bg_draw_list->ChannelsSetCurrent(0); + if (window->DockIsActive) + window->DockNode->LastBgColor = bg_col; + + bg_draw_list->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight()), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? 0 : ImDrawFlags_RoundCornersBottom); + + if (window->DockIsActive || (flags & ImGuiWindowFlags_DockNodeHost)) + bg_draw_list->ChannelsSetCurrent(1); } + if (window->DockIsActive) + window->DockNode->IsBgDrawnThisFrame = true; // Title bar // (when docked, DockNode are drawing their own title bar. Individual windows however do NOT set the _NoTitleBar flag, @@ -6346,6 +6364,11 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->IDStack.resize(1); window->DrawList->_ResetForNewFrame(); window->DC.CurrentTableIdx = -1; + if (flags & ImGuiWindowFlags_DockNodeHost) + { + window->DrawList->ChannelsSplit(2); + window->DrawList->ChannelsSetCurrent(1); // Render decorations on channel 1 as we will render the backgrounds manually later + } // Restore buffer capacity when woken from a compacted state, to avoid if (window->MemoryCompacted) @@ -12786,6 +12809,9 @@ void ImGui::DestroyPlatformWindows() // | BeginDockableDragDropTarget() // | - DockNodePreviewDockRender() //----------------------------------------------------------------------------- +// - EndFrame() +// | DockContextEndFrame() +//----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // Docking: Internal Types @@ -12940,6 +12966,7 @@ namespace ImGui // - DockContextRebuildNodes() // - DockContextNewFrameUpdateUndocking() // - DockContextNewFrameUpdateDocking() +// - DockContextEndFrame() // - DockContextFindNodeByID() // - DockContextBindNodeToWindow() // - DockContextGenNodeID() @@ -13075,6 +13102,22 @@ void ImGui::DockContextNewFrameUpdateDocking(ImGuiContext* ctx) DockNodeUpdate(node); } +void ImGui::DockContextEndFrame(ImGuiContext* ctx) +{ + // Draw backgrounds of node missing their window + ImGuiContext& g = *ctx; + ImGuiDockContext* dc = &g.DockContext; + for (int n = 0; n < dc->Nodes.Data.Size; n++) + if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p) + if (node->LastFrameActive == g.FrameCount && node->IsVisible && node->HostWindow && node->IsLeafNode() && !node->IsBgDrawnThisFrame) + { + ImRect bg_rect(node->Pos + ImVec2(0.0f, GetFrameHeight()), node->Pos + node->Size); + ImDrawFlags bg_rounding_flags = CalcRoundingFlagsForRectInRect(bg_rect, node->HostWindow->Rect(), DOCKING_SPLITTER_SIZE); + node->HostWindow->DrawList->ChannelsSetCurrent(0); + node->HostWindow->DrawList->AddRectFilled(bg_rect.Min, bg_rect.Max, node->LastBgColor, node->HostWindow->WindowRounding, bg_rounding_flags); + } +} + static ImGuiDockNode* ImGui::DockContextFindNodeByID(ImGuiContext* ctx, ImGuiID id) { return (ImGuiDockNode*)ctx->DockContext.Nodes.GetVoidPtr(id); @@ -13592,6 +13635,7 @@ ImGuiDockNode::ImGuiDockNode(ImGuiID id) SplitAxis = ImGuiAxis_None; State = ImGuiDockNodeState_Unknown; + LastBgColor = IM_COL32_WHITE; HostWindow = VisibleWindow = NULL; CentralNode = OnlyNodeWithWindows = NULL; CountNodeWithWindows = 0; @@ -13603,6 +13647,7 @@ ImGuiDockNode::ImGuiDockNode(ImGuiID id) AuthorityForViewport = ImGuiDataAuthority_Auto; IsVisible = true; IsFocused = HasCloseButton = HasWindowMenuButton = HasCentralNodeChild = false; + IsBgDrawnThisFrame = false; WantCloseAll = WantLockSizeOnce = WantMouseMove = WantHiddenTabBarUpdate = WantHiddenTabBarToggle = false; } @@ -14044,6 +14089,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) ImGuiContext& g = *GImGui; IM_ASSERT(node->LastFrameActive != g.FrameCount); node->LastFrameAlive = g.FrameCount; + node->IsBgDrawnThisFrame = false; node->CentralNode = node->OnlyNodeWithWindows = NULL; if (node->IsRootNode()) @@ -14186,6 +14232,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) window_flags |= ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus | ImGuiWindowFlags_NoCollapse; window_flags |= ImGuiWindowFlags_NoTitleBar; + SetNextWindowBgAlpha(0.0f); // Don't set ImGuiWindowFlags_NoBackground because it disables borders PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); Begin(window_label, NULL, window_flags); PopStyleVar(); @@ -14224,15 +14271,6 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) if (g.NavWindow && g.NavWindow->RootWindow->DockNode && g.NavWindow->RootWindow->ParentWindow == host_window) node->LastFocusedNodeId = g.NavWindow->RootWindow->DockNode->ID; - // We need to draw a background at the root level if requested by ImGuiDockNodeFlags_PassthruCentralNode, but we will only know the correct pos/size - // _after_ processing the resizing splitters. So we are using the DrawList channel splitting facility to submit drawing primitives out of order! - const bool render_dockspace_bg = node->IsRootNode() && host_window && (node_flags & ImGuiDockNodeFlags_PassthruCentralNode) != 0; - if (render_dockspace_bg) - { - host_window->DrawList->ChannelsSplit(2); - host_window->DrawList->ChannelsSetCurrent(1); - } - // Register a hit-test hole in the window unless we are currently dragging a window that is compatible with our dockspace ImGuiDockNode* central_node = node->CentralNode; const bool central_node_hole = node->IsRootNode() && host_window && (node_flags & ImGuiDockNodeFlags_PassthruCentralNode) != 0 && central_node != NULL && central_node->IsEmpty(); @@ -14265,15 +14303,25 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) // Update position/size, process and draw resizing splitters if (node->IsRootNode() && host_window) { + host_window->DrawList->ChannelsSetCurrent(1); DockNodeTreeUpdatePosSize(node, host_window->Pos, host_window->Size); DockNodeTreeUpdateSplitter(node); } // Draw empty node background (currently can only be the Central Node) - if (host_window && node->IsEmpty() && node->IsVisible && !(node_flags & ImGuiDockNodeFlags_PassthruCentralNode)) - host_window->DrawList->AddRectFilled(node->Pos, node->Pos + node->Size, GetColorU32(ImGuiCol_DockingEmptyBg)); + if (host_window && node->IsEmpty() && node->IsVisible) + { + host_window->DrawList->ChannelsSetCurrent(0); + node->LastBgColor = (node_flags & ImGuiDockNodeFlags_PassthruCentralNode) ? 0 : GetColorU32(ImGuiCol_DockingEmptyBg); + if (node->LastBgColor != 0) + host_window->DrawList->AddRectFilled(node->Pos, node->Pos + node->Size, node->LastBgColor); + node->IsBgDrawnThisFrame = true; + } // Draw whole dockspace background if ImGuiDockNodeFlags_PassthruCentralNode if set. + // We need to draw a background at the root level if requested by ImGuiDockNodeFlags_PassthruCentralNode, but we will only know the correct pos/size + // _after_ processing the resizing splitters. So we are using the DrawList channel splitting facility to submit drawing primitives out of order! + const bool render_dockspace_bg = node->IsRootNode() && host_window && (node_flags & ImGuiDockNodeFlags_PassthruCentralNode) != 0; if (render_dockspace_bg && node->IsVisible) { host_window->DrawList->ChannelsSetCurrent(0); @@ -14281,10 +14329,11 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) RenderRectFilledWithHole(host_window->DrawList, node->Rect(), central_node->Rect(), GetColorU32(ImGuiCol_WindowBg), 0.0f); else host_window->DrawList->AddRectFilled(node->Pos, node->Pos + node->Size, GetColorU32(ImGuiCol_WindowBg), 0.0f); - host_window->DrawList->ChannelsMerge(); } // Draw and populate Tab Bar + if (host_window) + host_window->DrawList->ChannelsSetCurrent(1); if (host_window && node->Windows.Size > 0) { DockNodeUpdateTabBar(node, host_window); @@ -14319,7 +14368,13 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) // Render outer borders last (after the tab bar) if (node->IsRootNode()) + { + host_window->DrawList->ChannelsSetCurrent(1); RenderWindowOuterBorders(host_window); + } + + // Further rendering (= hosted windows background) will be drawn on layer 0 + host_window->DrawList->ChannelsSetCurrent(0); } // End host window @@ -15330,7 +15385,8 @@ void ImGui::DockNodeTreeUpdateSplitter(ImGuiDockNode* node) float cur_size_1 = child_1->Size[axis]; float min_size_0 = resize_limits[0] - child_0->Pos[axis]; float min_size_1 = child_1->Pos[axis] + child_1->Size[axis] - resize_limits[1]; - if (SplitterBehavior(bb, GetID("##Splitter"), axis, &cur_size_0, &cur_size_1, min_size_0, min_size_1, WINDOWS_HOVER_PADDING, WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER)) + ImU32 bg_col = GetColorU32(ImGuiCol_WindowBg); + if (SplitterBehavior(bb, GetID("##Splitter"), axis, &cur_size_0, &cur_size_1, min_size_0, min_size_1, WINDOWS_HOVER_PADDING, WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER, bg_col)) { if (touching_nodes[0].Size > 0 && touching_nodes[1].Size > 0) { @@ -16061,7 +16117,9 @@ static ImGuiDockNode* ImGui::DockContextBindNodeToWindow(ImGuiContext* ctx, ImGu } // Add window to node + bool node_was_visible = node->IsVisible; DockNodeAddWindow(node, window, true); + node->IsVisible = node_was_visible; // Don't mark visible right away (so DockContextEndFrame() doesn't render it, maybe other side effects? will see) IM_ASSERT(node == window->DockNode); return node; } diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 437a4a4110ac..6a24a8cacc50 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -412,6 +412,8 @@ void ImDrawList::_ResetForNewFrame() IM_ASSERT(IM_OFFSETOF(ImDrawCmd, ClipRect) == 0); IM_ASSERT(IM_OFFSETOF(ImDrawCmd, TextureId) == sizeof(ImVec4)); IM_ASSERT(IM_OFFSETOF(ImDrawCmd, VtxOffset) == sizeof(ImVec4) + sizeof(ImTextureID)); + if (_Splitter._Count > 1) + _Splitter.Merge(this); CmdBuffer.resize(0); IdxBuffer.resize(0); diff --git a/imgui_internal.h b/imgui_internal.h index 4c7cd4af3a27..d4ce01dc1076 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1432,6 +1432,7 @@ struct IMGUI_API ImGuiDockNode ImVec2 SizeRef; // [Split node only] Last explicitly written-to size (overridden when using a splitter affecting the node), used to calculate Size. ImGuiAxis SplitAxis; // [Split node only] Split axis (X or Y) ImGuiWindowClass WindowClass; // [Root node only] + ImU32 LastBgColor; ImGuiWindow* HostWindow; ImGuiWindow* VisibleWindow; // Generally point to window which is ID is == SelectedTabID, but when CTRL+Tabbing this can be a different window. @@ -1449,6 +1450,7 @@ struct IMGUI_API ImGuiDockNode ImGuiDataAuthority AuthorityForViewport :3; bool IsVisible :1; // Set to false when the node is hidden (usually disabled as it has no active window) bool IsFocused :1; + bool IsBgDrawnThisFrame :1; bool HasCloseButton :1; // Provide space for a close button (if any of the docked window has one). Note that button may be hidden on window without one. bool HasWindowMenuButton :1; bool HasCentralNodeChild :1; @@ -2828,6 +2830,7 @@ namespace ImGui IMGUI_API void DockContextRebuildNodes(ImGuiContext* ctx); IMGUI_API void DockContextNewFrameUpdateUndocking(ImGuiContext* ctx); IMGUI_API void DockContextNewFrameUpdateDocking(ImGuiContext* ctx); + IMGUI_API void DockContextEndFrame(ImGuiContext* ctx); IMGUI_API ImGuiID DockContextGenNodeID(ImGuiContext* ctx); IMGUI_API void DockContextQueueDock(ImGuiContext* ctx, ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, float split_ratio, bool split_outer); IMGUI_API void DockContextQueueUndockWindow(ImGuiContext* ctx, ImGuiWindow* window); @@ -3005,7 +3008,7 @@ namespace ImGui IMGUI_API bool ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags = 0); IMGUI_API bool DragBehavior(ImGuiID id, ImGuiDataType data_type, void* p_v, float v_speed, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags); IMGUI_API bool SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, void* p_v, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags, ImRect* out_grab_bb); - IMGUI_API bool SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend = 0.0f, float hover_visibility_delay = 0.0f); + IMGUI_API bool SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend = 0.0f, float hover_visibility_delay = 0.0f, ImU32 bg_col = 0); IMGUI_API bool TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end = NULL); IMGUI_API bool TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags = 0); // Consume previous SetNextItemOpen() data, if any. May return true when logging IMGUI_API void TreePushOverrideID(ImGuiID id); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 8fa3a109efa3..122b9dc75197 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1435,7 +1435,7 @@ void ImGui::Separator() } // Using 'hover_visibility_delay' allows us to hide the highlight and mouse cursor for a short time, which can be convenient to reduce visual noise. -bool ImGui::SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend, float hover_visibility_delay) +bool ImGui::SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend, float hover_visibility_delay, ImU32 bg_col) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; @@ -1487,7 +1487,9 @@ bool ImGui::SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float } } - // Render + // Render at new position + if (bg_col & IM_COL32_A_MASK) + window->DrawList->AddRectFilled(bb_render.Min, bb_render.Max, bg_col, 0.0f); const ImU32 col = GetColorU32(held ? ImGuiCol_SeparatorActive : (hovered && g.HoveredIdTimer >= hover_visibility_delay) ? ImGuiCol_SeparatorHovered : ImGuiCol_Separator); window->DrawList->AddRectFilled(bb_render.Min, bb_render.Max, col, 0.0f); From c122c0ef892a843f611fe15c162509d0f06837e1 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 3 Dec 2021 16:10:24 +0100 Subject: [PATCH 764/828] Docking: Amend b16f738 fixed dimming of docked window + removed thin highlight around windows (never worked on docked window, not viewports friendly, hard to move to EndFrame) (#2700, #2539, #2522) --- imgui.cpp | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 2d0a096f2798..20bde9aaf3f6 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6730,16 +6730,11 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if (dim_bg_for_modal || dim_bg_for_window_list) { const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio); + if (window->DockIsActive || (flags & ImGuiWindowFlags_DockNodeHost)) + window->DrawList->ChannelsSetCurrent(0); window->DrawList->AddRectFilled(viewport_rect.Min, viewport_rect.Max, dim_bg_col); - } - - // Draw navigation selection/windowing rectangle background - if (dim_bg_for_window_list && window == g.NavWindowingTargetAnim) - { - ImRect bb = window->Rect(); - bb.Expand(g.FontSize); - if (!bb.Contains(viewport_rect)) // Avoid drawing if the window covers all the viewport anyway - window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha * 0.25f), g.Style.WindowRounding); + if (window->DockIsActive || (flags & ImGuiWindowFlags_DockNodeHost)) + window->DrawList->ChannelsSetCurrent(1); } // Child windows can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call (since 1.71) From 1dc3af381aeb0574d3080c1a105d43c56b087c7e Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 3 Dec 2021 18:53:28 +0100 Subject: [PATCH 765/828] Nav, Docking: reworked modal/ctrl+tab dimming system to be entirely processed at end of the frame, which will simplify things for an upcoming commit. (Will backport some of this back to master now.) --- imgui.cpp | 153 ++++++++++++++++++++++++++++------------------- imgui_internal.h | 1 + 2 files changed, 91 insertions(+), 63 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 20bde9aaf3f6..e7f8d042accf 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -959,7 +959,8 @@ static bool UpdateWindowManualResize(ImGuiWindow* window, const ImVe static void RenderWindowOuterBorders(ImGuiWindow* window); static void RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, bool handle_borders_and_resize_grips, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size); static void RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open); -static void EndFrameDrawDimmedBackgrounds(); +static void RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 col); +static void RenderDimmedBackgrounds(); // Viewports const ImGuiID IMGUI_VIEWPORT_DEFAULT_ID = 0x11111111; // Using an arbitrary constant instead of e.g. ImHashStr("ViewportDefault", 0); so it's easier to spot in the debugger. The exact value doesn't matter. @@ -4623,58 +4624,86 @@ static ImGuiWindow* FindFrontMostVisibleChildWindow(ImGuiWindow* window) return window; } -static void ImGui::EndFrameDrawDimmedBackgrounds() +static void ImGui::RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 col) { - ImGuiContext& g = *GImGui; + if ((col & IM_COL32_A_MASK) == 0) + return; - // Draw modal whitening background on _other_ viewports than the one the modal is one - ImGuiWindow* modal_window = GetTopMostPopupModal(); - const bool dim_bg_for_modal = (modal_window != NULL); - const bool dim_bg_for_window_list = (g.NavWindowingTargetAnim != NULL); - if (dim_bg_for_modal || dim_bg_for_window_list) - for (int viewport_n = 0; viewport_n < g.Viewports.Size; viewport_n++) - { - ImGuiViewportP* viewport = g.Viewports[viewport_n]; - if (modal_window && viewport == modal_window->Viewport) - continue; - if (g.NavWindowingListWindow && viewport == g.NavWindowingListWindow->Viewport) - continue; - if (g.NavWindowingTargetAnim && viewport == g.NavWindowingTargetAnim->Viewport) - continue; - if (viewport->Window && modal_window && IsWindowAbove(viewport->Window, modal_window)) - continue; - ImDrawList* draw_list = GetForegroundDrawList(viewport); - const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio); - draw_list->AddRectFilled(viewport->Pos, viewport->Pos + viewport->Size, dim_bg_col); - } + ImGuiViewportP* viewport = window->Viewport; + ImRect viewport_rect = viewport->GetMainRect(); - // Draw modal whitening background behind CTRL-TAB list - if (dim_bg_for_window_list && g.NavWindowingTargetAnim->Active) + // Draw behind window by moving the draw command at the FRONT of the draw list + { + ImDrawList* draw_list = window->RootWindowDockTree->DrawList; + draw_list->AddDrawCmd(); + draw_list->PushClipRect(viewport_rect.Min - ImVec2(1, 1), viewport_rect.Max + ImVec2(1, 1), false); // Ensure ImDrawCmd are not merged + draw_list->AddRectFilled(viewport_rect.Min, viewport_rect.Max, col); + ImDrawCmd cmd = draw_list->CmdBuffer.back(); + IM_ASSERT(cmd.ElemCount == 6); + draw_list->CmdBuffer.pop_back(); + draw_list->CmdBuffer.push_front(cmd); + } + + // Draw over sibling docking nodes in a same docking tree + if (window->RootWindow->DockIsActive) { - // Choose a draw list that will be front-most across all our children - // In the unlikely case that the window wasn't made active we can't rely on its drawlist and skip rendering all-together. - ImGuiWindow* window = g.NavWindowingTargetAnim; ImDrawList* draw_list = FindFrontMostVisibleChildWindow(window->RootWindowDockTree)->DrawList; - draw_list->PushClipRectFullScreen(); + draw_list->PushClipRect(viewport_rect.Min, viewport_rect.Max, false); + //if (window->RootWindowDockTree != window->RootWindow) + RenderRectFilledWithHole(draw_list, window->RootWindowDockTree->Rect(), window->RootWindow->Rect(), col, 0.0f);// window->RootWindowDockTree->WindowRounding); + draw_list->PopClipRect(); + } +} - // Docking: draw modal whitening background on other nodes of a same dock tree - // For CTRL+TAB within a docking node we need to render the dimming background in 8 steps - // (Because the root node renders the background in one shot, in order to avoid flickering when a child dock node is not submitted) - if (window->RootWindow->DockIsActive) - if (window->RootWindowDockTree != window->RootWindow) - RenderRectFilledWithHole(draw_list, window->RootWindowDockTree->Rect(), window->RootWindow->Rect(), GetColorU32(ImGuiCol_NavWindowingDimBg, g.DimBgRatio), g.Style.WindowRounding); +static void ImGui::RenderDimmedBackgrounds() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* modal_window = GetTopMostAndVisiblePopupModal(); + const bool dim_bg_for_modal = (modal_window != NULL); + const bool dim_bg_for_window_list = (g.NavWindowingTargetAnim != NULL && g.NavWindowingTargetAnim->Active); + if (!dim_bg_for_modal && !dim_bg_for_window_list) + return; + + ImGuiViewport* viewports_already_dimmed[2] = { NULL, NULL }; + if (dim_bg_for_modal) + { + // Draw dimming behind modal + RenderDimmedBackgroundBehindWindow(modal_window, GetColorU32(ImGuiCol_ModalWindowDimBg, g.DimBgRatio)); + viewports_already_dimmed[0] = modal_window->Viewport; + } + else if (dim_bg_for_window_list) + { + // Draw dimming behind CTRL+Tab target window and behind CTRL+Tab UI window + RenderDimmedBackgroundBehindWindow(g.NavWindowingTargetAnim, GetColorU32(ImGuiCol_NavWindowingDimBg, g.DimBgRatio)); + if (g.NavWindowingListWindow != NULL && g.NavWindowingListWindow->Viewport != g.NavWindowingTargetAnim->Viewport) + RenderDimmedBackgroundBehindWindow(g.NavWindowingListWindow, GetColorU32(ImGuiCol_NavWindowingDimBg, g.DimBgRatio)); + viewports_already_dimmed[0] = g.NavWindowingTargetAnim->Viewport; + viewports_already_dimmed[1] = g.NavWindowingListWindow ? g.NavWindowingListWindow->Viewport : NULL; - // Draw navigation selection/windowing rectangle border - float rounding = ImMax(window->WindowRounding, g.Style.WindowRounding); + // Draw border around CTRL+Tab target window + ImGuiWindow* window = g.NavWindowingTargetAnim; + ImGuiViewport* viewport = window->Viewport; + float distance = g.FontSize; ImRect bb = window->Rect(); - bb.Expand(g.FontSize); - if (!window->Viewport->GetMainRect().Contains(bb)) // If a window fits the entire viewport, adjust its highlight inward - { - bb.Expand(-g.FontSize - 1.0f); - rounding = window->WindowRounding; - } - draw_list->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), rounding, 0, 3.0f); - draw_list->PopClipRect(); + bb.Expand(distance); + if (bb.GetWidth() >= viewport->Size.x && bb.GetHeight() >= viewport->Size.y) + bb.Expand(-distance - 1.0f); // If a window fits the entire viewport, adjust its highlight inward + window->DrawList->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size); + window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), window->WindowRounding, 0, 3.0f); + window->DrawList->PopClipRect(); + } + + // Draw dimming background on _other_ viewports than the ones our windows are in + for (int viewport_n = 0; viewport_n < g.Viewports.Size; viewport_n++) + { + ImGuiViewportP* viewport = g.Viewports[viewport_n]; + if (viewport == viewports_already_dimmed[0] || viewport == viewports_already_dimmed[1]) + continue; + if (modal_window && viewport->Window && IsWindowAbove(viewport->Window, modal_window)) + continue; + ImDrawList* draw_list = GetForegroundDrawList(viewport); + const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio); + draw_list->AddRectFilled(viewport->Pos, viewport->Pos + viewport->Size, dim_bg_col); } } @@ -4740,9 +4769,6 @@ void ImGui::EndFrame() // Initiate moving window + handle left-click and right-click focus UpdateMouseMovingWindowEndFrame(); - // Draw modal/window whitening backgrounds - EndFrameDrawDimmedBackgrounds(); - // Update user-facing viewport list (g.Viewports -> g.PlatformIO.Viewports after filtering out some) UpdateViewportsEndFrame(); @@ -4785,6 +4811,7 @@ void ImGui::Render() if (g.FrameCountEnded != g.FrameCount) EndFrame(); + const bool first_render_of_frame = (g.FrameCountRendered != g.FrameCount); g.FrameCountRendered = g.FrameCount; g.IO.MetricsRenderWindows = 0; @@ -4814,6 +4841,10 @@ void ImGui::Render() if (windows_to_render_top_most[n] && IsWindowActiveAndVisible(windows_to_render_top_most[n])) // NavWindowingTarget is always temporarily displayed as the top-most window AddRootWindowToDrawData(windows_to_render_top_most[n]); + // Draw modal/window whitening backgrounds + if (first_render_of_frame) + RenderDimmedBackgrounds(); + ImVec2 mouse_cursor_offset, mouse_cursor_size, mouse_cursor_uv[4]; if (g.IO.MouseDrawCursor && g.MouseCursor != ImGuiMouseCursor_None) g.IO.Fonts->GetMouseCursorTexData(g.MouseCursor, &mouse_cursor_offset, &mouse_cursor_size, &mouse_cursor_uv[0], &mouse_cursor_uv[2]); @@ -4827,7 +4858,7 @@ void ImGui::Render() // Draw software mouse cursor if requested by io.MouseDrawCursor flag // (note we scale cursor by current viewport/monitor, however Windows 10 for its own hardware cursor seems to be using a different scale factor) - if (mouse_cursor_size.x > 0.0f && mouse_cursor_size.y > 0.0f) + if (mouse_cursor_size.x > 0.0f && mouse_cursor_size.y > 0.0f && first_render_of_frame) { float scale = g.Style.MouseCursorScale * viewport->DpiScale; if (viewport->GetMainRect().Overlaps(ImRect(g.IO.MousePos, g.IO.MousePos + ImVec2(mouse_cursor_size.x + 2, mouse_cursor_size.y + 2) * scale))) @@ -6723,20 +6754,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID); PushClipRect(host_rect.Min, host_rect.Max, false); - // Draw modal or window list full viewport dimming background (for other viewports we'll render them in EndFrame) - ImGuiWindow* window_window_list = g.NavWindowingListWindow; - const bool dim_bg_for_modal = (flags & ImGuiWindowFlags_Modal) && window == GetTopMostPopupModal() && window->HiddenFramesCannotSkipItems <= 0; - const bool dim_bg_for_window_list = g.NavWindowingTargetAnim && ((window == g.NavWindowingTargetAnim->RootWindowDockTree) || (window == window_window_list && window_window_list->Viewport != g.NavWindowingTargetAnim->Viewport)); - if (dim_bg_for_modal || dim_bg_for_window_list) - { - const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio); - if (window->DockIsActive || (flags & ImGuiWindowFlags_DockNodeHost)) - window->DrawList->ChannelsSetCurrent(0); - window->DrawList->AddRectFilled(viewport_rect.Min, viewport_rect.Max, dim_bg_col); - if (window->DockIsActive || (flags & ImGuiWindowFlags_DockNodeHost)) - window->DrawList->ChannelsSetCurrent(1); - } - // Child windows can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call (since 1.71) // When using overlapping child windows, this will break the assumption that child z-order is mapped to submission order. // FIXME: User code may rely on explicit sorting of overlapping child window and would need to disable this somehow. Please get in contact if you are affected (github #4493) @@ -8985,6 +9002,16 @@ ImGuiWindow* ImGui::GetTopMostPopupModal() return NULL; } +ImGuiWindow* ImGui::GetTopMostAndVisiblePopupModal() +{ + ImGuiContext& g = *GImGui; + for (int n = g.OpenPopupStack.Size - 1; n >= 0; n--) + if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window) + if ((popup->Flags & ImGuiWindowFlags_Modal) && IsWindowActiveAndVisible(popup)) + return popup; + return NULL; +} + void ImGui::OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags) { ImGuiContext& g = *GImGui; diff --git a/imgui_internal.h b/imgui_internal.h index d4ce01dc1076..a0aa89b80b5d 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2772,6 +2772,7 @@ namespace ImGui IMGUI_API void BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags extra_window_flags); IMGUI_API ImRect GetPopupAllowedExtentRect(ImGuiWindow* window); IMGUI_API ImGuiWindow* GetTopMostPopupModal(); + IMGUI_API ImGuiWindow* GetTopMostAndVisiblePopupModal(); IMGUI_API ImVec2 FindBestWindowPosForPopup(ImGuiWindow* window); IMGUI_API ImVec2 FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy); From fc198fe1dbae2e045b7fb31d53466a54f5368414 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 6 Dec 2021 19:50:42 +0100 Subject: [PATCH 766/828] Nav, Docking: Fix dimming on docked windows. --- imgui.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 056fbe96ebd8..92e97495aa9c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4828,10 +4828,6 @@ void ImGui::Render() AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[0], GetBackgroundDrawList(viewport)); } - // Draw modal/window whitening backgrounds - if (first_render_of_frame) - RenderDimmedBackgrounds(); - // Add ImDrawList to render ImGuiWindow* windows_to_render_top_most[2]; windows_to_render_top_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindowDockTree : NULL; @@ -4847,6 +4843,10 @@ void ImGui::Render() if (windows_to_render_top_most[n] && IsWindowActiveAndVisible(windows_to_render_top_most[n])) // NavWindowingTarget is always temporarily displayed as the top-most window AddRootWindowToDrawData(windows_to_render_top_most[n]); + // Draw modal/window whitening backgrounds + if (first_render_of_frame) + RenderDimmedBackgrounds(); + ImVec2 mouse_cursor_offset, mouse_cursor_size, mouse_cursor_uv[4]; if (g.IO.MouseDrawCursor && g.MouseCursor != ImGuiMouseCursor_None) g.IO.Fonts->GetMouseCursorTexData(g.MouseCursor, &mouse_cursor_offset, &mouse_cursor_size, &mouse_cursor_uv[0], &mouse_cursor_uv[2]); From 0647cf434c3eca69343471c5d5eb6d6e03183547 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 8 Dec 2021 17:02:24 +0100 Subject: [PATCH 767/828] Nav, Docking: Fix crash on dimming docked window and DockSpaceOverViewport() with PassthruCentralNode. (amend 1dc3af3, 23ef6c1, 657073a) --- imgui.cpp | 7 ++++++- imgui_draw.cpp | 5 +++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 92e97495aa9c..fa971f1fa3d6 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4636,7 +4636,11 @@ static void ImGui::RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 // Draw behind window by moving the draw command at the FRONT of the draw list { + // We've already called AddWindowToDrawData() which called DrawList->ChannelsMerge() on DockNodeHost windows, + // and draw list have been trimmed already, hence the explicit recreation of a draw command if missing. ImDrawList* draw_list = window->RootWindowDockTree->DrawList; + if (draw_list->CmdBuffer.Size == 0) + draw_list->AddDrawCmd(); draw_list->PushClipRect(viewport_rect.Min - ImVec2(1, 1), viewport_rect.Max + ImVec2(1, 1), false); // Ensure ImDrawCmd are not merged draw_list->AddRectFilled(viewport_rect.Min, viewport_rect.Max, col); ImDrawCmd cmd = draw_list->CmdBuffer.back(); @@ -4651,7 +4655,6 @@ static void ImGui::RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 { ImDrawList* draw_list = FindFrontMostVisibleChildWindow(window->RootWindowDockTree)->DrawList; draw_list->PushClipRect(viewport_rect.Min, viewport_rect.Max, false); - //if (window->RootWindowDockTree != window->RootWindow) RenderRectFilledWithHole(draw_list, window->RootWindowDockTree->Rect(), window->RootWindow->Rect(), col, 0.0f);// window->RootWindowDockTree->WindowRounding); draw_list->PopClipRect(); } @@ -4690,6 +4693,8 @@ static void ImGui::RenderDimmedBackgrounds() bb.Expand(distance); if (bb.GetWidth() >= viewport->Size.x && bb.GetHeight() >= viewport->Size.y) bb.Expand(-distance - 1.0f); // If a window fits the entire viewport, adjust its highlight inward + if (window->DrawList->CmdBuffer.Size == 0) + window->DrawList->AddDrawCmd(); window->DrawList->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size); window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), window->WindowRounding, 0, 3.0f); window->DrawList->PopClipRect(); diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 6a24a8cacc50..bcc320d8d4f1 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -481,6 +481,7 @@ void ImDrawList::_PopUnusedDrawCmd() void ImDrawList::AddCallback(ImDrawCallback callback, void* callback_data) { + IM_ASSERT_PARANOID(CmdBuffer.Size > 0); ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; IM_ASSERT(curr_cmd->UserCallback == NULL); if (curr_cmd->ElemCount != 0) @@ -502,6 +503,7 @@ void ImDrawList::AddCallback(ImDrawCallback callback, void* callback_data) // Try to merge two last draw commands void ImDrawList::_TryMergeDrawCmds() { + IM_ASSERT_PARANOID(CmdBuffer.Size > 0); ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; ImDrawCmd* prev_cmd = curr_cmd - 1; if (ImDrawCmd_HeaderCompare(curr_cmd, prev_cmd) == 0 && curr_cmd->UserCallback == NULL && prev_cmd->UserCallback == NULL) @@ -516,6 +518,7 @@ void ImDrawList::_TryMergeDrawCmds() void ImDrawList::_OnChangedClipRect() { // If current command is used with different settings we need to add a new command + IM_ASSERT_PARANOID(CmdBuffer.Size > 0); ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; if (curr_cmd->ElemCount != 0 && memcmp(&curr_cmd->ClipRect, &_CmdHeader.ClipRect, sizeof(ImVec4)) != 0) { @@ -538,6 +541,7 @@ void ImDrawList::_OnChangedClipRect() void ImDrawList::_OnChangedTextureID() { // If current command is used with different settings we need to add a new command + IM_ASSERT_PARANOID(CmdBuffer.Size > 0); ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; if (curr_cmd->ElemCount != 0 && curr_cmd->TextureId != _CmdHeader.TextureId) { @@ -561,6 +565,7 @@ void ImDrawList::_OnChangedVtxOffset() { // We don't need to compare curr_cmd->VtxOffset != _CmdHeader.VtxOffset because we know it'll be different at the time we call this. _VtxCurrentIdx = 0; + IM_ASSERT_PARANOID(CmdBuffer.Size > 0); ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; //IM_ASSERT(curr_cmd->VtxOffset != _CmdHeader.VtxOffset); // See #3349 if (curr_cmd->ElemCount != 0) From f605351307c172f83794484a59b4c017e8270abd Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 9 Dec 2021 15:35:02 +0100 Subject: [PATCH 768/828] Added an assertion for the common user mistake of using "" as an identifier at the root level of a window. (#1414, #2562, #2807, #4008, #4158, #4375, #4548, #4657, #4796) #4158, #4375, #4548, #4657, #4796) --- docs/CHANGELOG.txt | 4 ++++ imgui.cpp | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 0304eaf519b9..bb83fcf56189 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -110,6 +110,10 @@ Breaking Changes: Other Changes: +- Added an assertion for the common user mistake of using "" as an identifier at the root level of a window + instead of using "##something". Empty identifiers are valid and useful in a very small amount of cases, + but 99.9% of the time if you need an empty label you should use "##something". (#1414, #2562, #2807, #4008, + #4158, #4375, #4548, #4657, #4796). READ THE FAQ ABOUT HOW THE ID STACK WORKS -> https://dearimgui.org/faq - Added GetMouseClickedCount() function, returning the number of successive clicks. (#3229) [@kudaba] (so IsMouseDoubleClicked(ImGuiMouseButton_Left) is same as GetMouseClickedCount(ImGuiMouseButton_Left) == 2, but it allows testing for triple clicks and more). diff --git a/imgui.cpp b/imgui.cpp index 17e4558bf927..45418b4ddc48 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -8379,6 +8379,11 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGu if (window == g.NavWindow || ((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened)) NavProcessItem(); + // [DEBUG] People keep stumbling on this problem and using "" as identifier in the root of a window instead of "##something". + // Empty identifier are valid and useful in a small amount of cases, but 99.9% of the time you want to use "##something". + // READ THE FAQ: https://dearimgui.org/faq + IM_ASSERT(id != window->ID && "Cannot have an empty ID at the root of a window. If you need an empty label, use ## and read the FAQ about how the ID Stack works!"); + // [DEBUG] Item Picker tool, when enabling the "extended" version we perform the check in ItemAdd() #ifdef IMGUI_DEBUG_TOOL_ITEM_PICKER_EX if (id == g.DebugItemPickerBreakId) From 747f7fdbbaa1fdd46d5115c7333ab0722a7c2afc Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 9 Dec 2021 16:46:41 +0100 Subject: [PATCH 769/828] Docking: prevent docking any window created above a popup/modal. (#4317) --- docs/CHANGELOG.txt | 1 + imgui.cpp | 13 ++++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index bb83fcf56189..f548e785dbc6 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -184,6 +184,7 @@ Docking+Viewports Branch: - Docking, Style: Docked windows honor display their border properly. (#2522) - Docking: Fixed incorrectly rounded tab bars for dock node that are not at the top of their dock tree. - Docking: Fixed single-frame node pos/size inconsistencies when window stop or start being submitted. +- Docking: Prevent docking any window created above a popup/modal. (#4317) - Viewports: Made it possible to explicitly assign ImGuiWindowClass::ParentViewportId to 0 in order to ensure a window is not parented. Previously this would use the global default (which might be 0, but not always as it would depend on io.ConfigViewportsNoDefaultParent). (#3152, #2871) diff --git a/imgui.cpp b/imgui.cpp index 45418b4ddc48..8a51bfd8e0e0 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -14897,12 +14897,23 @@ static bool DockNodeIsDropAllowedOne(ImGuiWindow* payload, ImGuiWindow* host_win return false; } + // Prevent docking any window created above a popup + // Technically we should support it (e.g. in the case of a long-lived modal window that had fancy docking features), + // by e.g. adding a 'if (!ImGui::IsWindowWithinBeginStackOf(host_window, popup_window))' test. + // But it would requires more work on our end because the dock host windows is technically created in NewFrame() + // and our ->ParentXXX and ->RootXXX pointers inside windows are currently mislading or lacking. + ImGuiContext& g = *GImGui; + for (int i = g.OpenPopupStack.Size - 1; i >= 0; i--) + if (ImGuiWindow* popup_window = g.OpenPopupStack[i].Window) + if (ImGui::IsWindowWithinBeginStackOf(payload, popup_window)) // Payload is created from within a popup begin stack. + return false; + return true; } static bool ImGui::DockNodeIsDropAllowed(ImGuiWindow* host_window, ImGuiWindow* root_payload) { - if (root_payload->DockNodeAsHost && root_payload->DockNodeAsHost->IsSplitNode()) + if (root_payload->DockNodeAsHost && root_payload->DockNodeAsHost->IsSplitNode()) // FIXME-DOCK: Missing filtering return true; const int payload_count = root_payload->DockNodeAsHost ? root_payload->DockNodeAsHost->Windows.Size : 1; From 248ed1b01dd4ab78a33ba1417b167a5fd24dfaa1 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 15 Dec 2021 12:16:48 +0100 Subject: [PATCH 770/828] Internals: UpdateWindowInFocusOrderList: amend a528398 to fix docking. (#3496, #4797) --- imgui.cpp | 10 ++++++---- imgui_internal.h | 1 + 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index c34f721c92a8..b7e4a8e45852 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5547,15 +5547,16 @@ static void ApplyWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settin static void UpdateWindowInFocusOrderList(ImGuiWindow* window, bool just_created, ImGuiWindowFlags new_flags) { ImGuiContext& g = *GImGui; - const ImGuiWindowFlags old_flags = window->Flags; - const bool child_flag_changed = (new_flags & ImGuiWindowFlags_ChildWindow) != (old_flags & ImGuiWindowFlags_ChildWindow); - if ((just_created || child_flag_changed) && !(new_flags & ImGuiWindowFlags_ChildWindow)) + const bool new_is_explicit_child = (new_flags & ImGuiWindowFlags_ChildWindow) != 0; + const bool child_flag_changed = new_is_explicit_child != window->IsExplicitChild; + if ((just_created || child_flag_changed) && !new_is_explicit_child) { + IM_ASSERT(!g.WindowsFocusOrder.contains(window)); g.WindowsFocusOrder.push_back(window); window->FocusOrder = (short)(g.WindowsFocusOrder.Size - 1); } - else if (child_flag_changed && (new_flags & ImGuiWindowFlags_ChildWindow)) + else if (!just_created && child_flag_changed && new_is_explicit_child) { IM_ASSERT(g.WindowsFocusOrder[window->FocusOrder] == window); for (int n = window->FocusOrder + 1; n < g.WindowsFocusOrder.Size; n++) @@ -5563,6 +5564,7 @@ static void UpdateWindowInFocusOrderList(ImGuiWindow* window, bool just_created, g.WindowsFocusOrder.erase(g.WindowsFocusOrder.Data + window->FocusOrder); window->FocusOrder = -1; } + window->IsExplicitChild = new_is_explicit_child; } static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags) diff --git a/imgui_internal.h b/imgui_internal.h index f64d3bf2263e..e889acf12b5a 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2189,6 +2189,7 @@ struct IMGUI_API ImGuiWindow bool Appearing; // Set during the frame where the window is appearing (or re-appearing) bool Hidden; // Do not display (== HiddenFrames*** > 0) bool IsFallbackWindow; // Set on the "Debug##Default" window. + bool IsExplicitChild; // Set when passed _ChildWindow, left to false by BeginDocked() bool HasCloseButton; // Set when the window has a close button (p_open != NULL) signed char ResizeBorderHeld; // Current border being held for resize (-1: none, otherwise 0-3) short BeginCount; // Number of Begin() during the current frame (generally 0 or 1, 1+ if appending via multiple Begin/End pairs) From 06d5f9297de441d229f717bf6284bb14e71970f9 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 15 Dec 2021 16:03:20 +0100 Subject: [PATCH 771/828] Internals: reduced side-effects of setting window->HiddenFramesForRenderOnly > 0 --- imgui.cpp | 5 +++-- imgui.h | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index b7e4a8e45852..efa7dc6e08b9 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7070,7 +7070,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->HiddenFramesCanSkipItems = 1; // Update the Hidden flag - window->Hidden = (window->HiddenFramesCanSkipItems > 0) || (window->HiddenFramesCannotSkipItems > 0) || (window->HiddenFramesForRenderOnly > 0); + bool hidden_regular = (window->HiddenFramesCanSkipItems > 0) || (window->HiddenFramesCannotSkipItems > 0); + window->Hidden = hidden_regular || (window->HiddenFramesForRenderOnly > 0); // Disable inputs for requested number of frames if (window->DisableInputsFrames > 0) @@ -7081,7 +7082,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Update the SkipItems flag, used to early out of all items functions (no layout required) bool skip_items = false; - if (window->Collapsed || !window->Active || window->Hidden) + if (window->Collapsed || !window->Active || hidden_regular) if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && window->HiddenFramesCannotSkipItems <= 0) skip_items = true; window->SkipItems = skip_items; diff --git a/imgui.h b/imgui.h index c122fffd6e82..0dd92640b22b 100644 --- a/imgui.h +++ b/imgui.h @@ -65,7 +65,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.86 WIP" -#define IMGUI_VERSION_NUM 18521 +#define IMGUI_VERSION_NUM 18522 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE #define IMGUI_HAS_VIEWPORT // Viewport WIP branch From 9c8f288d1a9038e7f62ccb76328bb8f328c0b89d Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 30 Dec 2021 21:46:09 +0100 Subject: [PATCH 772/828] Viewports: Fixed a CTRL+TAB crash with viewports enabled (#4023, #787) (amend 1dc3af3, 23ef6c1, 657073a) + Expose FindHoveredViewportFromPlatformWindowStack() in imgui_internal.h --- docs/CHANGELOG.txt | 5 +++++ imgui.cpp | 4 ++-- imgui_internal.h | 3 ++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 93922230a373..fb5df301044b 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -108,6 +108,11 @@ Other changes: - Backends: Metal: Added Apple Metal C++ API support. (#4824, #4746) [@luigifcruz] Enable with '#define IMGUI_IMPL_METAL_CPP' in your imconfig.h file. +Docking+Viewports Branch: + +- Viewports: Fixed a CTRL+TAB crash with viewports enabled when the window list needs to appears in + its own viewport (regression from 1.86). (#4023, #787) + ----------------------------------------------------------------------- VERSION 1.86 (Released 2021-12-22) diff --git a/imgui.cpp b/imgui.cpp index fee88ba91874..5c415aec41e2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4700,7 +4700,7 @@ static void ImGui::RenderDimmedBackgrounds() { // Draw dimming behind CTRL+Tab target window and behind CTRL+Tab UI window RenderDimmedBackgroundBehindWindow(g.NavWindowingTargetAnim, GetColorU32(ImGuiCol_NavWindowingDimBg, g.DimBgRatio)); - if (g.NavWindowingListWindow != NULL && g.NavWindowingListWindow->Viewport != g.NavWindowingTargetAnim->Viewport) + if (g.NavWindowingListWindow != NULL && g.NavWindowingListWindow->Viewport && g.NavWindowingListWindow->Viewport != g.NavWindowingTargetAnim->Viewport) RenderDimmedBackgroundBehindWindow(g.NavWindowingListWindow, GetColorU32(ImGuiCol_NavWindowingDimBg, g.DimBgRatio)); viewports_already_dimmed[0] = g.NavWindowingTargetAnim->Viewport; viewports_already_dimmed[1] = g.NavWindowingListWindow ? g.NavWindowingListWindow->Viewport : NULL; @@ -12143,7 +12143,7 @@ void ImGui::ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale) // If the backend doesn't set MouseLastHoveredViewport or doesn't honor ImGuiViewportFlags_NoInputs, we do a search ourselves. // A) It won't take account of the possibility that non-imgui windows may be in-between our dragged window and our target window. // B) It requires Platform_GetWindowFocus to be implemented by backend. -static ImGuiViewportP* FindHoveredViewportFromPlatformWindowStack(const ImVec2 mouse_platform_pos) +ImGuiViewportP* ImGui::FindHoveredViewportFromPlatformWindowStack(const ImVec2& mouse_platform_pos) { ImGuiContext& g = *GImGui; ImGuiViewportP* best_candidate = NULL; diff --git a/imgui_internal.h b/imgui_internal.h index 90e62713fabc..5215d8b32a50 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2694,7 +2694,8 @@ namespace ImGui IMGUI_API void ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale); IMGUI_API void DestroyPlatformWindow(ImGuiViewportP* viewport); IMGUI_API void SetCurrentViewport(ImGuiWindow* window, ImGuiViewportP* viewport); - IMGUI_API const ImGuiPlatformMonitor* GetViewportPlatformMonitor(ImGuiViewport* viewport); + IMGUI_API const ImGuiPlatformMonitor* GetViewportPlatformMonitor(ImGuiViewport* viewport); + IMGUI_API ImGuiViewportP* FindHoveredViewportFromPlatformWindowStack(const ImVec2& mouse_platform_pos); // Settings IMGUI_API void MarkIniSettingsDirty(); From 8554c4cf5f0c3fba02bd995a997fad92853d2332 Mon Sep 17 00:00:00 2001 From: Adam <87928802+DnA-IntRicate@users.noreply.github.com> Date: Sun, 9 Jan 2022 14:15:02 +0200 Subject: [PATCH 773/828] Update .gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 86bed609c54e..8ed844a339cc 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,9 @@ ipch *.suo *.VC.db *.VC.VC.opendb +*.vcxproj +*.filters +*.user ## Commonly used CMake directories /build*/ From 9ce0f35ef36d4fe39052f8107d8017c8833ce771 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 10 Jan 2022 15:47:37 +0100 Subject: [PATCH 774/828] Backends: OSX: Fixed typo. --- backends/imgui_impl_osx.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm index e9c446498e00..0fe6a039d01c 100644 --- a/backends/imgui_impl_osx.mm +++ b/backends/imgui_impl_osx.mm @@ -658,7 +658,7 @@ bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view) { io.AddKeyEvent(key, (imgui_flags & imgui_mask) != 0); } - io.SetKeyEventNativeData(key, keycode, -1); // To support legacy indexing (<1.87 user code) + io.SetKeyEventNativeData(key, key_code, -1); // To support legacy indexing (<1.87 user code) } return io.WantCaptureKeyboard; From acfc7798fdbab692b8fa855c7dd4ec43bc0bd828 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 10 Jan 2022 16:59:31 +0100 Subject: [PATCH 775/828] Rename io.AddKeyModEvent() -> io.AddKeyModsEvent() and updated backends accordingly. (#2625, #4858) Amend 790132a (breaking) # Conflicts: # backends/imgui_impl_glfw.cpp # backends/imgui_impl_sdl.cpp # backends/imgui_impl_win32.cpp --- backends/imgui_impl_allegro5.cpp | 4 ++-- backends/imgui_impl_android.cpp | 4 ++-- backends/imgui_impl_glfw.cpp | 4 ++-- backends/imgui_impl_glut.cpp | 4 ++-- backends/imgui_impl_osx.mm | 4 ++-- backends/imgui_impl_sdl.cpp | 4 ++-- backends/imgui_impl_win32.cpp | 4 ++-- docs/CHANGELOG.txt | 2 +- imgui.cpp | 4 ++-- imgui.h | 2 +- 10 files changed, 18 insertions(+), 18 deletions(-) diff --git a/backends/imgui_impl_allegro5.cpp b/backends/imgui_impl_allegro5.cpp index ae50485fecad..68615160246f 100644 --- a/backends/imgui_impl_allegro5.cpp +++ b/backends/imgui_impl_allegro5.cpp @@ -17,7 +17,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. +// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2021-12-08: Renderer: Fixed mishandling of the the ImDrawCmd::IdxOffset field! This is an old bug but it never had an effect until some internal rendering changes in 1.86. // 2021-08-17: Calling io.AddFocusEvent() on ALLEGRO_EVENT_DISPLAY_SWITCH_OUT/ALLEGRO_EVENT_DISPLAY_SWITCH_IN events. // 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). @@ -570,7 +570,7 @@ void ImGui_ImplAllegro5_NewFrame() ((al_key_down(&keys, ALLEGRO_KEY_LSHIFT) || al_key_down(&keys, ALLEGRO_KEY_RSHIFT)) ? ImGuiKeyModFlags_Shift : 0) | ((al_key_down(&keys, ALLEGRO_KEY_ALT) || al_key_down(&keys, ALLEGRO_KEY_ALTGR)) ? ImGuiKeyModFlags_Alt : 0) | ((al_key_down(&keys, ALLEGRO_KEY_LWIN) || al_key_down(&keys, ALLEGRO_KEY_RWIN)) ? ImGuiKeyModFlags_Super : 0); - io.AddKeyModEvent(key_mods); + io.AddKeyModsEvent(key_mods); ImGui_ImplAllegro5_UpdateMouseCursor(); } diff --git a/backends/imgui_impl_android.cpp b/backends/imgui_impl_android.cpp index 1eb7a585abcf..fdf482fc9a22 100644 --- a/backends/imgui_impl_android.cpp +++ b/backends/imgui_impl_android.cpp @@ -19,7 +19,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. +// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2021-03-04: Initial version. #include "imgui.h" @@ -292,7 +292,7 @@ void ImGui_ImplAndroid_NewFrame() io.SetKeyEventNativeData(key_event.Key, key_event.NativeKeycode, key_event.NativeScancode); // To support legacy indexing (<1.87 user code) key_queue.second.pop(); } - io.AddKeyModEvent(g_KeyModFlags); + io.AddKeyModsEvent(g_KeyModFlags); // Setup display size (every frame to accommodate for window resizing) int32_t window_width = ANativeWindow_getWidth(g_Window); diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index 82c46f8be3dd..2a87b62cab03 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -21,7 +21,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) // 2021-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. -// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. +// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2022-01-05: Inputs: Converting GLFW untranslated keycodes back to translated keycodes (in the ImGui_ImplGlfw_KeyCallback() function) in order to match the behavior of every other backend, and facilitate the use of GLFW with lettered-shortcuts API. // 2021-08-17: *BREAKING CHANGE*: Now using glfwSetWindowFocusCallback() to calling io.AddFocusEvent(). If you called ImGui_ImplGlfw_InitXXX() with install_callbacks = false, you MUST install glfwSetWindowFocusCallback() and forward it to the backend via ImGui_ImplGlfw_WindowFocusCallback(). // 2021-07-29: *BREAKING CHANGE*: Now using glfwSetCursorEnterCallback(). MousePos is correctly reported when the host platform window is hovered but not focused. If you called ImGui_ImplGlfw_InitXXX() with install_callbacks = false, you MUST install glfwSetWindowFocusCallback() callback and forward it to the backend via ImGui_ImplGlfw_CursorEnterCallback(). @@ -676,7 +676,7 @@ static void ImGui_ImplGlfw_UpdateKeyModifiers() (((glfwGetKey(bd->Window, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS) || (glfwGetKey(bd->Window, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS)) ? ImGuiKeyModFlags_Shift : 0) | (((glfwGetKey(bd->Window, GLFW_KEY_LEFT_ALT) == GLFW_PRESS) || (glfwGetKey(bd->Window, GLFW_KEY_RIGHT_ALT) == GLFW_PRESS)) ? ImGuiKeyModFlags_Alt : 0) | (((glfwGetKey(bd->Window, GLFW_KEY_LEFT_SUPER) == GLFW_PRESS) || (glfwGetKey(bd->Window, GLFW_KEY_RIGHT_SUPER) == GLFW_PRESS)) ? ImGuiKeyModFlags_Super : 0); - io.AddKeyModEvent(key_mods); + io.AddKeyModsEvent(key_mods); } void ImGui_ImplGlfw_NewFrame() diff --git a/backends/imgui_impl_glut.cpp b/backends/imgui_impl_glut.cpp index 68e417f2da30..6eec280e33c4 100644 --- a/backends/imgui_impl_glut.cpp +++ b/backends/imgui_impl_glut.cpp @@ -20,7 +20,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. +// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2019-04-03: Misc: Renamed imgui_impl_freeglut.cpp/.h to imgui_impl_glut.cpp/.h. // 2019-03-25: Misc: Made io.DeltaTime always above zero. // 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window. @@ -210,7 +210,7 @@ static void ImGui_ImplGLUT_UpdateKeyboardMods() ((glut_key_mods & GLUT_ACTIVE_CTRL) ? ImGuiKeyModFlags_Ctrl : 0) | ((glut_key_mods & GLUT_ACTIVE_SHIFT) ? ImGuiKeyModFlags_Shift : 0) | ((glut_key_mods & GLUT_ACTIVE_ALT) ? ImGuiKeyModFlags_Alt : 0); - io.AddKeyModEvent(key_mods); + io.AddKeyModsEvent(key_mods); } static void ImGui_ImplGLUT_AddKeyEvent(ImGuiKey key, bool down, int native_keycode) diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm index 0fe6a039d01c..a3f3ec5ed60b 100644 --- a/backends/imgui_impl_osx.mm +++ b/backends/imgui_impl_osx.mm @@ -24,7 +24,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. +// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2021-12-13: *BREAKING CHANGE* Add NSView parameter to ImGui_ImplOSX_Init(). Generally fix keyboard support. Using kVK_* codes for keyboard keys. // 2021-12-13: Add game controller support. // 2021-09-21: Use mach_absolute_time as CFAbsoluteTimeGetCurrent can jump backwards. @@ -498,7 +498,7 @@ static void ImGui_ImplOSX_UpdateGamepads() static void ImGui_ImplOSX_UpdateKeyModifiers() { ImGuiIO& io = ImGui::GetIO(); - io.AddKeyModEvent(g_KeyModifiers); + io.AddKeyModsEvent(g_KeyModifiers); } void ImGui_ImplOSX_NewFrame(NSView* view) diff --git a/backends/imgui_impl_sdl.cpp b/backends/imgui_impl_sdl.cpp index 9cb52824f110..6711aa5f9f9e 100644 --- a/backends/imgui_impl_sdl.cpp +++ b/backends/imgui_impl_sdl.cpp @@ -21,7 +21,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) // 2021-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. -// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. +// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2021-08-17: Calling io.AddFocusEvent() on SDL_WINDOWEVENT_FOCUS_GAINED/SDL_WINDOWEVENT_FOCUS_LOST. // 2021-07-29: Inputs: MousePos is correctly reported when the host platform window is hovered but not focused (using SDL_GetMouseFocus() + SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, requires SDL 2.0.5+) // 2021-06:29: *BREAKING CHANGE* Removed 'SDL_Window* window' parameter to ImGui_ImplSDL2_NewFrame() which was unnecessary. @@ -599,7 +599,7 @@ static void ImGui_ImplSDL2_UpdateKeyModifiers() ((sdl_key_mods & KMOD_SHIFT) ? ImGuiKeyModFlags_Shift : 0) | ((sdl_key_mods & KMOD_ALT) ? ImGuiKeyModFlags_Alt : 0) | ((sdl_key_mods & KMOD_GUI) ? ImGuiKeyModFlags_Super : 0); - io.AddKeyModEvent(key_mods); + io.AddKeyModsEvent(key_mods); } void ImGui_ImplSDL2_NewFrame() diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index 873c20aceeef..6348e8a43b3a 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -35,7 +35,7 @@ typedef DWORD (WINAPI *PFN_XInputGetState)(DWORD, XINPUT_STATE*); // CHANGELOG // (minor and older changes stripped away, please see git history for details) // 2021-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. -// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. +// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2021-12-16: Inputs: Fill VK_LCONTROL/VK_RCONTROL/VK_LSHIFT/VK_RSHIFT/VK_LMENU/VK_RMENU for completeness. // 2021-08-17: Calling io.AddFocusEvent() on WM_SETFOCUS/WM_KILLFOCUS messages. // 2021-08-02: Inputs: Fixed keyboard modifiers being reported when host window doesn't have focus. @@ -251,7 +251,7 @@ static void ImGui_ImplWin32_UpdateKeyModifiers() ((IsVkDown(VK_LSHIFT) || IsVkDown(VK_RSHIFT)) ? ImGuiKeyModFlags_Shift : 0) | ((IsVkDown(VK_LMENU) || IsVkDown(VK_RMENU)) ? ImGuiKeyModFlags_Alt : 0) | ((IsVkDown(VK_LWIN) || IsVkDown(VK_RWIN)) ? ImGuiKeyModFlags_Super : 0); - io.AddKeyModEvent(key_mods); + io.AddKeyModsEvent(key_mods); } // This code supports multi-viewports (multiple OS Windows mapped into different Dear ImGui viewports) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 19558ae454c3..7912640d714d 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -106,7 +106,7 @@ Breaking Changes: - Reworked IO keyboard input system. (#2625, #3724) [@thedmd, @ocornut] - Added io.AddKeyEvent() function, obsoleting writing directly to io.KeyMap[], io.KeysDown[] arrays. - - Added io.AddKeyModEvent() function, obsoleting writing directly to io.KeyCtrl, io.KeyShift etc. + - Added io.AddKeyModsEvent() function, obsoleting writing directly to io.KeyCtrl, io.KeyShift etc. - Added io.SetKeyEventNativeData() function (optional) to pass native and old legacy indices. - Added full range of key enums in ImGuiKey (e.g. ImGuiKey_F1). - Added GetKeyName() helper function. diff --git a/imgui.cpp b/imgui.cpp index 193583feee7a..8f6d13ea3ac7 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -398,7 +398,7 @@ CODE - IsKeyPressed(MY_NATIVE_KEY_XXX) -> use IsKeyPressed(ImGuiKey_XXX) - IsKeyPressed(GetKeyIndex(ImGuiKey_XXX)) -> use IsKeyPressed(ImGuiKey_XXX) - Backend writing to io.KeyMap[],io.KeysDown[] -> backend should call io.AddKeyEvent() - - inputs: added io.AddKeyModEvent() instead of writing directly to io.KeyCtrl, io.KeyShift, io.KeyAlt, io.KeySuper. + - inputs: added io.AddKeyModsEvent() instead of writing directly to io.KeyCtrl, io.KeyShift, io.KeyAlt, io.KeySuper. - 2022/01/05 (1.87) - inputs: renamed ImGuiKey_KeyPadEnter to ImGuiKey_KeypadEnter to align with new symbols. Kept redirection enum. - 2022/01/05 (1.87) - removed io.ImeSetInputScreenPosFn() in favor of more flexible io.SetPlatformImeDataFn(). Removed 'void* io.ImeWindowHandle' in favor of writing to 'void* ImGuiViewport::PlatformHandleRaw'. - 2022/01/01 (1.87) - commented out redirecting functions/enums names that were marked obsolete in 1.69, 1.70, 1.71, 1.72 (March-July 2019) @@ -1318,7 +1318,7 @@ void ImGuiIO::SetKeyEventNativeData(ImGuiKey key, int native_keycode, int native #endif } -void ImGuiIO::AddKeyModEvent(ImGuiKeyModFlags modifiers) +void ImGuiIO::AddKeyModsEvent(ImGuiKeyModFlags modifiers) { KeyMods = modifiers; KeyCtrl = (modifiers & ImGuiKeyModFlags_Ctrl) != 0; diff --git a/imgui.h b/imgui.h index 709241edab19..9529b5eab31c 100644 --- a/imgui.h +++ b/imgui.h @@ -2098,7 +2098,7 @@ struct ImGuiIO // Input Functions IMGUI_API void AddKeyEvent(ImGuiKey key, bool down); // Queue a new key down/up event. Key should be "translated" (as in, generally ImGuiKey_A matches the key end-user would use to emit an 'A' character) - IMGUI_API void AddKeyModEvent(ImGuiKeyModFlags modifiers); // Queue a change of Ctrl/Shift/Alt/Super modifiers + IMGUI_API void AddKeyModsEvent(ImGuiKeyModFlags modifiers);// Queue a change of Ctrl/Shift/Alt/Super modifiers IMGUI_API void AddFocusEvent(bool focused); // Queue an hosting application/platform windows gain or loss of focus IMGUI_API void AddInputCharacter(unsigned int c); // Queue new character input IMGUI_API void AddInputCharacterUTF16(ImWchar16 c); // Queue new character input from an UTF-16 character, it can be a surrogate From e51a0a80ca89e21eb48c17d5be5185c890233f99 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 13 Jan 2022 18:42:18 +0100 Subject: [PATCH 776/828] IO: fix SetKeyEventNativeData() not handling ImGuiKey_None the same way as AddKeyEvent(). (#4905, #4858) --- imgui.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/imgui.cpp b/imgui.cpp index 91c97b6c810b..f319188f44f0 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1302,6 +1302,8 @@ void ImGuiIO::AddKeyEvent(ImGuiKey key, bool down) // If you are writing a backend in 2022 or don't use IsKeyXXX() with native values that are not ImGuiKey values, you can avoid calling this. void ImGuiIO::SetKeyEventNativeData(ImGuiKey key, int native_keycode, int native_scancode, int native_legacy_index) { + if (key == ImGuiKey_None) + return; IM_ASSERT(ImGui::IsNamedKey(key)); // >= 512 IM_ASSERT(native_legacy_index == -1 || ImGui::IsLegacyKey(native_legacy_index)); // >= 0 && <= 511 IM_UNUSED(native_keycode); // Yet unused From ec1e57ed4a616ee42f4711b4a955bc68b9c1f463 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 13 Jan 2022 18:52:19 +0100 Subject: [PATCH 777/828] Merge "Backends: SDL: Fix for Emscriptem. Amend 98ce013." + Fix bad merge from master of "is_app_focused" property (Amend 0647ba3) --- backends/imgui_impl_sdl.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backends/imgui_impl_sdl.cpp b/backends/imgui_impl_sdl.cpp index c1225ce26b8c..b75fa5e86941 100644 --- a/backends/imgui_impl_sdl.cpp +++ b/backends/imgui_impl_sdl.cpp @@ -463,8 +463,9 @@ static void ImGui_ImplSDL2_UpdateMouseData() // SDL_CaptureMouse() let the OS know e.g. that our imgui drag outside the SDL window boundaries shouldn't e.g. trigger other operations outside SDL_CaptureMouse(bd->MouseButtonsDown != 0 ? SDL_TRUE : SDL_FALSE); SDL_Window* focused_window = SDL_GetKeyboardFocus(); - const bool is_app_focused = (bd->Window == focused_window); + const bool is_app_focused = (focused_window && (bd->Window == focused_window || ImGui::FindViewportByPlatformHandle((void*)focused_window))); #else + SDL_Window* focused_window = bd->Window; const bool is_app_focused = (SDL_GetWindowFlags(bd->Window) & SDL_WINDOW_INPUT_FOCUS) != 0; // SDL 2.0.3 and non-windowed systems: single-viewport only #endif From 007a427e0af93529e483ebc78e50231bb055a8d3 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 18 Jan 2022 16:04:25 +0100 Subject: [PATCH 778/828] Viewports: Fixed active InputText() from preventing viewports to merge. (#4212) --- docs/CHANGELOG.txt | 1 + imgui.cpp | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 1b6fe96239d9..92780a2827fa 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -208,6 +208,7 @@ Docking+Viewports Branch: - Viewports: Fixed a CTRL+TAB crash with viewports enabled when the window list needs to appears in its own viewport (regression from 1.86). (#4023, #787) +- Viewports: Fixed active InputText() from preventing viewports to merge. (#4212) - (Breaking) Removed ImGuiPlatformIO::Platform_SetImeInputPos() in favor of newly standardized io.SetPlatformImeDataFn() function. Should not affect more than default backends. diff --git a/imgui.cpp b/imgui.cpp index ce2c9dd38261..1420f67df424 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -12925,7 +12925,8 @@ static void ImGui::WindowSelectViewport(ImGuiWindow* window) { // Merge into host viewport? // We cannot test window->ViewportOwned as it set lower in the function. - bool try_to_merge_into_host_viewport = (window->Viewport && window == window->Viewport->Window && g.ActiveId == 0); + // Testing (g.ActiveId == 0 || g.ActiveIdAllowOverlap) to avoid merging during a short-term widget interaction. Main intent was to avoid during resize (see #4212) + bool try_to_merge_into_host_viewport = (window->Viewport && window == window->Viewport->Window && (g.ActiveId == 0 || g.ActiveIdAllowOverlap)); if (try_to_merge_into_host_viewport) UpdateTryMergeWindowIntoHostViewports(window); } From 1338eb31f73a7ee1a3dda8cfc6f1e46970e5288c Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 18 Jan 2022 16:59:19 +0100 Subject: [PATCH 779/828] Viewports: Relaxed specs for backend supporting ImGuiBackendFlags_HasMouseHoveredViewport. Backends: SDL: Added support for simplified HasMouseHoveredViewport. (#1542, #4665) --- backends/imgui_impl_glfw.cpp | 16 ++++++++++------ backends/imgui_impl_osx.mm | 2 +- backends/imgui_impl_sdl.cpp | 22 +++++++++++++++++++++- backends/imgui_impl_win32.cpp | 16 ++++++++-------- docs/CHANGELOG.txt | 9 +++++++++ imgui.cpp | 12 +++++------- imgui.h | 4 ++-- 7 files changed, 56 insertions(+), 25 deletions(-) diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index 34790fbe3858..e9ca12ed558f 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -578,12 +578,14 @@ static void ImGui_ImplGlfw_UpdateMouseData() } // (Optional) When using multiple viewports: set io.MouseHoveredViewport to the viewport the OS mouse cursor is hovering. - // Important: this information is not easy to provide and many high-level windowing library won't be able to provide it correctly, because - // - This is _ignoring_ viewports with the ImGuiViewportFlags_NoInputs flag (pass-through windows). - // - This is _regardless_ of whether another viewport is focused or being dragged from. - // If ImGuiBackendFlags_HasMouseHoveredViewport is not set by the backend, imgui will ignore this field and infer the information by relying on the - // rectangles and last focused time of every viewports it knows about. It will be unaware of other windows that may be sitting between or over your windows. - // [GLFW] FIXME: This is currently only correct on Win32. See what we do below with the WM_NCHITTEST, missing an equivalent for other systems. + // If ImGuiBackendFlags_HasMouseHoveredViewport is not set by the backend, Dear imGui will ignore this field and infer the information using its flawed heuristic. + // - [X] GLFW >= 3.3 backend ON WINDOWS ONLY does correctly ignore viewports with the _NoInputs flag. + // - [!] GLFW <= 3.2 backend CANNOT correctly ignore viewports with the _NoInputs flag, and CANNOT reported Hovered Viewport because of mouse capture. + // Some backend are not able to handle that correctly. If a backend report an hovered viewport that has the _NoInputs flag (e.g. when dragging a window + // for docking, the viewport has the _NoInputs flag in order to allow us to find the viewport under), then Dear ImGui is forced to ignore the value reported + // by the backend, and use its flawed heuristic to guess the viewport behind. + // - [X] GLFW backend correctly reports this regardless of another viewport behind focused and dragged from (we need this to find a useful drag and drop target). + // FIXME: This is currently only correct on Win32. See what we do below with the WM_NCHITTEST, missing an equivalent for other systems. // See https://github.com/glfw/glfw/issues/1236 if you want to help in making this a GLFW feature. #if GLFW_HAS_MOUSE_PASSTHROUGH || (GLFW_HAS_WINDOW_HOVERED && defined(_WIN32)) const bool window_no_input = (viewport->Flags & ImGuiViewportFlags_NoInputs) != 0; @@ -592,6 +594,8 @@ static void ImGui_ImplGlfw_UpdateMouseData() #endif if (glfwGetWindowAttrib(window, GLFW_HOVERED) && !window_no_input) io.MouseHoveredViewport = viewport->ID; +#else + // We cannot use bd->MouseWindow maintained from CursorEnter/Leave callbacks, because it is locked to the window capturing mouse. #endif } } diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm index 3e00b8a31ca2..fb624f6c38ed 100644 --- a/backends/imgui_impl_osx.mm +++ b/backends/imgui_impl_osx.mm @@ -359,7 +359,7 @@ bool ImGui_ImplOSX_Init(NSView* view) io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) //io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) //io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional) - //io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can set io.MouseHoveredViewport correctly (optional, not easy) + //io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can set io.MouseHoveredViewport correctly (optional) io.BackendPlatformName = "imgui_impl_osx"; // Load cursors. Some of them are undocumented. diff --git a/backends/imgui_impl_sdl.cpp b/backends/imgui_impl_sdl.cpp index 3a488137d89d..3e723773621e 100644 --- a/backends/imgui_impl_sdl.cpp +++ b/backends/imgui_impl_sdl.cpp @@ -89,6 +89,7 @@ struct ImGui_ImplSDL2_Data { SDL_Window* Window; Uint64 Time; + Uint32 MouseWindowID; int MouseButtonsDown; SDL_Cursor* MouseCursors[ImGuiMouseCursor_COUNT]; char* ClipboardTextData; @@ -312,9 +313,16 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) } case SDL_WINDOWEVENT: { + // When capturing mouse, SDL will send a bunch of conflicting LEAVE/ENTER event on every mouse move, but the final ENTER tends to be right. + // However we won't get a correct LEAVE event for a captured window. Uint8 window_event = event->window.event; + if (window_event == SDL_WINDOWEVENT_ENTER) + bd->MouseWindowID = event->window.windowID; if (window_event == SDL_WINDOWEVENT_LEAVE) + { + bd->MouseWindowID = 0; io.AddMousePosEvent(-FLT_MAX, -FLT_MAX); + } if (window_event == SDL_WINDOWEVENT_FOCUS_GAINED) io.AddFocusEvent(true); else if (window_event == SDL_WINDOWEVENT_FOCUS_LOST) @@ -359,7 +367,10 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window, void* sdl_gl_context) io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) if (mouse_can_use_global_state) + { io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional) + io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport;//We can set io.MouseHoveredViewport correctly (optional) + } bd->Window = window; bd->MouseCanUseGlobalState = mouse_can_use_global_state; @@ -510,8 +521,17 @@ static void ImGui_ImplSDL2_UpdateMouseData() } } - // We don't support ImGuiBackendFlags_HasMouseHoveredViewport + // (Optional) When using multiple viewports: set io.MouseHoveredViewport to the viewport the OS mouse cursor is hovering. + // If ImGuiBackendFlags_HasMouseHoveredViewport is not set by the backend, Dear imGui will ignore this field and infer the information using its flawed heuristic. + // - [!] SDL backend does NOT correctly ignore viewports with the _NoInputs flag. + // Some backend are not able to handle that correctly. If a backend report an hovered viewport that has the _NoInputs flag (e.g. when dragging a window + // for docking, the viewport has the _NoInputs flag in order to allow us to find the viewport under), then Dear ImGui is forced to ignore the value reported + // by the backend, and use its flawed heuristic to guess the viewport behind. + // - [X] SDL backend correctly reports this regardless of another viewport behind focused and dragged from (we need this to find a useful drag and drop target). io.MouseHoveredViewport = 0; + if (SDL_Window* sdl_mouse_window = SDL_GetWindowFromID(bd->MouseWindowID)) + if (ImGuiViewport* mouse_viewport = ImGui::FindViewportByPlatformHandle((void*)sdl_mouse_window)) + io.MouseHoveredViewport = mouse_viewport->ID; } static void ImGui_ImplSDL2_UpdateMouseCursor() diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index a4fb035c71a3..7d7ff0fe0a7d 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -131,7 +131,7 @@ bool ImGui_ImplWin32_Init(void* hwnd) io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional) - io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can set io.MouseHoveredViewport correctly (optional, not easy) + io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can set io.MouseHoveredViewport correctly (optional) bd->hWnd = (HWND)hwnd; bd->WantUpdateHasGamepad = true; @@ -300,17 +300,17 @@ static void ImGui_ImplWin32_UpdateMouseData() } // (Optional) When using multiple viewports: set io.MouseHoveredViewport to the viewport the OS mouse cursor is hovering. - // Important: this information is not easy to provide and many high-level windowing library won't be able to provide it correctly, because - // - This is _ignoring_ viewports with the ImGuiViewportFlags_NoInputs flag (pass-through windows). - // - This is _regardless_ of whether another viewport is focused or being dragged from. - // If ImGuiBackendFlags_HasMouseHoveredViewport is not set by the backend, imgui will ignore this field and infer the information by relying on the - // rectangles and last focused time of every viewports it knows about. It will be unaware of foreign windows that may be sitting between or over your windows. + // If ImGuiBackendFlags_HasMouseHoveredViewport is not set by the backend, Dear imGui will ignore this field and infer the information using its flawed heuristic. + // - [X] Win32 backend correctly ignore viewports with the _NoInputs flag (here using ::WindowFromPoint with WM_NCHITTEST + HTTRANSPARENT in WndProc does that) + // Some backend are not able to handle that correctly. If a backend report an hovered viewport that has the _NoInputs flag (e.g. when dragging a window + // for docking, the viewport has the _NoInputs flag in order to allow us to find the viewport under), then Dear ImGui is forced to ignore the value reported + // by the backend, and use its flawed heuristic to guess the viewport behind. + // - [X] Win32 backend correctly reports this regardless of another viewport behind focused and dragged from (we need this to find a useful drag and drop target). io.MouseHoveredViewport = 0; if (has_mouse_screen_pos) if (HWND hovered_hwnd = ::WindowFromPoint(mouse_screen_pos)) if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle((void*)hovered_hwnd)) - if ((viewport->Flags & ImGuiViewportFlags_NoInputs) == 0) // FIXME: We still get our NoInputs window with WM_NCHITTEST/HTTRANSPARENT code when decorated? - io.MouseHoveredViewport = viewport->ID; + io.MouseHoveredViewport = viewport->ID; } // Gamepad navigation mapping diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 92780a2827fa..846768985fa9 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -206,9 +206,18 @@ Other Changes: Docking+Viewports Branch: +- Viewports: Relaxed specs for backend supporting ImGuiBackendFlags_HasMouseHoveredViewport: it is now _optional_ + for the backend to have to ignore viewports with the _NoInputs flag when setting io.MouseHoveredViewport. It is + much better if they can (Win32 and GLFW 3.3+ backends can, SDL and GLFW 3.2 backends cannot, they are lacking data). + A concrete example is: when dragging a viewport for docking, the viewport is marked with _NoInputs to allow us + to pick the target viewports for docking. If the backend reports a viewport with _NoInputs in io.MouseHoveredViewport, + then Dear ImGui will revert to its flawed heuristic to find the viewport under. + By lowering those specs, we allow the SDL and more backend to support this, only relying on the heuristic in a few + drag and drop situations rather that relying on it everywhere. - Viewports: Fixed a CTRL+TAB crash with viewports enabled when the window list needs to appears in its own viewport (regression from 1.86). (#4023, #787) - Viewports: Fixed active InputText() from preventing viewports to merge. (#4212) +- Backends: SDL: Added support for ImGuiBackendFlags_HasMouseHoveredViewport now that its specs have been lowered. - (Breaking) Removed ImGuiPlatformIO::Platform_SetImeInputPos() in favor of newly standardized io.SetPlatformImeDataFn() function. Should not affect more than default backends. diff --git a/imgui.cpp b/imgui.cpp index 1420f67df424..b67202e247c1 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -12701,17 +12701,14 @@ static void ImGui::UpdateViewportsNewFrame() { viewport_hovered = g.IO.MouseHoveredViewport ? (ImGuiViewportP*)FindViewportByID(g.IO.MouseHoveredViewport) : NULL; if (viewport_hovered && (viewport_hovered->Flags & ImGuiViewportFlags_NoInputs)) - { - // Backend failed at honoring its contract if it returned a viewport with the _NoInputs flag. - IM_ASSERT(0); - viewport_hovered = FindHoveredViewportFromPlatformWindowStack(g.IO.MousePos); - } + viewport_hovered = FindHoveredViewportFromPlatformWindowStack(g.IO.MousePos); // Backend failed to handle _NoInputs viewport: revert to our fallback. } else { // If the backend doesn't know how to honor ImGuiViewportFlags_NoInputs, we do a search ourselves. Note that this search: // A) won't take account of the possibility that non-imgui windows may be in-between our dragged window and our target window. - // B) uses LastFrameAsRefViewport as a flawed replacement for the last time a window was focused (we could/should fix that by introducing Focus functions in PlatformIO) + // B) won't take account of how the backend apply parent<>child relationship to secondary viewports, which affects their Z order. + // C) uses LastFrameAsRefViewport as a flawed replacement for the last time a window was focused (we could/should fix that by introducing Focus functions in PlatformIO) viewport_hovered = FindHoveredViewportFromPlatformWindowStack(g.IO.MousePos); } if (viewport_hovered != NULL) @@ -17663,7 +17660,8 @@ void ImGui::ShowMetricsWindow(bool* p_open) TreePop(); } - if (TreeNode("Inferred order (front-to-back)")) + BulletText("MouseViewport: 0x%08X (UserHovered 0x%08X, LastHovered 0x%08X)", g.MouseViewport ? g.MouseViewport->ID : 0, g.IO.MouseHoveredViewport, g.MouseLastHoveredViewport ? g.MouseLastHoveredViewport->ID : 0); + if (TreeNode("Inferred Z order (front-to-back)")) { static ImVector viewports; viewports.resize(g.Viewports.Size); diff --git a/imgui.h b/imgui.h index 97ee1dcbe3c5..5f63e704bdf0 100644 --- a/imgui.h +++ b/imgui.h @@ -1611,7 +1611,7 @@ enum ImGuiBackendFlags_ // [BETA] Viewports ImGuiBackendFlags_PlatformHasViewports = 1 << 10, // Backend Platform supports multiple viewports. - ImGuiBackendFlags_HasMouseHoveredViewport=1 << 11, // Backend Platform supports setting io.MouseHoveredViewport to the viewport directly under the mouse _IGNORING_ viewports with the ImGuiViewportFlags_NoInputs flag and _REGARDLESS_ of whether another viewport is focused and may be capturing the mouse. This information is _NOT EASY_ to provide correctly with most high-level engines! Don't set this without studying _carefully_ how the backends handle ImGuiViewportFlags_NoInputs! + ImGuiBackendFlags_HasMouseHoveredViewport=1 << 11, // Backend Platform supports setting io.MouseHoveredViewport to the viewport directly under the mouse. IF POSSIBLE, ignore viewports with the ImGuiViewportFlags_NoInputs flag (Win32 backend, GLFW 3.30+ backend can do this, SDL backend cannot). If this cannot be done, Dear ImGui needs to use a flawed heuristic to find the viewport under. ImGuiBackendFlags_RendererHasViewports = 1 << 12 // Backend Renderer supports multiple viewports. }; @@ -2076,7 +2076,7 @@ struct ImGuiIO bool MouseDown[5]; // Mouse buttons: 0=left, 1=right, 2=middle + extras (ImGuiMouseButton_COUNT == 5). Dear ImGui mostly uses left and right buttons. Others buttons allows us to track if the mouse is being used by your application + available to user as a convenience via IsMouse** API. float MouseWheel; // Mouse wheel Vertical: 1 unit scrolls about 5 lines text. float MouseWheelH; // Mouse wheel Horizontal. Most users don't have a mouse with an horizontal wheel, may not be filled by all backends. - ImGuiID MouseHoveredViewport; // (Optional) When using multiple viewports: viewport the OS mouse cursor is hovering _IGNORING_ viewports with the ImGuiViewportFlags_NoInputs flag, and _REGARDLESS_ of whether another viewport is focused. Set io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport if you can provide this info. If you don't imgui will infer the value using the rectangles and last focused time of the viewports it knows about (ignoring other OS windows). + ImGuiID MouseHoveredViewport; // (Optional) With multi-viewports: viewport the OS mouse is hovering. If possible _IGNORING_ viewports with the ImGuiViewportFlags_NoInputs flag is much better (few backends can handle that). Set io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport if you can provide this info. If you don't imgui will infer the value using the rectangles and last focused time of the viewports it knows about (ignoring other OS windows). bool KeyCtrl; // Keyboard modifier down: Control bool KeyShift; // Keyboard modifier down: Shift bool KeyAlt; // Keyboard modifier down: Alt From e278277d53c961b6b345388b79644187bd0d0339 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 18 Jan 2022 17:24:59 +0100 Subject: [PATCH 780/828] IO: added AddMouseViewportEvent() + used in backends. --- backends/imgui_impl_glfw.cpp | 14 ++++++++------ backends/imgui_impl_osx.mm | 2 +- backends/imgui_impl_sdl.cpp | 9 +++++---- backends/imgui_impl_win32.cpp | 11 ++++++----- docs/CHANGELOG.txt | 9 +++++---- imgui.cpp | 17 +++++++++++++++++ imgui.h | 5 +++-- imgui_internal.h | 3 +++ 8 files changed, 48 insertions(+), 22 deletions(-) diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index e9ca12ed558f..f6aad1b5c68f 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -418,7 +418,7 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional) #if GLFW_HAS_MOUSE_PASSTHROUGH || (GLFW_HAS_WINDOW_HOVERED && defined(_WIN32)) - io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can set io.MouseHoveredViewport correctly (optional, not easy) + io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can call io.AddMouseViewportEvent() with correct data (optional) #endif bd->Window = window; @@ -539,8 +539,7 @@ static void ImGui_ImplGlfw_UpdateMouseData() ImGuiIO& io = ImGui::GetIO(); ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); - io.MouseHoveredViewport = 0; - + ImGuiID mouse_viewport_id = 0; const ImVec2 mouse_pos_prev = io.MousePos; for (int n = 0; n < platform_io.Viewports.Size; n++) { @@ -577,7 +576,7 @@ static void ImGui_ImplGlfw_UpdateMouseData() } } - // (Optional) When using multiple viewports: set io.MouseHoveredViewport to the viewport the OS mouse cursor is hovering. + // (Optional) When using multiple viewports: call io.AddMouseViewportEvent() with the viewport the OS mouse cursor is hovering. // If ImGuiBackendFlags_HasMouseHoveredViewport is not set by the backend, Dear imGui will ignore this field and infer the information using its flawed heuristic. // - [X] GLFW >= 3.3 backend ON WINDOWS ONLY does correctly ignore viewports with the _NoInputs flag. // - [!] GLFW <= 3.2 backend CANNOT correctly ignore viewports with the _NoInputs flag, and CANNOT reported Hovered Viewport because of mouse capture. @@ -593,11 +592,14 @@ static void ImGui_ImplGlfw_UpdateMouseData() glfwSetWindowAttrib(window, GLFW_MOUSE_PASSTHROUGH, window_no_input); #endif if (glfwGetWindowAttrib(window, GLFW_HOVERED) && !window_no_input) - io.MouseHoveredViewport = viewport->ID; + mouse_viewport_id = viewport->ID; #else // We cannot use bd->MouseWindow maintained from CursorEnter/Leave callbacks, because it is locked to the window capturing mouse. #endif } + + if (io.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport) + io.AddMouseViewportEvent(mouse_viewport_id); } static void ImGui_ImplGlfw_UpdateMouseCursor() @@ -866,7 +868,7 @@ static LRESULT CALLBACK WndProcNoInputs(HWND hWnd, UINT msg, WPARAM wParam, LPAR { if (msg == WM_NCHITTEST) { - // Let mouse pass-through the window. This will allow the backend to set io.MouseHoveredViewport properly (which is OPTIONAL). + // Let mouse pass-through the window. This will allow the backend to call io.AddMouseViewportEvent() properly (which is OPTIONAL). // The ImGuiViewportFlags_NoInputs flag is set while dragging a viewport, as want to detect the window behind the one we are dragging. // If you cannot easily access those viewport flags from your windowing/event code: you may manually synchronize its state e.g. in // your main loop after calling UpdatePlatformWindows(). Iterate all viewports/platform windows and pass the flag to your windowing system. diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm index fb624f6c38ed..9ca9fe5ae0b9 100644 --- a/backends/imgui_impl_osx.mm +++ b/backends/imgui_impl_osx.mm @@ -359,7 +359,7 @@ bool ImGui_ImplOSX_Init(NSView* view) io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) //io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) //io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional) - //io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can set io.MouseHoveredViewport correctly (optional) + //io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can call io.AddMouseViewportEvent() with correct data (optional) io.BackendPlatformName = "imgui_impl_osx"; // Load cursors. Some of them are undocumented. diff --git a/backends/imgui_impl_sdl.cpp b/backends/imgui_impl_sdl.cpp index 3e723773621e..7137d7467722 100644 --- a/backends/imgui_impl_sdl.cpp +++ b/backends/imgui_impl_sdl.cpp @@ -369,7 +369,7 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window, void* sdl_gl_context) if (mouse_can_use_global_state) { io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional) - io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport;//We can set io.MouseHoveredViewport correctly (optional) + io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport;// We can call io.AddMouseViewportEvent() with correct data (optional) } bd->Window = window; @@ -521,17 +521,18 @@ static void ImGui_ImplSDL2_UpdateMouseData() } } - // (Optional) When using multiple viewports: set io.MouseHoveredViewport to the viewport the OS mouse cursor is hovering. + // (Optional) When using multiple viewports: call io.AddMouseViewportEvent() with the viewport the OS mouse cursor is hovering. // If ImGuiBackendFlags_HasMouseHoveredViewport is not set by the backend, Dear imGui will ignore this field and infer the information using its flawed heuristic. // - [!] SDL backend does NOT correctly ignore viewports with the _NoInputs flag. // Some backend are not able to handle that correctly. If a backend report an hovered viewport that has the _NoInputs flag (e.g. when dragging a window // for docking, the viewport has the _NoInputs flag in order to allow us to find the viewport under), then Dear ImGui is forced to ignore the value reported // by the backend, and use its flawed heuristic to guess the viewport behind. // - [X] SDL backend correctly reports this regardless of another viewport behind focused and dragged from (we need this to find a useful drag and drop target). - io.MouseHoveredViewport = 0; + ImGuiID mouse_viewport_id = 0; if (SDL_Window* sdl_mouse_window = SDL_GetWindowFromID(bd->MouseWindowID)) if (ImGuiViewport* mouse_viewport = ImGui::FindViewportByPlatformHandle((void*)sdl_mouse_window)) - io.MouseHoveredViewport = mouse_viewport->ID; + mouse_viewport_id = mouse_viewport->ID; + io.AddMouseViewportEvent(mouse_viewport_id); } static void ImGui_ImplSDL2_UpdateMouseCursor() diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index 7d7ff0fe0a7d..017baa57a584 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -131,7 +131,7 @@ bool ImGui_ImplWin32_Init(void* hwnd) io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional) - io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can set io.MouseHoveredViewport correctly (optional) + io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can call io.AddMouseViewportEvent() with correct data (optional) bd->hWnd = (HWND)hwnd; bd->WantUpdateHasGamepad = true; @@ -299,18 +299,19 @@ static void ImGui_ImplWin32_UpdateMouseData() } } - // (Optional) When using multiple viewports: set io.MouseHoveredViewport to the viewport the OS mouse cursor is hovering. + // (Optional) When using multiple viewports: call io.AddMouseViewportEvent() with the viewport the OS mouse cursor is hovering. // If ImGuiBackendFlags_HasMouseHoveredViewport is not set by the backend, Dear imGui will ignore this field and infer the information using its flawed heuristic. // - [X] Win32 backend correctly ignore viewports with the _NoInputs flag (here using ::WindowFromPoint with WM_NCHITTEST + HTTRANSPARENT in WndProc does that) // Some backend are not able to handle that correctly. If a backend report an hovered viewport that has the _NoInputs flag (e.g. when dragging a window // for docking, the viewport has the _NoInputs flag in order to allow us to find the viewport under), then Dear ImGui is forced to ignore the value reported // by the backend, and use its flawed heuristic to guess the viewport behind. // - [X] Win32 backend correctly reports this regardless of another viewport behind focused and dragged from (we need this to find a useful drag and drop target). - io.MouseHoveredViewport = 0; + ImGuiID mouse_viewport_id = 0; if (has_mouse_screen_pos) if (HWND hovered_hwnd = ::WindowFromPoint(mouse_screen_pos)) if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle((void*)hovered_hwnd)) - io.MouseHoveredViewport = viewport->ID; + mouse_viewport_id = viewport->ID; + io.AddMouseViewportEvent(mouse_viewport_id); } // Gamepad navigation mapping @@ -1077,7 +1078,7 @@ static LRESULT CALLBACK ImGui_ImplWin32_WndProcHandler_PlatformWindow(HWND hWnd, return MA_NOACTIVATE; break; case WM_NCHITTEST: - // Let mouse pass-through the window. This will allow the backend to set io.MouseHoveredViewport properly (which is OPTIONAL). + // Let mouse pass-through the window. This will allow the backend to call io.AddMouseViewportEvent() correctly. (which is optional). // The ImGuiViewportFlags_NoInputs flag is set while dragging a viewport, as want to detect the window behind the one we are dragging. // If you cannot easily access those viewport flags from your windowing/event code: you may manually synchronize its state e.g. in // your main loop after calling UpdatePlatformWindows(). Iterate all viewports/platform windows and pass the flag to your windowing system. diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 846768985fa9..e1fd7b5387db 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -90,7 +90,7 @@ Other changes: - Added io.ConfigViewportsNoDecoration option. - Added io.ConfigViewportsNoDefaultParent option. - Added ImGuiBackendFlags_PlatformHasViewports, ImGuiBackendFlags_RendererHasViewports, ImGuiBackendFlags_HasMouseHoveredViewport backend flags. -- Added io.MouseHoveredViewport (optional _even_ for multi-viewport support, tied to ImGuiBackendFlags_HasMouseHoveredViewport flag). +- Added io.AddMouseViewportEvent() (optional _even_ for multi-viewport support, tied to ImGuiBackendFlags_HasMouseHoveredViewport flag). - Expanded ImGuiViewport structure, ImGuiViewportFlags flags. - Added ImGuiWindowClass and SetNextWindowClass() for passing viewport related hints to the OS/platform back-end. - Examples: Renderer: OpenGL2, OpenGL3, DirectX9, DirectX10, DirectX11, DirectX12, Vulkan: Added support for multi-viewports. @@ -206,12 +206,13 @@ Other Changes: Docking+Viewports Branch: +- Viewports, IO: Added io.AddMouseViewportEvent() function to queue hovered viewport change (when known by backend). - Viewports: Relaxed specs for backend supporting ImGuiBackendFlags_HasMouseHoveredViewport: it is now _optional_ - for the backend to have to ignore viewports with the _NoInputs flag when setting io.MouseHoveredViewport. It is + for the backend to have to ignore viewports with the _NoInputs flag when call io.AddMouseViewportEvent(). It is much better if they can (Win32 and GLFW 3.3+ backends can, SDL and GLFW 3.2 backends cannot, they are lacking data). A concrete example is: when dragging a viewport for docking, the viewport is marked with _NoInputs to allow us - to pick the target viewports for docking. If the backend reports a viewport with _NoInputs in io.MouseHoveredViewport, - then Dear ImGui will revert to its flawed heuristic to find the viewport under. + to pick the target viewports for docking. If the backend reports a viewport with _NoInputs when calling the + io.AddMouseViewportEvent() function, then Dear ImGui will revert to its flawed heuristic to find the viewport under. By lowering those specs, we allow the SDL and more backend to support this, only relying on the heuristic in a few drag and drop situations rather that relying on it everywhere. - Viewports: Fixed a CTRL+TAB crash with viewports enabled when the window list needs to appears in diff --git a/imgui.cpp b/imgui.cpp index b67202e247c1..644b3d7a0fac 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1390,6 +1390,19 @@ void ImGuiIO::AddMouseButtonEvent(int mouse_button, bool down) g.InputEventsQueue.push_back(e); } +void ImGuiIO::AddMouseViewportEvent(ImGuiID viewport_id) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(&g.IO == this && "Can only add events to current context."); + IM_ASSERT(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport); + + ImGuiInputEvent e; + e.Type = ImGuiInputEventType_MouseViewport; + e.Source = ImGuiInputSource_Mouse; + e.MouseViewport.HoveredViewportID = viewport_id; + g.InputEventsQueue.push_back(e); +} + void ImGuiIO::AddFocusEvent(bool focused) { ImGuiContext& g = *GImGui; @@ -8354,6 +8367,10 @@ void ImGui::UpdateInputEvents(bool trickle_fast_inputs) mouse_wheeled = true; } } + else if (e->Type == ImGuiInputEventType_MouseViewport) + { + io.MouseHoveredViewport = e->MouseViewport.HoveredViewportID; + } else if (e->Type == ImGuiInputEventType_Key) { IM_ASSERT(e->Key.Key != ImGuiKey_None); diff --git a/imgui.h b/imgui.h index 5f63e704bdf0..3d60e3ac35d5 100644 --- a/imgui.h +++ b/imgui.h @@ -1611,7 +1611,7 @@ enum ImGuiBackendFlags_ // [BETA] Viewports ImGuiBackendFlags_PlatformHasViewports = 1 << 10, // Backend Platform supports multiple viewports. - ImGuiBackendFlags_HasMouseHoveredViewport=1 << 11, // Backend Platform supports setting io.MouseHoveredViewport to the viewport directly under the mouse. IF POSSIBLE, ignore viewports with the ImGuiViewportFlags_NoInputs flag (Win32 backend, GLFW 3.30+ backend can do this, SDL backend cannot). If this cannot be done, Dear ImGui needs to use a flawed heuristic to find the viewport under. + ImGuiBackendFlags_HasMouseHoveredViewport=1 << 11, // Backend Platform supports calling io.AddMouseViewportEvent() with the viewport under the mouse. IF POSSIBLE, ignore viewports with the ImGuiViewportFlags_NoInputs flag (Win32 backend, GLFW 3.30+ backend can do this, SDL backend cannot). If this cannot be done, Dear ImGui needs to use a flawed heuristic to find the viewport under. ImGuiBackendFlags_RendererHasViewports = 1 << 12 // Backend Renderer supports multiple viewports. }; @@ -2076,7 +2076,7 @@ struct ImGuiIO bool MouseDown[5]; // Mouse buttons: 0=left, 1=right, 2=middle + extras (ImGuiMouseButton_COUNT == 5). Dear ImGui mostly uses left and right buttons. Others buttons allows us to track if the mouse is being used by your application + available to user as a convenience via IsMouse** API. float MouseWheel; // Mouse wheel Vertical: 1 unit scrolls about 5 lines text. float MouseWheelH; // Mouse wheel Horizontal. Most users don't have a mouse with an horizontal wheel, may not be filled by all backends. - ImGuiID MouseHoveredViewport; // (Optional) With multi-viewports: viewport the OS mouse is hovering. If possible _IGNORING_ viewports with the ImGuiViewportFlags_NoInputs flag is much better (few backends can handle that). Set io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport if you can provide this info. If you don't imgui will infer the value using the rectangles and last focused time of the viewports it knows about (ignoring other OS windows). + ImGuiID MouseHoveredViewport; // (Optional) Modify using io.AddMouseViewportEvent(). With multi-viewports: viewport the OS mouse is hovering. If possible _IGNORING_ viewports with the ImGuiViewportFlags_NoInputs flag is much better (few backends can handle that). Set io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport if you can provide this info. If you don't imgui will infer the value using the rectangles and last focused time of the viewports it knows about (ignoring other OS windows). bool KeyCtrl; // Keyboard modifier down: Control bool KeyShift; // Keyboard modifier down: Shift bool KeyAlt; // Keyboard modifier down: Alt @@ -2089,6 +2089,7 @@ struct ImGuiIO IMGUI_API void AddMousePosEvent(float x, float y); // Queue a mouse position update. Use -FLT_MAX,-FLT_MAX to signify no mouse (e.g. app not focused and not hovered) IMGUI_API void AddMouseButtonEvent(int button, bool down); // Queue a mouse button change IMGUI_API void AddMouseWheelEvent(float wh_x, float wh_y); // Queue a mouse wheel update + IMGUI_API void AddMouseViewportEvent(ImGuiID id); // Queue a mouse hovered viewport. Requires backend to set ImGuiBackendFlags_HasMouseHoveredViewport to call this. IMGUI_API void AddFocusEvent(bool focused); // Queue an hosting application/platform windows gain or loss of focus IMGUI_API void AddInputCharacter(unsigned int c); // Queue new character input IMGUI_API void AddInputCharacterUTF16(ImWchar16 c); // Queue new character input from an UTF-16 character, it can be a surrogate diff --git a/imgui_internal.h b/imgui_internal.h index 9a5c7d9bc276..5f07d7f7616a 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -917,6 +917,7 @@ enum ImGuiInputEventType ImGuiInputEventType_MousePos, ImGuiInputEventType_MouseWheel, ImGuiInputEventType_MouseButton, + ImGuiInputEventType_MouseViewport, ImGuiInputEventType_Key, ImGuiInputEventType_KeyMods, ImGuiInputEventType_Char, @@ -940,6 +941,7 @@ enum ImGuiInputSource struct ImGuiInputEventMousePos { float PosX, PosY; }; struct ImGuiInputEventMouseWheel { float WheelX, WheelY; }; struct ImGuiInputEventMouseButton { int Button; bool Down; }; +struct ImGuiInputEventMouseViewport { ImGuiID HoveredViewportID; }; struct ImGuiInputEventKey { ImGuiKey Key; bool Down; }; struct ImGuiInputEventKeyMods { ImGuiKeyModFlags Mods; }; struct ImGuiInputEventText { unsigned int Char; }; @@ -955,6 +957,7 @@ struct ImGuiInputEvent ImGuiInputEventMousePos MousePos; // if Type == ImGuiInputEventType_MousePos ImGuiInputEventMouseWheel MouseWheel; // if Type == ImGuiInputEventType_MouseWheel ImGuiInputEventMouseButton MouseButton; // if Type == ImGuiInputEventType_MouseButton + ImGuiInputEventMouseViewport MouseViewport; // if Type == ImGuiInputEventType_MouseViewport ImGuiInputEventKey Key; // if Type == ImGuiInputEventType_Key ImGuiInputEventKeyMods KeyMods; // if Type == ImGuiInputEventType_Modifiers ImGuiInputEventText Text; // if Type == ImGuiInputEventType_Text From f1a073186c859facfc75823302b5c5b04f43293f Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 25 Jan 2022 17:28:43 +0100 Subject: [PATCH 781/828] Docking: Fixed a CTRL+TAB crash when aiming at an empty docked window. (#4792) --- docs/CHANGELOG.txt | 1 + imgui.cpp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 1ddb6cd6dff3..d20f802f5e54 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -232,6 +232,7 @@ Docking+Viewports Branch: io.AddMouseViewportEvent() function, then Dear ImGui will revert to its flawed heuristic to find the viewport under. By lowering those specs, we allow the SDL and more backend to support this, only relying on the heuristic in a few drag and drop situations rather that relying on it everywhere. +- Docking: Fixed a CTRL+TAB crash when aiming at an empty docked window. (#4792) - Viewports: Fixed a CTRL+TAB crash with viewports enabled when the window list needs to appears in its own viewport (regression from 1.86). (#4023, #787) - Viewports: Fixed active InputText() from preventing viewports to merge. (#4212) diff --git a/imgui.cpp b/imgui.cpp index 141306bc26ac..83857031ae69 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4892,6 +4892,8 @@ static void ImGui::RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 if (window->RootWindow->DockIsActive) { ImDrawList* draw_list = FindFrontMostVisibleChildWindow(window->RootWindowDockTree)->DrawList; + if (draw_list->CmdBuffer.Size == 0) + draw_list->AddDrawCmd(); draw_list->PushClipRect(viewport_rect.Min, viewport_rect.Max, false); RenderRectFilledWithHole(draw_list, window->RootWindowDockTree->Rect(), window->RootWindow->Rect(), col, 0.0f);// window->RootWindowDockTree->WindowRounding); draw_list->PopClipRect(); From 27004aca705e33e8a88f368372628c28f1ee4cd1 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 28 Jan 2022 15:53:09 +0100 Subject: [PATCH 782/828] Revert moving ImGuiKeyModFlags to internal.h (amendc906c65) # Conflicts: # imgui.cpp --- docs/CHANGELOG.txt | 2 -- imgui.cpp | 1 - imgui.h | 12 +++++++++++- imgui_internal.h | 10 ---------- 4 files changed, 11 insertions(+), 14 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index e3a421fd6fa1..e7f056d3f3a6 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -146,8 +146,6 @@ Breaking Changes: io.AddKeyEvent(), io.AddKeyAnalogEvent(). - Added io.AddKeyAnalogEvent() function, obsoleting writing directly to io.NavInputs[] arrays. - Renamed ImGuiKey_KeyPadEnter to ImGuiKey_KeypadEnter to align with new symbols. Kept redirection enum. (#2625) -- Moved ImGuiKeyModsFlags definition from imgui.h to imgui_internal.h. Was never advertised and used in a place - marked "private" in comments. Will still be used internally. - Removed support for legacy arithmetic operators (+,+-,*,/) when inputing text into a slider/drag. (#4917, #3184) This doesn't break any api/code but a feature that was accessible by end-users (which seemingly no one used). (Instead you may implement custom expression evaluators to provide a better version of this). diff --git a/imgui.cpp b/imgui.cpp index 76b277331a7c..e09a15850f29 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -392,7 +392,6 @@ CODE - likewise io.MousePos and GetMousePos() will use OS coordinates. If you query mouse positions to interact with non-imgui coordinates you will need to offset them, e.g. subtract GetWindowViewport()->Pos. - - 2022/01/26 (1.87) - inputs: moved ImGuiKeyModsFlags definition from imgui.h to imgui_internal.h. Was never advertised and used in a place marked "private" in comments. Will still be used internally. - 2022/01/20 (1.87) - inputs: reworded gamepad IO. - Backend writing to io.NavInputs[] -> backend should call io.AddKeyEvent()/io.AddKeyAnalogEvent() with ImGuiKey_GamepadXXX values. - 2022/01/19 (1.87) - sliders, drags: removed support for legacy arithmetic operators (+,+-,*,/) when inputing text. This doesn't break any api/code but a feature that used to be accessible by end-users (which seemingly no one used). diff --git a/imgui.h b/imgui.h index fffc7cd1c87a..a172aa3d20f0 100644 --- a/imgui.h +++ b/imgui.h @@ -65,7 +65,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.87 WIP" -#define IMGUI_VERSION_NUM 18614 +#define IMGUI_VERSION_NUM 18615 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE #define IMGUI_HAS_VIEWPORT // Viewport WIP branch @@ -1516,6 +1516,16 @@ enum ImGuiKey_ #endif }; +// Helper "flags" version of key-mods to store and compare multiple key-mods easily. Sometimes used for storage (e.g. io.KeyMods) but otherwise not much used in public API. +enum ImGuiKeyModFlags_ +{ + ImGuiKeyModFlags_None = 0, + ImGuiKeyModFlags_Ctrl = 1 << 0, + ImGuiKeyModFlags_Shift = 1 << 1, + ImGuiKeyModFlags_Alt = 1 << 2, + ImGuiKeyModFlags_Super = 1 << 3 // Cmd/Super/Windows key +}; + // Gamepad/Keyboard navigation // Keyboard: Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable. NewFrame() will automatically fill io.NavInputs[] based on your io.AddKeyEvent() calls. // Gamepad: Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable. Backend: set ImGuiBackendFlags_HasGamepad and fill the io.NavInputs[] fields before calling NewFrame(). Note that io.NavInputs[] is cleared by EndFrame(). diff --git a/imgui_internal.h b/imgui_internal.h index 13f7f7389608..db0de88ac716 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1184,16 +1184,6 @@ enum ImGuiKeyPrivate_ ImGuiKey_Gamepad_END = ImGuiKey_GamepadRStickRight + 1 }; -// Helper to store all mods easily. Stored in e.g. io.KeyMods. -enum ImGuiKeyModFlags_ -{ - ImGuiKeyModFlags_None = 0, - ImGuiKeyModFlags_Ctrl = 1 << 0, - ImGuiKeyModFlags_Shift = 1 << 1, - ImGuiKeyModFlags_Alt = 1 << 2, - ImGuiKeyModFlags_Super = 1 << 3 // Cmd/Super/Windows key -}; - enum ImGuiInputEventType { ImGuiInputEventType_None = 0, From 08350e53e79e2dd44ee40e1143ffcf79401af1fc Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 31 Jan 2022 11:47:44 +0100 Subject: [PATCH 783/828] Backends: SDL: no support for ImGuiBackendFlags_HasMouseHoveredViewport under OSX/LInux (#4960) --- backends/imgui_impl_sdl.cpp | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/backends/imgui_impl_sdl.cpp b/backends/imgui_impl_sdl.cpp index 7f74f91b3e4b..7255ccd5d5f6 100644 --- a/backends/imgui_impl_sdl.cpp +++ b/backends/imgui_impl_sdl.cpp @@ -367,10 +367,13 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window, void* sdl_gl_context) io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) if (mouse_can_use_global_state) - { io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional) + + // SDL on Linux/OSX doesn't report events for unfocused windows (see https://github.com/ocornut/imgui/issues/4960) +#ifdef _WIN32 + if (mouse_can_use_global_state) io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport;// We can call io.AddMouseViewportEvent() with correct data (optional) - } +#endif bd->Window = window; bd->MouseCanUseGlobalState = mouse_can_use_global_state; @@ -528,11 +531,14 @@ static void ImGui_ImplSDL2_UpdateMouseData() // for docking, the viewport has the _NoInputs flag in order to allow us to find the viewport under), then Dear ImGui is forced to ignore the value reported // by the backend, and use its flawed heuristic to guess the viewport behind. // - [X] SDL backend correctly reports this regardless of another viewport behind focused and dragged from (we need this to find a useful drag and drop target). - ImGuiID mouse_viewport_id = 0; - if (SDL_Window* sdl_mouse_window = SDL_GetWindowFromID(bd->MouseWindowID)) - if (ImGuiViewport* mouse_viewport = ImGui::FindViewportByPlatformHandle((void*)sdl_mouse_window)) - mouse_viewport_id = mouse_viewport->ID; - io.AddMouseViewportEvent(mouse_viewport_id); + if (io.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport) + { + ImGuiID mouse_viewport_id = 0; + if (SDL_Window* sdl_mouse_window = SDL_GetWindowFromID(bd->MouseWindowID)) + if (ImGuiViewport* mouse_viewport = ImGui::FindViewportByPlatformHandle((void*)sdl_mouse_window)) + mouse_viewport_id = mouse_viewport->ID; + io.AddMouseViewportEvent(mouse_viewport_id); + } } static void ImGui_ImplSDL2_UpdateMouseCursor() From c1ab3c406f3497f77c1a455557c331f0a2fa4bb5 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 2 Feb 2022 23:10:14 +0100 Subject: [PATCH 784/828] Docking: Fixed size constraints not working on single window holding on a dock id (still doesn't work on docked windows). --- docs/CHANGELOG.txt | 3 ++- imgui.cpp | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 5cf9a9afc550..3f324ea2f97f 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -231,6 +231,8 @@ Other Changes: Docking+Viewports Branch: +- Docking: Fixed a CTRL+TAB crash when aiming at an empty docked window. (#4792) +- Docking: Fixed size constraints not working on single window holding on a dock id (still doesn't work on docked windows). - Viewports, IO: Added io.AddMouseViewportEvent() function to queue hovered viewport change (when known by backend). - Viewports: Relaxed specs for backend supporting ImGuiBackendFlags_HasMouseHoveredViewport: it is now _optional_ for the backend to have to ignore viewports with the _NoInputs flag when call io.AddMouseViewportEvent(). It is @@ -240,7 +242,6 @@ Docking+Viewports Branch: io.AddMouseViewportEvent() function, then Dear ImGui will revert to its flawed heuristic to find the viewport under. By lowering those specs, we allow the SDL and more backend to support this, only relying on the heuristic in a few drag and drop situations rather that relying on it everywhere. -- Docking: Fixed a CTRL+TAB crash when aiming at an empty docked window. (#4792) - Viewports: Fixed a CTRL+TAB crash with viewports enabled when the window list needs to appears in its own viewport (regression from 1.86). (#4023, #787) - Viewports: Fixed active InputText() from preventing viewports to merge. (#4212) diff --git a/imgui.cpp b/imgui.cpp index dbea7ad93199..29ac68f4b0bd 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6383,7 +6383,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) IM_ASSERT(window->DockNode != NULL); // Docking currently override constraints - g.NextWindowData.Flags &= ~ImGuiNextWindowDataFlags_HasSizeConstraint; + if (window->DockIsActive) + g.NextWindowData.Flags &= ~ImGuiNextWindowDataFlags_HasSizeConstraint; // Amend the Appearing flag if (window->DockTabIsVisible && !dock_tab_was_visible && dock_node_was_visible && !window->Appearing && !window_was_appearing) From 8eb86893914b7865ac3594f7d4dbb121acd39e06 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 2 Feb 2022 23:23:13 +0100 Subject: [PATCH 785/828] Docking: Tabs use their own identifier (in order to make window->ID refer to whole window in test engine). Also prevents Tab ID from clashing with "" which was common. --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 39 ++++++++++++++++++++++----------------- imgui_internal.h | 1 + imgui_widgets.cpp | 8 ++++---- 4 files changed, 29 insertions(+), 21 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 3f324ea2f97f..971015de1b20 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -232,6 +232,8 @@ Other Changes: Docking+Viewports Branch: - Docking: Fixed a CTRL+TAB crash when aiming at an empty docked window. (#4792) +- Docking: Tabs use their own identifier instead of the Window identifier. + (This will invalidate some stored .ini data such as last selected tab, sorry!) - Docking: Fixed size constraints not working on single window holding on a dock id (still doesn't work on docked windows). - Viewports, IO: Added io.AddMouseViewportEvent() function to queue hovered viewport change (when known by backend). - Viewports: Relaxed specs for backend supporting ImGuiBackendFlags_HasMouseHoveredViewport: it is now _optional_ diff --git a/imgui.cpp b/imgui.cpp index 29ac68f4b0bd..0aa1a82c1791 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3264,6 +3264,7 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) : DrawListInst ViewportAllowPlatformMonitorExtend = -1; ViewportPos = ImVec2(FLT_MAX, FLT_MAX); MoveId = GetID("#MOVE"); + TabId = GetID("#TAB"); ScrollTarget = ImVec2(FLT_MAX, FLT_MAX); ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f); AutoFitFramesX = AutoFitFramesY = -1; @@ -3541,8 +3542,9 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) // Test if another item is active (e.g. being dragged) if ((flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) == 0) - if (g.ActiveId != 0 && g.ActiveId != g.LastItemData.ID && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId) - return false; + if (g.ActiveId != 0 && g.ActiveId != g.LastItemData.ID && !g.ActiveIdAllowOverlap) + if (g.ActiveId != window->MoveId && g.ActiveId != window->TabId) + return false; // Test if interactions on this window are blocked by an active popup or modal. // The ImGuiHoveredFlags_AllowWhenBlockedByPopup flag will be tested here. @@ -3554,7 +3556,8 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) return false; // Special handling for calling after Begin() which represent the title bar or tab. - // When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect the case. + // When the window is skipped/collapsed (SkipItems==true) that last item (always ->MoveId submitted by Begin) + // will never be overwritten so we need to detect the case. if (g.LastItemData.ID == window->MoveId && window->WriteAccessed) return false; } @@ -7299,7 +7302,7 @@ void ImGui::FocusWindow(ImGuiWindow* window) // Select in dock node if (dock_node && dock_node->TabBar) - dock_node->TabBar->SelectedTabId = dock_node->TabBar->NextSelectedTabId = window->ID; + dock_node->TabBar->SelectedTabId = dock_node->TabBar->NextSelectedTabId = window->TabId; // Bring to front BringWindowToFocusFront(focus_front_window); @@ -12456,6 +12459,7 @@ static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandl buf->appendf("Collapsed=%d\n", settings->Collapsed); if (settings->DockId != 0) { + //buf->appendf("TabId=0x%08X\n", ImHashStr("#TAB", 4, settings->ID)); // window->TabId: this is not read back but writing it makes "debugging" the .ini data easier. if (settings->DockOrder == -1) buf->appendf("DockId=0x%08X\n", settings->DockId); else @@ -14063,7 +14067,7 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) if (payload_node && payload_node->IsLeafNode()) next_selected_id = payload_node->TabBar->NextSelectedTabId ? payload_node->TabBar->NextSelectedTabId : payload_node->TabBar->SelectedTabId; if (payload_node == NULL) - next_selected_id = payload_window->ID; + next_selected_id = payload_window->TabId; } // FIXME-DOCK: When we are trying to dock an existing single-window node into a loose window, transfer Node ID as well @@ -14340,7 +14344,7 @@ int ImGui::DockNodeGetTabOrder(ImGuiWindow* window) ImGuiTabBar* tab_bar = window->DockNode->TabBar; if (tab_bar == NULL) return -1; - ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, window->ID); + ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, window->TabId); return tab ? tab_bar->GetTabOrder(tab) : -1; } @@ -14444,7 +14448,7 @@ static void ImGui::DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window node->WantHiddenTabBarUpdate = true; if (node->TabBar) { - TabBarRemoveTab(node->TabBar, window->ID); + TabBarRemoveTab(node->TabBar, window->TabId); const int tab_count_threshold_for_tab_bar = node->IsCentralNode() ? 1 : 2; if (node->Windows.Size < tab_count_threshold_for_tab_bar) DockNodeRemoveTabBar(node); @@ -14627,7 +14631,7 @@ static void ImGui::DockNodeUpdateFlagsAndCollapse(ImGuiDockNode* node) bool node_was_active = (node->LastFrameActive + 1 == g.FrameCount); bool remove = false; remove |= node_was_active && (window->LastFrameActive + 1 < g.FrameCount); - remove |= node_was_active && (node->WantCloseAll || node->WantCloseTabId == window->ID) && window->HasCloseButton && !(window->Flags & ImGuiWindowFlags_UnsavedDocument); // Submit all _expected_ closure from last frame + remove |= node_was_active && (node->WantCloseAll || node->WantCloseTabId == window->TabId) && window->HasCloseButton && !(window->Flags & ImGuiWindowFlags_UnsavedDocument); // Submit all _expected_ closure from last frame remove |= (window->DockTabWantClose); if (remove) { @@ -15159,7 +15163,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w if (is_focused || root_node->VisibleWindow == NULL) root_node->VisibleWindow = node->VisibleWindow; if (node->TabBar) - node->TabBar->VisibleTabId = node->VisibleWindow->ID; + node->TabBar->VisibleTabId = node->VisibleWindow->TabId; } return; } @@ -15210,7 +15214,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w for (int window_n = 0; window_n < node->Windows.Size; window_n++) { ImGuiWindow* window = node->Windows[window_n]; - if (TabBarFindTabByID(tab_bar, window->ID) == NULL) + if (TabBarFindTabByID(tab_bar, window->TabId) == NULL) TabBarAddTab(tab_bar, ImGuiTabItemFlags_Unsorted, window); } @@ -15255,7 +15259,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w if (tab_bar_is_recreated && TabBarFindTabByID(tab_bar, node->SelectedTabId) != NULL) tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = node->SelectedTabId; else if (tab_bar->Tabs.Size > tabs_count_old) - tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = tab_bar->Tabs.back().Window->ID; + tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = tab_bar->Tabs.back().Window->TabId; // Begin tab bar ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_Reorderable | ImGuiTabBarFlags_AutoSelectNewTabs; // | ImGuiTabBarFlags_NoTabListScrollingButtons); @@ -15275,7 +15279,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w for (int window_n = 0; window_n < node->Windows.Size; window_n++) { ImGuiWindow* window = node->Windows[window_n]; - if ((closed_all || closed_one == window->ID) && window->HasCloseButton && !(window->Flags & ImGuiWindowFlags_UnsavedDocument)) + if ((closed_all || closed_one == window->TabId) && window->HasCloseButton && !(window->Flags & ImGuiWindowFlags_UnsavedDocument)) continue; if (window->LastFrameActive + 1 >= g.FrameCount || !node_was_active) { @@ -15290,11 +15294,12 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w for (int color_n = 0; color_n < ImGuiWindowDockStyleCol_COUNT; color_n++) g.Style.Colors[GWindowDockStyleColors[color_n]] = ColorConvertU32ToFloat4(window->DockStyle.Colors[color_n]); + // Note that TabItemEx() calls TabBarCalcTabID() so our tab item ID will ignore the current ID stack (rightly so) bool tab_open = true; TabItemEx(tab_bar, window->Name, window->HasCloseButton ? &tab_open : NULL, tab_item_flags, window); if (!tab_open) - node->WantCloseTabId = window->ID; - if (tab_bar->VisibleTabId == window->ID) + node->WantCloseTabId = window->TabId; + if (tab_bar->VisibleTabId == window->TabId) node->VisibleWindow = window; // Store last item data so it can be queried with IsItemXXX functions after the user Begin() call @@ -15303,7 +15308,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w // Update navigation ID on menu layer if (g.NavWindow && g.NavWindow->RootWindow == window && (window->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) == 0) - host_window->NavLastIds[1] = window->ID; + host_window->NavLastIds[1] = window->TabId; } } @@ -15317,7 +15322,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w root_node->VisibleWindow = node->VisibleWindow; // Close button (after VisibleWindow was updated) - // Note that VisibleWindow may have been overrided by CTRL+Tabbing, so VisibleWindow->ID may be != from tab_bar->SelectedTabId + // Note that VisibleWindow may have been overrided by CTRL+Tabbing, so VisibleWindow->TabId may be != from tab_bar->SelectedTabId const bool close_button_is_enabled = node->HasCloseButton && node->VisibleWindow && node->VisibleWindow->HasCloseButton; const bool close_button_is_visible = node->HasCloseButton; //const bool close_button_is_visible = close_button_is_enabled; // Most people would expect this behavior of not even showing the button (leaving a hole since we can't claim that space as other windows in the tba bar have one) @@ -16931,7 +16936,7 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) if (node->TabBar && window->WasActive) window->DockOrder = (short)DockNodeGetTabOrder(window); - if ((node->WantCloseAll || node->WantCloseTabId == window->ID) && p_open != NULL) + if ((node->WantCloseAll || node->WantCloseTabId == window->TabId) && p_open != NULL) *p_open = false; // Update ChildId to allow returning from Child to Parent with Escape diff --git a/imgui_internal.h b/imgui_internal.h index 2697ac85fa33..b7c9437b7bff 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2231,6 +2231,7 @@ struct IMGUI_API ImGuiWindow float WindowBorderSize; // Window border size at the time of Begin(). int NameBufLen; // Size of buffer storing Name. May be larger than strlen(Name)! ImGuiID MoveId; // == window->GetID("#MOVE") + ImGuiID TabId; // == window->GetID("#TAB") ImGuiID ChildId; // ID of corresponding item in parent window (for navigation to return from child window to parent window) ImVec2 Scroll; ImVec2 ScrollMax; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index d6ee52960b1a..bb9c16b78a4d 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7584,7 +7584,7 @@ static ImU32 ImGui::TabBarCalcTabID(ImGuiTabBar* tab_bar, const char* label, I { IM_UNUSED(tab_bar); IM_ASSERT(tab_bar->Flags & ImGuiTabBarFlags_DockNode); - ImGuiID id = ImHashStr(label); + ImGuiID id = docked_window->TabId; KeepAliveID(id); return id; } @@ -7629,14 +7629,14 @@ ImGuiTabItem* ImGui::TabBarFindMostRecentlySelectedTabForActiveWindow(ImGuiTabBa void ImGui::TabBarAddTab(ImGuiTabBar* tab_bar, ImGuiTabItemFlags tab_flags, ImGuiWindow* window) { ImGuiContext& g = *GImGui; - IM_ASSERT(TabBarFindTabByID(tab_bar, window->ID) == NULL); + IM_ASSERT(TabBarFindTabByID(tab_bar, window->TabId) == NULL); IM_ASSERT(g.CurrentTabBar != tab_bar); // Can't work while the tab bar is active as our tab doesn't have an X offset yet, in theory we could/should test something like (tab_bar->CurrFrameVisible < g.FrameCount) but we'd need to solve why triggers the commented early-out assert in BeginTabBarEx() (probably dock node going from implicit to explicit in same frame) if (!window->HasCloseButton) tab_flags |= ImGuiTabItemFlags_NoCloseButton; // Set _NoCloseButton immediately because it will be used for first-frame width calculation. ImGuiTabItem new_tab; - new_tab.ID = window->ID; + new_tab.ID = window->TabId; new_tab.Flags = tab_flags; new_tab.LastFrameVisible = tab_bar->CurrFrameVisible; // Required so BeginTabBar() doesn't ditch the tab if (new_tab.LastFrameVisible == -1) @@ -8209,7 +8209,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, flags |= ImGuiTabItemFlags_NoCloseWithMiddleMouseButton; // Render tab label, process close button - const ImGuiID close_button_id = p_open ? GetIDWithSeed("#CLOSE", NULL, id) : 0; + const ImGuiID close_button_id = p_open ? GetIDWithSeed("#CLOSE", NULL, docked_window ? docked_window->ID : id) : 0; bool just_closed; bool text_clipped; TabItemLabelAndCloseButton(display_draw_list, bb, flags, tab_bar->FramePadding, label, id, close_button_id, tab_contents_visible, &just_closed, &text_clipped); From 37958ca1b5ecabeba1636a3d2e80661bf011a31d Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 3 Feb 2022 00:19:31 +0100 Subject: [PATCH 786/828] Docking: Fixed CTRL+TAB back into a docked window not selecting menu layer when no item are on main layer. Could merge on master. --- docs/CHANGELOG.txt | 1 + imgui.cpp | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 971015de1b20..1979d1da7202 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -235,6 +235,7 @@ Docking+Viewports Branch: - Docking: Tabs use their own identifier instead of the Window identifier. (This will invalidate some stored .ini data such as last selected tab, sorry!) - Docking: Fixed size constraints not working on single window holding on a dock id (still doesn't work on docked windows). +- Docking: Fixed CTRL+TAB back into a docked window not selecting menu layer when no item are on main layer. - Viewports, IO: Added io.AddMouseViewportEvent() function to queue hovered viewport change (when known by backend). - Viewports: Relaxed specs for backend supporting ImGuiBackendFlags_HasMouseHoveredViewport: it is now _optional_ for the backend to have to ignore viewports with the _NoInputs flag when call io.AddMouseViewportEvent(). It is diff --git a/imgui.cpp b/imgui.cpp index 0aa1a82c1791..676d830e8031 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6964,7 +6964,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DC.NavLayerCurrent = ImGuiNavLayer_Main; window->DC.NavLayersActiveMask = window->DC.NavLayersActiveMaskNext; - window->DC.NavLayersActiveMaskNext = 0x00; window->DC.NavHideHighlightOneFrame = false; window->DC.NavHasScroll = (window->ScrollMax.y > 0.0f); @@ -7125,6 +7124,10 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) skip_items = true; window->SkipItems = skip_items; + // Only clear NavLayersActiveMaskNext when marked as visible, so a CTRL+Tab back can use a safe value. + if (!window->SkipItems) + window->DC.NavLayersActiveMaskNext = 0x00; + // Sanity check: there are two spots which can set Appearing = true // - when 'window_just_activated_by_user' is set -> HiddenFramesCannotSkipItems is set -> SkipItems always false // - in BeginDocked() path when DockNodeIsVisible == DockTabIsVisible == true -> hidden _should_ be all zero // FIXME: Not formally proven, hence the assert. From 5017602752d87da9dbffa191fdb3b68c8bcac982 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 3 Feb 2022 11:49:36 +0100 Subject: [PATCH 787/828] Backends: SDL: Amend 08350e5, multi-viewports mouse tracking works under Linux. (#4960) + Reword tests to help static analysis. --- backends/imgui_impl_sdl.cpp | 2 +- imgui.cpp | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/backends/imgui_impl_sdl.cpp b/backends/imgui_impl_sdl.cpp index 7255ccd5d5f6..48812a047d7f 100644 --- a/backends/imgui_impl_sdl.cpp +++ b/backends/imgui_impl_sdl.cpp @@ -370,7 +370,7 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window, void* sdl_gl_context) io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional) // SDL on Linux/OSX doesn't report events for unfocused windows (see https://github.com/ocornut/imgui/issues/4960) -#ifdef _WIN32 +#ifndef __APPLE__ if (mouse_can_use_global_state) io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport;// We can call io.AddMouseViewportEvent() with correct data (optional) #endif diff --git a/imgui.cpp b/imgui.cpp index 676d830e8031..3e17a3871b37 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6383,11 +6383,10 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) BeginDocked(window, p_open); flags = window->Flags; if (window->DockIsActive) + { IM_ASSERT(window->DockNode != NULL); - - // Docking currently override constraints - if (window->DockIsActive) - g.NextWindowData.Flags &= ~ImGuiNextWindowDataFlags_HasSizeConstraint; + g.NextWindowData.Flags &= ~ImGuiNextWindowDataFlags_HasSizeConstraint; // Docking currently override constraints + } // Amend the Appearing flag if (window->DockTabIsVisible && !dock_tab_was_visible && dock_node_was_visible && !window->Appearing && !window_was_appearing) From 31762a81734301e620f4f075a7ddaf41ab8f9de7 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 3 Feb 2022 12:04:11 +0100 Subject: [PATCH 788/828] Docking: fixed potential crash if a passthrough dock node is submitted without a child intermediate (currently not possible via API) --- imgui.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 3e17a3871b37..614016bbb888 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -14979,7 +14979,8 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) if (central_node_hole && !hole_rect.IsInverted()) { SetWindowHitTestHole(host_window, hole_rect.Min, hole_rect.Max - hole_rect.Min); - SetWindowHitTestHole(host_window->ParentWindow, hole_rect.Min, hole_rect.Max - hole_rect.Min); + if (host_window->ParentWindow) + SetWindowHitTestHole(host_window->ParentWindow, hole_rect.Min, hole_rect.Max - hole_rect.Min); } } From aa8680009248061c83f3d6722ec53c1a320d872b Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 8 Feb 2022 12:22:46 +0100 Subject: [PATCH 789/828] Internals: rework RenderMouseCursor() signature so we can use it in docking branch more naturally. (Merged from master+ rework for docking) # Conflicts: # imgui.cpp # imgui_draw.cpp --- imgui.cpp | 43 +++++++++++++++++++++++++++++++------------ imgui.h | 2 +- imgui_draw.cpp | 22 ---------------------- imgui_internal.h | 2 +- 4 files changed, 33 insertions(+), 36 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index b5fa1bf22d02..5bc813356257 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3249,6 +3249,34 @@ void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFl } } +void ImGui::RenderMouseCursor(ImVec2 base_pos, float base_scale, ImGuiMouseCursor mouse_cursor, ImU32 col_fill, ImU32 col_border, ImU32 col_shadow) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(mouse_cursor > ImGuiMouseCursor_None && mouse_cursor < ImGuiMouseCursor_COUNT); + ImFontAtlas* font_atlas = g.DrawListSharedData.Font->ContainerAtlas; + for (int n = 0; n < g.Viewports.Size; n++) + { + // We scale cursor with current viewport/monitor, however Windows 10 for its own hardware cursor seems to be using a different scale factor. + ImVec2 offset, size, uv[4]; + if (!font_atlas->GetMouseCursorTexData(mouse_cursor, &offset, &size, &uv[0], &uv[2])) + continue; + ImGuiViewportP* viewport = g.Viewports[n]; + const ImVec2 pos = base_pos - offset; + const float scale = base_scale * viewport->DpiScale; + if (!viewport->GetMainRect().Overlaps(ImRect(pos, pos + ImVec2(size.x + 2, size.y + 2) * scale))) + continue; + ImDrawList* draw_list = GetForegroundDrawList(viewport); + ImTextureID tex_id = font_atlas->TexID; + draw_list->PushTextureID(tex_id); + draw_list->AddImage(tex_id, pos + ImVec2(1, 0) * scale, pos + (ImVec2(1, 0) + size) * scale, uv[2], uv[3], col_shadow); + draw_list->AddImage(tex_id, pos + ImVec2(2, 0) * scale, pos + (ImVec2(2, 0) + size) * scale, uv[2], uv[3], col_shadow); + draw_list->AddImage(tex_id, pos, pos + size * scale, uv[2], uv[3], col_border); + draw_list->AddImage(tex_id, pos, pos + size * scale, uv[0], uv[1], col_fill); + draw_list->PopTextureID(); + } +} + + //----------------------------------------------------------------------------- // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!) //----------------------------------------------------------------------------- @@ -5129,9 +5157,9 @@ void ImGui::Render() if (first_render_of_frame) RenderDimmedBackgrounds(); - ImVec2 mouse_cursor_offset, mouse_cursor_size, mouse_cursor_uv[4]; - if (g.IO.MouseDrawCursor && g.MouseCursor != ImGuiMouseCursor_None) - g.IO.Fonts->GetMouseCursorTexData(g.MouseCursor, &mouse_cursor_offset, &mouse_cursor_size, &mouse_cursor_uv[0], &mouse_cursor_uv[2]); + // Draw software mouse cursor if requested by io.MouseDrawCursor flag + if (g.IO.MouseDrawCursor && first_render_of_frame && g.MouseCursor != ImGuiMouseCursor_None) + RenderMouseCursor(g.IO.MousePos, g.Style.MouseCursorScale, g.MouseCursor, IM_COL32_WHITE, IM_COL32_BLACK, IM_COL32(0, 0, 0, 48)); // Setup ImDrawData structures for end-user g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = 0; @@ -5140,15 +5168,6 @@ void ImGui::Render() ImGuiViewportP* viewport = g.Viewports[n]; viewport->DrawDataBuilder.FlattenIntoSingleLayer(); - // Draw software mouse cursor if requested by io.MouseDrawCursor flag - // (note we scale cursor by current viewport/monitor, however Windows 10 for its own hardware cursor seems to be using a different scale factor) - if (mouse_cursor_size.x > 0.0f && mouse_cursor_size.y > 0.0f && first_render_of_frame) - { - float scale = g.Style.MouseCursorScale * viewport->DpiScale; - if (viewport->GetMainRect().Overlaps(ImRect(g.IO.MousePos, g.IO.MousePos + ImVec2(mouse_cursor_size.x + 2, mouse_cursor_size.y + 2) * scale))) - RenderMouseCursor(GetForegroundDrawList(viewport), g.IO.MousePos, scale, g.MouseCursor, IM_COL32_WHITE, IM_COL32_BLACK, IM_COL32(0, 0, 0, 48)); - } - // Add foreground ImDrawList (for each active viewport) if (viewport->DrawLists[1] != NULL) AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[0], GetForegroundDrawList(viewport)); diff --git a/imgui.h b/imgui.h index 079fb565cf0b..323832071783 100644 --- a/imgui.h +++ b/imgui.h @@ -65,7 +65,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.87" -#define IMGUI_VERSION_NUM 18700 +#define IMGUI_VERSION_NUM 18701 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE #define IMGUI_HAS_VIEWPORT // Viewport WIP branch diff --git a/imgui_draw.cpp b/imgui_draw.cpp index bb01786c011b..40d5f2d74bc6 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -3744,7 +3744,6 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col // - RenderArrow() // - RenderBullet() // - RenderCheckMark() -// - RenderMouseCursor() // - RenderArrowDockMenu() // - RenderArrowPointingAt() // - RenderRectFilledRangeH() @@ -3806,27 +3805,6 @@ void ImGui::RenderCheckMark(ImDrawList* draw_list, ImVec2 pos, ImU32 col, float draw_list->PathStroke(col, 0, thickness); } -void ImGui::RenderMouseCursor(ImDrawList* draw_list, ImVec2 pos, float scale, ImGuiMouseCursor mouse_cursor, ImU32 col_fill, ImU32 col_border, ImU32 col_shadow) -{ - if (mouse_cursor == ImGuiMouseCursor_None) - return; - IM_ASSERT(mouse_cursor > ImGuiMouseCursor_None && mouse_cursor < ImGuiMouseCursor_COUNT); - - ImFontAtlas* font_atlas = draw_list->_Data->Font->ContainerAtlas; - ImVec2 offset, size, uv[4]; - if (font_atlas->GetMouseCursorTexData(mouse_cursor, &offset, &size, &uv[0], &uv[2])) - { - pos -= offset; - ImTextureID tex_id = font_atlas->TexID; - draw_list->PushTextureID(tex_id); - draw_list->AddImage(tex_id, pos + ImVec2(1, 0) * scale, pos + (ImVec2(1, 0) + size) * scale, uv[2], uv[3], col_shadow); - draw_list->AddImage(tex_id, pos + ImVec2(2, 0) * scale, pos + (ImVec2(2, 0) + size) * scale, uv[2], uv[3], col_shadow); - draw_list->AddImage(tex_id, pos, pos + size * scale, uv[2], uv[3], col_border); - draw_list->AddImage(tex_id, pos, pos + size * scale, uv[0], uv[1], col_fill); - draw_list->PopTextureID(); - } -} - // Render an arrow. 'pos' is position of the arrow tip. half_sz.x is length from base to tip. half_sz.y is length on each side. void ImGui::RenderArrowPointingAt(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, ImGuiDir direction, ImU32 col) { diff --git a/imgui_internal.h b/imgui_internal.h index b24e190e1903..b8212198b164 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -3051,12 +3051,12 @@ namespace ImGui IMGUI_API void RenderColorRectWithAlphaCheckerboard(ImDrawList* draw_list, ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, float grid_step, ImVec2 grid_off, float rounding = 0.0f, ImDrawFlags flags = 0); IMGUI_API void RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags = ImGuiNavHighlightFlags_TypeDefault); // Navigation highlight IMGUI_API const char* FindRenderedTextEnd(const char* text, const char* text_end = NULL); // Find the optional ## from which we stop displaying text. + IMGUI_API void RenderMouseCursor(ImVec2 pos, float scale, ImGuiMouseCursor mouse_cursor, ImU32 col_fill, ImU32 col_border, ImU32 col_shadow); // Render helpers (those functions don't access any ImGui state!) IMGUI_API void RenderArrow(ImDrawList* draw_list, ImVec2 pos, ImU32 col, ImGuiDir dir, float scale = 1.0f); IMGUI_API void RenderBullet(ImDrawList* draw_list, ImVec2 pos, ImU32 col); IMGUI_API void RenderCheckMark(ImDrawList* draw_list, ImVec2 pos, ImU32 col, float sz); - IMGUI_API void RenderMouseCursor(ImDrawList* draw_list, ImVec2 pos, float scale, ImGuiMouseCursor mouse_cursor, ImU32 col_fill, ImU32 col_border, ImU32 col_shadow); IMGUI_API void RenderArrowPointingAt(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, ImGuiDir direction, ImU32 col); IMGUI_API void RenderArrowDockMenu(ImDrawList* draw_list, ImVec2 p_min, float sz, ImU32 col); IMGUI_API void RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, ImU32 col, float x_start_norm, float x_end_norm, float rounding); From 1e14cc5cae703b7cdf0e33c50165d6dd289fcb1f Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 10 Feb 2022 18:50:28 +0100 Subject: [PATCH 790/828] Viewports: Fixed main viewport size not matching ImDrawData::DisplaySize for one frame during resize when multi-viewports are disabled. (#4900) --- docs/CHANGELOG.txt | 10 ++++++++++ imgui.cpp | 5 +++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index f15ba372eba7..636321590ee5 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -98,6 +98,16 @@ Other changes: Note that Linux/Mac still have inconsistent support for multi-viewports. If you want to help see https://github.com/ocornut/imgui/issues/2117. +----------------------------------------------------------------------- + VERSION 1.88 WIP (In Progress) +----------------------------------------------------------------------- + +Docking+Viewports Branch: + +- Viewports: Fixed main viewport size not matching ImDrawData::DisplaySize for one frame during resize + when multi-viewports are disabled. (#4900) + + ----------------------------------------------------------------------- VERSION 1.87 (Released 2022-02-07) ----------------------------------------------------------------------- diff --git a/imgui.cpp b/imgui.cpp index 5bc813356257..44dc0c8385bf 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -12903,9 +12903,10 @@ ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const ImGuiViewportP* viewport = (ImGuiViewportP*)FindViewportByID(id); if (viewport) { - if (!viewport->PlatformRequestMove) + // Always update for main viewport as we are already pulling correct platform pos/size (see #4900) + if (!viewport->PlatformRequestMove || viewport->ID == IMGUI_VIEWPORT_DEFAULT_ID) viewport->Pos = pos; - if (!viewport->PlatformRequestResize) + if (!viewport->PlatformRequestResize || viewport->ID == IMGUI_VIEWPORT_DEFAULT_ID) viewport->Size = size; viewport->Flags = flags | (viewport->Flags & ImGuiViewportFlags_Minimized); // Preserve existing flags } From 64519c6875fde64796aa1d098c02e1b346ac99fe Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 22 Feb 2022 16:21:10 +0100 Subject: [PATCH 791/828] Docking: Fixed floating docked nodes not being clamped into viewport workrect to stay reachable when g.ConfigWindowsMoveFromTitleBarOnly is set and multi-viewports are disabled. (#5044) --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index a11c8c12b96d..9fd364eb405f 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -132,6 +132,8 @@ Other Changes: Docking+Viewports Branch: +- Docking: Fixed floating docked nodes not being clamped into viewport workrect to stay reachable + when g.ConfigWindowsMoveFromTitleBarOnly is set and multi-viewports are disabled. (#5044) - Viewports: Fixed main viewport size not matching ImDrawData::DisplaySize for one frame during resize when multi-viewports are disabled. (#4900) diff --git a/imgui.cpp b/imgui.cpp index 20774780aa1c..35c7c9ef3556 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6015,8 +6015,8 @@ static inline void ClampWindowRect(ImGuiWindow* window, const ImRect& visibility { ImGuiContext& g = *GImGui; ImVec2 size_for_clamping = window->Size; - if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(window->Flags & ImGuiWindowFlags_NoTitleBar)) - size_for_clamping.y = window->TitleBarHeight(); + if (g.IO.ConfigWindowsMoveFromTitleBarOnly && (!(window->Flags & ImGuiWindowFlags_NoTitleBar) || window->DockNodeAsHost)) + size_for_clamping.y = ImGui::GetFrameHeight(); // Not using window->TitleBarHeight() as DockNodeAsHost will report 0.0f here. window->Pos = ImClamp(window->Pos, visibility_rect.Min - size_for_clamping, visibility_rect.Max); } From 8639a2f9f8d6d53f4c7a221579de5871051153d9 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 25 Feb 2022 16:41:40 +0100 Subject: [PATCH 792/828] Viewports: Fixed translating a host viewport from briefly altering the size of AlwaysAutoResize windows. (#5057) --- docs/CHANGELOG.txt | 1 + imgui.cpp | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 9fd364eb405f..6186bbcb71c5 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -134,6 +134,7 @@ Docking+Viewports Branch: - Docking: Fixed floating docked nodes not being clamped into viewport workrect to stay reachable when g.ConfigWindowsMoveFromTitleBarOnly is set and multi-viewports are disabled. (#5044) +- Viewports: Fixed translating a host viewport from briefly altering the size of AlwaysAutoResize windows. (#5057) - Viewports: Fixed main viewport size not matching ImDrawData::DisplaySize for one frame during resize when multi-viewports are disabled. (#4900) diff --git a/imgui.cpp b/imgui.cpp index 35c7c9ef3556..a07004d8347f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4063,6 +4063,8 @@ void ImGui::UpdateMouseMovingWindowEndFrame() } } +// This is called during NewFrame()->UpdateViewportsNewFrame() only. +// Need to keep in sync with SetWindowPos() static void TranslateWindow(ImGuiWindow* window, const ImVec2& delta) { window->Pos += delta; @@ -4072,6 +4074,7 @@ static void TranslateWindow(ImGuiWindow* window, const ImVec2& delta) window->DC.CursorPos += delta; window->DC.CursorStartPos += delta; window->DC.CursorMaxPos += delta; + window->DC.IdealMaxPos += delta; } static void ScaleWindow(ImGuiWindow* window, float scale) @@ -7670,6 +7673,7 @@ void ImGui::SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond) const ImVec2 old_pos = window->Pos; window->Pos = ImFloor(pos); ImVec2 offset = window->Pos - old_pos; + // FIXME: share code with TranslateWindow(), need to confirm whether the 3 rect modified by TranslateWindow() are desirable here. window->DC.CursorPos += offset; // As we happen to move the window while it is being appended to (which is a bad idea - will smear) let's at least offset the cursor window->DC.CursorMaxPos += offset; // And more importantly we need to offset CursorMaxPos/CursorStartPos this so ContentSize calculation doesn't get affected. window->DC.IdealMaxPos += offset; From f3373780668fba1f9bd64c208d05c20b781c9a39 Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Fri, 25 Feb 2022 17:18:40 +0100 Subject: [PATCH 793/828] Backends: SDL: Fix multi-viewport dragging issue with SDL on some systems. (#5012) --- backends/imgui_impl_sdl.cpp | 22 ++++++++++++++++------ docs/CHANGELOG.txt | 1 + 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/backends/imgui_impl_sdl.cpp b/backends/imgui_impl_sdl.cpp index 9384cecee414..47cb6a140472 100644 --- a/backends/imgui_impl_sdl.cpp +++ b/backends/imgui_impl_sdl.cpp @@ -96,6 +96,7 @@ struct ImGui_ImplSDL2_Data Uint32 MouseWindowID; int MouseButtonsDown; SDL_Cursor* MouseCursors[ImGuiMouseCursor_COUNT]; + int PendingMouseLeaveFrame; char* ClipboardTextData; bool MouseCanUseGlobalState; bool UseVulkan; @@ -315,16 +316,19 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) } case SDL_WINDOWEVENT: { - // When capturing mouse, SDL will send a bunch of conflicting LEAVE/ENTER event on every mouse move, but the final ENTER tends to be right. - // However we won't get a correct LEAVE event for a captured window. + // - When capturing mouse, SDL will send a bunch of conflicting LEAVE/ENTER event on every mouse move, but the final ENTER tends to be right. + // - However we won't get a correct LEAVE event for a captured window. + // - In some cases, when detaching a window from main viewport SDL may send SDL_WINDOWEVENT_ENTER one frame too late, + // causing SDL_WINDOWEVENT_LEAVE on previous frame to interrupt drag operation by clear mouse position. This is why + // we delay process the SDL_WINDOWEVENT_LEAVE events by one frame. See issue #5012 for details. Uint8 window_event = event->window.event; if (window_event == SDL_WINDOWEVENT_ENTER) - bd->MouseWindowID = event->window.windowID; - if (window_event == SDL_WINDOWEVENT_LEAVE) { - bd->MouseWindowID = 0; - io.AddMousePosEvent(-FLT_MAX, -FLT_MAX); + bd->MouseWindowID = event->window.windowID; + bd->PendingMouseLeaveFrame = 0; } + if (window_event == SDL_WINDOWEVENT_LEAVE) + bd->PendingMouseLeaveFrame = ImGui::GetFrameCount() + 1; if (window_event == SDL_WINDOWEVENT_FOCUS_GAINED) io.AddFocusEvent(true); else if (window_event == SDL_WINDOWEVENT_FOCUS_LOST) @@ -665,6 +669,12 @@ void ImGui_ImplSDL2_NewFrame() io.DeltaTime = bd->Time > 0 ? (float)((double)(current_time - bd->Time) / frequency) : (float)(1.0f / 60.0f); bd->Time = current_time; + if (bd->PendingMouseLeaveFrame && bd->PendingMouseLeaveFrame == ImGui::GetFrameCount()) + { + bd->MouseWindowID = 0; + io.AddMousePosEvent(-FLT_MAX, -FLT_MAX); + } + ImGui_ImplSDL2_UpdateMouseData(); ImGui_ImplSDL2_UpdateMouseCursor(); diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 6186bbcb71c5..d730d7a7254d 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -134,6 +134,7 @@ Docking+Viewports Branch: - Docking: Fixed floating docked nodes not being clamped into viewport workrect to stay reachable when g.ConfigWindowsMoveFromTitleBarOnly is set and multi-viewports are disabled. (#5044) +- Backends: SDL: Fixed dragging out main viewport broken on some SDL setups. (#5012) [@rokups] - Viewports: Fixed translating a host viewport from briefly altering the size of AlwaysAutoResize windows. (#5057) - Viewports: Fixed main viewport size not matching ImDrawData::DisplaySize for one frame during resize when multi-viewports are disabled. (#4900) From 9b0c26b0b2adae3ccf66dc9552fae4945d735a0c Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 22 Mar 2022 18:06:48 +0100 Subject: [PATCH 794/828] Backends: SDL: Fix more dragging issues. SDL_CaptureMouse() is essentially broken. (#5012, #5082) master got c5f6721 which is combining f337378 and this commit. --- backends/imgui_impl_sdl.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/backends/imgui_impl_sdl.cpp b/backends/imgui_impl_sdl.cpp index 47cb6a140472..3f490361a474 100644 --- a/backends/imgui_impl_sdl.cpp +++ b/backends/imgui_impl_sdl.cpp @@ -21,6 +21,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) // 2022-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. +// 2022-03-22: Inputs: Fix mouse position issues when dragging outside of boundaries. SDL_CaptureMouse() erroneously still gives out LEAVE events when hovering OS decorations. // 2022-02-04: Added SDL_Renderer* parameter to ImGui_ImplSDL2_InitForSDLRenderer(), so we can use SDL_GetRendererOutputSize() instead of SDL_GL_GetDrawableSize() when bound to a SDL_Renderer. // 2022-01-26: Inputs: replaced short-lived io.AddKeyModsEvent() (added two weeks ago)with io.AddKeyEvent() using ImGuiKey_ModXXX flags. Sorry for the confusion. // 2021-01-20: Inputs: calling new io.AddKeyAnalogEvent() for gamepad support, instead of writing directly to io.NavInputs[]. @@ -669,9 +670,10 @@ void ImGui_ImplSDL2_NewFrame() io.DeltaTime = bd->Time > 0 ? (float)((double)(current_time - bd->Time) / frequency) : (float)(1.0f / 60.0f); bd->Time = current_time; - if (bd->PendingMouseLeaveFrame && bd->PendingMouseLeaveFrame == ImGui::GetFrameCount()) + if (bd->PendingMouseLeaveFrame && bd->PendingMouseLeaveFrame >= ImGui::GetFrameCount() && bd->MouseButtonsDown == 0) { bd->MouseWindowID = 0; + bd->PendingMouseLeaveFrame = 0; io.AddMousePosEvent(-FLT_MAX, -FLT_MAX); } From ec48681455694c85982861dfa3f304fd544d3137 Mon Sep 17 00:00:00 2001 From: Dima Koltun Date: Fri, 22 Apr 2022 20:20:40 +0200 Subject: [PATCH 795/828] Windows: Fixed first-time windows appearing in negative coordinates. (#5215, #3414) Regression added in 6af92b05b --- docs/CHANGELOG.txt | 3 +++ imgui.cpp | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index e2d27aaa464d..945bfbc0acd8 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -121,6 +121,9 @@ Other Changes: Not that even thought you shouldn't need to disable io.ConfigInputTrickleEventQueue, you can technically dynamically change its setting based on the context (e.g. disable only when hovering or interacting with a game/3D view). +- Windows: Fixed first-time windows appearing in negative coordinates from being initialized + with a wrong size. This would most often be noticeable in multi-viewport mode (docking branch) + when spawning a window in a monitor with negative coordinates. (#5215, #3414) [@DimaKoltun] - Clipper: Fixed a regression in 1.86 when not calling clipper.End() and late destructing the clipper instance. High-level languages (Lua,Rust etc.) would typically be affected. (#4822) - Layout: Fixed mixing up SameLine() and SetCursorPos() together from creating situations where line diff --git a/imgui.cpp b/imgui.cpp index 6cf902afe7e3..69e63e63f1d9 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5611,7 +5611,7 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags) SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false); ApplyWindowSettings(window, settings); } - window->DC.CursorStartPos = window->DC.CursorMaxPos = window->Pos; // So first call to CalcContentSize() doesn't return crazy values + window->DC.CursorStartPos = window->DC.CursorMaxPos = window->DC.IdealMaxPos = window->Pos; // So first call to CalcWindowContentSizes() doesn't return crazy values if ((flags & ImGuiWindowFlags_AlwaysAutoResize) != 0) { From 6868d11669e9a598189bfc65c05e7a40c87aa0f1 Mon Sep 17 00:00:00 2001 From: stuartcarnie Date: Tue, 3 May 2022 16:38:16 +0300 Subject: [PATCH 796/828] Backends: OSX, Metal: Added multi-viewports support. (#4821, #2778) --- backends/imgui_impl_metal.mm | 162 +++++++- backends/imgui_impl_osx.h | 3 +- backends/imgui_impl_osx.mm | 351 +++++++++++++++++- docs/CHANGELOG.txt | 1 + .../project.pbxproj | 25 +- examples/example_apple_metal/main.mm | 17 + 6 files changed, 536 insertions(+), 23 deletions(-) diff --git a/backends/imgui_impl_metal.mm b/backends/imgui_impl_metal.mm index b31e37fa332a..aba0cc77e59b 100644 --- a/backends/imgui_impl_metal.mm +++ b/backends/imgui_impl_metal.mm @@ -4,8 +4,7 @@ // Implemented features: // [X] Renderer: User texture binding. Use 'MTLTexture' as ImTextureID. Read the FAQ about ImTextureID! // [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. -// Missing features: -// [ ] Renderer: Multi-viewport / platform windows. +// [X] Renderer: Multi-viewport / platform windows. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. @@ -14,6 +13,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2022-05-03: Misc: Implemented support for viewports / platform windows. // 2022-04-27: Misc: Store backend data in a per-context struct, allowing to use this backend with multiple contexts. // 2022-01-03: Metal: Ignore ImDrawCmd where ElemCount == 0 (very rare but can technically be manufactured by user code). // 2021-12-30: Metal: Added Metal C++ support. Enable with '#define IMGUI_IMPL_METAL_CPP' in your imconfig.h file. @@ -32,6 +32,12 @@ #import #import +// Forward Declarations +static void ImGui_ImplMetal_InitPlatformInterface(); +static void ImGui_ImplMetal_ShutdownPlatformInterface(); +static void ImGui_ImplMetal_CreateDeviceObjectsForPlatformWindows(); +static void ImGui_ImplMetal_InvalidateDeviceObjectsForPlatformWindows(); + #pragma mark - Support classes // A wrapper around a MTLBuffer object that knows the last time it was reused @@ -137,15 +143,20 @@ bool ImGui_ImplMetal_Init(id device) io.BackendRendererUserData = (void*)bd; io.BackendRendererName = "imgui_impl_metal"; io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. + io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional) bd->SharedMetalContext = [[MetalContext alloc] init]; bd->SharedMetalContext.device = device; + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + ImGui_ImplMetal_InitPlatformInterface(); + return true; } void ImGui_ImplMetal_Shutdown() { + ImGui_ImplMetal_ShutdownPlatformInterface(); ImGui_ImplMetal_DestroyDeviceObjects(); ImGui_ImplMetal_DestroyBackendData(); } @@ -189,6 +200,7 @@ bool ImGui_ImplMetal_CreateDeviceObjects(id device) { ImGui_ImplMetal_Data* bd = ImGui_ImplMetal_GetBackendData(); [bd->SharedMetalContext makeDeviceObjectsWithDevice:device]; + ImGui_ImplMetal_CreateDeviceObjectsForPlatformWindows(); ImGui_ImplMetal_CreateFontsTexture(device); return true; @@ -198,9 +210,155 @@ void ImGui_ImplMetal_DestroyDeviceObjects() { ImGui_ImplMetal_Data* bd = ImGui_ImplMetal_GetBackendData(); ImGui_ImplMetal_DestroyFontsTexture(); + ImGui_ImplMetal_InvalidateDeviceObjectsForPlatformWindows(); [bd->SharedMetalContext emptyRenderPipelineStateCache]; } +#pragma mark - Multi-viewport support + +#import + +#if TARGET_OS_OSX +#import +#endif + +//-------------------------------------------------------------------------------------------------------- +// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT +// This is an _advanced_ and _optional_ feature, allowing the back-end to create and handle multiple viewports simultaneously. +// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first.. +//-------------------------------------------------------------------------------------------------------- + +struct ImGuiViewportDataMetal +{ + CAMetalLayer* MetalLayer; + id CommandQueue; + MTLRenderPassDescriptor* RenderPassDescriptor; + void* Handle = NULL; + bool FirstFrame = true; +}; + +static void ImGui_ImplMetal_CreateWindow(ImGuiViewport* viewport) +{ + ImGui_ImplMetal_Data* bd = ImGui_ImplMetal_GetBackendData(); + ImGuiViewportDataMetal* data = IM_NEW(ImGuiViewportDataMetal)(); + viewport->RendererUserData = data; + + // PlatformHandleRaw should always be a NSWindow*, whereas PlatformHandle might be a higher-level handle (e.g. GLFWWindow*, SDL_Window*). + // Some back-ends will leave PlatformHandleRaw NULL, in which case we assume PlatformHandle will contain the NSWindow*. + void* handle = viewport->PlatformHandleRaw ? viewport->PlatformHandleRaw : viewport->PlatformHandle; + IM_ASSERT(handle != NULL); + + id device = [bd->SharedMetalContext.depthStencilState device]; + CAMetalLayer* layer = [CAMetalLayer layer]; + layer.device = device; + layer.framebufferOnly = YES; + layer.pixelFormat = MTLPixelFormatBGRA8Unorm; +#if TARGET_OS_OSX + NSWindow* window = (__bridge NSWindow*)handle; + NSView* view = window.contentView; + view.layer = layer; + view.wantsLayer = YES; +#endif + data->MetalLayer = layer; + data->CommandQueue = [device newCommandQueue]; + data->RenderPassDescriptor = [[MTLRenderPassDescriptor alloc] init]; + data->Handle = handle; +} + +static void ImGui_ImplMetal_DestroyWindow(ImGuiViewport* viewport) +{ + // The main viewport (owned by the application) will always have RendererUserData == NULL since we didn't create the data for it. + if (ImGuiViewportDataMetal* data = (ImGuiViewportDataMetal*)viewport->RendererUserData) + IM_DELETE(data); + viewport->RendererUserData = NULL; +} + +inline static CGSize MakeScaledSize(CGSize size, CGFloat scale) +{ + return CGSizeMake(size.width * scale, size.height * scale); +} + +static void ImGui_ImplMetal_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) +{ + ImGuiViewportDataMetal* data = (ImGuiViewportDataMetal*)viewport->RendererUserData; + data->MetalLayer.drawableSize = MakeScaledSize(CGSizeMake(size.x, size.y), viewport->DpiScale); +} + +static void ImGui_ImplMetal_RenderWindow(ImGuiViewport* viewport, void*) +{ + ImGuiViewportDataMetal* data = (ImGuiViewportDataMetal*)viewport->RendererUserData; + +#if TARGET_OS_OSX + void* handle = viewport->PlatformHandleRaw ? viewport->PlatformHandleRaw : viewport->PlatformHandle; + NSWindow* window = (__bridge NSWindow*)handle; + + // Always render the first frame, regardless of occlusionState, to avoid an initial flicker + if ((window.occlusionState & NSWindowOcclusionStateVisible) == 0 && !data->FirstFrame) + { + // Do not render windows which are completely occluded. Calling -[CAMetalLayer nextDrawable] will hang for + // approximately 1 second if the Metal layer is completely occluded. + return; + } + data->FirstFrame = false; + + viewport->DpiScale = static_cast(window.backingScaleFactor); + if (data->MetalLayer.contentsScale != viewport->DpiScale) + { + data->MetalLayer.contentsScale = viewport->DpiScale; + data->MetalLayer.drawableSize = MakeScaledSize(window.frame.size, viewport->DpiScale); + } + viewport->DrawData->FramebufferScale = ImVec2(viewport->DpiScale, viewport->DpiScale); +#endif + + id drawable = [data->MetalLayer nextDrawable]; + if (drawable == nil) + return; + + MTLRenderPassDescriptor* renderPassDescriptor = data->RenderPassDescriptor; + renderPassDescriptor.colorAttachments[0].texture = drawable.texture; + renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0, 0, 0, 0); + if ((viewport->Flags & ImGuiViewportFlags_NoRendererClear) == 0) + renderPassDescriptor.colorAttachments[0].loadAction = MTLLoadActionClear; + + id commandBuffer = [data->CommandQueue commandBuffer]; + id renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor]; + ImGui_ImplMetal_RenderDrawData(viewport->DrawData, commandBuffer, renderEncoder); + [renderEncoder endEncoding]; + + [commandBuffer presentDrawable:drawable]; + [commandBuffer commit]; +} + +static void ImGui_ImplMetal_InitPlatformInterface() +{ + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.Renderer_CreateWindow = ImGui_ImplMetal_CreateWindow; + platform_io.Renderer_DestroyWindow = ImGui_ImplMetal_DestroyWindow; + platform_io.Renderer_SetWindowSize = ImGui_ImplMetal_SetWindowSize; + platform_io.Renderer_RenderWindow = ImGui_ImplMetal_RenderWindow; +} + +static void ImGui_ImplMetal_ShutdownPlatformInterface() +{ + ImGui::DestroyPlatformWindows(); +} + +static void ImGui_ImplMetal_CreateDeviceObjectsForPlatformWindows() +{ + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + for (int i = 1; i < platform_io.Viewports.Size; i++) + if (!platform_io.Viewports[i]->RendererUserData) + ImGui_ImplMetal_CreateWindow(platform_io.Viewports[i]); +} + +static void ImGui_ImplMetal_InvalidateDeviceObjectsForPlatformWindows() +{ + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + for (int i = 1; i < platform_io.Viewports.Size; i++) + if (platform_io.Viewports[i]->RendererUserData) + ImGui_ImplMetal_DestroyWindow(platform_io.Viewports[i]); +} + #pragma mark - MetalBuffer implementation @implementation MetalBuffer diff --git a/backends/imgui_impl_osx.h b/backends/imgui_impl_osx.h index ddbf0eb7c6b3..18a7554f264e 100644 --- a/backends/imgui_impl_osx.h +++ b/backends/imgui_impl_osx.h @@ -8,8 +8,7 @@ // [X] Platform: OSX clipboard is supported within core Dear ImGui (no specific code in this backend). // [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. // [X] Platform: IME support. -// Issues: -// [ ] Platform: Multi-viewport / platform windows. +// [X] Platform: Multi-viewport / platform windows. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm index fdc88c0f1a2a..42f24ea1505b 100644 --- a/backends/imgui_impl_osx.mm +++ b/backends/imgui_impl_osx.mm @@ -8,8 +8,7 @@ // [X] Platform: OSX clipboard is supported within core Dear ImGui (no specific code in this backend). // [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. // [X] Platform: IME support. -// Issues: -// [ ] Platform: Multi-viewport / platform windows. +// [X] Platform: Multi-viewport / platform windows. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. @@ -25,6 +24,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2022-05-03: Misc: Implemented support for viewports / platform windows. // 2022-05-03: Inputs: Removed ImGui_ImplOSX_HandleEvent() from backend API in favor of backend automatically handling event capture. // 2022-04-27: Misc: Store backend data in a per-context struct, allowing to use this backend with multiple contexts. // 2022-03-22: Inputs: Monitor NSKeyUp events to catch missing keyUp for key when user press Cmd + key @@ -70,6 +70,7 @@ KeyEventResponder* KeyEventResponder; NSTextInputContext* InputContext; id Monitor; + NSWindow* Window; ImGui_ImplOSX_Data() { memset(this, 0, sizeof(*this)); } }; @@ -81,6 +82,9 @@ static inline CFTimeInterval GetMachAbsoluteTimeInSeconds() { return static_cast(static_cast(clock_gettime_nsec_np(CLOCK_UPTIME_RAW)) / 1e9); } // Forward Declarations +static void ImGui_ImplOSX_InitPlatformInterface(); +static void ImGui_ImplOSX_ShutdownPlatformInterface(); +static void ImGui_ImplOSX_UpdateMonitors(); static void ImGui_ImplOSX_AddTrackingArea(NSView* _Nonnull view); static bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view); @@ -224,6 +228,7 @@ @interface ImGuiObserver : NSObject - (void)onApplicationBecomeActive:(NSNotification*)aNotification; - (void)onApplicationBecomeInactive:(NSNotification*)aNotification; +- (void)displaysDidChange:(NSNotification*)aNotification; @end @@ -241,6 +246,11 @@ - (void)onApplicationBecomeInactive:(NSNotification*)aNotification io.AddFocusEvent(false); } +- (void)displaysDidChange:(NSNotification*)aNotification +{ + ImGui_ImplOSX_UpdateMonitors(); +} + @end // Functions @@ -376,11 +386,16 @@ bool ImGui_ImplOSX_Init(NSView* view) // Setup backend capabilities flags io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) //io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) - //io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional) + io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional) //io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can call io.AddMouseViewportEvent() with correct data (optional) io.BackendPlatformName = "imgui_impl_osx"; bd->Observer = [ImGuiObserver new]; + bd->Window = view.window ?: NSApp.orderedWindows.firstObject; + ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + main_viewport->PlatformHandle = main_viewport->PlatformHandleRaw = (__bridge_retained void*)bd->Window; + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + ImGui_ImplOSX_InitPlatformInterface(); // Load cursors. Some of them are undocumented. bd->MouseCursorHidden = false; @@ -460,13 +475,7 @@ bool ImGui_ImplOSX_Init(NSView* view) void ImGui_ImplOSX_Shutdown() { - ImGui_ImplOSX_Data* bd = ImGui_ImplOSX_GetBackendData(); - bd->Observer = NULL; - if (bd->Monitor != NULL) - { - [NSEvent removeMonitor:bd->Monitor]; - bd->Monitor = NULL; - } + ImGui_ImplOSX_ShutdownPlatformInterface(); ImGui_ImplOSX_DestroyBackendData(); } @@ -618,10 +627,22 @@ static bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view) if (event.type == NSEventTypeMouseMoved || event.type == NSEventTypeLeftMouseDragged || event.type == NSEventTypeRightMouseDragged || event.type == NSEventTypeOtherMouseDragged) { - NSPoint mousePoint = event.locationInWindow; - mousePoint = [view convertPoint:mousePoint fromView:nil]; - mousePoint = NSMakePoint(mousePoint.x, view.bounds.size.height - mousePoint.y); + NSPoint mousePoint; + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + mousePoint = NSEvent.mouseLocation; + mousePoint.y = CGDisplayPixelsHigh(kCGDirectMainDisplay) - mousePoint.y; // Normalize y coordinate to top-left of main display. + } + else + { + mousePoint = event.locationInWindow; + mousePoint = [view convertPoint:mousePoint fromView:nil]; // Convert to local coordinates of view + CGSize size = view.bounds.size; + mousePoint.y = size.height - mousePoint.y; + } + io.AddMousePosEvent((float)mousePoint.x, (float)mousePoint.y); + return io.WantCaptureMouse; } if (event.type == NSEventTypeScrollWheel) @@ -745,3 +766,307 @@ static void ImGui_ImplOSX_AddTrackingArea(NSView* _Nonnull view) return event; }]; } + +//-------------------------------------------------------------------------------------------------------- +// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT +// This is an _advanced_ and _optional_ feature, allowing the back-end to create and handle multiple viewports simultaneously. +// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first.. +//-------------------------------------------------------------------------------------------------------- + +struct ImGuiViewportDataOSX +{ + NSWindow* Window; + bool WindowOwned; + + ImGuiViewportDataOSX() { WindowOwned = false; } + ~ImGuiViewportDataOSX() { IM_ASSERT(Window == nil); } +}; + +@interface ImGui_ImplOSX_Window: NSWindow +@end + +@implementation ImGui_ImplOSX_Window + +- (BOOL)canBecomeKeyWindow +{ + return YES; +} + +@end + +static void ConvertNSRect(NSScreen* screen, NSRect* r) +{ + r->origin.y = CGDisplayPixelsHigh(kCGDirectMainDisplay) - r->origin.y - r->size.height; +} + +static void ImGui_ImplOSX_CreateWindow(ImGuiViewport* viewport) +{ + ImGui_ImplOSX_Data* bd = ImGui_ImplOSX_GetBackendData(); + ImGuiViewportDataOSX* data = IM_NEW(ImGuiViewportDataOSX)(); + viewport->PlatformUserData = data; + + NSScreen* screen = bd->Window.screen; + NSRect rect = NSMakeRect(viewport->Pos.x, viewport->Pos.y, viewport->Size.x, viewport->Size.y); + ConvertNSRect(screen, &rect); + + NSWindowStyleMask styleMask = 0; + if (viewport->Flags & ImGuiViewportFlags_NoDecoration) + styleMask |= NSWindowStyleMaskBorderless; + else + styleMask |= NSWindowStyleMaskTitled | NSWindowStyleMaskResizable | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable; + + NSWindow* window = [[ImGui_ImplOSX_Window alloc] initWithContentRect:rect + styleMask:styleMask + backing:NSBackingStoreBuffered + defer:YES + screen:screen]; + if (viewport->Flags & ImGuiViewportFlags_TopMost) + [window setLevel:NSFloatingWindowLevel]; + + window.title = @"Untitled"; + window.opaque = YES; + if (viewport->Flags & ImGuiViewportFlags_NoFocusOnAppearing) + [window orderFront:nil]; + else + [window makeKeyAndOrderFront:nil]; + + [window setIsVisible:YES]; + + KeyEventResponder* view = [[KeyEventResponder alloc] initWithFrame:rect]; + if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6) + [view setWantsBestResolutionOpenGLSurface:YES]; + + window.contentView = view; + + data->Window = window; + data->WindowOwned = true; + viewport->PlatformRequestResize = false; + viewport->PlatformHandle = viewport->PlatformHandleRaw = (__bridge_retained void*)window; +} + +static void ImGui_ImplOSX_DestroyWindow(ImGuiViewport* viewport) +{ + NSWindow* window = (__bridge_transfer NSWindow*)viewport->PlatformHandleRaw; + window = nil; + + if (ImGuiViewportDataOSX* data = (ImGuiViewportDataOSX*)viewport->PlatformUserData) + { + NSWindow* window = data->Window; + if (window != nil && data->WindowOwned) + { + window.contentView = nil; + window.contentViewController = nil; + [window orderOut:nil]; + } + data->Window = nil; + IM_DELETE(data); + } + viewport->PlatformUserData = viewport->PlatformHandle = viewport->PlatformHandleRaw = NULL; +} + +static void ImGui_ImplOSX_ShowWindow(ImGuiViewport* viewport) +{ + ImGuiViewportDataOSX* data = (ImGuiViewportDataOSX*)viewport->PlatformUserData; + IM_ASSERT(data->Window != nil); +} + +static void ImGui_ImplOSX_UpdateWindow(ImGuiViewport* viewport) +{ + ImGuiViewportDataOSX* data = (ImGuiViewportDataOSX*)viewport->PlatformUserData; + IM_ASSERT(data->Window != 0); +} + +static ImVec2 ImGui_ImplOSX_GetWindowPos(ImGuiViewport* viewport) +{ + ImGuiViewportDataOSX* data = (ImGuiViewportDataOSX*)viewport->PlatformUserData; + IM_ASSERT(data->Window != 0); + + NSWindow* window = data->Window; + NSScreen* screen = window.screen; + NSSize size = screen.frame.size; + NSRect frame = window.frame; + NSRect rect = window.contentLayoutRect; + return ImVec2(frame.origin.x, size.height - frame.origin.y - rect.size.height); +} + +static void ImGui_ImplOSX_SetWindowPos(ImGuiViewport* viewport, ImVec2 pos) +{ + ImGuiViewportDataOSX* data = (ImGuiViewportDataOSX*)viewport->PlatformUserData; + IM_ASSERT(data->Window != 0); + + NSWindow* window = data->Window; + NSSize size = window.frame.size; + + NSRect r = NSMakeRect(pos.x, pos.y, size.width, size.height); + ConvertNSRect(window.screen, &r); + [window setFrameOrigin:r.origin]; +} + +static ImVec2 ImGui_ImplOSX_GetWindowSize(ImGuiViewport* viewport) +{ + ImGuiViewportDataOSX* data = (ImGuiViewportDataOSX*)viewport->PlatformUserData; + IM_ASSERT(data->Window != 0); + + NSWindow* window = data->Window; + NSSize size = window.contentLayoutRect.size; + return ImVec2(size.width, size.width); +} + +static void ImGui_ImplOSX_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) +{ + ImGuiViewportDataOSX* data = (ImGuiViewportDataOSX*)viewport->PlatformUserData; + IM_ASSERT(data->Window != 0); + + NSWindow* window = data->Window; + NSRect rect = window.frame; + rect.origin.y -= (size.y - rect.size.height); + rect.size.width = size.x; + rect.size.height = size.y; + [window setFrame:rect display:YES]; +} + +static void ImGui_ImplOSX_SetWindowFocus(ImGuiViewport* viewport) +{ + ImGui_ImplOSX_Data* bd = ImGui_ImplOSX_GetBackendData(); + ImGuiViewportDataOSX* data = (ImGuiViewportDataOSX*)viewport->PlatformUserData; + IM_ASSERT(data->Window != 0); + [data->Window makeKeyAndOrderFront:bd->Window]; +} + +static bool ImGui_ImplOSX_GetWindowFocus(ImGuiViewport* viewport) +{ + ImGuiViewportDataOSX* data = (ImGuiViewportDataOSX*)viewport->PlatformUserData; + IM_ASSERT(data->Window != 0); + + return data->Window.isKeyWindow; +} + +static bool ImGui_ImplOSX_GetWindowMinimized(ImGuiViewport* viewport) +{ + ImGuiViewportDataOSX* data = (ImGuiViewportDataOSX*)viewport->PlatformUserData; + IM_ASSERT(data->Window != 0); + + return data->Window.isMiniaturized; +} + +static void ImGui_ImplOSX_SetWindowTitle(ImGuiViewport* viewport, const char* title) +{ + ImGuiViewportDataOSX* data = (ImGuiViewportDataOSX*)viewport->PlatformUserData; + IM_ASSERT(data->Window != 0); + + data->Window.title = [NSString stringWithUTF8String:title]; +} + +static void ImGui_ImplOSX_SetWindowAlpha(ImGuiViewport* viewport, float alpha) +{ + ImGuiViewportDataOSX* data = (ImGuiViewportDataOSX*)viewport->PlatformUserData; + IM_ASSERT(data->Window != 0); + IM_ASSERT(alpha >= 0.0f && alpha <= 1.0f); + + data->Window.alphaValue = alpha; +} + +static float ImGui_ImplOSX_GetWindowDpiScale(ImGuiViewport* viewport) +{ + ImGuiViewportDataOSX* data = (ImGuiViewportDataOSX*)viewport->PlatformUserData; + IM_ASSERT(data->Window != 0); + + return data->Window.backingScaleFactor; +} + +// FIXME-DPI: Testing DPI related ideas +static void ImGui_ImplOSX_OnChangedViewport(ImGuiViewport* viewport) +{ + (void)viewport; +#if 0 + ImGuiStyle default_style; + //default_style.WindowPadding = ImVec2(0, 0); + //default_style.WindowBorderSize = 0.0f; + //default_style.ItemSpacing.y = 3.0f; + //default_style.FramePadding = ImVec2(0, 0); + default_style.ScaleAllSizes(viewport->DpiScale); + ImGuiStyle& style = ImGui::GetStyle(); + style = default_style; +#endif +} + +static void ImGui_ImplOSX_UpdateMonitors() +{ + ImGui::GetPlatformIO().Monitors.resize(static_cast(NSScreen.screens.count)); + + int i = 0; + for (NSScreen* screen in NSScreen.screens) + { + NSRect frame = screen.frame; + NSRect visibleFrame = screen.visibleFrame; + + ImGuiPlatformMonitor imgui_monitor; + imgui_monitor.MainPos = ImVec2(frame.origin.x, frame.origin.y); + imgui_monitor.MainSize = ImVec2(frame.size.width, frame.size.height); + imgui_monitor.WorkPos = ImVec2(visibleFrame.origin.x, visibleFrame.origin.y); + imgui_monitor.WorkSize = ImVec2(visibleFrame.size.width, visibleFrame.size.height); + imgui_monitor.DpiScale = screen.backingScaleFactor; + + ImGuiPlatformIO& io = ImGui::GetPlatformIO(); + io.Monitors[i] = imgui_monitor; + i += 1; + } +} + +static void ImGui_ImplOSX_InitPlatformInterface() +{ + ImGui_ImplOSX_Data* bd = ImGui_ImplOSX_GetBackendData(); + ImGui_ImplOSX_UpdateMonitors(); + + // Register platform interface (will be coupled with a renderer interface) + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.Platform_CreateWindow = ImGui_ImplOSX_CreateWindow; + platform_io.Platform_DestroyWindow = ImGui_ImplOSX_DestroyWindow; + platform_io.Platform_ShowWindow = ImGui_ImplOSX_ShowWindow; + platform_io.Platform_SetWindowPos = ImGui_ImplOSX_SetWindowPos; + platform_io.Platform_GetWindowPos = ImGui_ImplOSX_GetWindowPos; + platform_io.Platform_SetWindowSize = ImGui_ImplOSX_SetWindowSize; + platform_io.Platform_GetWindowSize = ImGui_ImplOSX_GetWindowSize; + platform_io.Platform_SetWindowFocus = ImGui_ImplOSX_SetWindowFocus; + platform_io.Platform_GetWindowFocus = ImGui_ImplOSX_GetWindowFocus; + platform_io.Platform_GetWindowMinimized = ImGui_ImplOSX_GetWindowMinimized; + platform_io.Platform_SetWindowTitle = ImGui_ImplOSX_SetWindowTitle; + platform_io.Platform_SetWindowAlpha = ImGui_ImplOSX_SetWindowAlpha; + platform_io.Platform_UpdateWindow = ImGui_ImplOSX_UpdateWindow; + platform_io.Platform_GetWindowDpiScale = ImGui_ImplOSX_GetWindowDpiScale; // FIXME-DPI + platform_io.Platform_OnChangedViewport = ImGui_ImplOSX_OnChangedViewport; // FIXME-DPI + + // Register main window handle (which is owned by the main application, not by us) + ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + ImGuiViewportDataOSX* data = IM_NEW(ImGuiViewportDataOSX)(); + data->Window = bd->Window; + data->WindowOwned = false; + main_viewport->PlatformUserData = data; + main_viewport->PlatformHandle = (__bridge void*)bd->Window; + + [NSNotificationCenter.defaultCenter addObserver:bd->Observer + selector:@selector(displaysDidChange:) + name:NSApplicationDidChangeScreenParametersNotification + object:nil]; +} + +static void ImGui_ImplOSX_ShutdownPlatformInterface() +{ + ImGui_ImplOSX_Data* bd = ImGui_ImplOSX_GetBackendData(); + [NSNotificationCenter.defaultCenter removeObserver:bd->Observer + name:NSApplicationDidChangeScreenParametersNotification + object:nil]; + bd->Observer = NULL; + bd->Window = NULL; + if (bd->Monitor != NULL) + { + [NSEvent removeMonitor:bd->Monitor]; + bd->Monitor = NULL; + } + + ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + ImGuiViewportDataOSX* data = (ImGuiViewportDataOSX*)main_viewport->PlatformUserData; + IM_DELETE(data); + main_viewport->PlatformUserData = NULL; + ImGui::DestroyPlatformWindows(); +} diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 5580504d65d6..ec047071deeb 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -175,6 +175,7 @@ Other Changes: - Backends: OSX: Monitor NSKeyUp events to catch missing keyUp for key when user press Cmd + key (#5128) [@thedmd] - Backends: OSX, Metal: Store backend data in a per-context struct, allowing to use these backends with multiple contexts. (#5203, #5221, #4141) [@noisewuwei] +- Backends: OSX, Metal: Implemented support for viewports / platform windows. [@stuartcarnie] - Examples: Emscripten+WebGPU: Fix building for latest WebGPU specs. (#3632) - Examples: OSX+Metal, OSX+OpenGL: Removed now-unnecessary calls to ImGui_ImplOSX_HandleEvent(). (#4821) diff --git a/examples/example_apple_metal/example_apple_metal.xcodeproj/project.pbxproj b/examples/example_apple_metal/example_apple_metal.xcodeproj/project.pbxproj index 4bb4fc288791..3ebf9ccf9ecc 100644 --- a/examples/example_apple_metal/example_apple_metal.xcodeproj/project.pbxproj +++ b/examples/example_apple_metal/example_apple_metal.xcodeproj/project.pbxproj @@ -7,7 +7,10 @@ objects = { /* Begin PBXBuildFile section */ + 050450AB2768052600AB6805 /* imgui_tables.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5079822D257677DB0038A28D /* imgui_tables.cpp */; }; + 050450AD276863B000AB6805 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 050450AC276863B000AB6805 /* QuartzCore.framework */; }; 05318E0F274C397200A8DE2E /* GameController.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05318E0E274C397200A8DE2E /* GameController.framework */; }; + 05A275442773BEA20084EF39 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05A275432773BEA20084EF39 /* QuartzCore.framework */; }; 07A82ED82139413D0078D120 /* imgui_widgets.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 07A82ED72139413C0078D120 /* imgui_widgets.cpp */; }; 07A82ED92139418F0078D120 /* imgui_widgets.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 07A82ED72139413C0078D120 /* imgui_widgets.cpp */; }; 5079822E257677DB0038A28D /* imgui_tables.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5079822D257677DB0038A28D /* imgui_tables.cpp */; }; @@ -33,7 +36,11 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 050450AC276863B000AB6805 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; 05318E0E274C397200A8DE2E /* GameController.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GameController.framework; path = System/Library/Frameworks/GameController.framework; sourceTree = SDKROOT; }; + 05A2754027728F5B0084EF39 /* imgui_impl_metal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = imgui_impl_metal.h; path = ../../backends/imgui_impl_metal.h; sourceTree = ""; }; + 05A2754127728F5B0084EF39 /* imgui_impl_osx.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = imgui_impl_osx.h; path = ../../backends/imgui_impl_osx.h; sourceTree = ""; }; + 05A275432773BEA20084EF39 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS15.2.sdk/System/Library/Frameworks/QuartzCore.framework; sourceTree = DEVELOPER_DIR; }; 07A82ED62139413C0078D120 /* imgui_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = imgui_internal.h; path = ../../imgui_internal.h; sourceTree = ""; }; 07A82ED72139413C0078D120 /* imgui_widgets.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = imgui_widgets.cpp; path = ../../imgui_widgets.cpp; sourceTree = ""; }; 5079822D257677DB0038A28D /* imgui_tables.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = imgui_tables.cpp; path = ../../imgui_tables.cpp; sourceTree = ""; }; @@ -66,6 +73,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 05A275442773BEA20084EF39 /* QuartzCore.framework in Frameworks */, 8309BD8F253CCAAA0045E2A1 /* UIKit.framework in Frameworks */, 83BBE9E720EB46BD00295997 /* MetalKit.framework in Frameworks */, 83BBE9E520EB46B900295997 /* Metal.framework in Frameworks */, @@ -76,6 +84,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 050450AD276863B000AB6805 /* QuartzCore.framework in Frameworks */, 8309BDC6253CCCFE0045E2A1 /* AppKit.framework in Frameworks */, 83BBE9EC20EB471700295997 /* MetalKit.framework in Frameworks */, 05318E0F274C397200A8DE2E /* GameController.framework in Frameworks */, @@ -136,6 +145,8 @@ 83BBE9E320EB46B800295997 /* Frameworks */ = { isa = PBXGroup; children = ( + 050450AC276863B000AB6805 /* QuartzCore.framework */, + 05A275432773BEA20084EF39 /* QuartzCore.framework */, 05318E0E274C397200A8DE2E /* GameController.framework */, 8309BDC5253CCCFE0045E2A1 /* AppKit.framework */, 8309BD8E253CCAAA0045E2A1 /* UIKit.framework */, @@ -153,7 +164,9 @@ isa = PBXGroup; children = ( 5079822D257677DB0038A28D /* imgui_tables.cpp */, + 05A2754027728F5B0084EF39 /* imgui_impl_metal.h */, 8309BDB5253CCC9D0045E2A1 /* imgui_impl_metal.mm */, + 05A2754127728F5B0084EF39 /* imgui_impl_osx.h */, 8309BDB6253CCC9D0045E2A1 /* imgui_impl_osx.mm */, 83BBEA0420EB54E700295997 /* imconfig.h */, 83BBEA0320EB54E700295997 /* imgui.cpp */, @@ -268,9 +281,9 @@ 8309BDBB253CCCAD0045E2A1 /* imgui_impl_metal.mm in Sources */, 83BBEA0920EB54E700295997 /* imgui.cpp in Sources */, 83BBEA0720EB54E700295997 /* imgui_demo.cpp in Sources */, - 83BBEA0520EB54E700295997 /* imgui_draw.cpp in Sources */, + 83BBEA0520EB54E700295997 /* imgui_draw.cpp in Sources */, 5079822E257677DB0038A28D /* imgui_tables.cpp in Sources */, - 07A82ED82139413D0078D120 /* imgui_widgets.cpp in Sources */, + 07A82ED82139413D0078D120 /* imgui_widgets.cpp in Sources */, 8309BDA5253CCC070045E2A1 /* main.mm in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -281,10 +294,10 @@ files = ( 8309BDBE253CCCB60045E2A1 /* imgui_impl_metal.mm in Sources */, 8309BDBF253CCCB60045E2A1 /* imgui_impl_osx.mm in Sources */, - 83BBEA0A20EB54E700295997 /* imgui.cpp in Sources */, - 83BBEA0820EB54E700295997 /* imgui_demo.cpp in Sources */, - 83BBEA0620EB54E700295997 /* imgui_draw.cpp in Sources */, - 5079822E257677DB0038A28D /* imgui_tables.cpp in Sources */, + 83BBEA0A20EB54E700295997 /* imgui.cpp in Sources */, + 83BBEA0820EB54E700295997 /* imgui_demo.cpp in Sources */, + 83BBEA0620EB54E700295997 /* imgui_draw.cpp in Sources */, + 050450AB2768052600AB6805 /* imgui_tables.cpp in Sources */, 07A82ED92139418F0078D120 /* imgui_widgets.cpp in Sources */, 8309BDA8253CCC080045E2A1 /* main.mm in Sources */, ); diff --git a/examples/example_apple_metal/main.mm b/examples/example_apple_metal/main.mm index d29f8d9677c8..abe0406e6594 100644 --- a/examples/example_apple_metal/main.mm +++ b/examples/example_apple_metal/main.mm @@ -56,11 +56,21 @@ -(instancetype)initWithNibName:(nullable NSString *)nibNameOrNil bundle:(nullabl ImGuiIO& io = ImGui::GetIO(); (void)io; //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking + io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows // Setup Dear ImGui style ImGui::StyleColorsDark(); //ImGui::StyleColorsClassic(); + // When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones. + ImGuiStyle& style = ImGui::GetStyle(); + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + style.WindowRounding = 0.0f; + style.Colors[ImGuiCol_WindowBg].w = 1.0f; + } + // Setup Renderer backend ImGui_ImplMetal_Init(_device); @@ -190,6 +200,13 @@ -(void)drawInMTKView:(MTKView*)view // Present [commandBuffer presentDrawable:view.currentDrawable]; [commandBuffer commit]; + + // Update and Render additional Platform Windows + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); + } } -(void)mtkView:(MTKView*)view drawableSizeWillChange:(CGSize)size From d666a1d4737739274449dbe0e8558454bba82ec4 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 3 May 2022 17:53:47 +0200 Subject: [PATCH 797/828] Backends: OSX, Metal: Amend d111133, tidying up, remove unused, misc tweaks. . (#4821, #2778) --- backends/imgui_impl_dx10.cpp | 4 +-- backends/imgui_impl_dx10.h | 4 +-- backends/imgui_impl_dx11.cpp | 4 +-- backends/imgui_impl_dx11.h | 4 +-- backends/imgui_impl_dx12.cpp | 4 +-- backends/imgui_impl_dx12.h | 4 +-- backends/imgui_impl_dx9.cpp | 4 +-- backends/imgui_impl_dx9.h | 4 +-- backends/imgui_impl_metal.h | 5 ++-- backends/imgui_impl_metal.mm | 6 ++-- backends/imgui_impl_opengl2.cpp | 2 +- backends/imgui_impl_opengl2.h | 2 +- backends/imgui_impl_opengl3.cpp | 4 +-- backends/imgui_impl_opengl3.h | 4 +-- backends/imgui_impl_osx.mm | 41 +++----------------------- backends/imgui_impl_sdlrenderer.cpp | 2 +- backends/imgui_impl_sdlrenderer.h | 2 +- backends/imgui_impl_vulkan.cpp | 4 +-- backends/imgui_impl_vulkan.h | 4 +-- backends/imgui_impl_wgpu.cpp | 2 +- backends/imgui_impl_wgpu.h | 2 +- docs/CHANGELOG.txt | 5 ++-- examples/example_apple_opengl2/main.mm | 16 ++++++++++ 23 files changed, 58 insertions(+), 75 deletions(-) diff --git a/backends/imgui_impl_dx10.cpp b/backends/imgui_impl_dx10.cpp index 9f13f97f4372..bd8e32a62dee 100644 --- a/backends/imgui_impl_dx10.cpp +++ b/backends/imgui_impl_dx10.cpp @@ -3,8 +3,8 @@ // Implemented features: // [X] Renderer: User texture binding. Use 'ID3D10ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID! -// [X] Renderer: Multi-viewport support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. -// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. +// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. +// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. diff --git a/backends/imgui_impl_dx10.h b/backends/imgui_impl_dx10.h index 84ec353ff1ea..94957d43894b 100644 --- a/backends/imgui_impl_dx10.h +++ b/backends/imgui_impl_dx10.h @@ -3,8 +3,8 @@ // Implemented features: // [X] Renderer: User texture binding. Use 'ID3D10ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID! -// [X] Renderer: Multi-viewport support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. -// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. +// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. +// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. diff --git a/backends/imgui_impl_dx11.cpp b/backends/imgui_impl_dx11.cpp index 6c2623ce44e4..78b8147e2e01 100644 --- a/backends/imgui_impl_dx11.cpp +++ b/backends/imgui_impl_dx11.cpp @@ -3,8 +3,8 @@ // Implemented features: // [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID! -// [X] Renderer: Multi-viewport support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. -// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. +// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. +// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. diff --git a/backends/imgui_impl_dx11.h b/backends/imgui_impl_dx11.h index 07495a0150d1..8e2aa685c5d3 100644 --- a/backends/imgui_impl_dx11.h +++ b/backends/imgui_impl_dx11.h @@ -3,8 +3,8 @@ // Implemented features: // [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID! -// [X] Renderer: Multi-viewport support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. -// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. +// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. +// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. diff --git a/backends/imgui_impl_dx12.cpp b/backends/imgui_impl_dx12.cpp index 700af250cec7..c74455221d7e 100644 --- a/backends/imgui_impl_dx12.cpp +++ b/backends/imgui_impl_dx12.cpp @@ -3,9 +3,9 @@ // Implemented features: // [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as ImTextureID. Read the FAQ about ImTextureID! -// [X] Renderer: Multi-viewport support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. +// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. // FIXME: The transition from removing a viewport and moving the window in an existing hosted viewport tends to flicker. -// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. +// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. // Important: to compile on 32-bit systems, this backend requires code to be compiled with '#define ImTextureID ImU64'. // This is because we need ImTextureID to carry a 64-bit value and by default ImTextureID is defined as void*. diff --git a/backends/imgui_impl_dx12.h b/backends/imgui_impl_dx12.h index e453218c27fe..63a3c1a8b5de 100644 --- a/backends/imgui_impl_dx12.h +++ b/backends/imgui_impl_dx12.h @@ -3,8 +3,8 @@ // Implemented features: // [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as ImTextureID. Read the FAQ about ImTextureID! -// [X] Renderer: Multi-viewport support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. -// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. +// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. +// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. // Important: to compile on 32-bit systems, this backend requires code to be compiled with '#define ImTextureID ImU64'. // See imgui_impl_dx12.cpp file for details. diff --git a/backends/imgui_impl_dx9.cpp b/backends/imgui_impl_dx9.cpp index cc27c286abb3..e400626036cc 100644 --- a/backends/imgui_impl_dx9.cpp +++ b/backends/imgui_impl_dx9.cpp @@ -3,8 +3,8 @@ // Implemented features: // [X] Renderer: User texture binding. Use 'LPDIRECT3DTEXTURE9' as ImTextureID. Read the FAQ about ImTextureID! -// [X] Renderer: Multi-viewport support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. -// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. +// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. +// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. diff --git a/backends/imgui_impl_dx9.h b/backends/imgui_impl_dx9.h index 773acda7e0b0..2d75662de0e8 100644 --- a/backends/imgui_impl_dx9.h +++ b/backends/imgui_impl_dx9.h @@ -3,8 +3,8 @@ // Implemented features: // [X] Renderer: User texture binding. Use 'LPDIRECT3DTEXTURE9' as ImTextureID. Read the FAQ about ImTextureID! -// [X] Renderer: Multi-viewport support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. -// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. +// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. +// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. diff --git a/backends/imgui_impl_metal.h b/backends/imgui_impl_metal.h index 180fd777e652..d2fd2c239b7d 100644 --- a/backends/imgui_impl_metal.h +++ b/backends/imgui_impl_metal.h @@ -3,9 +3,8 @@ // Implemented features: // [X] Renderer: User texture binding. Use 'MTLTexture' as ImTextureID. Read the FAQ about ImTextureID! -// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. -// Missing features: -// [ ] Renderer: Multi-viewport / platform windows. +// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. +// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. diff --git a/backends/imgui_impl_metal.mm b/backends/imgui_impl_metal.mm index aba0cc77e59b..eedee90812a1 100644 --- a/backends/imgui_impl_metal.mm +++ b/backends/imgui_impl_metal.mm @@ -3,8 +3,8 @@ // Implemented features: // [X] Renderer: User texture binding. Use 'MTLTexture' as ImTextureID. Read the FAQ about ImTextureID! -// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. -// [X] Renderer: Multi-viewport / platform windows. +// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. +// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. @@ -13,7 +13,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2022-05-03: Misc: Implemented support for viewports / platform windows. +// 2022-XX-XX: Metal: Added support for multiple windows via the ImGuiPlatformIO interface. // 2022-04-27: Misc: Store backend data in a per-context struct, allowing to use this backend with multiple contexts. // 2022-01-03: Metal: Ignore ImDrawCmd where ElemCount == 0 (very rare but can technically be manufactured by user code). // 2021-12-30: Metal: Added Metal C++ support. Enable with '#define IMGUI_IMPL_METAL_CPP' in your imconfig.h file. diff --git a/backends/imgui_impl_opengl2.cpp b/backends/imgui_impl_opengl2.cpp index 6f906d21a9fb..ccd00554e53d 100644 --- a/backends/imgui_impl_opengl2.cpp +++ b/backends/imgui_impl_opengl2.cpp @@ -3,7 +3,7 @@ // Implemented features: // [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID! -// [X] Renderer: Multi-viewport support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. +// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. diff --git a/backends/imgui_impl_opengl2.h b/backends/imgui_impl_opengl2.h index 780204a22405..91b3b1daabb5 100644 --- a/backends/imgui_impl_opengl2.h +++ b/backends/imgui_impl_opengl2.h @@ -3,7 +3,7 @@ // Implemented features: // [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID! -// [X] Renderer: Multi-viewport support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. +// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. diff --git a/backends/imgui_impl_opengl3.cpp b/backends/imgui_impl_opengl3.cpp index 925f4ba4965b..091e59625cfe 100644 --- a/backends/imgui_impl_opengl3.cpp +++ b/backends/imgui_impl_opengl3.cpp @@ -5,8 +5,8 @@ // Implemented features: // [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID! -// [X] Renderer: Multi-viewport support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. -// [x] Renderer: Desktop GL only: Support for large meshes (64k+ vertices) with 16-bit indices. +// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. +// [x] Renderer: Large meshes support (64k+ vertices) with 16-bit indices (Desktop OpenGL only). // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. diff --git a/backends/imgui_impl_opengl3.h b/backends/imgui_impl_opengl3.h index e7f652fb6cc8..81722bdf0b91 100644 --- a/backends/imgui_impl_opengl3.h +++ b/backends/imgui_impl_opengl3.h @@ -5,8 +5,8 @@ // Implemented features: // [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID! -// [X] Renderer: Multi-viewport support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. -// [x] Renderer: Desktop GL only: Support for large meshes (64k+ vertices) with 16-bit indices. +// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. +// [x] Renderer: Large meshes support (64k+ vertices) with 16-bit indices (Desktop OpenGL only). // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm index 42f24ea1505b..a4d33151602b 100644 --- a/backends/imgui_impl_osx.mm +++ b/backends/imgui_impl_osx.mm @@ -24,7 +24,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2022-05-03: Misc: Implemented support for viewports / platform windows. +// 2022-XX-XX: Added support for multiple windows via the ImGuiPlatformIO interface. // 2022-05-03: Inputs: Removed ImGui_ImplOSX_HandleEvent() from backend API in favor of backend automatically handling event capture. // 2022-04-27: Misc: Store backend data in a per-context struct, allowing to use this backend with multiple contexts. // 2022-03-22: Inputs: Monitor NSKeyUp events to catch missing keyUp for key when user press Cmd + key @@ -864,18 +864,6 @@ static void ImGui_ImplOSX_DestroyWindow(ImGuiViewport* viewport) viewport->PlatformUserData = viewport->PlatformHandle = viewport->PlatformHandleRaw = NULL; } -static void ImGui_ImplOSX_ShowWindow(ImGuiViewport* viewport) -{ - ImGuiViewportDataOSX* data = (ImGuiViewportDataOSX*)viewport->PlatformUserData; - IM_ASSERT(data->Window != nil); -} - -static void ImGui_ImplOSX_UpdateWindow(ImGuiViewport* viewport) -{ - ImGuiViewportDataOSX* data = (ImGuiViewportDataOSX*)viewport->PlatformUserData; - IM_ASSERT(data->Window != 0); -} - static ImVec2 ImGui_ImplOSX_GetWindowPos(ImGuiViewport* viewport) { ImGuiViewportDataOSX* data = (ImGuiViewportDataOSX*)viewport->PlatformUserData; @@ -974,27 +962,11 @@ static float ImGui_ImplOSX_GetWindowDpiScale(ImGuiViewport* viewport) return data->Window.backingScaleFactor; } -// FIXME-DPI: Testing DPI related ideas -static void ImGui_ImplOSX_OnChangedViewport(ImGuiViewport* viewport) -{ - (void)viewport; -#if 0 - ImGuiStyle default_style; - //default_style.WindowPadding = ImVec2(0, 0); - //default_style.WindowBorderSize = 0.0f; - //default_style.ItemSpacing.y = 3.0f; - //default_style.FramePadding = ImVec2(0, 0); - default_style.ScaleAllSizes(viewport->DpiScale); - ImGuiStyle& style = ImGui::GetStyle(); - style = default_style; -#endif -} - static void ImGui_ImplOSX_UpdateMonitors() { - ImGui::GetPlatformIO().Monitors.resize(static_cast(NSScreen.screens.count)); + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.Monitors.resize(0); - int i = 0; for (NSScreen* screen in NSScreen.screens) { NSRect frame = screen.frame; @@ -1007,9 +979,7 @@ static void ImGui_ImplOSX_UpdateMonitors() imgui_monitor.WorkSize = ImVec2(visibleFrame.size.width, visibleFrame.size.height); imgui_monitor.DpiScale = screen.backingScaleFactor; - ImGuiPlatformIO& io = ImGui::GetPlatformIO(); - io.Monitors[i] = imgui_monitor; - i += 1; + platform_io.Monitors.push_back(imgui_monitor); } } @@ -1022,7 +992,6 @@ static void ImGui_ImplOSX_InitPlatformInterface() ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); platform_io.Platform_CreateWindow = ImGui_ImplOSX_CreateWindow; platform_io.Platform_DestroyWindow = ImGui_ImplOSX_DestroyWindow; - platform_io.Platform_ShowWindow = ImGui_ImplOSX_ShowWindow; platform_io.Platform_SetWindowPos = ImGui_ImplOSX_SetWindowPos; platform_io.Platform_GetWindowPos = ImGui_ImplOSX_GetWindowPos; platform_io.Platform_SetWindowSize = ImGui_ImplOSX_SetWindowSize; @@ -1032,9 +1001,7 @@ static void ImGui_ImplOSX_InitPlatformInterface() platform_io.Platform_GetWindowMinimized = ImGui_ImplOSX_GetWindowMinimized; platform_io.Platform_SetWindowTitle = ImGui_ImplOSX_SetWindowTitle; platform_io.Platform_SetWindowAlpha = ImGui_ImplOSX_SetWindowAlpha; - platform_io.Platform_UpdateWindow = ImGui_ImplOSX_UpdateWindow; platform_io.Platform_GetWindowDpiScale = ImGui_ImplOSX_GetWindowDpiScale; // FIXME-DPI - platform_io.Platform_OnChangedViewport = ImGui_ImplOSX_OnChangedViewport; // FIXME-DPI // Register main window handle (which is owned by the main application, not by us) ImGuiViewport* main_viewport = ImGui::GetMainViewport(); diff --git a/backends/imgui_impl_sdlrenderer.cpp b/backends/imgui_impl_sdlrenderer.cpp index d3dd15fcd858..6b128737201d 100644 --- a/backends/imgui_impl_sdlrenderer.cpp +++ b/backends/imgui_impl_sdlrenderer.cpp @@ -10,7 +10,7 @@ // Implemented features: // [X] Renderer: User texture binding. Use 'SDL_Texture*' as ImTextureID. Read the FAQ about ImTextureID! -// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. +// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. // Missing features: // [ ] Renderer: Multi-viewport support (multiple windows). diff --git a/backends/imgui_impl_sdlrenderer.h b/backends/imgui_impl_sdlrenderer.h index 37adce66a6d7..78d9bcbe1d8a 100644 --- a/backends/imgui_impl_sdlrenderer.h +++ b/backends/imgui_impl_sdlrenderer.h @@ -10,7 +10,7 @@ // Implemented features: // [X] Renderer: User texture binding. Use 'SDL_Texture*' as ImTextureID. Read the FAQ about ImTextureID! -// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. +// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. // Missing features: // [ ] Renderer: Multi-viewport support (multiple windows). diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp index 8318ff9f7c55..1fc210e12c85 100644 --- a/backends/imgui_impl_vulkan.cpp +++ b/backends/imgui_impl_vulkan.cpp @@ -2,8 +2,8 @@ // This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..) // Implemented features: -// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. -// [x] Platform: Multi-viewport / platform windows. With issues (flickering when creating a new viewport). +// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. +// [x] Renderer: Multi-viewport / platform windows. With issues (flickering when creating a new viewport). // [!] Renderer: User texture binding. Use 'VkDescriptorSet' as ImTextureID. Read the FAQ about ImTextureID! See https://github.com/ocornut/imgui/pull/914 for discussions. // Important: on 32-bit systems, user texture binding is only supported if your imconfig file has '#define ImTextureID ImU64'. diff --git a/backends/imgui_impl_vulkan.h b/backends/imgui_impl_vulkan.h index 7a8e535c8b0d..d2cb0ab4f2d9 100644 --- a/backends/imgui_impl_vulkan.h +++ b/backends/imgui_impl_vulkan.h @@ -2,8 +2,8 @@ // This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..) // Implemented features: -// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. -// [x] Platform: Multi-viewport / platform windows. With issues (flickering when creating a new viewport). +// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. +// [x] Renderer: Multi-viewport / platform windows. With issues (flickering when creating a new viewport). // [!] Renderer: User texture binding. Use 'VkDescriptorSet' as ImTextureID. Read the FAQ about ImTextureID! See https://github.com/ocornut/imgui/pull/914 for discussions. // Important: on 32-bit systems, user texture binding is only supported if your imconfig file has '#define ImTextureID ImU64'. diff --git a/backends/imgui_impl_wgpu.cpp b/backends/imgui_impl_wgpu.cpp index 9c512caddd17..b9f363b9027c 100644 --- a/backends/imgui_impl_wgpu.cpp +++ b/backends/imgui_impl_wgpu.cpp @@ -4,7 +4,7 @@ // Implemented features: // [X] Renderer: User texture binding. Use 'WGPUTextureView' as ImTextureID. Read the FAQ about ImTextureID! -// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. +// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. diff --git a/backends/imgui_impl_wgpu.h b/backends/imgui_impl_wgpu.h index ec10768e99ea..8f80acaf0fc9 100644 --- a/backends/imgui_impl_wgpu.h +++ b/backends/imgui_impl_wgpu.h @@ -4,7 +4,7 @@ // Implemented features: // [X] Renderer: User texture binding. Use 'WGPUTextureView' as ImTextureID. Read the FAQ about ImTextureID! -// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. +// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index ec047071deeb..cab7182acfa1 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -175,7 +175,6 @@ Other Changes: - Backends: OSX: Monitor NSKeyUp events to catch missing keyUp for key when user press Cmd + key (#5128) [@thedmd] - Backends: OSX, Metal: Store backend data in a per-context struct, allowing to use these backends with multiple contexts. (#5203, #5221, #4141) [@noisewuwei] -- Backends: OSX, Metal: Implemented support for viewports / platform windows. [@stuartcarnie] - Examples: Emscripten+WebGPU: Fix building for latest WebGPU specs. (#3632) - Examples: OSX+Metal, OSX+OpenGL: Removed now-unnecessary calls to ImGui_ImplOSX_HandleEvent(). (#4821) @@ -183,10 +182,12 @@ Docking+Viewports Branch: - Docking: Fixed floating docked nodes not being clamped into viewport workrect to stay reachable when g.ConfigWindowsMoveFromTitleBarOnly is set and multi-viewports are disabled. (#5044) -- Backends: SDL: Fixed dragging out main viewport broken on some SDL setups. (#5012) [@rokups] - Viewports: Fixed translating a host viewport from briefly altering the size of AlwaysAutoResize windows. (#5057) - Viewports: Fixed main viewport size not matching ImDrawData::DisplaySize for one frame during resize when multi-viewports are disabled. (#4900) +- Backends: SDL: Fixed dragging out main viewport broken on some SDL setups. (#5012) [@rokups] +- Backends: OSX: Added suppot for multi-viewports. [@stuartcarnie, @metarutaiga] (#4821, #2778) +- Backends: Metal: Added suppot for multi-viewports. [@stuartcarnie, @metarutaiga] (#4821, #2778) ----------------------------------------------------------------------- diff --git a/examples/example_apple_opengl2/main.mm b/examples/example_apple_opengl2/main.mm index 3abb53d4cc65..f336fa99c8eb 100644 --- a/examples/example_apple_opengl2/main.mm +++ b/examples/example_apple_opengl2/main.mm @@ -44,11 +44,20 @@ -(void)initialize //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking + io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows // Setup Dear ImGui style ImGui::StyleColorsDark(); //ImGui::StyleColorsClassic(); + // When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones. + ImGuiStyle& style = ImGui::GetStyle(); + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + style.WindowRounding = 0.0f; + style.Colors[ImGuiCol_WindowBg].w = 1.0f; + } + // Setup Platform/Renderer backends ImGui_ImplOSX_Init(self); ImGui_ImplOpenGL2_Init(); @@ -131,6 +140,13 @@ -(void)updateAndDrawDemoView ImGui_ImplOpenGL2_RenderDrawData(draw_data); + // Update and Render additional Platform Windows + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); + } + // Present [[self openGLContext] flushBuffer]; From 693b4c57fe6d2cb1069c9f99147565e6be2dc0bd Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 9 May 2022 10:45:56 +0200 Subject: [PATCH 798/828] Backends: OSX: Implement ImGui_ImplOSX_ShowWindow(). (#5299) --- backends/imgui_impl_osx.mm | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm index a4d33151602b..49526d9ff11b 100644 --- a/backends/imgui_impl_osx.mm +++ b/backends/imgui_impl_osx.mm @@ -825,12 +825,6 @@ static void ImGui_ImplOSX_CreateWindow(ImGuiViewport* viewport) window.title = @"Untitled"; window.opaque = YES; - if (viewport->Flags & ImGuiViewportFlags_NoFocusOnAppearing) - [window orderFront:nil]; - else - [window makeKeyAndOrderFront:nil]; - - [window setIsVisible:YES]; KeyEventResponder* view = [[KeyEventResponder alloc] initWithFrame:rect]; if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6) @@ -864,6 +858,19 @@ static void ImGui_ImplOSX_DestroyWindow(ImGuiViewport* viewport) viewport->PlatformUserData = viewport->PlatformHandle = viewport->PlatformHandleRaw = NULL; } +static void ImGui_ImplOSX_ShowWindow(ImGuiViewport* viewport) +{ + ImGuiViewportDataOSX* data = (ImGuiViewportDataOSX*)viewport->PlatformUserData; + IM_ASSERT(data->Window != 0); + + if (viewport->Flags & ImGuiViewportFlags_NoFocusOnAppearing) + [data->Window orderFront:nil]; + else + [data->Window makeKeyAndOrderFront:nil]; + + [data->Window setIsVisible:YES]; +} + static ImVec2 ImGui_ImplOSX_GetWindowPos(ImGuiViewport* viewport) { ImGuiViewportDataOSX* data = (ImGuiViewportDataOSX*)viewport->PlatformUserData; @@ -992,6 +999,7 @@ static void ImGui_ImplOSX_InitPlatformInterface() ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); platform_io.Platform_CreateWindow = ImGui_ImplOSX_CreateWindow; platform_io.Platform_DestroyWindow = ImGui_ImplOSX_DestroyWindow; + platform_io.Platform_ShowWindow = ImGui_ImplOSX_ShowWindow; platform_io.Platform_SetWindowPos = ImGui_ImplOSX_SetWindowPos; platform_io.Platform_GetWindowPos = ImGui_ImplOSX_GetWindowPos; platform_io.Platform_SetWindowSize = ImGui_ImplOSX_SetWindowSize; From 2fa60bec27999fd0b9e2291dd4dbcc4646e2fab4 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 9 May 2022 11:30:52 +0200 Subject: [PATCH 799/828] Examples: Apple+OpenGL: Fix build. --- examples/example_apple_opengl2/main.mm | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/example_apple_opengl2/main.mm b/examples/example_apple_opengl2/main.mm index f336fa99c8eb..9b3fa8831150 100644 --- a/examples/example_apple_opengl2/main.mm +++ b/examples/example_apple_opengl2/main.mm @@ -81,6 +81,7 @@ -(void)initialize -(void)updateAndDrawDemoView { // Start the Dear ImGui frame + ImGuiIO& io = ImGui::GetIO(); ImGui_ImplOpenGL2_NewFrame(); ImGui_ImplOSX_NewFrame(self); ImGui::NewFrame(); From 36055213c544683fbd41fbbc06ff096dfe0a567e Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Thu, 19 May 2022 14:01:18 +0300 Subject: [PATCH 800/828] Docking: Fixed moving window being interrupted when undocing a window with "io.ConfigDockingAlwaysTabBar = true". (#5324) Regression introduced in 6b7766817 --- docs/CHANGELOG.txt | 4 +++- imgui.cpp | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index dac5e8cc3ea8..128b8ac43a63 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -188,7 +188,9 @@ Other Changes: Docking+Viewports Branch: - Docking: Fixed floating docked nodes not being clamped into viewport workrect to stay reachable - when g.ConfigWindowsMoveFromTitleBarOnly is set and multi-viewports are disabled. (#5044) + when io.ConfigWindowsMoveFromTitleBarOnly is true and multi-viewports are disabled. (#5044) +- Docking: Fixed a regression where moving window would be interrupted after undocking a tab + when io.ConfigDockingAlwaysTabBar is true. (#5324) [@rokups] - Viewports: Fixed translating a host viewport from briefly altering the size of AlwaysAutoResize windows. (#5057) - Viewports: Fixed main viewport size not matching ImDrawData::DisplaySize for one frame during resize when multi-viewports are disabled. (#4900) diff --git a/imgui.cpp b/imgui.cpp index 44ee4c75d22b..77fe3bb4a7a8 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3936,7 +3936,7 @@ void ImGui::UpdateMouseMovingWindowNewFrame() ImGuiWindow* moving_window = g.MovingWindow->RootWindowDockTree; // When a window stop being submitted while being dragged, it may will its viewport until next Begin() - const bool window_disappared = (!moving_window->WasActive || moving_window->Viewport == NULL); + const bool window_disappared = ((!moving_window->WasActive && !moving_window->Active) || moving_window->Viewport == NULL); if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos) && !window_disappared) { ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset; From 250333d895b1067533533dcfab137512745b9689 Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Thu, 19 May 2022 11:48:36 +0300 Subject: [PATCH 801/828] Docking: Fix unhiding tab bar regression. (#5325, #5181) Broken by 90386780 --- imgui.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 77fe3bb4a7a8..92f628d86c73 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6128,8 +6128,10 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar float unhide_sz_hit = ImFloor(g.FontSize * 0.55f); ImVec2 p = node->Pos; ImRect r(p, p + ImVec2(unhide_sz_hit, unhide_sz_hit)); + ImGuiID unhide_id = window->GetID("#UNHIDE"); + KeepAliveID(unhide_id); bool hovered, held; - if (ButtonBehavior(r, window->GetID("#UNHIDE"), &hovered, &held, ImGuiButtonFlags_FlattenChildren)) + if (ButtonBehavior(r, unhide_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren)) node->WantHiddenTabBarToggle = true; else if (held && IsMouseDragging(0)) StartMouseMovingWindowOrNode(window, node, true); From 4789c7e485244aa6489f89dbb03b19d4ad0ea1ec Mon Sep 17 00:00:00 2001 From: Andrej Redeky <41929176+WSSDude420@users.noreply.github.com> Date: Sun, 5 Jun 2022 13:34:10 +0200 Subject: [PATCH 802/828] Misc: Fix custom assertion macro failing to compile imgui.cpp (#5378) --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 3f1191a6eff2..320ba5ce1734 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -15658,7 +15658,7 @@ static void ImGui::DockNodePreviewDockSetup(ImGuiWindow* host_window, ImGuiDockN ImGuiDockNode* root_payload_as_host = root_payload->DockNodeAsHost; ImGuiDockNode* ref_node_for_rect = (host_node && !host_node->IsVisible) ? DockNodeGetRootNode(host_node) : host_node; if (ref_node_for_rect) - IM_ASSERT(ref_node_for_rect->IsVisible); + IM_ASSERT(ref_node_for_rect->IsVisible == true); // Filter, figure out where we are allowed to dock ImGuiDockNodeFlags src_node_flags = root_payload_as_host ? root_payload_as_host->MergedFlags : root_payload->WindowClass.DockNodeFlagsOverrideSet; From 24dfebf455ac1f7685e1a72272d37b72601fe70c Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 13 Jun 2022 20:12:41 +0200 Subject: [PATCH 803/828] Docking: Fixed incorrect focus highlight on docking node when focusing empty central node or a child window which was manually injected into a dockspace window. --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 15 ++++++++++----- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 08c8926d66f7..ee72abd37014 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -223,6 +223,8 @@ Docking+Viewports Branch: when io.ConfigWindowsMoveFromTitleBarOnly is true and multi-viewports are disabled. (#5044) - Docking: Fixed a regression where moving window would be interrupted after undocking a tab when io.ConfigDockingAlwaysTabBar is true. (#5324) [@rokups] +- Docking: Fixed incorrect focus highlight on docking node when focusing empty central node + or a child window which was manually injected into a dockspace window. - Viewports: Fixed translating a host viewport from briefly altering the size of AlwaysAutoResize windows. (#5057) - Viewports: Fixed main viewport size not matching ImDrawData::DisplaySize for one frame during resize when multi-viewports are disabled. (#4900) diff --git a/imgui.cpp b/imgui.cpp index ad83558fc644..1fc45cf8895c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -15259,7 +15259,11 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w if (g.NavWindowingTarget) is_focused = (g.NavWindowingTarget->DockNode == node); else if (g.NavWindow && g.NavWindow->RootWindowForTitleBarHighlight == host_window->RootWindowDockTree && root_node->LastFocusedNodeId == node->ID) - is_focused = true; + { + // FIXME-DOCKING: May want alternative to treat central node void differently? e.g. if (g.NavWindow == host_window) + if (g.NavWindow->DockNode && DockNodeIsInHierarchyOf(g.NavWindow->DockNode, root_node)) // Omit child windows injected in window hierarchy + is_focused = true; + } // Hidden tab bar will show a triangle on the upper-left (in Begin) if (node->IsHiddenTabBar() || node->IsNoTabBar()) @@ -18263,10 +18267,11 @@ void ImGui::DebugNodeDockNode(ImGuiDockNode* node, const char* label) const bool is_active = (g.FrameCount - node->LastFrameActive < 2); // Submitted if (!is_alive) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); } bool open; + ImGuiTreeNodeFlags tree_node_flags = node->IsFocused ? ImGuiTreeNodeFlags_Selected : ImGuiTreeNodeFlags_None; if (node->Windows.Size > 0) - open = TreeNode((void*)(intptr_t)node->ID, "%s 0x%04X%s: %d windows (vis: '%s')", label, node->ID, node->IsVisible ? "" : " (hidden)", node->Windows.Size, node->VisibleWindow ? node->VisibleWindow->Name : "NULL"); + open = TreeNodeEx((void*)(intptr_t)node->ID, tree_node_flags, "%s 0x%04X%s: %d windows (vis: '%s')", label, node->ID, node->IsVisible ? "" : " (hidden)", node->Windows.Size, node->VisibleWindow ? node->VisibleWindow->Name : "NULL"); else - open = TreeNode((void*)(intptr_t)node->ID, "%s 0x%04X%s: %s split (vis: '%s')", label, node->ID, node->IsVisible ? "" : " (hidden)", (node->SplitAxis == ImGuiAxis_X) ? "horizontal" : (node->SplitAxis == ImGuiAxis_Y) ? "vertical" : "n/a", node->VisibleWindow ? node->VisibleWindow->Name : "NULL"); + open = TreeNodeEx((void*)(intptr_t)node->ID, tree_node_flags, "%s 0x%04X%s: %s split (vis: '%s')", label, node->ID, node->IsVisible ? "" : " (hidden)", (node->SplitAxis == ImGuiAxis_X) ? "horizontal" : (node->SplitAxis == ImGuiAxis_Y) ? "vertical" : "n/a", node->VisibleWindow ? node->VisibleWindow->Name : "NULL"); if (!is_alive) { PopStyleColor(); } if (is_active && IsItemHovered()) if (ImGuiWindow* window = node->HostWindow ? node->HostWindow : node->VisibleWindow) @@ -18280,10 +18285,10 @@ void ImGui::DebugNodeDockNode(ImGuiDockNode* node, const char* label) DebugNodeWindow(node->HostWindow, "HostWindow"); DebugNodeWindow(node->VisibleWindow, "VisibleWindow"); BulletText("SelectedTabID: 0x%08X, LastFocusedNodeID: 0x%08X", node->SelectedTabId, node->LastFocusedNodeId); - BulletText("Misc:%s%s%s%s%s%s", + BulletText("Misc:%s%s%s%s%s%s%s", node->IsDockSpace() ? " IsDockSpace" : "", node->IsCentralNode() ? " IsCentralNode" : "", - is_alive ? " IsAlive" : "", is_active ? " IsActive" : "", + is_alive ? " IsAlive" : "", is_active ? " IsActive" : "", node->IsFocused ? " IsFocused" : "", node->WantLockSizeOnce ? " WantLockSizeOnce" : "", node->HasCentralNodeChild ? " HasCentralNodeChild" : ""); if (TreeNode("flags", "Flags Merged: 0x%04X, Local: 0x%04X, InWindows: 0x%04X, Shared: 0x%04X", node->MergedFlags, node->LocalFlags, node->LocalFlagsInWindows, node->SharedFlags)) From 101aec95d94d8e102e73dfb1ed3faa96fafb43a5 Mon Sep 17 00:00:00 2001 From: rokups Date: Thu, 16 Jun 2022 11:15:26 +0300 Subject: [PATCH 804/828] Backends: SDL+GLFW, Examples: SDL+Metal, GLFW+Metal: Fix viewport support with Metal backend. Fixes #5392 + alignment fixes and removed static_cast<> + Amended with fix. --- backends/imgui_impl_glfw.cpp | 12 +++++++++++- backends/imgui_impl_metal.mm | 14 +++++++------- backends/imgui_impl_osx.mm | 28 ++++++++++++++-------------- backends/imgui_impl_sdl.cpp | 14 ++++++++++++-- docs/CHANGELOG.txt | 5 +++-- examples/example_glfw_metal/main.mm | 19 ++++++++++++++++++- examples/example_sdl_metal/main.mm | 17 +++++++++++++++++ 7 files changed, 82 insertions(+), 27 deletions(-) diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index 92f6c5521df3..e996fa63425d 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -71,11 +71,17 @@ // GLFW #include + #ifdef _WIN32 #undef APIENTRY #define GLFW_EXPOSE_NATIVE_WIN32 -#include // for glfwGetWin32Window +#include // for glfwGetWin32Window() +#endif +#ifdef __APPLE__ +#define GLFW_EXPOSE_NATIVE_COCOA +#include // for glfwGetCocoaWindow() #endif + #define GLFW_HAS_WINDOW_TOPMOST (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ GLFW_FLOATING #define GLFW_HAS_WINDOW_HOVERED (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ GLFW_HOVERED #define GLFW_HAS_WINDOW_ALPHA (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ glfwSetWindowOpacity @@ -538,6 +544,8 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw main_viewport->PlatformHandle = (void*)bd->Window; #ifdef _WIN32 main_viewport->PlatformHandleRaw = glfwGetWin32Window(bd->Window); +#elif defined(__APPLE__) + main_viewport->PlatformHandleRaw = (void*)glfwGetCocoaWindow(bd->Window); #endif if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) ImGui_ImplGlfw_InitPlatformInterface(); @@ -877,6 +885,8 @@ static void ImGui_ImplGlfw_CreateWindow(ImGuiViewport* viewport) viewport->PlatformHandle = (void*)vd->Window; #ifdef _WIN32 viewport->PlatformHandleRaw = glfwGetWin32Window(vd->Window); +#elif defined(__APPLE__) + viewport->PlatformHandleRaw = (void*)glfwGetCocoaWindow(vd->Window); #endif glfwSetWindowPos(vd->Window, (int)viewport->Pos.x, (int)viewport->Pos.y); diff --git a/backends/imgui_impl_metal.mm b/backends/imgui_impl_metal.mm index ae9eff4fb8d3..dfadfba8aef4 100644 --- a/backends/imgui_impl_metal.mm +++ b/backends/imgui_impl_metal.mm @@ -75,16 +75,16 @@ - (MetalBuffer*)dequeueReusableBufferOfLength:(NSUInteger)length device:(id(static_cast(clock_gettime_nsec_np(CLOCK_UPTIME_RAW)) / 1e9); } +static inline CFTimeInterval GetMachAbsoluteTimeInSeconds() { return (CFTimeInterval)(double)(clock_gettime_nsec_np(CLOCK_UPTIME_RAW) / 1e9); } #ifdef IMGUI_IMPL_METAL_CPP @@ -462,7 +462,7 @@ static void ImGui_ImplMetal_RenderWindow(ImGuiViewport* viewport, void*) } data->FirstFrame = false; - viewport->DpiScale = static_cast(window.backingScaleFactor); + viewport->DpiScale = (float)window.backingScaleFactor; if (data->MetalLayer.contentsScale != viewport->DpiScale) { data->MetalLayer.contentsScale = viewport->DpiScale; diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm index 49526d9ff11b..c95cf91c3d5e 100644 --- a/backends/imgui_impl_osx.mm +++ b/backends/imgui_impl_osx.mm @@ -63,23 +63,23 @@ // Data struct ImGui_ImplOSX_Data { - CFTimeInterval Time; - NSCursor* MouseCursors[ImGuiMouseCursor_COUNT]; - bool MouseCursorHidden; - ImGuiObserver* Observer; - KeyEventResponder* KeyEventResponder; - NSTextInputContext* InputContext; - id Monitor; - NSWindow* Window; - - ImGui_ImplOSX_Data() { memset(this, 0, sizeof(*this)); } + CFTimeInterval Time; + NSCursor* MouseCursors[ImGuiMouseCursor_COUNT]; + bool MouseCursorHidden; + ImGuiObserver* Observer; + KeyEventResponder* KeyEventResponder; + NSTextInputContext* InputContext; + id Monitor; + NSWindow* Window; + + ImGui_ImplOSX_Data() { memset(this, 0, sizeof(*this)); } }; -static ImGui_ImplOSX_Data* ImGui_ImplOSX_CreateBackendData() { return IM_NEW(ImGui_ImplOSX_Data)(); } -static ImGui_ImplOSX_Data* ImGui_ImplOSX_GetBackendData() { return (ImGui_ImplOSX_Data*)ImGui::GetIO().BackendPlatformUserData; } -static void ImGui_ImplOSX_DestroyBackendData() { IM_DELETE(ImGui_ImplOSX_GetBackendData()); } +static ImGui_ImplOSX_Data* ImGui_ImplOSX_CreateBackendData() { return IM_NEW(ImGui_ImplOSX_Data)(); } +static ImGui_ImplOSX_Data* ImGui_ImplOSX_GetBackendData() { return (ImGui_ImplOSX_Data*)ImGui::GetIO().BackendPlatformUserData; } +static void ImGui_ImplOSX_DestroyBackendData() { IM_DELETE(ImGui_ImplOSX_GetBackendData()); } -static inline CFTimeInterval GetMachAbsoluteTimeInSeconds() { return static_cast(static_cast(clock_gettime_nsec_np(CLOCK_UPTIME_RAW)) / 1e9); } +static inline CFTimeInterval GetMachAbsoluteTimeInSeconds() { return (CFTimeInterval)(double)(clock_gettime_nsec_np(CLOCK_UPTIME_RAW) / 1e9); } // Forward Declarations static void ImGui_ImplOSX_InitPlatformInterface(); diff --git a/backends/imgui_impl_sdl.cpp b/backends/imgui_impl_sdl.cpp index 0f0fa8bbcbca..45d2a9d17ea1 100644 --- a/backends/imgui_impl_sdl.cpp +++ b/backends/imgui_impl_sdl.cpp @@ -408,12 +408,16 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window, SDL_Renderer* renderer, void // Our mouse update function expect PlatformHandle to be filled for the main viewport ImGuiViewport* main_viewport = ImGui::GetMainViewport(); main_viewport->PlatformHandle = (void*)window; -#ifdef _WIN32 SDL_SysWMinfo info; SDL_VERSION(&info.version); if (SDL_GetWindowWMInfo(window, &info)) + { +#ifdef _WIN32 main_viewport->PlatformHandleRaw = (void*)info.info.win.window; +#elif defined(__APPLE__) + main_viewport->PlatformHandleRaw = (void*)info.info.cocoa.window; #endif + } // Set SDL hint to receive mouse click events on window focus, otherwise SDL doesn't emit the event. // Without this, when clicking to gain focus, our widgets wouldn't activate even though they showed as hovered. @@ -639,6 +643,8 @@ static void ImGui_ImplSDL2_UpdateMonitors() monitor.WorkSize = ImVec2((float)r.w, (float)r.h); #endif #if SDL_HAS_PER_MONITOR_DPI + // FIXME-VIEWPORT: On MacOS SDL reports actual monitor DPI scale, ignoring OS configuration. We may want to set + // DpiScale to cocoa_window.backingScaleFactor here. float dpi = 0.0f; if (!SDL_GetDisplayDPI(n, &dpi, NULL, NULL)) monitor.DpiScale = dpi / 96.0f; @@ -748,12 +754,16 @@ static void ImGui_ImplSDL2_CreateWindow(ImGuiViewport* viewport) SDL_GL_MakeCurrent(vd->Window, backup_context); viewport->PlatformHandle = (void*)vd->Window; -#if defined(_WIN32) SDL_SysWMinfo info; SDL_VERSION(&info.version); if (SDL_GetWindowWMInfo(vd->Window, &info)) + { +#if defined(_WIN32) viewport->PlatformHandleRaw = info.info.win.window; +#elif defined(__APPLE__) + viewport->PlatformHandleRaw = (void*)info.info.cocoa.window; #endif + } } static void ImGui_ImplSDL2_DestroyWindow(ImGuiViewport* viewport) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 7dd15120d83b..cb7ddadfab46 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -233,8 +233,9 @@ Docking+Viewports Branch: - Viewports: Fixed main viewport size not matching ImDrawData::DisplaySize for one frame during resize when multi-viewports are disabled. (#4900) - Backends: SDL: Fixed dragging out main viewport broken on some SDL setups. (#5012) [@rokups] -- Backends: OSX: Added suppot for multi-viewports. [@stuartcarnie, @metarutaiga] (#4821, #2778) -- Backends: Metal: Added suppot for multi-viewports. [@stuartcarnie, @metarutaiga] (#4821, #2778) +- Backends: OSX: Added support for multi-viewports. [@stuartcarnie, @metarutaiga] (#4821, #2778) +- Backends: Metal: Added support for multi-viewports. [@stuartcarnie, @metarutaiga] (#4821, #2778) +- Examples: OSX+Metal, SDL+Metal, GLFW+Metal: Added support for multi-viewports. [@rokups] ----------------------------------------------------------------------- diff --git a/examples/example_glfw_metal/main.mm b/examples/example_glfw_metal/main.mm index 6e5966d6f38c..d701abd64d95 100644 --- a/examples/example_glfw_metal/main.mm +++ b/examples/example_glfw_metal/main.mm @@ -29,11 +29,21 @@ int main(int, char**) ImGuiIO& io = ImGui::GetIO(); (void)io; //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking + io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows // Setup style ImGui::StyleColorsDark(); //ImGui::StyleColorsClassic(); + // When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones. + ImGuiStyle& style = ImGui::GetStyle(); + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + style.WindowRounding = 0.0f; + style.Colors[ImGuiCol_WindowBg].w = 1.0f; + } + // Load Fonts // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. @@ -64,7 +74,7 @@ int main(int, char**) id commandQueue = [device newCommandQueue]; // Setup Platform/Renderer backends - ImGui_ImplGlfw_InitForOpenGL(window, true); + ImGui_ImplGlfw_InitForOther(window, true); ImGui_ImplMetal_Init(device); NSWindow *nswin = glfwGetCocoaWindow(window); @@ -152,6 +162,13 @@ int main(int, char**) ImGui::Render(); ImGui_ImplMetal_RenderDrawData(ImGui::GetDrawData(), commandBuffer, renderEncoder); + // Update and Render additional Platform Windows + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); + } + [renderEncoder popDebugGroup]; [renderEncoder endEncoding]; diff --git a/examples/example_sdl_metal/main.mm b/examples/example_sdl_metal/main.mm index e836b25b5127..cae754d652c5 100644 --- a/examples/example_sdl_metal/main.mm +++ b/examples/example_sdl_metal/main.mm @@ -20,11 +20,21 @@ int main(int, char**) ImGuiIO& io = ImGui::GetIO(); (void)io; //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking + io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows // Setup style ImGui::StyleColorsDark(); //ImGui::StyleColorsClassic(); + // When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones. + ImGuiStyle& style = ImGui::GetStyle(); + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + style.WindowRounding = 0.0f; + style.Colors[ImGuiCol_WindowBg].w = 1.0f; + } + // Load Fonts // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. @@ -160,6 +170,13 @@ int main(int, char**) ImGui::Render(); ImGui_ImplMetal_RenderDrawData(ImGui::GetDrawData(), commandBuffer, renderEncoder); + // Update and Render additional Platform Windows + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); + } + [renderEncoder popDebugGroup]; [renderEncoder endEncoding]; From 506717390fa927024abe8cd7d52be94fac0a916a Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 16 Jun 2022 18:43:33 +0200 Subject: [PATCH 805/828] Docking, Modal: Fixed a crash when opening popup from a parent which is being docked on the same frame. (#5401) Ideally we should untangle the purpose of parent_window_in_stack / ParentWindowInBeginStack better. --- docs/CHANGELOG.txt | 1 + imgui.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index cb7ddadfab46..4fb87872c4c4 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -229,6 +229,7 @@ Docking+Viewports Branch: when io.ConfigDockingAlwaysTabBar is true. (#5324) [@rokups] - Docking: Fixed incorrect focus highlight on docking node when focusing empty central node or a child window which was manually injected into a dockspace window. +- Docking, Modal: Fixed a crash when opening popup from a parent which is being docked on the same frame. (#5401) - Viewports: Fixed translating a host viewport from briefly altering the size of AlwaysAutoResize windows. (#5057) - Viewports: Fixed main viewport size not matching ImDrawData::DisplaySize for one frame during resize when multi-viewports are disabled. (#4900) diff --git a/imgui.cpp b/imgui.cpp index 179c9568b17b..20793f14a1b3 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6464,7 +6464,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) } // Parent window is latched only on the first call to Begin() of the frame, so further append-calls can be done from a different window stack - ImGuiWindow* parent_window_in_stack = window->DockIsActive ? window->DockNode->HostWindow : g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back().Window; + ImGuiWindow* parent_window_in_stack = (window->DockIsActive && window->DockNode->HostWindow) ? window->DockNode->HostWindow : g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back().Window; ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow; IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow)); From 9764adc7bb7a582601dd4dd1c34d4fa17ab5c8e8 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 17 Jun 2022 17:11:23 +0200 Subject: [PATCH 806/828] Docking: Amend 24dfebf. Fixed incorrect focus highlight on docking node with nested hierarchies. --- imgui.cpp | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 20793f14a1b3..71b92f0a770c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6325,7 +6325,7 @@ void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags } if (parent_window && (flags & ImGuiWindowFlags_Popup)) window->RootWindowPopupTree = parent_window->RootWindowPopupTree; - if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup))) + if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup))) // FIXME: simply use _NoTitleBar ? window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight; while (window->RootWindowForNav->Flags & ImGuiWindowFlags_NavFlattened) { @@ -15293,6 +15293,21 @@ void ImGui::DockNodeEndAmendTabBar() End(); } +static bool IsDockNodeTitleBarHighlighted(ImGuiDockNode* node, ImGuiDockNode* root_node, ImGuiWindow* host_window) +{ + // CTRL+Tab highlight (only highlighting leaf node, not whole hierarchy) + ImGuiContext& g = *GImGui; + if (g.NavWindowingTarget) + return (g.NavWindowingTarget->DockNode == node); + + // FIXME-DOCKING: May want alternative to treat central node void differently? e.g. if (g.NavWindow == host_window) + if (g.NavWindow && g.NavWindow->RootWindowForTitleBarHighlight == host_window->RootWindowDockTree && root_node->LastFocusedNodeId == node->ID) + for (ImGuiDockNode* parent_node = g.NavWindow->RootWindow->DockNode; parent_node != NULL; parent_node = parent_node->HostWindow ? parent_node->HostWindow->RootWindow->DockNode : NULL) + if ((parent_node = ImGui::DockNodeGetRootNode(parent_node)) == root_node) + return true; + return false; +} + // Submit the tab bar corresponding to a dock node and various housekeeping details. static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_window) { @@ -15308,14 +15323,8 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w // Decide if we should use a focused title bar color bool is_focused = false; ImGuiDockNode* root_node = DockNodeGetRootNode(node); - if (g.NavWindowingTarget) - is_focused = (g.NavWindowingTarget->DockNode == node); - else if (g.NavWindow && g.NavWindow->RootWindowForTitleBarHighlight == host_window->RootWindowDockTree && root_node->LastFocusedNodeId == node->ID) - { - // FIXME-DOCKING: May want alternative to treat central node void differently? e.g. if (g.NavWindow == host_window) - if (g.NavWindow->DockNode && DockNodeIsInHierarchyOf(g.NavWindow->DockNode, root_node)) // Omit child windows injected in window hierarchy - is_focused = true; - } + if (IsDockNodeTitleBarHighlighted(node, root_node, host_window)) + is_focused = true; // Hidden tab bar will show a triangle on the upper-left (in Begin) if (node->IsHiddenTabBar() || node->IsNoTabBar()) From 58eb40db76783f5da09e592ca3eb421f4f2197e3 Mon Sep 17 00:00:00 2001 From: Runik Date: Wed, 29 Jun 2022 15:46:13 +0200 Subject: [PATCH 807/828] Backends: GLFW: Fixed leftover static variable preventing from changing or reinitializing backend while application is running. (#4616, #5434) --- backends/imgui_impl_glfw.cpp | 12 ++++++++---- docs/CHANGELOG.txt | 14 ++++++++++++++ 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index e996fa63425d..bc2d778190da 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -123,6 +123,9 @@ struct ImGui_ImplGlfw_Data GLFWwindow* KeyOwnerWindows[GLFW_KEY_LAST]; bool InstalledCallbacks; bool WantUpdateMonitors; +#ifdef _WIN32 + WNDPROC GlfwWndProc; +#endif // Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any. GLFWwindowfocusfun PrevUserCallbackWindowFocus; @@ -937,9 +940,9 @@ static void ImGui_ImplGlfw_DestroyWindow(ImGuiViewport* viewport) // We have submitted https://github.com/glfw/glfw/pull/1568 to allow GLFW to support "transparent inputs". // In the meanwhile we implement custom per-platform workarounds here (FIXME-VIEWPORT: Implement same work-around for Linux/OSX!) #if !GLFW_HAS_MOUSE_PASSTHROUGH && GLFW_HAS_WINDOW_HOVERED && defined(_WIN32) -static WNDPROC g_GlfwWndProc = NULL; static LRESULT CALLBACK WndProcNoInputs(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); if (msg == WM_NCHITTEST) { // Let mouse pass-through the window. This will allow the backend to call io.AddMouseViewportEvent() properly (which is OPTIONAL). @@ -950,7 +953,7 @@ static LRESULT CALLBACK WndProcNoInputs(HWND hWnd, UINT msg, WPARAM wParam, LPAR if (viewport->Flags & ImGuiViewportFlags_NoInputs) return HTTRANSPARENT; } - return ::CallWindowProc(g_GlfwWndProc, hWnd, msg, wParam, lParam); + return ::CallWindowProc(bd->GlfwWndProc, hWnd, msg, wParam, lParam); } #endif @@ -971,9 +974,10 @@ static void ImGui_ImplGlfw_ShowWindow(ImGuiViewport* viewport) // GLFW hack: install hook for WM_NCHITTEST message handler #if !GLFW_HAS_MOUSE_PASSTHROUGH && GLFW_HAS_WINDOW_HOVERED && defined(_WIN32) + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); ::SetPropA(hwnd, "IMGUI_VIEWPORT", viewport); - if (g_GlfwWndProc == NULL) - g_GlfwWndProc = (WNDPROC)::GetWindowLongPtr(hwnd, GWLP_WNDPROC); + if (bd->GlfwWndProc == NULL) + bd->GlfwWndProc = (WNDPROC)::GetWindowLongPtr(hwnd, GWLP_WNDPROC); ::SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)WndProcNoInputs); #endif diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 09b4ce43cff5..caaf6315c6a4 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -98,6 +98,20 @@ Other changes: Note that Linux/Mac still have inconsistent support for multi-viewports. If you want to help see https://github.com/ocornut/imgui/issues/2117. +----------------------------------------------------------------------- + VERSION 1.89 WIP (In Progress) +----------------------------------------------------------------------- + +Breaking changes: + +Other Changes: + +Docking+Viewports Branch: + +- Backends: GLFW: Fixed leftover static variable preventing from changing or + reinitializing backend while application is running. (#4616, #5434) [@rtoumazet] + + ----------------------------------------------------------------------- VERSION 1.88 (Released 2022-06-21) ----------------------------------------------------------------------- From ad5aa5416630059c9213a4cfe00a827c3991e964 Mon Sep 17 00:00:00 2001 From: "Stephen H. Gerstacker" Date: Thu, 30 Jun 2022 20:19:23 +0200 Subject: [PATCH 808/828] Backends, Viewport: Metal: Pull format from shared context. (#5403, #5437) --- backends/imgui_impl_metal.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backends/imgui_impl_metal.mm b/backends/imgui_impl_metal.mm index dd14b50161f0..043e9a9543ed 100644 --- a/backends/imgui_impl_metal.mm +++ b/backends/imgui_impl_metal.mm @@ -413,7 +413,7 @@ static void ImGui_ImplMetal_CreateWindow(ImGuiViewport* viewport) CAMetalLayer* layer = [CAMetalLayer layer]; layer.device = device; layer.framebufferOnly = YES; - layer.pixelFormat = MTLPixelFormatBGRA8Unorm; + layer.pixelFormat = bd->SharedMetalContext.framebufferDescriptor.colorPixelFormat; #if TARGET_OS_OSX NSWindow* window = (__bridge NSWindow*)handle; NSView* view = window.contentView; From 77637fd9363ac7ef7b6fb6419b760602192a38bd Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 12 Jul 2022 10:51:14 +0200 Subject: [PATCH 809/828] Docking, Nav: Fixed using gamepad/keyboard navigation not being able enter menu layer (#5463, #4792) Fix 37958ca --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index bd4af514ffec..a4ccf34fdd1a 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -133,6 +133,8 @@ Other Changes: Docking+Viewports Branch: +- Docking, Nav: Fixed using gamepad/keyboard navigation not being able enter menu layer when + it only contained the standard Collapse/Close buttons and no actual menu. (#5463, #4792) - Backends: GLFW: Fixed leftover static variable preventing from changing or reinitializing backend while application is running. (#4616, #5434) [@rtoumazet] diff --git a/imgui.cpp b/imgui.cpp index 7c1639b0b946..2c691f675deb 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7061,6 +7061,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DC.NavLayerCurrent = ImGuiNavLayer_Main; window->DC.NavLayersActiveMask = window->DC.NavLayersActiveMaskNext; + window->DC.NavLayersActiveMaskNext = 0x00; window->DC.NavHideHighlightOneFrame = false; window->DC.NavHasScroll = (window->ScrollMax.y > 0.0f); @@ -7221,9 +7222,9 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) skip_items = true; window->SkipItems = skip_items; - // Only clear NavLayersActiveMaskNext when marked as visible, so a CTRL+Tab back can use a safe value. - if (!window->SkipItems) - window->DC.NavLayersActiveMaskNext = 0x00; + // Restore NavLayersActiveMaskNext to previous value when not visible, so a CTRL+Tab back can use a safe value. + if (window->SkipItems) + window->DC.NavLayersActiveMaskNext = window->DC.NavLayersActiveMask; // Sanity check: there are two spots which can set Appearing = true // - when 'window_just_activated_by_user' is set -> HiddenFramesCannotSkipItems is set -> SkipItems always false From cb8ead1f7198924f97e52973d115e1d4eaeda2f3 Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Tue, 12 Jul 2022 16:21:44 +0300 Subject: [PATCH 810/828] Docking: Fix docked window contents not rendering when switching with CTRL+Tab. (regression from 8eb8689). --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 4 ++-- imgui_widgets.cpp | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index a4ccf34fdd1a..2511c5125a53 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -135,6 +135,8 @@ Docking+Viewports Branch: - Docking, Nav: Fixed using gamepad/keyboard navigation not being able enter menu layer when it only contained the standard Collapse/Close buttons and no actual menu. (#5463, #4792) +- Docking: Fixed regression introduced in v1.87 when docked window content not rendered + while switching between with CTRL+Tab. [@rokups] - Backends: GLFW: Fixed leftover static variable preventing from changing or reinitializing backend while application is running. (#4616, #5434) [@rtoumazet] diff --git a/imgui.cpp b/imgui.cpp index 2c691f675deb..162539636eea 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -15218,7 +15218,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) if (node->TabBar && node->TabBar->SelectedTabId) node->SelectedTabId = node->TabBar->SelectedTabId; else if (node->Windows.Size > 0) - node->SelectedTabId = node->Windows[0]->ID; + node->SelectedTabId = node->Windows[0]->TabId; // Draw payload drop target if (host_window && node->IsVisible) @@ -15456,7 +15456,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w // Apply NavWindow focus back to the tab bar if (g.NavWindow && g.NavWindow->RootWindow->DockNode == node) - tab_bar->SelectedTabId = g.NavWindow->RootWindow->ID; + tab_bar->SelectedTabId = g.NavWindow->RootWindow->TabId; // Selected newly added tabs, or persistent tab ID if the tab bar was just recreated if (tab_bar_is_recreated && TabBarFindTabByID(tab_bar, node->SelectedTabId) != NULL) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 46772f6b49fd..4fe2260f1aef 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7694,7 +7694,7 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) // CTRL+TAB can override visible tab temporarily if (g.NavWindowingTarget != NULL && g.NavWindowingTarget->DockNode && g.NavWindowingTarget->DockNode->TabBar == tab_bar) - tab_bar->VisibleTabId = scroll_to_tab_id = g.NavWindowingTarget->ID; + tab_bar->VisibleTabId = scroll_to_tab_id = g.NavWindowingTarget->TabId; // Update scrolling if (scroll_to_tab_id != 0) From 28a123ca472625a8823e19808581ab4aaa16e130 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 3 Aug 2022 14:37:35 +0200 Subject: [PATCH 811/828] Internals: Docking: make DockContextFindNodeByID() more visible (instead of DockBuilderGetNode) + using defines for channel changes. --- imgui.cpp | 23 +++++++++++------------ imgui_internal.h | 4 ++++ 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 162539636eea..b96807a62e86 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6179,14 +6179,14 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar // Render, for docked windows and host windows we ensure bg goes before decorations ImDrawList* bg_draw_list = window->DockIsActive ? window->DockNode->HostWindow->DrawList : window->DrawList; if (window->DockIsActive || (flags & ImGuiWindowFlags_DockNodeHost)) - bg_draw_list->ChannelsSetCurrent(0); + bg_draw_list->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_BG); if (window->DockIsActive) window->DockNode->LastBgColor = bg_col; bg_draw_list->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight()), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? 0 : ImDrawFlags_RoundCornersBottom); if (window->DockIsActive || (flags & ImGuiWindowFlags_DockNodeHost)) - bg_draw_list->ChannelsSetCurrent(1); + bg_draw_list->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_FG); } if (window->DockIsActive) window->DockNode->IsBgDrawnThisFrame = true; @@ -6601,7 +6601,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if (flags & ImGuiWindowFlags_DockNodeHost) { window->DrawList->ChannelsSplit(2); - window->DrawList->ChannelsSetCurrent(1); // Render decorations on channel 1 as we will render the backgrounds manually later + window->DrawList->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_FG); // Render decorations on channel 1 as we will render the backgrounds manually later } // Restore buffer capacity when woken from a compacted state, to avoid @@ -13766,7 +13766,6 @@ namespace ImGui static void DockContextProcessUndockWindow(ImGuiContext* ctx, ImGuiWindow* window, bool clear_persistent_docking_ref = true); static void DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node); static void DockContextPruneUnusedSettingsNodes(ImGuiContext* ctx); - static ImGuiDockNode* DockContextFindNodeByID(ImGuiContext* ctx, ImGuiID id); static ImGuiDockNode* DockContextBindNodeToWindow(ImGuiContext* ctx, ImGuiWindow* window); static void DockContextBuildNodesFromSettings(ImGuiContext* ctx, ImGuiDockNodeSettings* node_settings_array, int node_settings_count); static void DockContextBuildAddWindowsToNodes(ImGuiContext* ctx, ImGuiID root_id); // Use root_id==0 to add all @@ -13981,12 +13980,12 @@ void ImGui::DockContextEndFrame(ImGuiContext* ctx) { ImRect bg_rect(node->Pos + ImVec2(0.0f, GetFrameHeight()), node->Pos + node->Size); ImDrawFlags bg_rounding_flags = CalcRoundingFlagsForRectInRect(bg_rect, node->HostWindow->Rect(), DOCKING_SPLITTER_SIZE); - node->HostWindow->DrawList->ChannelsSetCurrent(0); + node->HostWindow->DrawList->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_BG); node->HostWindow->DrawList->AddRectFilled(bg_rect.Min, bg_rect.Max, node->LastBgColor, node->HostWindow->WindowRounding, bg_rounding_flags); } } -static ImGuiDockNode* ImGui::DockContextFindNodeByID(ImGuiContext* ctx, ImGuiID id) +ImGuiDockNode* ImGui::DockContextFindNodeByID(ImGuiContext* ctx, ImGuiID id) { return (ImGuiDockNode*)ctx->DockContext.Nodes.GetVoidPtr(id); } @@ -15174,7 +15173,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) // Update position/size, process and draw resizing splitters if (node->IsRootNode() && host_window) { - host_window->DrawList->ChannelsSetCurrent(1); + host_window->DrawList->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_FG); DockNodeTreeUpdatePosSize(node, host_window->Pos, host_window->Size); DockNodeTreeUpdateSplitter(node); } @@ -15182,7 +15181,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) // Draw empty node background (currently can only be the Central Node) if (host_window && node->IsEmpty() && node->IsVisible) { - host_window->DrawList->ChannelsSetCurrent(0); + host_window->DrawList->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_BG); node->LastBgColor = (node_flags & ImGuiDockNodeFlags_PassthruCentralNode) ? 0 : GetColorU32(ImGuiCol_DockingEmptyBg); if (node->LastBgColor != 0) host_window->DrawList->AddRectFilled(node->Pos, node->Pos + node->Size, node->LastBgColor); @@ -15195,7 +15194,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) const bool render_dockspace_bg = node->IsRootNode() && host_window && (node_flags & ImGuiDockNodeFlags_PassthruCentralNode) != 0; if (render_dockspace_bg && node->IsVisible) { - host_window->DrawList->ChannelsSetCurrent(0); + host_window->DrawList->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_BG); if (central_node_hole) RenderRectFilledWithHole(host_window->DrawList, node->Rect(), central_node->Rect(), GetColorU32(ImGuiCol_WindowBg), 0.0f); else @@ -15204,7 +15203,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) // Draw and populate Tab Bar if (host_window) - host_window->DrawList->ChannelsSetCurrent(1); + host_window->DrawList->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_FG); if (host_window && node->Windows.Size > 0) { DockNodeUpdateTabBar(node, host_window); @@ -15240,12 +15239,12 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) // Render outer borders last (after the tab bar) if (node->IsRootNode()) { - host_window->DrawList->ChannelsSetCurrent(1); + host_window->DrawList->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_FG); RenderWindowOuterBorders(host_window); } // Further rendering (= hosted windows background) will be drawn on layer 0 - host_window->DrawList->ChannelsSetCurrent(0); + host_window->DrawList->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_BG); } // End host window diff --git a/imgui_internal.h b/imgui_internal.h index 335a53b55f0a..243212b9045f 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1448,6 +1448,9 @@ struct ImGuiOldColumns // [SECTION] Docking support //----------------------------------------------------------------------------- +#define DOCKING_HOST_DRAW_CHANNEL_BG 0 // Dock host: background fill +#define DOCKING_HOST_DRAW_CHANNEL_FG 1 // Dock host: decorations and contents + #ifdef IMGUI_HAS_DOCK // Extend ImGuiDockNodeFlags_ @@ -2966,6 +2969,7 @@ namespace ImGui IMGUI_API void DockContextQueueUndockWindow(ImGuiContext* ctx, ImGuiWindow* window); IMGUI_API void DockContextQueueUndockNode(ImGuiContext* ctx, ImGuiDockNode* node); IMGUI_API bool DockContextCalcDropPosForDocking(ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, bool split_outer, ImVec2* out_pos); + IMGUI_API ImGuiDockNode*DockContextFindNodeByID(ImGuiContext* ctx, ImGuiID id); IMGUI_API bool DockNodeBeginAmendTabBar(ImGuiDockNode* node); IMGUI_API void DockNodeEndAmendTabBar(); inline ImGuiDockNode* DockNodeGetRootNode(ImGuiDockNode* node) { while (node->ParentNode) node = node->ParentNode; return node; } From 0abe7d1cc50dfc63b1f4da69dcc9270aaef62e1f Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 3 Aug 2022 16:11:39 +0200 Subject: [PATCH 812/828] Docking: Fixed splitting/docking into a node that has buttons amended into tab bar. Windows were not moved correctly. (#5515) --- imgui.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index d2858a983a51..ad8aaf722878 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -14738,10 +14738,14 @@ static void ImGui::DockNodeMoveWindows(ImGuiDockNode* dst_node, ImGuiDockNode* s src_node->TabBar = NULL; } - for (int n = 0; n < src_node->Windows.Size; n++) + for (int n_from_node = 0, n_from_tab_bar = 0; n_from_node < src_node->Windows.Size; n_from_node++, n_from_tab_bar++) { // DockNode's TabBar may have non-window Tabs manually appended by user - if (ImGuiWindow* window = src_tab_bar ? src_tab_bar->Tabs[n].Window : src_node->Windows[n]) + while (src_tab_bar && src_tab_bar->Tabs[n_from_tab_bar].Window == NULL) + n_from_tab_bar++; + + // Using TabBar order (FIXME: Why? Clarify + add tests for it) + if (ImGuiWindow* window = src_tab_bar ? src_tab_bar->Tabs[n_from_tab_bar].Window : src_node->Windows[n_from_node]) { window->DockNode = NULL; window->DockIsActive = false; From b12e056c21dbe4da63b81cac3017e2747df6c3ec Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 3 Aug 2022 17:00:59 +0200 Subject: [PATCH 813/828] Docking: Fixed amending into an existing tab bar from rendering invisible items. (#5515, amend b16f738d #2700, #2539) Commit b16f738d left us with a "current" channel 0 which seems inadequate. Undoing that, assuming default is always 1, code filling bg color does a switch. Only DockContextEndFrame() leave it at 0 and it's not particularly necessary. --- docs/CHANGELOG.txt | 1 + imgui.cpp | 13 ++----------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 8512217b22b6..2966f0f6d459 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -145,6 +145,7 @@ Docking+Viewports Branch: it only contained the standard Collapse/Close buttons and no actual menu. (#5463, #4792) - Docking: Fixed regression introduced in v1.87 when docked window content not rendered while switching between with CTRL+Tab. [@rokups] +- Docking: Fixed amending into an existing tab bar from rendering invisible items. (#5515) - Backends: GLFW: Fixed leftover static variable preventing from changing or reinitializing backend while application is running. (#4616, #5434) [@rtoumazet] diff --git a/imgui.cpp b/imgui.cpp index ad8aaf722878..b2c2fd0dcef1 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6205,14 +6205,12 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar } // Render, for docked windows and host windows we ensure bg goes before decorations + if (window->DockIsActive) + window->DockNode->LastBgColor = bg_col; ImDrawList* bg_draw_list = window->DockIsActive ? window->DockNode->HostWindow->DrawList : window->DrawList; if (window->DockIsActive || (flags & ImGuiWindowFlags_DockNodeHost)) bg_draw_list->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_BG); - if (window->DockIsActive) - window->DockNode->LastBgColor = bg_col; - bg_draw_list->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight()), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? 0 : ImDrawFlags_RoundCornersBottom); - if (window->DockIsActive || (flags & ImGuiWindowFlags_DockNodeHost)) bg_draw_list->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_FG); } @@ -15218,7 +15216,6 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) // Update position/size, process and draw resizing splitters if (node->IsRootNode() && host_window) { - host_window->DrawList->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_FG); DockNodeTreeUpdatePosSize(node, host_window->Pos, host_window->Size); DockNodeTreeUpdateSplitter(node); } @@ -15283,13 +15280,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node) // Render outer borders last (after the tab bar) if (node->IsRootNode()) - { - host_window->DrawList->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_FG); RenderWindowOuterBorders(host_window); - } - - // Further rendering (= hosted windows background) will be drawn on layer 0 - host_window->DrawList->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_BG); } // End host window From f573ebf31a04d1ab953e8dec4aedacd15d7b588a Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Tue, 26 Jul 2022 12:35:44 +0300 Subject: [PATCH 814/828] Docking+Viewports: Fix undocking window node causing parent viewport to become unresponsive. (#5503) Amend 67be485e, Somehow ties to 58f5092c + 0eb45a05 (#4310) Unsure of exact chain of event but this caused a parent link msimatch between the time of the MouseMoving test in AddUpdateViewport() setting _NoInputs on the wrong parent., and the release clearing _NoInputs on the rght one. --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 16 ++++++++-------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 2966f0f6d459..f01e3629d03c 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -146,6 +146,8 @@ Docking+Viewports Branch: - Docking: Fixed regression introduced in v1.87 when docked window content not rendered while switching between with CTRL+Tab. [@rokups] - Docking: Fixed amending into an existing tab bar from rendering invisible items. (#5515) +- Docking+Viewports: Fixed undocking window node causing parent viewports to become unresponsive + in certain situation (e.g. hidden tab bar). (#5503) [@rokups] - Backends: GLFW: Fixed leftover static variable preventing from changing or reinitializing backend while application is running. (#4616, #5434) [@rtoumazet] diff --git a/imgui.cpp b/imgui.cpp index b2c2fd0dcef1..1cdfd055b2fe 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -14460,14 +14460,6 @@ void ImGui::DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node) new_node->SizeRef = node->SizeRef; DockNodeMoveWindows(new_node, node); DockSettingsRenameNodeReferences(node->ID, new_node->ID); - for (int n = 0; n < new_node->Windows.Size; n++) - { - ImGuiWindow* window = new_node->Windows[n]; - window->Flags &= ~ImGuiWindowFlags_ChildWindow; - if (window->ParentWindow) - window->ParentWindow->DC.ChildWindows.find_erase(window); - UpdateWindowParentAndRootLinks(window, window->Flags, NULL); - } node = new_node; } else @@ -14480,6 +14472,14 @@ void ImGui::DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node) node->ParentNode->AuthorityForViewport = ImGuiDataAuthority_Window; // The node that stays in place keeps the viewport, so our newly dragged out node will create a new viewport node->ParentNode = NULL; } + for (int n = 0; n < node->Windows.Size; n++) + { + ImGuiWindow* window = node->Windows[n]; + window->Flags &= ~ImGuiWindowFlags_ChildWindow; + if (window->ParentWindow) + window->ParentWindow->DC.ChildWindows.find_erase(window); + UpdateWindowParentAndRootLinks(window, window->Flags, NULL); + } node->AuthorityForPos = node->AuthorityForSize = ImGuiDataAuthority_DockNode; node->Size = FixLargeWindowsWhenUndocking(node->Size, node->Windows[0]->Viewport); node->WantMouseMove = true; From ff1567e2406797cbd1a3ef3d716888d6c3a66bd7 Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Fri, 5 Aug 2022 12:49:31 +0300 Subject: [PATCH 815/828] Docking: Simplify logic of moving tabs between nodes. Amends 0abe7d. (#5515) The idea is that in the absence of a tab bar, as new one gets created new tabs will be sorted based on window->DockOrder so this may work but we're not 100% sure. --- imgui.cpp | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 140104fe078b..7fdd173f6b70 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -14749,19 +14749,12 @@ static void ImGui::DockNodeMoveWindows(ImGuiDockNode* dst_node, ImGuiDockNode* s src_node->TabBar = NULL; } - for (int n_from_node = 0, n_from_tab_bar = 0; n_from_node < src_node->Windows.Size; n_from_node++, n_from_tab_bar++) + // Tab order is not important here, it is preserved by sorting in DockNodeUpdateTabBar(). + for (ImGuiWindow* window : src_node->Windows) { - // DockNode's TabBar may have non-window Tabs manually appended by user - while (src_tab_bar && src_tab_bar->Tabs[n_from_tab_bar].Window == NULL) - n_from_tab_bar++; - - // Using TabBar order (FIXME: Why? Clarify + add tests for it) - if (ImGuiWindow* window = src_tab_bar ? src_tab_bar->Tabs[n_from_tab_bar].Window : src_node->Windows[n_from_node]) - { - window->DockNode = NULL; - window->DockIsActive = false; - DockNodeAddWindow(dst_node, window, move_tab_bar ? false : true); - } + window->DockNode = NULL; + window->DockIsActive = false; + DockNodeAddWindow(dst_node, window, !move_tab_bar); } src_node->Windows.clear(); From 8cbd391f096b9314a08670052cc0025cbcadb249 Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Mon, 8 Aug 2022 16:05:25 +0300 Subject: [PATCH 816/828] Docking: Add source dock node parameter DockContextCalcDropPosForDocking() to facilitate test engine (un)docking nodes before they are split out to their own window. Metrics: Display dock_node->Windows in node metrics. --- imgui.cpp | 25 ++++++++++++++----------- imgui_internal.h | 2 +- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 7fdd173f6b70..e54ab5d682d0 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -13841,7 +13841,7 @@ namespace ImGui static void DockNodeUpdateVisibleFlag(ImGuiDockNode* node); static void DockNodeStartMouseMovingWindow(ImGuiDockNode* node, ImGuiWindow* window); static bool DockNodeIsDropAllowed(ImGuiWindow* host_window, ImGuiWindow* payload_window); - static void DockNodePreviewDockSetup(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* payload_window, ImGuiDockPreviewData* preview_data, bool is_explicit_target, bool is_outer_docking); + static void DockNodePreviewDockSetup(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* payload_window, ImGuiDockNode* payload_node, ImGuiDockPreviewData* preview_data, bool is_explicit_target, bool is_outer_docking); static void DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* payload_window, const ImGuiDockPreviewData* preview_data); static void DockNodeCalcTabBarLayout(const ImGuiDockNode* node, ImRect* out_title_rect, ImRect* out_tab_bar_rect, ImVec2* out_window_menu_button_pos, ImVec2* out_close_button_pos); static void DockNodeCalcSplitRects(ImVec2& pos_old, ImVec2& size_old, ImVec2& pos_new, ImVec2& size_new, ImGuiDir dir, ImVec2 size_new_desired); @@ -14500,14 +14500,14 @@ void ImGui::DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node) } // This is mostly used for automation. -bool ImGui::DockContextCalcDropPosForDocking(ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, bool split_outer, ImVec2* out_pos) +bool ImGui::DockContextCalcDropPosForDocking(ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload_window, ImGuiDockNode* payload_node, ImGuiDir split_dir, bool split_outer, ImVec2* out_pos) { // In DockNodePreviewDockSetup() for a root central node instead of showing both "inner" and "outer" drop rects // (which would be functionally identical) we only show the outer one. Reflect this here. if (target_node && target_node->ParentNode == NULL && target_node->IsCentralNode() && split_dir != ImGuiDir_None) split_outer = true; ImGuiDockPreviewData split_data; - DockNodePreviewDockSetup(target, target_node, payload, &split_data, false, split_outer); + DockNodePreviewDockSetup(target, target_node, payload_window, payload_node, &split_data, false, split_outer); if (split_data.DropRectsDraw[split_dir+1].IsInverted()) return false; *out_pos = split_data.DropRectsDraw[split_dir+1].GetCenter(); @@ -15829,20 +15829,21 @@ bool ImGui::DockNodeCalcDropRectsAndTestMousePos(const ImRect& parent, ImGuiDir // host_node may be NULL if the window doesn't have a DockNode already. // FIXME-DOCK: This is misnamed since it's also doing the filtering. -static void ImGui::DockNodePreviewDockSetup(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* root_payload, ImGuiDockPreviewData* data, bool is_explicit_target, bool is_outer_docking) +static void ImGui::DockNodePreviewDockSetup(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* payload_window, ImGuiDockNode* payload_node, ImGuiDockPreviewData* data, bool is_explicit_target, bool is_outer_docking) { ImGuiContext& g = *GImGui; // There is an edge case when docking into a dockspace which only has inactive nodes. // In this case DockNodeTreeFindNodeByPos() will have selected a leaf node which is inactive. // Because the inactive leaf node doesn't have proper pos/size yet, we'll use the root node as reference. - ImGuiDockNode* root_payload_as_host = root_payload->DockNodeAsHost; + if (payload_node == NULL) + payload_node = payload_window->DockNodeAsHost; ImGuiDockNode* ref_node_for_rect = (host_node && !host_node->IsVisible) ? DockNodeGetRootNode(host_node) : host_node; if (ref_node_for_rect) IM_ASSERT(ref_node_for_rect->IsVisible == true); // Filter, figure out where we are allowed to dock - ImGuiDockNodeFlags src_node_flags = root_payload_as_host ? root_payload_as_host->MergedFlags : root_payload->WindowClass.DockNodeFlagsOverrideSet; + ImGuiDockNodeFlags src_node_flags = payload_node ? payload_node->MergedFlags : payload_window->WindowClass.DockNodeFlagsOverrideSet; ImGuiDockNodeFlags dst_node_flags = host_node ? host_node->MergedFlags : host_window->WindowClass.DockNodeFlagsOverrideSet; data->IsCenterAvailable = true; if (is_outer_docking) @@ -15851,7 +15852,7 @@ static void ImGui::DockNodePreviewDockSetup(ImGuiWindow* host_window, ImGuiDockN data->IsCenterAvailable = false; else if (host_node && (dst_node_flags & ImGuiDockNodeFlags_NoDockingInCentralNode) && host_node->IsCentralNode()) data->IsCenterAvailable = false; - else if ((!host_node || !host_node->IsEmpty()) && root_payload_as_host && root_payload_as_host->IsSplitNode() && (root_payload_as_host->OnlyNodeWithWindows == NULL)) // Is _visibly_ split? + else if ((!host_node || !host_node->IsEmpty()) && payload_node && payload_node->IsSplitNode() && (payload_node->OnlyNodeWithWindows == NULL)) // Is _visibly_ split? data->IsCenterAvailable = false; else if (dst_node_flags & ImGuiDockNodeFlags_NoDockingOverMe) data->IsCenterAvailable = false; @@ -15869,7 +15870,7 @@ static void ImGui::DockNodePreviewDockSetup(ImGuiWindow* host_window, ImGuiDockN data->IsSidesAvailable = false; // Build a tentative future node (reuse same structure because it is practical. Shape will be readjusted when previewing a split) - data->FutureNode.HasCloseButton = (host_node ? host_node->HasCloseButton : host_window->HasCloseButton) || (root_payload->HasCloseButton); + data->FutureNode.HasCloseButton = (host_node ? host_node->HasCloseButton : host_window->HasCloseButton) || (payload_window->HasCloseButton); data->FutureNode.HasWindowMenuButton = host_node ? true : ((host_window->Flags & ImGuiWindowFlags_NoCollapse) == 0); data->FutureNode.Pos = ref_node_for_rect ? ref_node_for_rect->Pos : host_window->Pos; data->FutureNode.Size = ref_node_for_rect ? ref_node_for_rect->Size : host_window->Size; @@ -15906,7 +15907,7 @@ static void ImGui::DockNodePreviewDockSetup(ImGuiWindow* host_window, ImGuiDockN ImGuiAxis split_axis = (split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y; ImVec2 pos_new, pos_old = data->FutureNode.Pos; ImVec2 size_new, size_old = data->FutureNode.Size; - DockNodeCalcSplitRects(pos_old, size_old, pos_new, size_new, split_dir, root_payload->Size); + DockNodeCalcSplitRects(pos_old, size_old, pos_new, size_new, split_dir, payload_window->Size); // Calculate split ratio so we can pass it down the docking request float split_ratio = ImSaturate(size_new[split_axis] / data->FutureNode.Size[split_axis]); @@ -17274,11 +17275,11 @@ void ImGui::BeginDockableDragDropTarget(ImGuiWindow* window) if (node && (node->ParentNode || node->IsCentralNode())) if (ImGuiDockNode* root_node = DockNodeGetRootNode(node)) { - DockNodePreviewDockSetup(window, root_node, payload_window, &split_outer, is_explicit_target, true); + DockNodePreviewDockSetup(window, root_node, payload_window, NULL, &split_outer, is_explicit_target, true); if (split_outer.IsSplitDirExplicit) split_data = &split_outer; } - DockNodePreviewDockSetup(window, node, payload_window, &split_inner, is_explicit_target, false); + DockNodePreviewDockSetup(window, node, payload_window, NULL, &split_inner, is_explicit_target, false); if (split_data == &split_outer) split_inner.IsDropAllowed = false; @@ -18449,6 +18450,8 @@ void ImGui::DebugNodeDockNode(ImGuiDockNode* node, const char* label) DebugNodeDockNode(node->ChildNodes[1], "Child[1]"); if (node->TabBar) DebugNodeTabBar(node->TabBar, "TabBar"); + DebugNodeWindowsList(&node->Windows, "Windows"); + TreePop(); } } diff --git a/imgui_internal.h b/imgui_internal.h index dfe6049a725c..a6fec12a93c3 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2971,7 +2971,7 @@ namespace ImGui IMGUI_API void DockContextQueueDock(ImGuiContext* ctx, ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, float split_ratio, bool split_outer); IMGUI_API void DockContextQueueUndockWindow(ImGuiContext* ctx, ImGuiWindow* window); IMGUI_API void DockContextQueueUndockNode(ImGuiContext* ctx, ImGuiDockNode* node); - IMGUI_API bool DockContextCalcDropPosForDocking(ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, bool split_outer, ImVec2* out_pos); + IMGUI_API bool DockContextCalcDropPosForDocking(ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload_window, ImGuiDockNode* payload_node, ImGuiDir split_dir, bool split_outer, ImVec2* out_pos); IMGUI_API ImGuiDockNode*DockContextFindNodeByID(ImGuiContext* ctx, ImGuiID id); IMGUI_API bool DockNodeBeginAmendTabBar(ImGuiDockNode* node); IMGUI_API void DockNodeEndAmendTabBar(); From b78738ff23747888e66bb2d3fe9127cd50d4c28f Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 22 Aug 2022 19:14:22 +0200 Subject: [PATCH 817/828] Internals: Docking: rename HoveredDockNode to DebugHoveredDockNode to clarify that it isn't usable for much other than debugging. --- imgui.cpp | 12 ++++++------ imgui_internal.h | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index e54ab5d682d0..9780402dcb0a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -13998,13 +13998,13 @@ void ImGui::DockContextNewFrameUpdateDocking(ImGuiContext* ctx) // [DEBUG] Store hovered dock node. // We could in theory use DockNodeTreeFindVisibleNodeByPos() on the root host dock node, but using ->DockNode is a good shortcut. // Note this is mostly a debug thing and isn't actually used for docking target, because docking involve more detailed filtering. - g.HoveredDockNode = NULL; + g.DebugHoveredDockNode = NULL; if (ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow) { if (hovered_window->DockNodeAsHost) - g.HoveredDockNode = DockNodeTreeFindVisibleNodeByPos(hovered_window->DockNodeAsHost, g.IO.MousePos); + g.DebugHoveredDockNode = DockNodeTreeFindVisibleNodeByPos(hovered_window->DockNodeAsHost, g.IO.MousePos); else if (hovered_window->RootWindow->DockNode) - g.HoveredDockNode = hovered_window->RootWindow->DockNode; + g.DebugHoveredDockNode = hovered_window->RootWindow->DockNode; } // Process Docking requests @@ -18250,7 +18250,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL"); Text("HoveredWindow->Root: '%s'", g.HoveredWindow ? g.HoveredWindow->RootWindowDockTree->Name : "NULL"); Text("HoveredWindowUnderMovingWindow: '%s'", g.HoveredWindowUnderMovingWindow ? g.HoveredWindowUnderMovingWindow->Name : "NULL"); - Text("HoveredDockNode: 0x%08X", g.HoveredDockNode ? g.HoveredDockNode->ID : 0); + Text("HoveredDockNode: 0x%08X", g.DebugHoveredDockNode ? g.DebugHoveredDockNode->ID : 0); Text("MovingWindow: '%s'", g.MovingWindow ? g.MovingWindow->Name : "NULL"); Text("MouseViewport: 0x%08X (UserHovered 0x%08X, LastHovered 0x%08X)", g.MouseViewport->ID, g.IO.MouseHoveredViewport, g.MouseLastHoveredViewport ? g.MouseLastHoveredViewport->ID : 0); Unindent(); @@ -18338,11 +18338,11 @@ void ImGui::ShowMetricsWindow(bool* p_open) #ifdef IMGUI_HAS_DOCK // Overlay: Display Docking info - if (cfg->ShowDockingNodes && g.IO.KeyCtrl && g.HoveredDockNode) + if (cfg->ShowDockingNodes && g.IO.KeyCtrl && g.DebugHoveredDockNode) { char buf[64] = ""; char* p = buf; - ImGuiDockNode* node = g.HoveredDockNode; + ImGuiDockNode* node = g.DebugHoveredDockNode; ImDrawList* overlay_draw_list = node->HostWindow ? GetForegroundDrawList(node->HostWindow) : GetForegroundDrawList(GetMainViewport()); p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "DockId: %X%s\n", node->ID, node->IsCentralNode() ? " *CentralNode*" : ""); p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "WindowClass: %08X\n", node->WindowClass.ClassId); diff --git a/imgui_internal.h b/imgui_internal.h index a6fec12a93c3..19b88086be7a 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1800,7 +1800,6 @@ struct ImGuiContext ImGuiWindow* CurrentWindow; // Window being drawn into ImGuiWindow* HoveredWindow; // Window the mouse is hovering. Will typically catch mouse inputs. ImGuiWindow* HoveredWindowUnderMovingWindow; // Hovered window ignoring MovingWindow. Only set if MovingWindow is set. - ImGuiDockNode* HoveredDockNode; // [Debug] Hovered dock node. ImGuiWindow* MovingWindow; // Track the window we clicked on (in order to preserve focus). The actual window that is moved is generally MovingWindow->RootWindowDockTree. ImGuiWindow* WheelingWindow; // Track the window we started mouse-wheeling on. Until a timer elapse or mouse has moved, generally keep scrolling the same window even if during the course of scrolling the mouse ends up hovering a child window. ImVec2 WheelingWindowRefMousePos; @@ -2033,6 +2032,7 @@ struct ImGuiContext ImGuiID DebugItemPickerBreakId; // Will call IM_DEBUG_BREAK() when encountering this ID ImGuiMetricsConfig DebugMetricsConfig; ImGuiStackTool DebugStackTool; + ImGuiDockNode* DebugHoveredDockNode; // Hovered dock node. // Misc float FramerateSecPerFrame[60]; // Calculate estimate of framerate for user over the last 60 frames.. @@ -2064,7 +2064,6 @@ struct ImGuiContext CurrentWindow = NULL; HoveredWindow = NULL; HoveredWindowUnderMovingWindow = NULL; - HoveredDockNode = NULL; MovingWindow = NULL; WheelingWindow = NULL; WheelingWindowTimer = 0.0f; @@ -2199,6 +2198,7 @@ struct ImGuiContext DebugItemPickerActive = false; DebugItemPickerMouseButton = ImGuiMouseButton_Left; DebugItemPickerBreakId = 0; + DebugHoveredDockNode = NULL; memset(FramerateSecPerFrame, 0, sizeof(FramerateSecPerFrame)); FramerateSecPerFrameIdx = FramerateSecPerFrameCount = 0; From 71a0701920dbc83155f718182f01132d1ec2d51e Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 24 Aug 2022 14:41:57 +0200 Subject: [PATCH 818/828] Nav: Fixed regression in e99c4fc preventing CTR+Tab to work without NavEnableKeyboard (#5504, #4023); --- imgui.cpp | 6 +----- imgui.h | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index cc222b924e8c..212fcb922dd8 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10963,10 +10963,6 @@ static void ImGui::NavUpdate() for (ImGuiKey key : nav_keyboard_keys_to_change_source) if (IsKeyDown(key)) g.NavInputSource = ImGuiInputSource_Keyboard; - if (!nav_gamepad_active && g.NavInputSource == ImGuiInputSource_Gamepad) - g.NavInputSource = ImGuiInputSource_None; - if (!nav_keyboard_active && g.NavInputSource == ImGuiInputSource_Keyboard) - g.NavInputSource = ImGuiInputSource_None; // Process navigation init request (select first/default focus) if (g.NavInitResultId != 0) @@ -11616,7 +11612,7 @@ static void ImGui::NavUpdateWindowing() const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; const bool start_windowing_with_gamepad = allow_windowing && nav_gamepad_active && !g.NavWindowingTarget && IsKeyPressed(ImGuiKey_NavGamepadMenu, false); - const bool start_windowing_with_keyboard = allow_windowing && nav_keyboard_active && !g.NavWindowingTarget && io.KeyCtrl && IsKeyPressed(ImGuiKey_Tab, false); + const bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && io.KeyCtrl && IsKeyPressed(ImGuiKey_Tab, false); // Note: enabled even without NavEnableKeyboard! if (start_windowing_with_gamepad || start_windowing_with_keyboard) if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1)) { diff --git a/imgui.h b/imgui.h index 13d5a731660c..c68125988c2f 100644 --- a/imgui.h +++ b/imgui.h @@ -65,7 +65,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.89 WIP" -#define IMGUI_VERSION_NUM 18809 +#define IMGUI_VERSION_NUM 18810 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE #define IMGUI_HAS_VIEWPORT // Viewport WIP branch From aceab9a877de0258d19d29a5d87a51b63a8999bf Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 5 Aug 2022 21:19:12 +0200 Subject: [PATCH 819/828] Obsoleted using SetCursorPos()/SetCursorScreenPos() to extend parent window/cell boundaries. (#5548) This incorrect pattern has been mentioned or suggested in: #4510, #3355, #1760, #1490, #4152, #150 # Conflicts: # imgui.cpp --- docs/CHANGELOG.txt | 14 +++++++++++ imgui.cpp | 63 +++++++++++++++++++++++++++++++++++++++++----- imgui.h | 2 +- imgui_internal.h | 2 ++ imgui_tables.cpp | 5 +++- 5 files changed, 78 insertions(+), 8 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index f59dc2b15e6b..732c705fe9ed 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -127,6 +127,20 @@ Breaking changes: - requires an explicit identifier. You may still use e.g. PushID() calls and then pass an empty identifier. - always uses style.FramePadding for padding, to be consistent with other buttons. You may use PushStyleVar() to alter this. - As always we are keeping a redirection function available (will obsolete later). + - Obsoleted using SetCursorPos()/SetCursorScreenPos() to extend parent window/cell boundaries. (#5548) + This relates to when moving the cursor position beyond current boundaries WITHOUT submitting an item. + - Previously this would make the window content size ~200x200: + Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + End(); + - Instead, please submit an item: + Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + Dummy(ImVec2(0,0)) + End(); + - Alternative: + Begin(...) + Dummy(ImVec2(200,200)) + End(); + Content size is now only extended when submitting an item. + With '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' this will now be detected and assert. + Without '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' this will silently be fixed until we obsolete it. + (This incorrect pattern has been mentioned or suggested in: #4510, #3355, #1760, #1490, #4152, #150, + threads have been amended to refer to this issue). + Other Changes: diff --git a/imgui.cpp b/imgui.cpp index 9682dd5fd658..f90557907650 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -392,6 +392,17 @@ CODE - likewise io.MousePos and GetMousePos() will use OS coordinates. If you query mouse positions to interact with non-imgui coordinates you will need to offset them, e.g. subtract GetWindowViewport()->Pos. + - 2022/09/02 (1.89) - obsoleted using SetCursorPos()/SetCursorScreenPos() to extend parent window/cell boundaries. + this relates to when moving the cursor position beyond current boundaries WITHOUT submitting an item. + - previously this would make the window content size ~200x200: + Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + End(); + - instead, please submit an item: + Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + Dummy(ImVec2(0,0)) + End(); + - alternative: + Begin(...) + Dummy(ImVec2(200,200)) + End(); + - content size is now only extended when submitting an item! + - with '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' this will now be detected and assert. + - without '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' this will silently be fixed until we obsolete it. - 2022/08/03 (1.89) - changed signature of ImageButton() function. Kept redirection function (will obsolete). - added 'const char* str_id' parameter + removed 'int frame_padding = -1' parameter. - old signature: bool ImageButton(ImTextureID tex_id, ImVec2 size, ImVec2 uv0 = ImVec2(0,0), ImVec2 uv1 = ImVec2(1,1), int frame_padding = -1, ImVec4 bg_col = ImVec4(0,0,0,0), ImVec4 tint_col = ImVec4(1,1,1,1)); @@ -7158,7 +7169,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DC.IdealMaxPos = window->DC.CursorStartPos; window->DC.CurrLineSize = window->DC.PrevLineSize = ImVec2(0.0f, 0.0f); window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f; - window->DC.IsSameLine = false; + window->DC.IsSameLine = window->DC.IsSetPos = false; window->DC.NavLayerCurrent = ImGuiNavLayer_Main; window->DC.NavLayersActiveMask = window->DC.NavLayersActiveMaskNext; @@ -7364,6 +7375,9 @@ void ImGui::End() if (!(window->Flags & ImGuiWindowFlags_ChildWindow)) // FIXME: add more options for scope of logging LogFinish(); + if (window->DC.IsSetPos) + ErrorCheckUsingSetCursorPosToExtendParentBoundaries(); + // Docking: report contents sizes to parent to allow for auto-resize if (window->DockNode && window->DockTabIsVisible) if (ImGuiWindow* host_window = window->DockNode->HostWindow) // FIXME-DOCK @@ -8831,6 +8845,36 @@ bool ImGui::DebugCheckVersionAndDataLayout(const char* version, size_t sz_io, si return !error; } +// Until 1.89 (IMGUI_VERSION_NUM < 18814) it was legal to use SetCursorPos() to extend the boundary of a parent (e.g. window or table cell) +// This is causing issues and ambiguity and we need to retire that. +// See https://github.com/ocornut/imgui/issues/5548 for more details. +// [Scenario 1] +// Previously this would make the window content size ~200x200: +// Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + End(); // NOT OK +// Instead, please submit an item: +// Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + Dummy(ImVec2(0,0)) + End(); // OK +// Alternative: +// Begin(...) + Dummy(ImVec2(200,200)) + End(); // OK +// [Scenario 2] +// For reference this is one of the issue what we aim to fix with this change: +// BeginGroup() + SomeItem("foobar") + SetCursorScreenPos(GetCursorScreenPos()) + EndGroup() +// The previous logic made SetCursorScreenPos(GetCursorScreenPos()) have a side-effect! It would erroneously incorporate ItemSpacing.y after the item into content size, making the group taller! +// While this code is a little twisted, no-one would expect SetXXX(GetXXX()) to have a side-effect. Using vertical alignment patterns could trigger this issue. +void ImGui::ErrorCheckUsingSetCursorPosToExtendParentBoundaries() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + IM_ASSERT(window->DC.IsSetPos); + window->DC.IsSetPos = false; +#ifdef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + if (window->DC.CursorPos.x <= window->DC.CursorMaxPos.x && window->DC.CursorPos.y <= window->DC.CursorMaxPos.y) + return; + IM_ASSERT(0 && "Code uses SetCursorPos()/SetCursorScreenPos() to extend window/parent boundaries. Please submit an item e.g. Dummy() to validate extent."); +#else + window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos); +#endif +} + static void ImGui::ErrorCheckNewFrameSanityChecks() { ImGuiContext& g = *GImGui; @@ -9141,7 +9185,7 @@ void ImGui::ItemSize(const ImVec2& size, float text_baseline_y) window->DC.CurrLineSize.y = 0.0f; window->DC.PrevLineTextBaseOffset = ImMax(window->DC.CurrLineTextBaseOffset, text_baseline_y); window->DC.CurrLineTextBaseOffset = 0.0f; - window->DC.IsSameLine = false; + window->DC.IsSameLine = window->DC.IsSetPos = false; // Horizontal layout mode if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) @@ -9261,7 +9305,8 @@ void ImGui::SetCursorScreenPos(const ImVec2& pos) { ImGuiWindow* window = GetCurrentWindow(); window->DC.CursorPos = pos; - window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos); + //window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos); + window->DC.IsSetPos = true; } // User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient. @@ -9288,21 +9333,24 @@ void ImGui::SetCursorPos(const ImVec2& local_pos) { ImGuiWindow* window = GetCurrentWindow(); window->DC.CursorPos = window->Pos - window->Scroll + local_pos; - window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos); + //window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos); + window->DC.IsSetPos = true; } void ImGui::SetCursorPosX(float x) { ImGuiWindow* window = GetCurrentWindow(); window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x; - window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x); + //window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x); + window->DC.IsSetPos = true; } void ImGui::SetCursorPosY(float y) { ImGuiWindow* window = GetCurrentWindow(); window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y; - window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y); + //window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y); + window->DC.IsSetPos = true; } ImVec2 ImGui::GetCursorStartPos() @@ -9519,6 +9567,9 @@ void ImGui::EndGroup() ImGuiGroupData& group_data = g.GroupStack.back(); IM_ASSERT(group_data.WindowID == window->ID); // EndGroup() in wrong window? + if (window->DC.IsSetPos) + ErrorCheckUsingSetCursorPosToExtendParentBoundaries(); + ImRect group_bb(group_data.BackupCursorPos, ImMax(window->DC.CursorMaxPos, group_data.BackupCursorPos)); window->DC.CursorPos = group_data.BackupCursorPos; diff --git a/imgui.h b/imgui.h index 9b6ebc313c8d..a82113a94f18 100644 --- a/imgui.h +++ b/imgui.h @@ -65,7 +65,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.89 WIP" -#define IMGUI_VERSION_NUM 18813 +#define IMGUI_VERSION_NUM 18814 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE #define IMGUI_HAS_VIEWPORT // Viewport WIP branch diff --git a/imgui_internal.h b/imgui_internal.h index 6414f2da90d9..df7e650a93ff 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2239,6 +2239,7 @@ struct IMGUI_API ImGuiWindowTempData float CurrLineTextBaseOffset; // Baseline offset (0.0f by default on a new line, generally == style.FramePadding.y when a framed item has been added). float PrevLineTextBaseOffset; bool IsSameLine; + bool IsSetPos; ImVec1 Indent; // Indentation / start position from left of window (increased by TreePush/TreePop, etc.) ImVec1 ColumnsOffset; // Offset to the current column (if ColumnsCurrent > 0). FIXME: This and the above should be a stack to allow use cases like Tree->Column->Tree. Need revamp columns API. ImVec1 GroupOffset; @@ -3209,6 +3210,7 @@ namespace ImGui // Debug Tools IMGUI_API void ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, void* user_data = NULL); IMGUI_API void ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, void* user_data = NULL); + IMGUI_API void ErrorCheckUsingSetCursorPosToExtendParentBoundaries(); inline void DebugDrawItemRect(ImU32 col = IM_COL32(255,0,0,255)) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; GetForegroundDrawList(window)->AddRect(g.LastItemData.Rect.Min, g.LastItemData.Rect.Max, col); } inline void DebugStartItemPicker() { ImGuiContext& g = *GImGui; g.DebugItemPickerActive = true; } IMGUI_API void ShowFontAtlas(ImFontAtlas* atlas); diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 872f1ec1bc55..3692c248fa99 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -1718,7 +1718,7 @@ void ImGui::TableBeginRow(ImGuiTable* table) table->RowIndentOffsetX = window->DC.Indent.x - table->HostIndentX; // Lock indent window->DC.PrevLineTextBaseOffset = 0.0f; window->DC.CurrLineSize = ImVec2(0.0f, 0.0f); - window->DC.IsSameLine = false; + window->DC.IsSameLine = window->DC.IsSetPos = false; window->DC.CursorMaxPos.y = next_y1; // Making the header BG color non-transparent will allow us to overlay it multiple times when handling smooth dragging. @@ -2000,6 +2000,9 @@ void ImGui::TableEndCell(ImGuiTable* table) ImGuiTableColumn* column = &table->Columns[table->CurrentColumn]; ImGuiWindow* window = table->InnerWindow; + if (window->DC.IsSetPos) + ErrorCheckUsingSetCursorPosToExtendParentBoundaries(); + // Report maximum position so we can infer content size per column. float* p_max_pos_x; if (table->RowFlags & ImGuiTableRowFlags_Headers) From caf4b7f1e6190ec4b32c66aca3e94a9ef15c0378 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 9 Sep 2022 16:45:16 +0200 Subject: [PATCH 820/828] Backends: SDL: Fixed building backend under non-OSX Apple targets (e.g. iPhone). (#5665) --- backends/imgui_impl_sdl.cpp | 6 ++++-- docs/CHANGELOG.txt | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/backends/imgui_impl_sdl.cpp b/backends/imgui_impl_sdl.cpp index 0ead88cf4619..e375c3a43a1d 100644 --- a/backends/imgui_impl_sdl.cpp +++ b/backends/imgui_impl_sdl.cpp @@ -408,13 +408,14 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window, SDL_Renderer* renderer, void // Our mouse update function expect PlatformHandle to be filled for the main viewport ImGuiViewport* main_viewport = ImGui::GetMainViewport(); main_viewport->PlatformHandle = (void*)window; + main_viewport->PlatformHandleRaw = NULL; SDL_SysWMinfo info; SDL_VERSION(&info.version); if (SDL_GetWindowWMInfo(window, &info)) { #ifdef _WIN32 main_viewport->PlatformHandleRaw = (void*)info.info.win.window; -#elif defined(__APPLE__) +#elif defined(__APPLE__) && defined(SDL_VIDEO_DRIVER_COCOA) main_viewport->PlatformHandleRaw = (void*)info.info.cocoa.window; #endif } @@ -754,13 +755,14 @@ static void ImGui_ImplSDL2_CreateWindow(ImGuiViewport* viewport) SDL_GL_MakeCurrent(vd->Window, backup_context); viewport->PlatformHandle = (void*)vd->Window; + viewport->PlatformHandleRaw = NULL; SDL_SysWMinfo info; SDL_VERSION(&info.version); if (SDL_GetWindowWMInfo(vd->Window, &info)) { #if defined(_WIN32) viewport->PlatformHandleRaw = info.info.win.window; -#elif defined(__APPLE__) +#elif defined(__APPLE__) && defined(SDL_VIDEO_DRIVER_COCOA) viewport->PlatformHandleRaw = (void*)info.info.cocoa.window; #endif } diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 732c705fe9ed..9db0ecfad3b6 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -192,6 +192,7 @@ Docking+Viewports Branch: - Docking: Fixed amending into an existing tab bar from rendering invisible items. (#5515) - Docking+Viewports: Fixed undocking window node causing parent viewports to become unresponsive in certain situation (e.g. hidden tab bar). (#5503) [@rokups] +- Backends: SDL: Fixed building backend under non-OSX Apple targets (e.g. iPhone). (#5665) - Backends: GLFW: Fixed leftover static variable preventing from changing or reinitializing backend while application is running. (#4616, #5434) [@rtoumazet] From 8f434874c01da46d57ce66af0494f2653ac1bfc3 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 26 Sep 2022 15:24:44 +0200 Subject: [PATCH 821/828] Docking: Fixed incorrect focus highlight on docking node when focusing a menu. (#5702) --- docs/CHANGELOG.txt | 1 + imgui.cpp | 14 ++++++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index bda6618a5672..747b07073351 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -224,6 +224,7 @@ Other Changes: Docking+Viewports Branch: +- Docking: Fixed incorrect focus highlight on docking node when focusing a menu. (#5702) - Docking, Nav: Fixed using gamepad/keyboard navigation not being able enter menu layer when it only contained the standard Collapse/Close buttons and no actual menu. (#5463, #4792) - Docking: Fixed regression introduced in v1.87 when docked window content not rendered diff --git a/imgui.cpp b/imgui.cpp index 986c7e3f55fb..942fe30a5cb8 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -15514,7 +15514,7 @@ void ImGui::DockNodeEndAmendTabBar() End(); } -static bool IsDockNodeTitleBarHighlighted(ImGuiDockNode* node, ImGuiDockNode* root_node, ImGuiWindow* host_window) +static bool IsDockNodeTitleBarHighlighted(ImGuiDockNode* node, ImGuiDockNode* root_node) { // CTRL+Tab highlight (only highlighting leaf node, not whole hierarchy) ImGuiContext& g = *GImGui; @@ -15522,10 +15522,16 @@ static bool IsDockNodeTitleBarHighlighted(ImGuiDockNode* node, ImGuiDockNode* ro return (g.NavWindowingTarget->DockNode == node); // FIXME-DOCKING: May want alternative to treat central node void differently? e.g. if (g.NavWindow == host_window) - if (g.NavWindow && g.NavWindow->RootWindowForTitleBarHighlight == host_window->RootWindowDockTree && root_node->LastFocusedNodeId == node->ID) - for (ImGuiDockNode* parent_node = g.NavWindow->RootWindow->DockNode; parent_node != NULL; parent_node = parent_node->HostWindow ? parent_node->HostWindow->RootWindow->DockNode : NULL) + if (g.NavWindow && root_node->LastFocusedNodeId == node->ID) + { + // FIXME: This could all be backed in RootWindowForTitleBarHighlight? Probably need to reorganize for both dock nodes + other RootWindowForTitleBarHighlight users (not-node) + ImGuiWindow* parent_window = g.NavWindow->RootWindow; + while (parent_window->Flags & ImGuiWindowFlags_ChildMenu) + parent_window = parent_window->ParentWindow->RootWindow; + for (ImGuiDockNode* parent_node = parent_window->DockNode; parent_node != NULL; parent_node = parent_node->HostWindow ? parent_node->HostWindow->RootWindow->DockNode : NULL) if ((parent_node = ImGui::DockNodeGetRootNode(parent_node)) == root_node) return true; + } return false; } @@ -15544,7 +15550,7 @@ static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_w // Decide if we should use a focused title bar color bool is_focused = false; ImGuiDockNode* root_node = DockNodeGetRootNode(node); - if (IsDockNodeTitleBarHighlighted(node, root_node, host_window)) + if (IsDockNodeTitleBarHighlighted(node, root_node)) is_focused = true; // Hidden tab bar will show a triangle on the upper-left (in Begin) From 11f5be0cafcefa501799912f257a4a24817d23df Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 14 Sep 2022 15:46:27 +0200 Subject: [PATCH 822/828] Backends: OpenGL: Add ability to #define IMGUI_IMPL_OPENGL_DEBUG. (#4468, #4825, #4832, #5127, #5655, #5709) # Conflicts: # backends/imgui_impl_opengl3.cpp --- backends/imgui_impl_opengl3.cpp | 73 +++++++++++++++++++-------------- 1 file changed, 43 insertions(+), 30 deletions(-) diff --git a/backends/imgui_impl_opengl3.cpp b/backends/imgui_impl_opengl3.cpp index b7587d0e746e..bd045667193b 100644 --- a/backends/imgui_impl_opengl3.cpp +++ b/backends/imgui_impl_opengl3.cpp @@ -16,6 +16,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) // 2022-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. +// 2022-09-27: OpenGL: Added ability to '#define IMGUI_IMPL_OPENGL_DEBUG'. // 2022-05-23: OpenGL: Reworking 2021-12-15 "Using buffer orphaning" so it only happens on Intel GPU, seems to cause problems otherwise. (#4468, #4825, #4832, #5127). // 2022-05-13: OpenGL: Fix state corruption on OpenGL ES 2.0 due to not preserving GL_ELEMENT_ARRAY_BUFFER_BINDING and vertex attribute states. // 2021-12-15: OpenGL: Using buffer orphaning + glBufferSubData(), seems to fix leaks with multi-viewports with some Intel HD drivers. @@ -180,6 +181,15 @@ #define IMGUI_IMPL_OPENGL_MAY_HAVE_EXTENSIONS #endif +// [Debugging] +//#define IMGUI_IMPL_OPENGL_DEBUG +#ifdef IMGUI_IMPL_OPENGL_DEBUG +#include +#define GL_CALL(_CALL) do { _CALL; GLenum gl_err = glGetError(); if (gl_err != 0) fprintf(stderr, "GL error 0x%x returned from '%s'.\n", gl_err, #_CALL); } while (0) // Call with error check +#else +#define GL_CALL(_CALL) _CALL // Call without error check +#endif + // OpenGL Data struct ImGui_ImplOpenGL3_Data { @@ -276,11 +286,14 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version) if (strncmp(vendor, "Intel", 5) == 0) bd->UseBufferSubData = true; #endif - //printf("GL_MAJOR_VERSION = %d\nGL_MINOR_VERSION = %d\nGL_VENDOR = '%s'\nGL_RENDERER = '%s'\n", major, minor, (const char*)glGetString(GL_VENDOR), (const char*)glGetString(GL_RENDERER)); // [DEBUG] #else bd->GlVersion = 200; // GLES 2 #endif +#ifdef IMGUI_IMPL_OPENGL_DEBUG + printf("GL_MAJOR_VERSION = %d\nGL_MINOR_VERSION = %d\nGL_VENDOR = '%s'\nGL_RENDERER = '%s'\n", major, minor, (const char*)glGetString(GL_VENDOR), (const char*)glGetString(GL_RENDERER)); // [DEBUG] +#endif + #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET if (bd->GlVersion >= 320) io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. @@ -384,7 +397,7 @@ static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_wid // Setup viewport, orthographic projection matrix // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps. - glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height); + GL_CALL(glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height)); float L = draw_data->DisplayPos.x; float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x; float T = draw_data->DisplayPos.y; @@ -414,14 +427,14 @@ static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_wid #endif // Bind vertex/index buffers and setup attributes for ImDrawVert - glBindBuffer(GL_ARRAY_BUFFER, bd->VboHandle); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bd->ElementsHandle); - glEnableVertexAttribArray(bd->AttribLocationVtxPos); - glEnableVertexAttribArray(bd->AttribLocationVtxUV); - glEnableVertexAttribArray(bd->AttribLocationVtxColor); - glVertexAttribPointer(bd->AttribLocationVtxPos, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, pos)); - glVertexAttribPointer(bd->AttribLocationVtxUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, uv)); - glVertexAttribPointer(bd->AttribLocationVtxColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, col)); + GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, bd->VboHandle)); + GL_CALL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bd->ElementsHandle)); + GL_CALL(glEnableVertexAttribArray(bd->AttribLocationVtxPos)); + GL_CALL(glEnableVertexAttribArray(bd->AttribLocationVtxUV)); + GL_CALL(glEnableVertexAttribArray(bd->AttribLocationVtxColor)); + GL_CALL(glVertexAttribPointer(bd->AttribLocationVtxPos, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, pos))); + GL_CALL(glVertexAttribPointer(bd->AttribLocationVtxUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, uv))); + GL_CALL(glVertexAttribPointer(bd->AttribLocationVtxColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, col))); } // OpenGL3 Render function. @@ -481,7 +494,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) // The renderer would actually work without any VAO bound, but then our VertexAttrib calls would overwrite the default one currently bound. GLuint vertex_array_object = 0; #ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY - glGenVertexArrays(1, &vertex_array_object); + GL_CALL(glGenVertexArrays(1, &vertex_array_object)); #endif ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object); @@ -505,20 +518,20 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) if (bd->VertexBufferSize < vtx_buffer_size) { bd->VertexBufferSize = vtx_buffer_size; - glBufferData(GL_ARRAY_BUFFER, bd->VertexBufferSize, NULL, GL_STREAM_DRAW); + GL_CALL(glBufferData(GL_ARRAY_BUFFER, bd->VertexBufferSize, NULL, GL_STREAM_DRAW)); } if (bd->IndexBufferSize < idx_buffer_size) { bd->IndexBufferSize = idx_buffer_size; - glBufferData(GL_ELEMENT_ARRAY_BUFFER, bd->IndexBufferSize, NULL, GL_STREAM_DRAW); + GL_CALL(glBufferData(GL_ELEMENT_ARRAY_BUFFER, bd->IndexBufferSize, NULL, GL_STREAM_DRAW)); } - glBufferSubData(GL_ARRAY_BUFFER, 0, vtx_buffer_size, (const GLvoid*)cmd_list->VtxBuffer.Data); - glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, idx_buffer_size, (const GLvoid*)cmd_list->IdxBuffer.Data); + GL_CALL(glBufferSubData(GL_ARRAY_BUFFER, 0, vtx_buffer_size, (const GLvoid*)cmd_list->VtxBuffer.Data)); + GL_CALL(glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, idx_buffer_size, (const GLvoid*)cmd_list->IdxBuffer.Data)); } else { - glBufferData(GL_ARRAY_BUFFER, vtx_buffer_size, (const GLvoid*)cmd_list->VtxBuffer.Data, GL_STREAM_DRAW); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, idx_buffer_size, (const GLvoid*)cmd_list->IdxBuffer.Data, GL_STREAM_DRAW); + GL_CALL(glBufferData(GL_ARRAY_BUFFER, vtx_buffer_size, (const GLvoid*)cmd_list->VtxBuffer.Data, GL_STREAM_DRAW)); + GL_CALL(glBufferData(GL_ELEMENT_ARRAY_BUFFER, idx_buffer_size, (const GLvoid*)cmd_list->IdxBuffer.Data, GL_STREAM_DRAW)); } for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) @@ -542,23 +555,23 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) continue; // Apply scissor/clipping rectangle (Y is inverted in OpenGL) - glScissor((int)clip_min.x, (int)((float)fb_height - clip_max.y), (int)(clip_max.x - clip_min.x), (int)(clip_max.y - clip_min.y)); + GL_CALL(glScissor((int)clip_min.x, (int)((float)fb_height - clip_max.y), (int)(clip_max.x - clip_min.x), (int)(clip_max.y - clip_min.y))); // Bind texture, Draw - glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->GetTexID()); + GL_CALL(glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->GetTexID())); #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET if (bd->GlVersion >= 320) - glDrawElementsBaseVertex(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)), (GLint)pcmd->VtxOffset); + GL_CALL(glDrawElementsBaseVertex(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)), (GLint)pcmd->VtxOffset)); else #endif - glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx))); + GL_CALL(glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)))); } } } // Destroy the temporary VAO #ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY - glDeleteVertexArrays(1, &vertex_array_object); + GL_CALL(glDeleteVertexArrays(1, &vertex_array_object)); #endif // Restore modified GL state @@ -611,21 +624,21 @@ bool ImGui_ImplOpenGL3_CreateFontsTexture() // Upload texture to graphics system // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling) GLint last_texture; - glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); - glGenTextures(1, &bd->FontTexture); - glBindTexture(GL_TEXTURE_2D, bd->FontTexture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + GL_CALL(glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture)); + GL_CALL(glGenTextures(1, &bd->FontTexture)); + GL_CALL(glBindTexture(GL_TEXTURE_2D, bd->FontTexture)); + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); #ifdef GL_UNPACK_ROW_LENGTH // Not on WebGL/ES - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + GL_CALL(glPixelStorei(GL_UNPACK_ROW_LENGTH, 0)); #endif - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels)); // Store our identifier io.Fonts->SetTexID((ImTextureID)(intptr_t)bd->FontTexture); // Restore state - glBindTexture(GL_TEXTURE_2D, last_texture); + GL_CALL(glBindTexture(GL_TEXTURE_2D, last_texture)); return true; } From cc62008f56b99e94be31e0cf38509941e5053088 Mon Sep 17 00:00:00 2001 From: Adam Foflonker <87928802+DnA-IntRicate@users.noreply.github.com> Date: Wed, 28 Sep 2022 16:21:02 +0200 Subject: [PATCH 823/828] Update imgui_impl_vulkan.h to match martty/imgui https://github.com/martty/imgui/blob/master/examples/imgui_impl_vulkan.h --- backends/imgui_impl_vulkan.h | 101 ++++++++++++----------------------- 1 file changed, 35 insertions(+), 66 deletions(-) diff --git a/backends/imgui_impl_vulkan.h b/backends/imgui_impl_vulkan.h index d2cb0ab4f2d9..d8b1391b3024 100644 --- a/backends/imgui_impl_vulkan.h +++ b/backends/imgui_impl_vulkan.h @@ -1,85 +1,56 @@ -// dear imgui: Renderer Backend for Vulkan -// This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..) +// dear imgui: Renderer for Vulkan +// This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..) // Implemented features: -// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. -// [x] Renderer: Multi-viewport / platform windows. With issues (flickering when creating a new viewport). -// [!] Renderer: User texture binding. Use 'VkDescriptorSet' as ImTextureID. Read the FAQ about ImTextureID! See https://github.com/ocornut/imgui/pull/914 for discussions. +// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. +// In this binding, ImTextureID is used to store a 'VkDescriptorSet' texture identifier. Read the FAQ about ImTextureID in imgui.cpp. -// Important: on 32-bit systems, user texture binding is only supported if your imconfig file has '#define ImTextureID ImU64'. -// See imgui_impl_vulkan.cpp file for details. - -// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. -// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. -// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. -// Read online: https://github.com/ocornut/imgui/tree/master/docs +// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. +// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp. +// https://github.com/ocornut/imgui // The aim of imgui_impl_vulkan.h/.cpp is to be usable in your engine without any modification. // IF YOU FEEL YOU NEED TO MAKE ANY CHANGE TO THIS CODE, please share them and your feedback at https://github.com/ocornut/imgui/ // Important note to the reader who wish to integrate imgui_impl_vulkan.cpp/.h in their own engine/app. // - Common ImGui_ImplVulkan_XXX functions and structures are used to interface with imgui_impl_vulkan.cpp/.h. -// You will use those if you want to use this rendering backend in your engine/app. +// You will use those if you want to use this rendering back-end in your engine/app. // - Helper ImGui_ImplVulkanH_XXX functions and structures are only used by this example (main.cpp) and by -// the backend itself (imgui_impl_vulkan.cpp), but should PROBABLY NOT be used by your own engine/app code. +// the back-end itself (imgui_impl_vulkan.cpp), but should PROBABLY NOT be used by your own engine/app code. // Read comments in imgui_impl_vulkan.h. #pragma once -#include "imgui.h" // IMGUI_IMPL_API - -// [Configuration] in order to use a custom Vulkan function loader: -// (1) You'll need to disable default Vulkan function prototypes. -// We provide a '#define IMGUI_IMPL_VULKAN_NO_PROTOTYPES' convenience configuration flag. -// In order to make sure this is visible from the imgui_impl_vulkan.cpp compilation unit: -// - Add '#define IMGUI_IMPL_VULKAN_NO_PROTOTYPES' in your imconfig.h file -// - Or as a compilation flag in your build system -// - Or uncomment here (not recommended because you'd be modifying imgui sources!) -// - Do not simply add it in a .cpp file! -// (2) Call ImGui_ImplVulkan_LoadFunctions() before ImGui_ImplVulkan_Init() with your custom function. -// If you have no idea what this is, leave it alone! -//#define IMGUI_IMPL_VULKAN_NO_PROTOTYPES - -// Vulkan includes -#if defined(IMGUI_IMPL_VULKAN_NO_PROTOTYPES) && !defined(VK_NO_PROTOTYPES) -#define VK_NO_PROTOTYPES -#endif + #include // Initialization data, for ImGui_ImplVulkan_Init() // [Please zero-clear before use!] struct ImGui_ImplVulkan_InitInfo { - VkInstance Instance; - VkPhysicalDevice PhysicalDevice; - VkDevice Device; - uint32_t QueueFamily; - VkQueue Queue; - VkPipelineCache PipelineCache; - VkDescriptorPool DescriptorPool; - uint32_t Subpass; - uint32_t MinImageCount; // >= 2 - uint32_t ImageCount; // >= MinImageCount - VkSampleCountFlagBits MSAASamples; // >= VK_SAMPLE_COUNT_1_BIT (0 -> default to VK_SAMPLE_COUNT_1_BIT) - const VkAllocationCallbacks* Allocator; - void (*CheckVkResultFn)(VkResult err); + VkInstance Instance; + VkPhysicalDevice PhysicalDevice; + VkDevice Device; + uint32_t QueueFamily; + VkQueue Queue; + VkPipelineCache PipelineCache; + VkDescriptorPool DescriptorPool; + uint32_t MinImageCount; // >= 2 + uint32_t ImageCount; // >= MinImageCount + VkSampleCountFlagBits MSAASamples; // >= VK_SAMPLE_COUNT_1_BIT + const VkAllocationCallbacks* Allocator; + void (*CheckVkResultFn)(VkResult err); }; // Called by user code -IMGUI_IMPL_API bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info, VkRenderPass render_pass); -IMGUI_IMPL_API void ImGui_ImplVulkan_Shutdown(); -IMGUI_IMPL_API void ImGui_ImplVulkan_NewFrame(); -IMGUI_IMPL_API void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer command_buffer, VkPipeline pipeline = VK_NULL_HANDLE); -IMGUI_IMPL_API bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer); -IMGUI_IMPL_API void ImGui_ImplVulkan_DestroyFontUploadObjects(); -IMGUI_IMPL_API void ImGui_ImplVulkan_SetMinImageCount(uint32_t min_image_count); // To override MinImageCount after initialization (e.g. if swap chain is recreated) - -// Register a texture (VkDescriptorSet == ImTextureID) -// FIXME: This is experimental in the sense that we are unsure how to best design/tackle this problem, please post to https://github.com/ocornut/imgui/pull/914 if you have suggestions. -IMGUI_IMPL_API VkDescriptorSet ImGui_ImplVulkan_AddTexture(VkSampler sampler, VkImageView image_view, VkImageLayout image_layout); - -// Optional: load Vulkan functions with a custom function loader -// This is only useful with IMGUI_IMPL_VULKAN_NO_PROTOTYPES / VK_NO_PROTOTYPES -IMGUI_IMPL_API bool ImGui_ImplVulkan_LoadFunctions(PFN_vkVoidFunction(*loader_func)(const char* function_name, void* user_data), void* user_data = NULL); +IMGUI_IMPL_API bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info, VkRenderPass render_pass); +IMGUI_IMPL_API void ImGui_ImplVulkan_Shutdown(); +IMGUI_IMPL_API void ImGui_ImplVulkan_NewFrame(); +IMGUI_IMPL_API void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer command_buffer); +IMGUI_IMPL_API bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer); +IMGUI_IMPL_API void ImGui_ImplVulkan_DestroyFontUploadObjects(); +IMGUI_IMPL_API void ImGui_ImplVulkan_SetMinImageCount(uint32_t min_image_count); // To override MinImageCount after initialization (e.g. if swap chain is recreated) +IMGUI_IMPL_API ImTextureID ImGui_ImplVulkan_AddTexture(VkSampler sampler, VkImageView image_view, VkImageLayout image_layout); + //------------------------------------------------------------------------- // Internal / Miscellaneous Vulkan Helpers @@ -88,7 +59,7 @@ IMGUI_IMPL_API bool ImGui_ImplVulkan_LoadFunctions(PFN_vkVoidFunction(*l // You probably do NOT need to use or care about those functions. // Those functions only exist because: // 1) they facilitate the readability and maintenance of the multiple main.cpp examples files. -// 2) the multi-viewport / platform window implementation needs them internally. +// 2) the upcoming multi-viewport feature will need them internally. // Generally we avoid exposing any kind of superfluous high-level helpers in the bindings, // but it is too much code to duplicate everywhere so we exceptionally expose them. // @@ -101,7 +72,7 @@ struct ImGui_ImplVulkanH_Frame; struct ImGui_ImplVulkanH_Window; // Helpers -IMGUI_IMPL_API void ImGui_ImplVulkanH_CreateOrResizeWindow(VkInstance instance, VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wnd, uint32_t queue_family, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count); +IMGUI_IMPL_API void ImGui_ImplVulkanH_CreateWindow(VkInstance instance, VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wnd, uint32_t queue_family, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count); IMGUI_IMPL_API void ImGui_ImplVulkanH_DestroyWindow(VkInstance instance, VkDevice device, ImGui_ImplVulkanH_Window* wnd, const VkAllocationCallbacks* allocator); IMGUI_IMPL_API VkSurfaceFormatKHR ImGui_ImplVulkanH_SelectSurfaceFormat(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkFormat* request_formats, int request_formats_count, VkColorSpaceKHR request_color_space); IMGUI_IMPL_API VkPresentModeKHR ImGui_ImplVulkanH_SelectPresentMode(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkPresentModeKHR* request_modes, int request_modes_count); @@ -137,7 +108,6 @@ struct ImGui_ImplVulkanH_Window VkSurfaceFormatKHR SurfaceFormat; VkPresentModeKHR PresentMode; VkRenderPass RenderPass; - VkPipeline Pipeline; // The window pipeline may uses a different VkRenderPass than the one passed in ImGui_ImplVulkan_InitInfo bool ClearEnable; VkClearValue ClearValue; uint32_t FrameIndex; // Current frame being rendered to (0 <= FrameIndex < FrameInFlightCount) @@ -148,9 +118,8 @@ struct ImGui_ImplVulkanH_Window ImGui_ImplVulkanH_Window() { - memset((void*)this, 0, sizeof(*this)); - PresentMode = (VkPresentModeKHR)~0; // Ensure we get an error if user doesn't set this. + memset(this, 0, sizeof(*this)); + PresentMode = VK_PRESENT_MODE_MAX_ENUM_KHR; ClearEnable = true; } }; - From d95ce46c48782e860424a2f4ce6f6048158870d3 Mon Sep 17 00:00:00 2001 From: Adam Foflonker <87928802+DnA-IntRicate@users.noreply.github.com> Date: Wed, 28 Sep 2022 16:22:05 +0200 Subject: [PATCH 824/828] Update imgui_impl_vulkan.cpp to match martty/imgui https://github.com/martty/imgui/blob/master/examples/imgui_impl_vulkan.cpp --- backends/imgui_impl_vulkan.cpp | 988 ++++++++------------------------- 1 file changed, 230 insertions(+), 758 deletions(-) diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp index 9a80a524c647..2de6b40301bd 100644 --- a/backends/imgui_impl_vulkan.cpp +++ b/backends/imgui_impl_vulkan.cpp @@ -1,46 +1,26 @@ -// dear imgui: Renderer Backend for Vulkan -// This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..) +// dear imgui: Renderer for Vulkan +// This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..) // Implemented features: -// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. -// [x] Renderer: Multi-viewport / platform windows. With issues (flickering when creating a new viewport). -// [!] Renderer: User texture binding. Use 'VkDescriptorSet' as ImTextureID. Read the FAQ about ImTextureID! See https://github.com/ocornut/imgui/pull/914 for discussions. - -// Important: on 32-bit systems, user texture binding is only supported if your imconfig file has '#define ImTextureID ImU64'. -// This is because we need ImTextureID to carry a 64-bit value and by default ImTextureID is defined as void*. -// To build this on 32-bit systems and support texture changes: -// - [Solution 1] IDE/msbuild: in "Properties/C++/Preprocessor Definitions" add 'ImTextureID=ImU64' (this is what we do in our .vcxproj files) -// - [Solution 2] IDE/msbuild: in "Properties/C++/Preprocessor Definitions" add 'IMGUI_USER_CONFIG="my_imgui_config.h"' and inside 'my_imgui_config.h' add '#define ImTextureID ImU64' and as many other options as you like. -// - [Solution 3] IDE/msbuild: edit imconfig.h and add '#define ImTextureID ImU64' (prefer solution 2 to create your own config file!) -// - [Solution 4] command-line: add '/D ImTextureID=ImU64' to your cl.exe command-line (this is what we do in our batch files) - -// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. -// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. -// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. -// Read online: https://github.com/ocornut/imgui/tree/master/docs +// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. +// In this binding, ImTextureID is used to store a 'VkDescriptorSet' texture identifier. Read the FAQ about ImTextureID in imgui.cpp. + +// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. +// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp. +// https://github.com/ocornut/imgui // The aim of imgui_impl_vulkan.h/.cpp is to be usable in your engine without any modification. // IF YOU FEEL YOU NEED TO MAKE ANY CHANGE TO THIS CODE, please share them and your feedback at https://github.com/ocornut/imgui/ // Important note to the reader who wish to integrate imgui_impl_vulkan.cpp/.h in their own engine/app. // - Common ImGui_ImplVulkan_XXX functions and structures are used to interface with imgui_impl_vulkan.cpp/.h. -// You will use those if you want to use this rendering backend in your engine/app. +// You will use those if you want to use this rendering back-end in your engine/app. // - Helper ImGui_ImplVulkanH_XXX functions and structures are only used by this example (main.cpp) and by -// the backend itself (imgui_impl_vulkan.cpp), but should PROBABLY NOT be used by your own engine/app code. +// the back-end itself (imgui_impl_vulkan.cpp), but should PROBABLY NOT be used by your own engine/app code. // Read comments in imgui_impl_vulkan.h. // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2022-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. -// 2021-10-15: Vulkan: Call vkCmdSetScissor() at the end of render a full-viewport to reduce likehood of issues with people using VK_DYNAMIC_STATE_SCISSOR in their app without calling vkCmdSetScissor() explicitly every frame. -// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). -// 2021-03-22: Vulkan: Fix mapped memory validation error when buffer sizes are not multiple of VkPhysicalDeviceLimits::nonCoherentAtomSize. -// 2021-02-18: Vulkan: Change blending equation to preserve alpha in output buffer. -// 2021-01-27: Vulkan: Added support for custom function load and IMGUI_IMPL_VULKAN_NO_PROTOTYPES by using ImGui_ImplVulkan_LoadFunctions(). -// 2020-11-11: Vulkan: Added support for specifying which subpass to reference during VkPipeline creation. -// 2020-09-07: Vulkan: Added VkPipeline parameter to ImGui_ImplVulkan_RenderDrawData (default to one passed to ImGui_ImplVulkan_Init). -// 2020-05-04: Vulkan: Fixed crash if initial frame has no vertices. -// 2020-04-26: Vulkan: Fixed edge case where render callbacks wouldn't be called if the ImDrawData didn't have vertices. // 2019-08-01: Vulkan: Added support for specifying multisample count. Set ImGui_ImplVulkan_InitInfo::MSAASamples to one of the VkSampleCountFlagBits values to use, default is non-multisampled as before. // 2019-05-29: Vulkan: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag. // 2019-04-30: Vulkan: Added support for special ImDrawCallback_ResetRenderState callback to reset render state. @@ -51,7 +31,7 @@ // 2019-02-16: Vulkan: Viewport and clipping rectangles correctly using draw_data->FramebufferScale to allow retina display. // 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window. // 2018-08-25: Vulkan: Fixed mishandled VkSurfaceCapabilitiesKHR::maxImageCount=0 case. -// 2018-06-22: Inverted the parameters to ImGui_ImplVulkan_RenderDrawData() to be consistent with other backends. +// 2018-06-22: Inverted the parameters to ImGui_ImplVulkan_RenderDrawData() to be consistent with other bindings. // 2018-06-08: Misc: Extracted imgui_impl_vulkan.cpp/.h away from the old combined GLFW+Vulkan example. // 2018-06-08: Vulkan: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle. // 2018-03-03: Vulkan: Various refactor, created a couple of ImGui_ImplVulkanH_XXX helper that the example can use and that viewport support will use. @@ -63,14 +43,10 @@ // 2016-10-18: Vulkan: Add location decorators & change to use structs as in/out in glsl, update embedded spv (produced with glslangValidator -x). Null the released resources. // 2016-08-27: Vulkan: Fix Vulkan example for use when a depth buffer is active. +#include "imgui.h" #include "imgui_impl_vulkan.h" #include -// Visual Studio warnings -#ifdef _MSC_VER -#pragma warning (disable: 4127) // condition expression is constant -#endif - // Reusable buffers used for rendering 1 current in-flight frame, for ImGui_ImplVulkan_RenderDrawData() // [Please zero-clear before use!] struct ImGui_ImplVulkanH_FrameRenderBuffers @@ -92,50 +68,25 @@ struct ImGui_ImplVulkanH_WindowRenderBuffers ImGui_ImplVulkanH_FrameRenderBuffers* FrameRenderBuffers; }; -// For multi-viewport support: -// Helper structure we store in the void* RenderUserData field of each ImGuiViewport to easily retrieve our backend data. -struct ImGui_ImplVulkan_ViewportData -{ - bool WindowOwned; - ImGui_ImplVulkanH_Window Window; // Used by secondary viewports only - ImGui_ImplVulkanH_WindowRenderBuffers RenderBuffers; // Used by all viewports - - ImGui_ImplVulkan_ViewportData() { WindowOwned = false; memset(&RenderBuffers, 0, sizeof(RenderBuffers)); } - ~ImGui_ImplVulkan_ViewportData() { } -}; - // Vulkan data -struct ImGui_ImplVulkan_Data -{ - ImGui_ImplVulkan_InitInfo VulkanInitInfo; - VkRenderPass RenderPass; - VkDeviceSize BufferMemoryAlignment; - VkPipelineCreateFlags PipelineCreateFlags; - VkDescriptorSetLayout DescriptorSetLayout; - VkPipelineLayout PipelineLayout; - VkPipeline Pipeline; - uint32_t Subpass; - VkShaderModule ShaderModuleVert; - VkShaderModule ShaderModuleFrag; - - // Font data - VkSampler FontSampler; - VkDeviceMemory FontMemory; - VkImage FontImage; - VkImageView FontView; - VkDescriptorSet FontDescriptorSet; - VkDeviceMemory UploadBufferMemory; - VkBuffer UploadBuffer; - - // Render buffers for main window - ImGui_ImplVulkanH_WindowRenderBuffers MainWindowRenderBuffers; - - ImGui_ImplVulkan_Data() - { - memset((void*)this, 0, sizeof(*this)); - BufferMemoryAlignment = 256; - } -}; +static ImGui_ImplVulkan_InitInfo g_VulkanInitInfo = {}; +static VkRenderPass g_RenderPass = VK_NULL_HANDLE; +static VkDeviceSize g_BufferMemoryAlignment = 256; +static VkPipelineCreateFlags g_PipelineCreateFlags = 0x00; +static VkDescriptorSetLayout g_DescriptorSetLayout = VK_NULL_HANDLE; +static VkPipelineLayout g_PipelineLayout = VK_NULL_HANDLE; +static VkPipeline g_Pipeline = VK_NULL_HANDLE; + +// Font data +static VkSampler g_FontSampler = VK_NULL_HANDLE; +static VkDeviceMemory g_FontMemory = VK_NULL_HANDLE; +static VkImage g_FontImage = VK_NULL_HANDLE; +static VkImageView g_FontView = VK_NULL_HANDLE; +static VkDeviceMemory g_UploadBufferMemory = VK_NULL_HANDLE; +static VkBuffer g_UploadBuffer = VK_NULL_HANDLE; + +// Render buffers +static ImGui_ImplVulkanH_WindowRenderBuffers g_MainWindowRenderBuffers; // Forward Declarations bool ImGui_ImplVulkan_CreateDeviceObjects(); @@ -144,103 +95,13 @@ void ImGui_ImplVulkanH_DestroyFrame(VkDevice device, ImGui_ImplVulkanH_Frame* fd void ImGui_ImplVulkanH_DestroyFrameSemaphores(VkDevice device, ImGui_ImplVulkanH_FrameSemaphores* fsd, const VkAllocationCallbacks* allocator); void ImGui_ImplVulkanH_DestroyFrameRenderBuffers(VkDevice device, ImGui_ImplVulkanH_FrameRenderBuffers* buffers, const VkAllocationCallbacks* allocator); void ImGui_ImplVulkanH_DestroyWindowRenderBuffers(VkDevice device, ImGui_ImplVulkanH_WindowRenderBuffers* buffers, const VkAllocationCallbacks* allocator); -void ImGui_ImplVulkanH_DestroyAllViewportsRenderBuffers(VkDevice device, const VkAllocationCallbacks* allocator); void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count); void ImGui_ImplVulkanH_CreateWindowCommandBuffers(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, uint32_t queue_family, const VkAllocationCallbacks* allocator); -// Vulkan prototypes for use with custom loaders -// (see description of IMGUI_IMPL_VULKAN_NO_PROTOTYPES in imgui_impl_vulkan.h -#ifdef VK_NO_PROTOTYPES -static bool g_FunctionsLoaded = false; -#else -static bool g_FunctionsLoaded = true; -#endif -#ifdef VK_NO_PROTOTYPES -#define IMGUI_VULKAN_FUNC_MAP(IMGUI_VULKAN_FUNC_MAP_MACRO) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkAllocateCommandBuffers) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkAllocateDescriptorSets) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkAllocateMemory) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkBindBufferMemory) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkBindImageMemory) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdBindDescriptorSets) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdBindIndexBuffer) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdBindPipeline) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdBindVertexBuffers) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdCopyBufferToImage) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdDrawIndexed) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdPipelineBarrier) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdPushConstants) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdSetScissor) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdSetViewport) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateBuffer) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateCommandPool) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateDescriptorSetLayout) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateFence) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateFramebuffer) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateGraphicsPipelines) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateImage) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateImageView) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreatePipelineLayout) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateRenderPass) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateSampler) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateSemaphore) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateShaderModule) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateSwapchainKHR) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyBuffer) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyCommandPool) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyDescriptorSetLayout) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyFence) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyFramebuffer) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyImage) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyImageView) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyPipeline) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyPipelineLayout) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyRenderPass) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroySampler) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroySemaphore) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyShaderModule) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroySurfaceKHR) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroySwapchainKHR) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkDeviceWaitIdle) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkFlushMappedMemoryRanges) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkFreeCommandBuffers) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkFreeMemory) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetBufferMemoryRequirements) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetImageMemoryRequirements) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetPhysicalDeviceMemoryProperties) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetPhysicalDeviceSurfaceCapabilitiesKHR) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetPhysicalDeviceSurfaceFormatsKHR) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetPhysicalDeviceSurfacePresentModesKHR) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetSwapchainImagesKHR) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkMapMemory) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkUnmapMemory) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkUpdateDescriptorSets) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetPhysicalDeviceSurfaceSupportKHR) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkWaitForFences) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdBeginRenderPass) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdEndRenderPass) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkQueuePresentKHR) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkBeginCommandBuffer) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkEndCommandBuffer) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkResetFences) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkQueueSubmit) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkResetCommandPool) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkAcquireNextImageKHR) - -// Define function pointers -#define IMGUI_VULKAN_FUNC_DEF(func) static PFN_##func func; -IMGUI_VULKAN_FUNC_MAP(IMGUI_VULKAN_FUNC_DEF) -#undef IMGUI_VULKAN_FUNC_DEF -#endif // VK_NO_PROTOTYPES - //----------------------------------------------------------------------------- // SHADERS //----------------------------------------------------------------------------- -// Forward Declarations -static void ImGui_ImplVulkan_InitPlatformInterface(); -static void ImGui_ImplVulkan_ShutdownPlatformInterface(); - // glsl_shader.vert, compiled with: // # glslangValidator -V -x -o glsl_shader.vert.u32 glsl_shader.vert /* @@ -350,47 +211,34 @@ static uint32_t __glsl_shader_frag_spv[] = // FUNCTIONS //----------------------------------------------------------------------------- -// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts -// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts. -// FIXME: multi-context support is not tested and probably dysfunctional in this backend. -static ImGui_ImplVulkan_Data* ImGui_ImplVulkan_GetBackendData() -{ - return ImGui::GetCurrentContext() ? (ImGui_ImplVulkan_Data*)ImGui::GetIO().BackendRendererUserData : NULL; -} - static uint32_t ImGui_ImplVulkan_MemoryType(VkMemoryPropertyFlags properties, uint32_t type_bits) { - ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); - ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; + ImGui_ImplVulkan_InitInfo* v = &g_VulkanInitInfo; VkPhysicalDeviceMemoryProperties prop; vkGetPhysicalDeviceMemoryProperties(v->PhysicalDevice, &prop); for (uint32_t i = 0; i < prop.memoryTypeCount; i++) - if ((prop.memoryTypes[i].propertyFlags & properties) == properties && type_bits & (1 << i)) + if ((prop.memoryTypes[i].propertyFlags & properties) == properties && type_bits & (1<VulkanInitInfo; + ImGui_ImplVulkan_InitInfo* v = &g_VulkanInitInfo; if (v->CheckVkResultFn) v->CheckVkResultFn(err); } static void CreateOrResizeBuffer(VkBuffer& buffer, VkDeviceMemory& buffer_memory, VkDeviceSize& p_buffer_size, size_t new_size, VkBufferUsageFlagBits usage) { - ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); - ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; + ImGui_ImplVulkan_InitInfo* v = &g_VulkanInitInfo; VkResult err; if (buffer != VK_NULL_HANDLE) vkDestroyBuffer(v->Device, buffer, v->Allocator); if (buffer_memory != VK_NULL_HANDLE) vkFreeMemory(v->Device, buffer_memory, v->Allocator); - VkDeviceSize vertex_buffer_size_aligned = ((new_size - 1) / bd->BufferMemoryAlignment + 1) * bd->BufferMemoryAlignment; + VkDeviceSize vertex_buffer_size_aligned = ((new_size - 1) / g_BufferMemoryAlignment + 1) * g_BufferMemoryAlignment; VkBufferCreateInfo buffer_info = {}; buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; buffer_info.size = vertex_buffer_size_aligned; @@ -401,7 +249,7 @@ static void CreateOrResizeBuffer(VkBuffer& buffer, VkDeviceMemory& buffer_memory VkMemoryRequirements req; vkGetBufferMemoryRequirements(v->Device, buffer, &req); - bd->BufferMemoryAlignment = (bd->BufferMemoryAlignment > req.alignment) ? bd->BufferMemoryAlignment : req.alignment; + g_BufferMemoryAlignment = (g_BufferMemoryAlignment > req.alignment) ? g_BufferMemoryAlignment : req.alignment; VkMemoryAllocateInfo alloc_info = {}; alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; alloc_info.allocationSize = req.size; @@ -411,20 +259,17 @@ static void CreateOrResizeBuffer(VkBuffer& buffer, VkDeviceMemory& buffer_memory err = vkBindBufferMemory(v->Device, buffer, buffer_memory, 0); check_vk_result(err); - p_buffer_size = req.size; + p_buffer_size = new_size; } -static void ImGui_ImplVulkan_SetupRenderState(ImDrawData* draw_data, VkPipeline pipeline, VkCommandBuffer command_buffer, ImGui_ImplVulkanH_FrameRenderBuffers* rb, int fb_width, int fb_height) +static void ImGui_ImplVulkan_SetupRenderState(ImDrawData* draw_data, VkCommandBuffer command_buffer, ImGui_ImplVulkanH_FrameRenderBuffers* rb, int fb_width, int fb_height) { - ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); - // Bind pipeline: { - vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, g_Pipeline); } // Bind Vertex And Index Buffer: - if (draw_data->TotalVtxCount > 0) { VkBuffer vertex_buffers[1] = { rb->VertexBuffer }; VkDeviceSize vertex_offset[1] = { 0 }; @@ -453,29 +298,25 @@ static void ImGui_ImplVulkan_SetupRenderState(ImDrawData* draw_data, VkPipeline float translate[2]; translate[0] = -1.0f - draw_data->DisplayPos.x * scale[0]; translate[1] = -1.0f - draw_data->DisplayPos.y * scale[1]; - vkCmdPushConstants(command_buffer, bd->PipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, sizeof(float) * 0, sizeof(float) * 2, scale); - vkCmdPushConstants(command_buffer, bd->PipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, sizeof(float) * 2, sizeof(float) * 2, translate); + vkCmdPushConstants(command_buffer, g_PipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, sizeof(float) * 0, sizeof(float) * 2, scale); + vkCmdPushConstants(command_buffer, g_PipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, sizeof(float) * 2, sizeof(float) * 2, translate); } } // Render function -void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer command_buffer, VkPipeline pipeline) +// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop) +void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer command_buffer) { // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) int fb_width = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x); int fb_height = (int)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y); - if (fb_width <= 0 || fb_height <= 0) + if (fb_width <= 0 || fb_height <= 0 || draw_data->TotalVtxCount == 0) return; - ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); - ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; - if (pipeline == VK_NULL_HANDLE) - pipeline = bd->Pipeline; + ImGui_ImplVulkan_InitInfo* v = &g_VulkanInitInfo; - // Allocate array to store enough vertex/index buffers. Each unique viewport gets its own storage. - ImGui_ImplVulkan_ViewportData* viewport_renderer_data = (ImGui_ImplVulkan_ViewportData*)draw_data->OwnerViewport->RendererUserData; - IM_ASSERT(viewport_renderer_data != NULL); - ImGui_ImplVulkanH_WindowRenderBuffers* wrb = &viewport_renderer_data->RenderBuffers; + // Allocate array to store enough vertex/index buffers + ImGui_ImplVulkanH_WindowRenderBuffers* wrb = &g_MainWindowRenderBuffers; if (wrb->FrameRenderBuffers == NULL) { wrb->Index = 0; @@ -487,22 +328,23 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm wrb->Index = (wrb->Index + 1) % wrb->Count; ImGui_ImplVulkanH_FrameRenderBuffers* rb = &wrb->FrameRenderBuffers[wrb->Index]; - if (draw_data->TotalVtxCount > 0) + VkResult err; + + // Create or resize the vertex/index buffers + size_t vertex_size = draw_data->TotalVtxCount * sizeof(ImDrawVert); + size_t index_size = draw_data->TotalIdxCount * sizeof(ImDrawIdx); + if (rb->VertexBuffer == VK_NULL_HANDLE || rb->VertexBufferSize < vertex_size) + CreateOrResizeBuffer(rb->VertexBuffer, rb->VertexBufferMemory, rb->VertexBufferSize, vertex_size, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT); + if (rb->IndexBuffer == VK_NULL_HANDLE || rb->IndexBufferSize < index_size) + CreateOrResizeBuffer(rb->IndexBuffer, rb->IndexBufferMemory, rb->IndexBufferSize, index_size, VK_BUFFER_USAGE_INDEX_BUFFER_BIT); + + // Upload vertex/index data into a single contiguous GPU buffer { - // Create or resize the vertex/index buffers - size_t vertex_size = draw_data->TotalVtxCount * sizeof(ImDrawVert); - size_t index_size = draw_data->TotalIdxCount * sizeof(ImDrawIdx); - if (rb->VertexBuffer == VK_NULL_HANDLE || rb->VertexBufferSize < vertex_size) - CreateOrResizeBuffer(rb->VertexBuffer, rb->VertexBufferMemory, rb->VertexBufferSize, vertex_size, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT); - if (rb->IndexBuffer == VK_NULL_HANDLE || rb->IndexBufferSize < index_size) - CreateOrResizeBuffer(rb->IndexBuffer, rb->IndexBufferMemory, rb->IndexBufferSize, index_size, VK_BUFFER_USAGE_INDEX_BUFFER_BIT); - - // Upload vertex/index data into a single contiguous GPU buffer ImDrawVert* vtx_dst = NULL; ImDrawIdx* idx_dst = NULL; - VkResult err = vkMapMemory(v->Device, rb->VertexBufferMemory, 0, rb->VertexBufferSize, 0, (void**)(&vtx_dst)); + err = vkMapMemory(v->Device, rb->VertexBufferMemory, 0, vertex_size, 0, (void**)(&vtx_dst)); check_vk_result(err); - err = vkMapMemory(v->Device, rb->IndexBufferMemory, 0, rb->IndexBufferSize, 0, (void**)(&idx_dst)); + err = vkMapMemory(v->Device, rb->IndexBufferMemory, 0, index_size, 0, (void**)(&idx_dst)); check_vk_result(err); for (int n = 0; n < draw_data->CmdListsCount; n++) { @@ -526,7 +368,7 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm } // Setup desired Vulkan state - ImGui_ImplVulkan_SetupRenderState(draw_data, pipeline, command_buffer, rb, fb_width, fb_height); + ImGui_ImplVulkan_SetupRenderState(draw_data, command_buffer, rb, fb_width, fb_height); // Will project scissor/clipping rectangles into framebuffer space ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports @@ -547,71 +389,58 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm // User callback, registered via ImDrawList::AddCallback() // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) - ImGui_ImplVulkan_SetupRenderState(draw_data, pipeline, command_buffer, rb, fb_width, fb_height); + ImGui_ImplVulkan_SetupRenderState(draw_data, command_buffer, rb, fb_width, fb_height); else pcmd->UserCallback(cmd_list, pcmd); } else { // Project scissor/clipping rectangles into framebuffer space - ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y); - ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y); - - // Clamp to viewport as vkCmdSetScissor() won't accept values that are off bounds - if (clip_min.x < 0.0f) { clip_min.x = 0.0f; } - if (clip_min.y < 0.0f) { clip_min.y = 0.0f; } - if (clip_max.x > fb_width) { clip_max.x = (float)fb_width; } - if (clip_max.y > fb_height) { clip_max.y = (float)fb_height; } - if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y) - continue; - - // Apply scissor/clipping rectangle - VkRect2D scissor; - scissor.offset.x = (int32_t)(clip_min.x); - scissor.offset.y = (int32_t)(clip_min.y); - scissor.extent.width = (uint32_t)(clip_max.x - clip_min.x); - scissor.extent.height = (uint32_t)(clip_max.y - clip_min.y); - vkCmdSetScissor(command_buffer, 0, 1, &scissor); - - // Bind DescriptorSet with font or user texture - VkDescriptorSet desc_set[1] = { (VkDescriptorSet)pcmd->TextureId }; - if (sizeof(ImTextureID) < sizeof(ImU64)) + ImVec4 clip_rect; + clip_rect.x = (pcmd->ClipRect.x - clip_off.x) * clip_scale.x; + clip_rect.y = (pcmd->ClipRect.y - clip_off.y) * clip_scale.y; + clip_rect.z = (pcmd->ClipRect.z - clip_off.x) * clip_scale.x; + clip_rect.w = (pcmd->ClipRect.w - clip_off.y) * clip_scale.y; + + if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f) { - // We don't support texture switches if ImTextureID hasn't been redefined to be 64-bit. Do a flaky check that other textures haven't been used. - IM_ASSERT(pcmd->TextureId == (ImTextureID)bd->FontDescriptorSet); - desc_set[0] = bd->FontDescriptorSet; + // Negative offsets are illegal for vkCmdSetScissor + if (clip_rect.x < 0.0f) + clip_rect.x = 0.0f; + if (clip_rect.y < 0.0f) + clip_rect.y = 0.0f; + + // Apply scissor/clipping rectangle + VkRect2D scissor; + scissor.offset.x = (int32_t)(clip_rect.x); + scissor.offset.y = (int32_t)(clip_rect.y); + scissor.extent.width = (uint32_t)(clip_rect.z - clip_rect.x); + scissor.extent.height = (uint32_t)(clip_rect.w - clip_rect.y); + vkCmdSetScissor(command_buffer, 0, 1, &scissor); + + // Bind descriptorset with font or user texture + VkDescriptorSet desc_set[1] = { (VkDescriptorSet)pcmd->TextureId }; + vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, g_PipelineLayout, 0, 1, desc_set, 0, NULL); + + // Draw + vkCmdDrawIndexed(command_buffer, pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset, 0); } - vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 0, 1, desc_set, 0, NULL); - - // Draw - vkCmdDrawIndexed(command_buffer, pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset, 0); } } global_idx_offset += cmd_list->IdxBuffer.Size; global_vtx_offset += cmd_list->VtxBuffer.Size; } - - // Note: at this point both vkCmdSetViewport() and vkCmdSetScissor() have been called. - // Our last values will leak into user/application rendering IF: - // - Your app uses a pipeline with VK_DYNAMIC_STATE_VIEWPORT or VK_DYNAMIC_STATE_SCISSOR dynamic state - // - And you forgot to call vkCmdSetViewport() and vkCmdSetScissor() yourself to explicitly set that state. - // If you use VK_DYNAMIC_STATE_VIEWPORT or VK_DYNAMIC_STATE_SCISSOR you are responsible for setting the values before rendering. - // In theory we should aim to backup/restore those values but I am not sure this is possible. - // We perform a call to vkCmdSetScissor() to set back a full viewport which is likely to fix things for 99% users but technically this is not perfect. (See github #4644) - VkRect2D scissor = { { 0, 0 }, { (uint32_t)fb_width, (uint32_t)fb_height } }; - vkCmdSetScissor(command_buffer, 0, 1, &scissor); } bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer) { + ImGui_ImplVulkan_InitInfo* v = &g_VulkanInitInfo; ImGuiIO& io = ImGui::GetIO(); - ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); - ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); - size_t upload_size = width * height * 4 * sizeof(char); + size_t upload_size = width*height*4*sizeof(char); VkResult err; @@ -631,17 +460,17 @@ bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer) info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - err = vkCreateImage(v->Device, &info, v->Allocator, &bd->FontImage); + err = vkCreateImage(v->Device, &info, v->Allocator, &g_FontImage); check_vk_result(err); VkMemoryRequirements req; - vkGetImageMemoryRequirements(v->Device, bd->FontImage, &req); + vkGetImageMemoryRequirements(v->Device, g_FontImage, &req); VkMemoryAllocateInfo alloc_info = {}; alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; alloc_info.allocationSize = req.size; alloc_info.memoryTypeIndex = ImGui_ImplVulkan_MemoryType(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, req.memoryTypeBits); - err = vkAllocateMemory(v->Device, &alloc_info, v->Allocator, &bd->FontMemory); + err = vkAllocateMemory(v->Device, &alloc_info, v->Allocator, &g_FontMemory); check_vk_result(err); - err = vkBindImageMemory(v->Device, bd->FontImage, bd->FontMemory, 0); + err = vkBindImageMemory(v->Device, g_FontImage, g_FontMemory, 0); check_vk_result(err); } @@ -649,18 +478,17 @@ bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer) { VkImageViewCreateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - info.image = bd->FontImage; + info.image = g_FontImage; info.viewType = VK_IMAGE_VIEW_TYPE_2D; info.format = VK_FORMAT_R8G8B8A8_UNORM; info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; info.subresourceRange.levelCount = 1; info.subresourceRange.layerCount = 1; - err = vkCreateImageView(v->Device, &info, v->Allocator, &bd->FontView); + err = vkCreateImageView(v->Device, &info, v->Allocator, &g_FontView); check_vk_result(err); } - // Create the Descriptor Set: - bd->FontDescriptorSet = (VkDescriptorSet)ImGui_ImplVulkan_AddTexture(bd->FontSampler, bd->FontView, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + VkDescriptorSet font_descriptor_set = (VkDescriptorSet)ImGui_ImplVulkan_AddTexture(g_FontSampler, g_FontView, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); // Create the Upload Buffer: { @@ -669,34 +497,34 @@ bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer) buffer_info.size = upload_size; buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - err = vkCreateBuffer(v->Device, &buffer_info, v->Allocator, &bd->UploadBuffer); + err = vkCreateBuffer(v->Device, &buffer_info, v->Allocator, &g_UploadBuffer); check_vk_result(err); VkMemoryRequirements req; - vkGetBufferMemoryRequirements(v->Device, bd->UploadBuffer, &req); - bd->BufferMemoryAlignment = (bd->BufferMemoryAlignment > req.alignment) ? bd->BufferMemoryAlignment : req.alignment; + vkGetBufferMemoryRequirements(v->Device, g_UploadBuffer, &req); + g_BufferMemoryAlignment = (g_BufferMemoryAlignment > req.alignment) ? g_BufferMemoryAlignment : req.alignment; VkMemoryAllocateInfo alloc_info = {}; alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; alloc_info.allocationSize = req.size; alloc_info.memoryTypeIndex = ImGui_ImplVulkan_MemoryType(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, req.memoryTypeBits); - err = vkAllocateMemory(v->Device, &alloc_info, v->Allocator, &bd->UploadBufferMemory); + err = vkAllocateMemory(v->Device, &alloc_info, v->Allocator, &g_UploadBufferMemory); check_vk_result(err); - err = vkBindBufferMemory(v->Device, bd->UploadBuffer, bd->UploadBufferMemory, 0); + err = vkBindBufferMemory(v->Device, g_UploadBuffer, g_UploadBufferMemory, 0); check_vk_result(err); } // Upload to Buffer: { char* map = NULL; - err = vkMapMemory(v->Device, bd->UploadBufferMemory, 0, upload_size, 0, (void**)(&map)); + err = vkMapMemory(v->Device, g_UploadBufferMemory, 0, upload_size, 0, (void**)(&map)); check_vk_result(err); memcpy(map, pixels, upload_size); VkMappedMemoryRange range[1] = {}; range[0].sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; - range[0].memory = bd->UploadBufferMemory; + range[0].memory = g_UploadBufferMemory; range[0].size = upload_size; err = vkFlushMappedMemoryRanges(v->Device, 1, range); check_vk_result(err); - vkUnmapMemory(v->Device, bd->UploadBufferMemory); + vkUnmapMemory(v->Device, g_UploadBufferMemory); } // Copy to Image: @@ -708,7 +536,7 @@ bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer) copy_barrier[0].newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; copy_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; copy_barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - copy_barrier[0].image = bd->FontImage; + copy_barrier[0].image = g_FontImage; copy_barrier[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; copy_barrier[0].subresourceRange.levelCount = 1; copy_barrier[0].subresourceRange.layerCount = 1; @@ -720,7 +548,7 @@ bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer) region.imageExtent.width = width; region.imageExtent.height = height; region.imageExtent.depth = 1; - vkCmdCopyBufferToImage(command_buffer, bd->UploadBuffer, bd->FontImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); + vkCmdCopyBufferToImage(command_buffer, g_UploadBuffer, g_FontImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); VkImageMemoryBarrier use_barrier[1] = {}; use_barrier[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; @@ -730,7 +558,7 @@ bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer) use_barrier[0].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; use_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; use_barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - use_barrier[0].image = bd->FontImage; + use_barrier[0].image = g_FontImage; use_barrier[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; use_barrier[0].subresourceRange.levelCount = 1; use_barrier[0].subresourceRange.layerCount = 1; @@ -738,114 +566,92 @@ bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer) } // Store our identifier - io.Fonts->SetTexID((ImTextureID)bd->FontDescriptorSet); + io.Fonts->TexID = (ImTextureID)font_descriptor_set; return true; } -static void ImGui_ImplVulkan_CreateShaderModules(VkDevice device, const VkAllocationCallbacks* allocator) +bool ImGui_ImplVulkan_CreateDeviceObjects() { - // Create the shader modules - ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); - if (bd->ShaderModuleVert == VK_NULL_HANDLE) + ImGui_ImplVulkan_InitInfo* v = &g_VulkanInitInfo; + VkResult err; + VkShaderModule vert_module; + VkShaderModule frag_module; + + // Create The Shader Modules: { VkShaderModuleCreateInfo vert_info = {}; vert_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; vert_info.codeSize = sizeof(__glsl_shader_vert_spv); vert_info.pCode = (uint32_t*)__glsl_shader_vert_spv; - VkResult err = vkCreateShaderModule(device, &vert_info, allocator, &bd->ShaderModuleVert); + err = vkCreateShaderModule(v->Device, &vert_info, v->Allocator, &vert_module); check_vk_result(err); - } - if (bd->ShaderModuleFrag == VK_NULL_HANDLE) - { VkShaderModuleCreateInfo frag_info = {}; frag_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; frag_info.codeSize = sizeof(__glsl_shader_frag_spv); frag_info.pCode = (uint32_t*)__glsl_shader_frag_spv; - VkResult err = vkCreateShaderModule(device, &frag_info, allocator, &bd->ShaderModuleFrag); + err = vkCreateShaderModule(v->Device, &frag_info, v->Allocator, &frag_module); check_vk_result(err); } -} -static void ImGui_ImplVulkan_CreateFontSampler(VkDevice device, const VkAllocationCallbacks* allocator) -{ - ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); - if (bd->FontSampler) - return; - - // Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling. - VkSamplerCreateInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; - info.magFilter = VK_FILTER_LINEAR; - info.minFilter = VK_FILTER_LINEAR; - info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; - info.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; - info.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; - info.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; - info.minLod = -1000; - info.maxLod = 1000; - info.maxAnisotropy = 1.0f; - VkResult err = vkCreateSampler(device, &info, allocator, &bd->FontSampler); - check_vk_result(err); -} - -static void ImGui_ImplVulkan_CreateDescriptorSetLayout(VkDevice device, const VkAllocationCallbacks* allocator) -{ - ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); - if (bd->DescriptorSetLayout) - return; - - ImGui_ImplVulkan_CreateFontSampler(device, allocator); - VkSampler sampler[1] = { bd->FontSampler }; - VkDescriptorSetLayoutBinding binding[1] = {}; - binding[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - binding[0].descriptorCount = 1; - binding[0].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; - binding[0].pImmutableSamplers = sampler; - VkDescriptorSetLayoutCreateInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; - info.bindingCount = 1; - info.pBindings = binding; - VkResult err = vkCreateDescriptorSetLayout(device, &info, allocator, &bd->DescriptorSetLayout); - check_vk_result(err); -} - -static void ImGui_ImplVulkan_CreatePipelineLayout(VkDevice device, const VkAllocationCallbacks* allocator) -{ - ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); - if (bd->PipelineLayout) - return; + if (!g_FontSampler) + { + VkSamplerCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + info.magFilter = VK_FILTER_LINEAR; + info.minFilter = VK_FILTER_LINEAR; + info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + info.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; + info.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; + info.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; + info.minLod = -1000; + info.maxLod = 1000; + info.maxAnisotropy = 1.0f; + err = vkCreateSampler(v->Device, &info, v->Allocator, &g_FontSampler); + check_vk_result(err); + } - // Constants: we are using 'vec2 offset' and 'vec2 scale' instead of a full 3d projection matrix - ImGui_ImplVulkan_CreateDescriptorSetLayout(device, allocator); - VkPushConstantRange push_constants[1] = {}; - push_constants[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; - push_constants[0].offset = sizeof(float) * 0; - push_constants[0].size = sizeof(float) * 4; - VkDescriptorSetLayout set_layout[1] = { bd->DescriptorSetLayout }; - VkPipelineLayoutCreateInfo layout_info = {}; - layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; - layout_info.setLayoutCount = 1; - layout_info.pSetLayouts = set_layout; - layout_info.pushConstantRangeCount = 1; - layout_info.pPushConstantRanges = push_constants; - VkResult err = vkCreatePipelineLayout(device, &layout_info, allocator, &bd->PipelineLayout); - check_vk_result(err); -} + if (!g_DescriptorSetLayout) + { + VkSampler sampler[1] = {g_FontSampler}; + VkDescriptorSetLayoutBinding binding[1] = {}; + binding[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + binding[0].descriptorCount = 1; + binding[0].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + VkDescriptorSetLayoutCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + info.bindingCount = 1; + info.pBindings = binding; + err = vkCreateDescriptorSetLayout(v->Device, &info, v->Allocator, &g_DescriptorSetLayout); + check_vk_result(err); + } -static void ImGui_ImplVulkan_CreatePipeline(VkDevice device, const VkAllocationCallbacks* allocator, VkPipelineCache pipelineCache, VkRenderPass renderPass, VkSampleCountFlagBits MSAASamples, VkPipeline* pipeline, uint32_t subpass) -{ - ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); - ImGui_ImplVulkan_CreateShaderModules(device, allocator); + if (!g_PipelineLayout) + { + // Constants: we are using 'vec2 offset' and 'vec2 scale' instead of a full 3d projection matrix + VkPushConstantRange push_constants[1] = {}; + push_constants[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + push_constants[0].offset = sizeof(float) * 0; + push_constants[0].size = sizeof(float) * 4; + VkDescriptorSetLayout set_layout[1] = { g_DescriptorSetLayout }; + VkPipelineLayoutCreateInfo layout_info = {}; + layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + layout_info.setLayoutCount = 1; + layout_info.pSetLayouts = set_layout; + layout_info.pushConstantRangeCount = 1; + layout_info.pPushConstantRanges = push_constants; + err = vkCreatePipelineLayout(v->Device, &layout_info, v->Allocator, &g_PipelineLayout); + check_vk_result(err); + } VkPipelineShaderStageCreateInfo stage[2] = {}; stage[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; stage[0].stage = VK_SHADER_STAGE_VERTEX_BIT; - stage[0].module = bd->ShaderModuleVert; + stage[0].module = vert_module; stage[0].pName = "main"; stage[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; stage[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; - stage[1].module = bd->ShaderModuleFrag; + stage[1].module = frag_module; stage[1].pName = "main"; VkVertexInputBindingDescription binding_desc[1] = {}; @@ -891,15 +697,18 @@ static void ImGui_ImplVulkan_CreatePipeline(VkDevice device, const VkAllocationC VkPipelineMultisampleStateCreateInfo ms_info = {}; ms_info.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; - ms_info.rasterizationSamples = (MSAASamples != 0) ? MSAASamples : VK_SAMPLE_COUNT_1_BIT; + if (v->MSAASamples != 0) + ms_info.rasterizationSamples = v->MSAASamples; + else + ms_info.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; VkPipelineColorBlendAttachmentState color_attachment[1] = {}; color_attachment[0].blendEnable = VK_TRUE; color_attachment[0].srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; color_attachment[0].dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; color_attachment[0].colorBlendOp = VK_BLEND_OP_ADD; - color_attachment[0].srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; - color_attachment[0].dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + color_attachment[0].srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + color_attachment[0].dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; color_attachment[0].alphaBlendOp = VK_BLEND_OP_ADD; color_attachment[0].colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; @@ -917,11 +726,9 @@ static void ImGui_ImplVulkan_CreatePipeline(VkDevice device, const VkAllocationC dynamic_state.dynamicStateCount = (uint32_t)IM_ARRAYSIZE(dynamic_states); dynamic_state.pDynamicStates = dynamic_states; - ImGui_ImplVulkan_CreatePipelineLayout(device, allocator); - VkGraphicsPipelineCreateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; - info.flags = bd->PipelineCreateFlags; + info.flags = g_PipelineCreateFlags; info.stageCount = 2; info.pStages = stage; info.pVertexInputState = &vertex_info; @@ -932,143 +739,53 @@ static void ImGui_ImplVulkan_CreatePipeline(VkDevice device, const VkAllocationC info.pDepthStencilState = &depth_info; info.pColorBlendState = &blend_info; info.pDynamicState = &dynamic_state; - info.layout = bd->PipelineLayout; - info.renderPass = renderPass; - info.subpass = subpass; - VkResult err = vkCreateGraphicsPipelines(device, pipelineCache, 1, &info, allocator, pipeline); + info.layout = g_PipelineLayout; + info.renderPass = g_RenderPass; + err = vkCreateGraphicsPipelines(v->Device, v->PipelineCache, 1, &info, v->Allocator, &g_Pipeline); check_vk_result(err); -} - -bool ImGui_ImplVulkan_CreateDeviceObjects() -{ - ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); - ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; - VkResult err; - - if (!bd->FontSampler) - { - VkSamplerCreateInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; - info.magFilter = VK_FILTER_LINEAR; - info.minFilter = VK_FILTER_LINEAR; - info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; - info.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; - info.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; - info.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; - info.minLod = -1000; - info.maxLod = 1000; - info.maxAnisotropy = 1.0f; - err = vkCreateSampler(v->Device, &info, v->Allocator, &bd->FontSampler); - check_vk_result(err); - } - - if (!bd->DescriptorSetLayout) - { - VkSampler sampler[1] = {bd->FontSampler}; - VkDescriptorSetLayoutBinding binding[1] = {}; - binding[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - binding[0].descriptorCount = 1; - binding[0].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; - binding[0].pImmutableSamplers = sampler; - VkDescriptorSetLayoutCreateInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; - info.bindingCount = 1; - info.pBindings = binding; - err = vkCreateDescriptorSetLayout(v->Device, &info, v->Allocator, &bd->DescriptorSetLayout); - check_vk_result(err); - } - - if (!bd->PipelineLayout) - { - // Constants: we are using 'vec2 offset' and 'vec2 scale' instead of a full 3d projection matrix - VkPushConstantRange push_constants[1] = {}; - push_constants[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; - push_constants[0].offset = sizeof(float) * 0; - push_constants[0].size = sizeof(float) * 4; - VkDescriptorSetLayout set_layout[1] = { bd->DescriptorSetLayout }; - VkPipelineLayoutCreateInfo layout_info = {}; - layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; - layout_info.setLayoutCount = 1; - layout_info.pSetLayouts = set_layout; - layout_info.pushConstantRangeCount = 1; - layout_info.pPushConstantRanges = push_constants; - err = vkCreatePipelineLayout(v->Device, &layout_info, v->Allocator, &bd->PipelineLayout); - check_vk_result(err); - } - ImGui_ImplVulkan_CreatePipeline(v->Device, v->Allocator, v->PipelineCache, bd->RenderPass, v->MSAASamples, &bd->Pipeline, bd->Subpass); + vkDestroyShaderModule(v->Device, vert_module, v->Allocator); + vkDestroyShaderModule(v->Device, frag_module, v->Allocator); return true; } void ImGui_ImplVulkan_DestroyFontUploadObjects() { - ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); - ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; - if (bd->UploadBuffer) + ImGui_ImplVulkan_InitInfo* v = &g_VulkanInitInfo; + if (g_UploadBuffer) { - vkDestroyBuffer(v->Device, bd->UploadBuffer, v->Allocator); - bd->UploadBuffer = VK_NULL_HANDLE; + vkDestroyBuffer(v->Device, g_UploadBuffer, v->Allocator); + g_UploadBuffer = VK_NULL_HANDLE; } - if (bd->UploadBufferMemory) + if (g_UploadBufferMemory) { - vkFreeMemory(v->Device, bd->UploadBufferMemory, v->Allocator); - bd->UploadBufferMemory = VK_NULL_HANDLE; + vkFreeMemory(v->Device, g_UploadBufferMemory, v->Allocator); + g_UploadBufferMemory = VK_NULL_HANDLE; } } void ImGui_ImplVulkan_DestroyDeviceObjects() { - ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); - ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; - ImGui_ImplVulkanH_DestroyAllViewportsRenderBuffers(v->Device, v->Allocator); + ImGui_ImplVulkan_InitInfo* v = &g_VulkanInitInfo; + ImGui_ImplVulkanH_DestroyWindowRenderBuffers(v->Device, &g_MainWindowRenderBuffers, v->Allocator); ImGui_ImplVulkan_DestroyFontUploadObjects(); - if (bd->ShaderModuleVert) { vkDestroyShaderModule(v->Device, bd->ShaderModuleVert, v->Allocator); bd->ShaderModuleVert = VK_NULL_HANDLE; } - if (bd->ShaderModuleFrag) { vkDestroyShaderModule(v->Device, bd->ShaderModuleFrag, v->Allocator); bd->ShaderModuleFrag = VK_NULL_HANDLE; } - if (bd->FontView) { vkDestroyImageView(v->Device, bd->FontView, v->Allocator); bd->FontView = VK_NULL_HANDLE; } - if (bd->FontImage) { vkDestroyImage(v->Device, bd->FontImage, v->Allocator); bd->FontImage = VK_NULL_HANDLE; } - if (bd->FontMemory) { vkFreeMemory(v->Device, bd->FontMemory, v->Allocator); bd->FontMemory = VK_NULL_HANDLE; } - if (bd->FontSampler) { vkDestroySampler(v->Device, bd->FontSampler, v->Allocator); bd->FontSampler = VK_NULL_HANDLE; } - if (bd->DescriptorSetLayout) { vkDestroyDescriptorSetLayout(v->Device, bd->DescriptorSetLayout, v->Allocator); bd->DescriptorSetLayout = VK_NULL_HANDLE; } - if (bd->PipelineLayout) { vkDestroyPipelineLayout(v->Device, bd->PipelineLayout, v->Allocator); bd->PipelineLayout = VK_NULL_HANDLE; } - if (bd->Pipeline) { vkDestroyPipeline(v->Device, bd->Pipeline, v->Allocator); bd->Pipeline = VK_NULL_HANDLE; } -} - -bool ImGui_ImplVulkan_LoadFunctions(PFN_vkVoidFunction(*loader_func)(const char* function_name, void* user_data), void* user_data) -{ - // Load function pointers - // You can use the default Vulkan loader using: - // ImGui_ImplVulkan_LoadFunctions([](const char* function_name, void*) { return vkGetInstanceProcAddr(your_vk_isntance, function_name); }); - // But this would be equivalent to not setting VK_NO_PROTOTYPES. -#ifdef VK_NO_PROTOTYPES -#define IMGUI_VULKAN_FUNC_LOAD(func) \ - func = reinterpret_cast(loader_func(#func, user_data)); \ - if (func == NULL) \ - return false; - IMGUI_VULKAN_FUNC_MAP(IMGUI_VULKAN_FUNC_LOAD) -#undef IMGUI_VULKAN_FUNC_LOAD -#else - IM_UNUSED(loader_func); - IM_UNUSED(user_data); -#endif - g_FunctionsLoaded = true; - return true; + if (g_FontView) { vkDestroyImageView(v->Device, g_FontView, v->Allocator); g_FontView = VK_NULL_HANDLE; } + if (g_FontImage) { vkDestroyImage(v->Device, g_FontImage, v->Allocator); g_FontImage = VK_NULL_HANDLE; } + if (g_FontMemory) { vkFreeMemory(v->Device, g_FontMemory, v->Allocator); g_FontMemory = VK_NULL_HANDLE; } + if (g_FontSampler) { vkDestroySampler(v->Device, g_FontSampler, v->Allocator); g_FontSampler = VK_NULL_HANDLE; } + if (g_DescriptorSetLayout) { vkDestroyDescriptorSetLayout(v->Device, g_DescriptorSetLayout, v->Allocator); g_DescriptorSetLayout = VK_NULL_HANDLE; } + if (g_PipelineLayout) { vkDestroyPipelineLayout(v->Device, g_PipelineLayout, v->Allocator); g_PipelineLayout = VK_NULL_HANDLE; } + if (g_Pipeline) { vkDestroyPipeline(v->Device, g_Pipeline, v->Allocator); g_Pipeline = VK_NULL_HANDLE; } } bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info, VkRenderPass render_pass) { - IM_ASSERT(g_FunctionsLoaded && "Need to call ImGui_ImplVulkan_LoadFunctions() if IMGUI_IMPL_VULKAN_NO_PROTOTYPES or VK_NO_PROTOTYPES are set!"); - + // Setup back-end capabilities flags ImGuiIO& io = ImGui::GetIO(); - IM_ASSERT(io.BackendRendererUserData == NULL && "Already initialized a renderer backend!"); - - // Setup backend capabilities flags - ImGui_ImplVulkan_Data* bd = IM_NEW(ImGui_ImplVulkan_Data)(); - io.BackendRendererUserData = (void*)bd; io.BackendRendererName = "imgui_impl_vulkan"; io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. - io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional) IM_ASSERT(info->Instance != VK_NULL_HANDLE); IM_ASSERT(info->PhysicalDevice != VK_NULL_HANDLE); @@ -1079,103 +796,35 @@ bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info, VkRenderPass rend IM_ASSERT(info->ImageCount >= info->MinImageCount); IM_ASSERT(render_pass != VK_NULL_HANDLE); - bd->VulkanInitInfo = *info; - bd->RenderPass = render_pass; - bd->Subpass = info->Subpass; - + g_VulkanInitInfo = *info; + g_RenderPass = render_pass; ImGui_ImplVulkan_CreateDeviceObjects(); - // Our render function expect RendererUserData to be storing the window render buffer we need (for the main viewport we won't use ->Window) - ImGuiViewport* main_viewport = ImGui::GetMainViewport(); - main_viewport->RendererUserData = IM_NEW(ImGui_ImplVulkan_ViewportData)(); - - if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) - ImGui_ImplVulkan_InitPlatformInterface(); - return true; } void ImGui_ImplVulkan_Shutdown() { - ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); - IM_ASSERT(bd != NULL && "No renderer backend to shutdown, or already shutdown?"); - ImGuiIO& io = ImGui::GetIO(); - - // First destroy objects in all viewports ImGui_ImplVulkan_DestroyDeviceObjects(); - - // Manually delete main viewport render data in-case we haven't initialized for viewports - ImGuiViewport* main_viewport = ImGui::GetMainViewport(); - if (ImGui_ImplVulkan_ViewportData* vd = (ImGui_ImplVulkan_ViewportData*)main_viewport->RendererUserData) - IM_DELETE(vd); - main_viewport->RendererUserData = NULL; - - // Clean up windows - ImGui_ImplVulkan_ShutdownPlatformInterface(); - - io.BackendRendererName = NULL; - io.BackendRendererUserData = NULL; - IM_DELETE(bd); } void ImGui_ImplVulkan_NewFrame() { - ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); - IM_ASSERT(bd != NULL && "Did you call ImGui_ImplVulkan_Init()?"); - IM_UNUSED(bd); } void ImGui_ImplVulkan_SetMinImageCount(uint32_t min_image_count) { - ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); IM_ASSERT(min_image_count >= 2); - if (bd->VulkanInitInfo.MinImageCount == min_image_count) + if (g_VulkanInitInfo.MinImageCount == min_image_count) return; - IM_ASSERT(0); // FIXME-VIEWPORT: Unsupported. Need to recreate all swap chains! - ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; + ImGui_ImplVulkan_InitInfo* v = &g_VulkanInitInfo; VkResult err = vkDeviceWaitIdle(v->Device); check_vk_result(err); - ImGui_ImplVulkanH_DestroyAllViewportsRenderBuffers(v->Device, v->Allocator); - - bd->VulkanInitInfo.MinImageCount = min_image_count; + ImGui_ImplVulkanH_DestroyWindowRenderBuffers(v->Device, &g_MainWindowRenderBuffers, v->Allocator); + g_VulkanInitInfo.MinImageCount = min_image_count; } -// Register a texture -// FIXME: This is experimental in the sense that we are unsure how to best design/tackle this problem, please post to https://github.com/ocornut/imgui/pull/914 if you have suggestions. -VkDescriptorSet ImGui_ImplVulkan_AddTexture(VkSampler sampler, VkImageView image_view, VkImageLayout image_layout) -{ - ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); - ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; - - // Create Descriptor Set: - VkDescriptorSet descriptor_set; - { - VkDescriptorSetAllocateInfo alloc_info = {}; - alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; - alloc_info.descriptorPool = v->DescriptorPool; - alloc_info.descriptorSetCount = 1; - alloc_info.pSetLayouts = &bd->DescriptorSetLayout; - VkResult err = vkAllocateDescriptorSets(v->Device, &alloc_info, &descriptor_set); - check_vk_result(err); - } - - // Update the Descriptor Set: - { - VkDescriptorImageInfo desc_image[1] = {}; - desc_image[0].sampler = sampler; - desc_image[0].imageView = image_view; - desc_image[0].imageLayout = image_layout; - VkWriteDescriptorSet write_desc[1] = {}; - write_desc[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - write_desc[0].dstSet = descriptor_set; - write_desc[0].descriptorCount = 1; - write_desc[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - write_desc[0].pImageInfo = desc_image; - vkUpdateDescriptorSets(v->Device, 1, write_desc, 0, NULL); - } - return descriptor_set; -} //------------------------------------------------------------------------- // Internal / Miscellaneous Vulkan Helpers @@ -1185,7 +834,7 @@ VkDescriptorSet ImGui_ImplVulkan_AddTexture(VkSampler sampler, VkImageView image // Those functions only exist because: // 1) they facilitate the readability and maintenance of the multiple main.cpp examples files. // 2) the upcoming multi-viewport feature will need them internally. -// Generally we avoid exposing any kind of superfluous high-level helpers in the backends, +// Generally we avoid exposing any kind of superfluous high-level helpers in the bindings, // but it is too much code to duplicate everywhere so we exceptionally expose them. // // Your engine/app will likely _already_ have code to setup all that stuff (swap chain, render pass, frame buffers, etc.). @@ -1195,7 +844,6 @@ VkDescriptorSet ImGui_ImplVulkan_AddTexture(VkSampler sampler, VkImageView image VkSurfaceFormatKHR ImGui_ImplVulkanH_SelectSurfaceFormat(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkFormat* request_formats, int request_formats_count, VkColorSpaceKHR request_color_space) { - IM_ASSERT(g_FunctionsLoaded && "Need to call ImGui_ImplVulkan_LoadFunctions() if IMGUI_IMPL_VULKAN_NO_PROTOTYPES or VK_NO_PROTOTYPES are set!"); IM_ASSERT(request_formats != NULL); IM_ASSERT(request_formats_count > 0); @@ -1240,7 +888,6 @@ VkSurfaceFormatKHR ImGui_ImplVulkanH_SelectSurfaceFormat(VkPhysicalDevice physic VkPresentModeKHR ImGui_ImplVulkanH_SelectPresentMode(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkPresentModeKHR* request_modes, int request_modes_count) { - IM_ASSERT(g_FunctionsLoaded && "Need to call ImGui_ImplVulkan_LoadFunctions() if IMGUI_IMPL_VULKAN_NO_PROTOTYPES or VK_NO_PROTOTYPES are set!"); IM_ASSERT(request_modes != NULL); IM_ASSERT(request_modes_count > 0); @@ -1325,7 +972,6 @@ void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, V { VkResult err; VkSwapchainKHR old_swapchain = wd->Swapchain; - wd->Swapchain = VK_NULL_HANDLE; err = vkDeviceWaitIdle(device); check_vk_result(err); @@ -1343,8 +989,6 @@ void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, V wd->ImageCount = 0; if (wd->RenderPass) vkDestroyRenderPass(device, wd->RenderPass, allocator); - if (wd->Pipeline) - vkDestroyPipeline(device, wd->Pipeline, allocator); // If min image count was not specified, request different count of images dependent on selected present mode if (min_image_count == 0) @@ -1440,10 +1084,6 @@ void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, V info.pDependencies = &dependency; err = vkCreateRenderPass(device, &info, allocator, &wd->RenderPass); check_vk_result(err); - - // We do not create a pipeline by default as this is also used by examples' main.cpp, - // but secondary viewport in multi-viewport mode may want to create one with: - //ImGui_ImplVulkan_CreatePipeline(device, allocator, VK_NULL_HANDLE, wd->RenderPass, VK_SAMPLE_COUNT_1_BIT, &wd->Pipeline, bd->Subpass); } // Create The Image Views @@ -1488,20 +1128,17 @@ void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, V } } -// Create or resize window -void ImGui_ImplVulkanH_CreateOrResizeWindow(VkInstance instance, VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, uint32_t queue_family, const VkAllocationCallbacks* allocator, int width, int height, uint32_t min_image_count) +void ImGui_ImplVulkanH_CreateWindow(VkInstance instance, VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, uint32_t queue_family, const VkAllocationCallbacks* allocator, int width, int height, uint32_t min_image_count) { - IM_ASSERT(g_FunctionsLoaded && "Need to call ImGui_ImplVulkan_LoadFunctions() if IMGUI_IMPL_VULKAN_NO_PROTOTYPES or VK_NO_PROTOTYPES are set!"); (void)instance; ImGui_ImplVulkanH_CreateWindowSwapChain(physical_device, device, wd, allocator, width, height, min_image_count); - //ImGui_ImplVulkan_CreatePipeline(device, allocator, VK_NULL_HANDLE, wd->RenderPass, VK_SAMPLE_COUNT_1_BIT, &wd->Pipeline, g_VulkanInitInfo.Subpass); ImGui_ImplVulkanH_CreateWindowCommandBuffers(physical_device, device, wd, queue_family, allocator); } void ImGui_ImplVulkanH_DestroyWindow(VkInstance instance, VkDevice device, ImGui_ImplVulkanH_Window* wd, const VkAllocationCallbacks* allocator) { vkDeviceWaitIdle(device); // FIXME: We could wait on the Queue if we had the queue in wd-> (otherwise VulkanH functions can't use globals) - //vkQueueWaitIdle(bd->Queue); + //vkQueueWaitIdle(g_Queue); for (uint32_t i = 0; i < wd->ImageCount; i++) { @@ -1512,7 +1149,6 @@ void ImGui_ImplVulkanH_DestroyWindow(VkInstance instance, VkDevice device, ImGui IM_FREE(wd->FrameSemaphores); wd->Frames = NULL; wd->FrameSemaphores = NULL; - vkDestroyPipeline(device, wd->Pipeline, allocator); vkDestroyRenderPass(device, wd->RenderPass, allocator); vkDestroySwapchainKHR(device, wd->Swapchain, allocator); vkDestroySurfaceKHR(instance, wd->Surface, allocator); @@ -1560,200 +1196,36 @@ void ImGui_ImplVulkanH_DestroyWindowRenderBuffers(VkDevice device, ImGui_ImplVul buffers->Count = 0; } -void ImGui_ImplVulkanH_DestroyAllViewportsRenderBuffers(VkDevice device, const VkAllocationCallbacks* allocator) -{ - ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); - for (int n = 0; n < platform_io.Viewports.Size; n++) - if (ImGui_ImplVulkan_ViewportData* vd = (ImGui_ImplVulkan_ViewportData*)platform_io.Viewports[n]->RendererUserData) - ImGui_ImplVulkanH_DestroyWindowRenderBuffers(device, &vd->RenderBuffers, allocator); -} - -//-------------------------------------------------------------------------------------------------------- -// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT -// This is an _advanced_ and _optional_ feature, allowing the backend to create and handle multiple viewports simultaneously. -// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first.. -//-------------------------------------------------------------------------------------------------------- - -static void ImGui_ImplVulkan_CreateWindow(ImGuiViewport* viewport) -{ - ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); - ImGui_ImplVulkan_ViewportData* vd = IM_NEW(ImGui_ImplVulkan_ViewportData)(); - viewport->RendererUserData = vd; - ImGui_ImplVulkanH_Window* wd = &vd->Window; - ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; - - // Create surface - ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); - VkResult err = (VkResult)platform_io.Platform_CreateVkSurface(viewport, (ImU64)v->Instance, (const void*)v->Allocator, (ImU64*)&wd->Surface); - check_vk_result(err); - - // Check for WSI support - VkBool32 res; - vkGetPhysicalDeviceSurfaceSupportKHR(v->PhysicalDevice, v->QueueFamily, wd->Surface, &res); - if (res != VK_TRUE) - { - IM_ASSERT(0); // Error: no WSI support on physical device - return; - } - - // Select Surface Format - const VkFormat requestSurfaceImageFormat[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM }; - const VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; - wd->SurfaceFormat = ImGui_ImplVulkanH_SelectSurfaceFormat(v->PhysicalDevice, wd->Surface, requestSurfaceImageFormat, (size_t)IM_ARRAYSIZE(requestSurfaceImageFormat), requestSurfaceColorSpace); - - // Select Present Mode - // FIXME-VULKAN: Even thought mailbox seems to get us maximum framerate with a single window, it halves framerate with a second window etc. (w/ Nvidia and SDK 1.82.1) - VkPresentModeKHR present_modes[] = { VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_FIFO_KHR }; - wd->PresentMode = ImGui_ImplVulkanH_SelectPresentMode(v->PhysicalDevice, wd->Surface, &present_modes[0], IM_ARRAYSIZE(present_modes)); - //printf("[vulkan] Secondary window selected PresentMode = %d\n", wd->PresentMode); - - // Create SwapChain, RenderPass, Framebuffer, etc. - wd->ClearEnable = (viewport->Flags & ImGuiViewportFlags_NoRendererClear) ? false : true; - ImGui_ImplVulkanH_CreateOrResizeWindow(v->Instance, v->PhysicalDevice, v->Device, wd, v->QueueFamily, v->Allocator, (int)viewport->Size.x, (int)viewport->Size.y, v->MinImageCount); - vd->WindowOwned = true; -} - -static void ImGui_ImplVulkan_DestroyWindow(ImGuiViewport* viewport) -{ - // The main viewport (owned by the application) will always have RendererUserData == NULL since we didn't create the data for it. - ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); - if (ImGui_ImplVulkan_ViewportData* vd = (ImGui_ImplVulkan_ViewportData*)viewport->RendererUserData) - { - ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; - if (vd->WindowOwned) - ImGui_ImplVulkanH_DestroyWindow(v->Instance, v->Device, &vd->Window, v->Allocator); - ImGui_ImplVulkanH_DestroyWindowRenderBuffers(v->Device, &vd->RenderBuffers, v->Allocator); - IM_DELETE(vd); - } - viewport->RendererUserData = NULL; -} - -static void ImGui_ImplVulkan_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) -{ - ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); - ImGui_ImplVulkan_ViewportData* vd = (ImGui_ImplVulkan_ViewportData*)viewport->RendererUserData; - if (vd == NULL) // This is NULL for the main viewport (which is left to the user/app to handle) - return; - ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; - vd->Window.ClearEnable = (viewport->Flags & ImGuiViewportFlags_NoRendererClear) ? false : true; - ImGui_ImplVulkanH_CreateOrResizeWindow(v->Instance, v->PhysicalDevice, v->Device, &vd->Window, v->QueueFamily, v->Allocator, (int)size.x, (int)size.y, v->MinImageCount); -} - -static void ImGui_ImplVulkan_RenderWindow(ImGuiViewport* viewport, void*) -{ - ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); - ImGui_ImplVulkan_ViewportData* vd = (ImGui_ImplVulkan_ViewportData*)viewport->RendererUserData; - ImGui_ImplVulkanH_Window* wd = &vd->Window; - ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; +ImTextureID ImGui_ImplVulkan_AddTexture(VkSampler sampler, VkImageView image_view, VkImageLayout image_layout){ VkResult err; - ImGui_ImplVulkanH_Frame* fd = &wd->Frames[wd->FrameIndex]; - ImGui_ImplVulkanH_FrameSemaphores* fsd = &wd->FrameSemaphores[wd->SemaphoreIndex]; + ImGui_ImplVulkan_InitInfo* v = &g_VulkanInitInfo; + VkDescriptorSet descriptor_set; + // Create Descriptor Set: { - { - err = vkAcquireNextImageKHR(v->Device, wd->Swapchain, UINT64_MAX, fsd->ImageAcquiredSemaphore, VK_NULL_HANDLE, &wd->FrameIndex); - check_vk_result(err); - fd = &wd->Frames[wd->FrameIndex]; - } - for (;;) - { - err = vkWaitForFences(v->Device, 1, &fd->Fence, VK_TRUE, 100); - if (err == VK_SUCCESS) break; - if (err == VK_TIMEOUT) continue; - check_vk_result(err); - } - { - err = vkResetCommandPool(v->Device, fd->CommandPool, 0); - check_vk_result(err); - VkCommandBufferBeginInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - err = vkBeginCommandBuffer(fd->CommandBuffer, &info); - check_vk_result(err); - } - { - ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); - memcpy(&wd->ClearValue.color.float32[0], &clear_color, 4 * sizeof(float)); - - VkRenderPassBeginInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; - info.renderPass = wd->RenderPass; - info.framebuffer = fd->Framebuffer; - info.renderArea.extent.width = wd->Width; - info.renderArea.extent.height = wd->Height; - info.clearValueCount = (viewport->Flags & ImGuiViewportFlags_NoRendererClear) ? 0 : 1; - info.pClearValues = (viewport->Flags & ImGuiViewportFlags_NoRendererClear) ? NULL : &wd->ClearValue; - vkCmdBeginRenderPass(fd->CommandBuffer, &info, VK_SUBPASS_CONTENTS_INLINE); - } + VkDescriptorSetAllocateInfo alloc_info = {}; + alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + alloc_info.descriptorPool = v->DescriptorPool; + alloc_info.descriptorSetCount = 1; + alloc_info.pSetLayouts = &g_DescriptorSetLayout; + err = vkAllocateDescriptorSets(v->Device, &alloc_info, &descriptor_set); + check_vk_result(err); } - ImGui_ImplVulkan_RenderDrawData(viewport->DrawData, fd->CommandBuffer, wd->Pipeline); - + // Update the Descriptor Set: { - vkCmdEndRenderPass(fd->CommandBuffer); - { - VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - VkSubmitInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - info.waitSemaphoreCount = 1; - info.pWaitSemaphores = &fsd->ImageAcquiredSemaphore; - info.pWaitDstStageMask = &wait_stage; - info.commandBufferCount = 1; - info.pCommandBuffers = &fd->CommandBuffer; - info.signalSemaphoreCount = 1; - info.pSignalSemaphores = &fsd->RenderCompleteSemaphore; - - err = vkEndCommandBuffer(fd->CommandBuffer); - check_vk_result(err); - err = vkResetFences(v->Device, 1, &fd->Fence); - check_vk_result(err); - err = vkQueueSubmit(v->Queue, 1, &info, fd->Fence); - check_vk_result(err); - } + VkDescriptorImageInfo desc_image[1] = {}; + desc_image[0].sampler = sampler; + desc_image[0].imageView = image_view; + desc_image[0].imageLayout = image_layout; + VkWriteDescriptorSet write_desc[1] = {}; + write_desc[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + write_desc[0].dstSet = descriptor_set; + write_desc[0].descriptorCount = 1; + write_desc[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + write_desc[0].pImageInfo = desc_image; + vkUpdateDescriptorSets(v->Device, 1, write_desc, 0, NULL); } -} - -static void ImGui_ImplVulkan_SwapBuffers(ImGuiViewport* viewport, void*) -{ - ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); - ImGui_ImplVulkan_ViewportData* vd = (ImGui_ImplVulkan_ViewportData*)viewport->RendererUserData; - ImGui_ImplVulkanH_Window* wd = &vd->Window; - ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; - VkResult err; - uint32_t present_index = wd->FrameIndex; - - ImGui_ImplVulkanH_FrameSemaphores* fsd = &wd->FrameSemaphores[wd->SemaphoreIndex]; - VkPresentInfoKHR info = {}; - info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; - info.waitSemaphoreCount = 1; - info.pWaitSemaphores = &fsd->RenderCompleteSemaphore; - info.swapchainCount = 1; - info.pSwapchains = &wd->Swapchain; - info.pImageIndices = &present_index; - err = vkQueuePresentKHR(v->Queue, &info); - if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) - ImGui_ImplVulkanH_CreateOrResizeWindow(v->Instance, v->PhysicalDevice, v->Device, &vd->Window, v->QueueFamily, v->Allocator, (int)viewport->Size.x, (int)viewport->Size.y, v->MinImageCount); - else - check_vk_result(err); - - wd->FrameIndex = (wd->FrameIndex + 1) % wd->ImageCount; // This is for the next vkWaitForFences() - wd->SemaphoreIndex = (wd->SemaphoreIndex + 1) % wd->ImageCount; // Now we can use the next set of semaphores -} - -void ImGui_ImplVulkan_InitPlatformInterface() -{ - ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); - if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) - IM_ASSERT(platform_io.Platform_CreateVkSurface != NULL && "Platform needs to setup the CreateVkSurface handler."); - platform_io.Renderer_CreateWindow = ImGui_ImplVulkan_CreateWindow; - platform_io.Renderer_DestroyWindow = ImGui_ImplVulkan_DestroyWindow; - platform_io.Renderer_SetWindowSize = ImGui_ImplVulkan_SetWindowSize; - platform_io.Renderer_RenderWindow = ImGui_ImplVulkan_RenderWindow; - platform_io.Renderer_SwapBuffers = ImGui_ImplVulkan_SwapBuffers; -} - -void ImGui_ImplVulkan_ShutdownPlatformInterface() -{ - ImGui::DestroyPlatformWindows(); + return (ImTextureID)descriptor_set; } From 69beaa1d0b7fc8f4b448dcf1780b08cfc959da65 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 29 Sep 2022 20:00:29 +0200 Subject: [PATCH 825/828] Viewports: Fix AddMouseViewportEvent() to honor AppAcceptingEvents, filter duplicate, add to debug log. --- imgui.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/imgui.cpp b/imgui.cpp index 4dc9e5519e3d..1bf5468e5b23 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1508,6 +1508,14 @@ void ImGuiIO::AddMouseViewportEvent(ImGuiID viewport_id) ImGuiContext& g = *GImGui; IM_ASSERT(&g.IO == this && "Can only add events to current context."); IM_ASSERT(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport); + if (!AppAcceptingEvents) + return; + + // Filter duplicate + const ImGuiInputEvent* latest_event = FindLatestInputEvent(ImGuiInputEventType_MouseViewport); + const ImGuiID latest_viewport_id = latest_event ? latest_event->MouseViewport.HoveredViewportID : g.IO.MouseHoveredViewport; + if (latest_viewport_id == viewport_id) + return; ImGuiInputEvent e; e.Type = ImGuiInputEventType_MouseViewport; @@ -8728,6 +8736,7 @@ static void DebugPrintInputEvent(const char* prefix, const ImGuiInputEvent* e) if (e->Type == ImGuiInputEventType_MousePos) { if (e->MousePos.PosX == -FLT_MAX && e->MousePos.PosY == -FLT_MAX) IMGUI_DEBUG_LOG_IO("%s: MousePos (-FLT_MAX, -FLT_MAX)\n", prefix); else IMGUI_DEBUG_LOG_IO("%s: MousePos (%.1f, %.1f)\n", prefix, e->MousePos.PosX, e->MousePos.PosY); return; } if (e->Type == ImGuiInputEventType_MouseButton) { IMGUI_DEBUG_LOG_IO("%s: MouseButton %d %s\n", prefix, e->MouseButton.Button, e->MouseButton.Down ? "Down" : "Up"); return; } if (e->Type == ImGuiInputEventType_MouseWheel) { IMGUI_DEBUG_LOG_IO("%s: MouseWheel (%.1f, %.1f)\n", prefix, e->MouseWheel.WheelX, e->MouseWheel.WheelY); return; } + if (e->Type == ImGuiInputEventType_MouseViewport){IMGUI_DEBUG_LOG_IO("%s: MouseViewport (0x%08X)\n", prefix, e->MouseViewport.HoveredViewportID); return; } if (e->Type == ImGuiInputEventType_Key) { IMGUI_DEBUG_LOG_IO("%s: Key \"%s\" %s\n", prefix, ImGui::GetKeyName(e->Key.Key), e->Key.Down ? "Down" : "Up"); return; } if (e->Type == ImGuiInputEventType_Text) { IMGUI_DEBUG_LOG_IO("%s: Text: %c (U+%08X)\n", prefix, e->Text.Char, e->Text.Char); return; } if (e->Type == ImGuiInputEventType_Focus) { IMGUI_DEBUG_LOG_IO("%s: AppFocused %d\n", prefix, e->AppFocused.Focused); return; } From fb0b9c7160514b1c524c0c98fcf8bac2be4c7f3a Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 3 Oct 2022 14:55:22 +0200 Subject: [PATCH 826/828] Docking: Fixed missing highlight when using dock node host window borders. (#5702) Amend 8f434874, 9764adc7b, 24dfebf45 --- imgui.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 1bf5468e5b23..a158950043d4 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -15563,7 +15563,8 @@ static bool IsDockNodeTitleBarHighlighted(ImGuiDockNode* node, ImGuiDockNode* ro ImGuiWindow* parent_window = g.NavWindow->RootWindow; while (parent_window->Flags & ImGuiWindowFlags_ChildMenu) parent_window = parent_window->ParentWindow->RootWindow; - for (ImGuiDockNode* parent_node = parent_window->DockNode; parent_node != NULL; parent_node = parent_node->HostWindow ? parent_node->HostWindow->RootWindow->DockNode : NULL) + ImGuiDockNode* start_parent_node = parent_window->DockNodeAsHost ? parent_window->DockNodeAsHost : parent_window->DockNode; + for (ImGuiDockNode* parent_node = start_parent_node; parent_node != NULL; parent_node = parent_node->HostWindow ? parent_node->HostWindow->RootWindow->DockNode : NULL) if ((parent_node = ImGui::DockNodeGetRootNode(parent_node)) == root_node) return true; } From cb04326b460b38e4db5b9638be70b315ec23ca0f Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 4 Oct 2022 18:00:42 +0200 Subject: [PATCH 827/828] Fixed ImGuiWindowFlags_UnsavedDocument clipping label in docked windows with no close button. [changes for docking] (#5745) + TabBar: starts displaying the unsaved document marker with a frame delay to match how close button is processed, otherwise the transition would be noticeable. --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 6 +++--- imgui_internal.h | 3 ++- imgui_widgets.cpp | 18 ++++++++++++------ 4 files changed, 19 insertions(+), 10 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 838546c45c2e..e2347b1d1429 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -952,6 +952,8 @@ Docking+Viewports Branch: (#4292, #3834, #3633, #3521, #3492, #3335, #2999, #2648) - Docking: (Internal/Experimental) Removed DockNodeFlagsOverrideClear flags from ImGuiWindowClass as it is ambiguous how to apply them and we haven't got a use out of them yet. +- Docking: Fixed ImGuiWindowFlags_UnsavedDocument clipping label in docked windows when there are + no close button. (#5745) - Viewports: Fix popup/tooltip created without a parent window from being given a ParentViewportId value from the implicit/fallback window. (#4236, #2409) - Backends: Vulkan: Fix the use of the incorrect fence for secondary viewports. (#4208) [@FunMiles] diff --git a/imgui.cpp b/imgui.cpp index a158950043d4..deb8adddc8dd 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -16158,11 +16158,11 @@ static void ImGui::DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDock if (!host_node->IsHiddenTabBar() && !host_node->IsNoTabBar()) tab_pos.x += host_node->TabBar->WidthAllTabs + g.Style.ItemInnerSpacing.x; // We don't use OffsetNewTab because when using non-persistent-order tab bar it is incremented with each Tab submission. else - tab_pos.x += g.Style.ItemInnerSpacing.x + TabItemCalcSize(host_node->Windows[0]->Name, host_node->Windows[0]->HasCloseButton).x; + tab_pos.x += g.Style.ItemInnerSpacing.x + TabItemCalcSize(host_node->Windows[0]).x; } else if (!(host_window->Flags & ImGuiWindowFlags_DockNodeHost)) { - tab_pos.x += g.Style.ItemInnerSpacing.x + TabItemCalcSize(host_window->Name, host_window->HasCloseButton).x; // Account for slight offset which will be added when changing from title bar to tab bar + tab_pos.x += g.Style.ItemInnerSpacing.x + TabItemCalcSize(host_window).x; // Account for slight offset which will be added when changing from title bar to tab bar } // Draw tab shape/label preview (payload may be a loose window or a host window carrying multiple tabbed windows) @@ -16180,7 +16180,7 @@ static void ImGui::DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDock continue; // Calculate the tab bounding box for each payload window - ImVec2 tab_size = TabItemCalcSize(payload_window->Name, payload_window->HasCloseButton); + ImVec2 tab_size = TabItemCalcSize(payload_window); ImRect tab_bb(tab_pos.x, tab_pos.y, tab_pos.x + tab_size.x, tab_pos.y + tab_size.y); tab_pos.x += tab_size.x + g.Style.ItemInnerSpacing.x; const ImU32 overlay_col_text = GetColorU32(payload_window->DockStyle.Colors[ImGuiWindowDockStyleCol_Text]); diff --git a/imgui_internal.h b/imgui_internal.h index e0f86141393e..dc4853292159 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -3118,7 +3118,8 @@ namespace ImGui IMGUI_API void TabBarQueueReorderFromMousePos(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, ImVec2 mouse_pos); IMGUI_API bool TabBarProcessReorder(ImGuiTabBar* tab_bar); IMGUI_API bool TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags, ImGuiWindow* docked_window); - IMGUI_API ImVec2 TabItemCalcSize(const char* label, bool has_close_button); + IMGUI_API ImVec2 TabItemCalcSize(const char* label, bool has_close_button_or_unsaved_marker); + IMGUI_API ImVec2 TabItemCalcSize(ImGuiWindow* window); IMGUI_API void TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImU32 col); IMGUI_API void TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImVec2 frame_padding, const char* label, ImGuiID tab_id, ImGuiID close_button_id, bool is_contents_visible, bool* out_just_closed, bool* out_text_clipped); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 2baa16aa3e87..30413be7d10b 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7599,8 +7599,8 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) // Additionally, when using TabBarAddTab() to manipulate tab bar order we occasionally insert new tabs that don't have a width yet, // and we cannot wait for the next BeginTabItem() call. We cannot compute this width within TabBarAddTab() because font size depends on the active window. const char* tab_name = tab_bar->GetTabName(tab); - const bool has_close_button = (tab->Flags & ImGuiTabItemFlags_NoCloseButton) ? false : true; - tab->ContentWidth = (tab->RequestedWidth >= 0.0f) ? tab->RequestedWidth : TabItemCalcSize(tab_name, has_close_button).x; + const bool has_close_button_or_unsaved_marker = (tab->Flags & ImGuiTabItemFlags_NoCloseButton) == 0 || (tab->Flags & ImGuiTabItemFlags_UnsavedDocument); + tab->ContentWidth = (tab->RequestedWidth >= 0.0f) ? tab->RequestedWidth : TabItemCalcSize(tab_name, has_close_button_or_unsaved_marker).x; int section_n = TabItemGetSectionIdx(tab); ImGuiTabBarSection* section = §ions[section_n]; @@ -8168,7 +8168,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, tab_bar->LastTabItemIdx = (ImS16)tab_bar->Tabs.index_from_ptr(tab); // Calculate tab contents size - ImVec2 size = TabItemCalcSize(label, p_open != NULL); + ImVec2 size = TabItemCalcSize(label, (p_open != NULL) || (flags & ImGuiTabItemFlags_UnsavedDocument)); tab->RequestedWidth = -1.0f; if (g.NextItemData.Flags & ImGuiNextItemDataFlags_HasWidth) size.x = tab->RequestedWidth = g.NextItemData.Width; @@ -8180,6 +8180,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, const bool tab_bar_appearing = (tab_bar->PrevFrameVisible + 1 < g.FrameCount); const bool tab_bar_focused = (tab_bar->Flags & ImGuiTabBarFlags_IsFocused) != 0; const bool tab_appearing = (tab->LastFrameVisible + 1 < g.FrameCount); + const bool tab_just_unsaved = (flags & ImGuiTabItemFlags_UnsavedDocument) && !(tab->Flags & ImGuiTabItemFlags_UnsavedDocument); const bool is_tab_button = (flags & ImGuiTabItemFlags_Button) != 0; tab->LastFrameVisible = g.FrameCount; tab->Flags = flags; @@ -8374,7 +8375,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, const ImGuiID close_button_id = p_open ? GetIDWithSeed("#CLOSE", NULL, docked_window ? docked_window->ID : id) : 0; bool just_closed; bool text_clipped; - TabItemLabelAndCloseButton(display_draw_list, bb, flags, tab_bar->FramePadding, label, id, close_button_id, tab_contents_visible, &just_closed, &text_clipped); + TabItemLabelAndCloseButton(display_draw_list, bb, tab_just_unsaved ? (flags & ~ImGuiTabItemFlags_UnsavedDocument) : flags, tab_bar->FramePadding, label, id, close_button_id, tab_contents_visible, &just_closed, &text_clipped); if (just_closed && p_open != NULL) { *p_open = false; @@ -8433,18 +8434,23 @@ void ImGui::SetTabItemClosed(const char* label) } } -ImVec2 ImGui::TabItemCalcSize(const char* label, bool has_close_button) +ImVec2 ImGui::TabItemCalcSize(const char* label, bool has_close_button_or_unsaved_marker) { ImGuiContext& g = *GImGui; ImVec2 label_size = CalcTextSize(label, NULL, true); ImVec2 size = ImVec2(label_size.x + g.Style.FramePadding.x, label_size.y + g.Style.FramePadding.y * 2.0f); - if (has_close_button) + if (has_close_button_or_unsaved_marker) size.x += g.Style.FramePadding.x + (g.Style.ItemInnerSpacing.x + g.FontSize); // We use Y intentionally to fit the close button circle. else size.x += g.Style.FramePadding.x + 1.0f; return ImVec2(ImMin(size.x, TabBarCalcMaxTabWidth()), size.y); } +ImVec2 ImGui::TabItemCalcSize(ImGuiWindow* window) +{ + return TabItemCalcSize(window->Name, window->HasCloseButton || (window->Flags & ImGuiWindowFlags_UnsavedDocument)); +} + void ImGui::TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImU32 col) { // While rendering tabs, we trim 1 pixel off the top of our bounding box so they can fit within a regular frame height while looking "detached" from it. From d4dfec3f93fa5c0d1246380fc5aee8bebb8cbac2 Mon Sep 17 00:00:00 2001 From: Adam Foflonker <87928802+DnA-IntRicate@users.noreply.github.com> Date: Wed, 5 Oct 2022 09:09:29 +0200 Subject: [PATCH 828/828] Fix for Vulkan validation layer warnings Fix from: https://github.com/TheCherno/imgui/pull/15/commits/add065f24887a84de146d8ce32894e4f88c1e9fd# https://github.com/ocornut/imgui/pull/3957 --- backends/imgui_impl_vulkan.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp index 2de6b40301bd..0b4b2663d6b3 100644 --- a/backends/imgui_impl_vulkan.cpp +++ b/backends/imgui_impl_vulkan.cpp @@ -259,7 +259,7 @@ static void CreateOrResizeBuffer(VkBuffer& buffer, VkDeviceMemory& buffer_memory err = vkBindBufferMemory(v->Device, buffer, buffer_memory, 0); check_vk_result(err); - p_buffer_size = new_size; + p_buffer_size = req.size; } static void ImGui_ImplVulkan_SetupRenderState(ImDrawData* draw_data, VkCommandBuffer command_buffer, ImGui_ImplVulkanH_FrameRenderBuffers* rb, int fb_width, int fb_height) @@ -342,9 +342,9 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm { ImDrawVert* vtx_dst = NULL; ImDrawIdx* idx_dst = NULL; - err = vkMapMemory(v->Device, rb->VertexBufferMemory, 0, vertex_size, 0, (void**)(&vtx_dst)); + err = vkMapMemory(v->Device, rb->VertexBufferMemory, 0, rb->VertexBufferSize, 0, (void**)(&vtx_dst)); check_vk_result(err); - err = vkMapMemory(v->Device, rb->IndexBufferMemory, 0, index_size, 0, (void**)(&idx_dst)); + err = vkMapMemory(v->Device, rb->IndexBufferMemory, 0, rb->IndexBufferSize, 0, (void**)(&idx_dst)); check_vk_result(err); for (int n = 0; n < draw_data->CmdListsCount; n++) {